%-----------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %-----------------------------------------------------------------------------% % Copyright (C) 2002-2012 The University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %-----------------------------------------------------------------------------% % % File: compile_target_code.m. % Main authors: fjh, stayl. % % Code to compile the generated `.c', `.s', `.o', etc, files. % %-----------------------------------------------------------------------------% :- module backend_libs.compile_target_code. :- interface. :- import_module libs. :- import_module libs.globals. :- import_module libs.file_util. :- import_module parse_tree. :- import_module parse_tree.module_imports. :- import_module mdbcomp. :- import_module mdbcomp.sym_name. :- import_module bool. :- import_module io. :- import_module list. :- import_module maybe. %-----------------------------------------------------------------------------% % Are we generating position independent code (for use in a shared % library)? On some architectures, pic and non-pic code are incompatible, % so we need to generate `.o' and `.pic_o' files. % :- type pic ---> pic ; non_pic. % compile_c_file(Globals, ErrorStream, PIC, ModuleName, Succeeded, !IO) % :- pred compile_c_file(globals::in, io.output_stream::in, pic::in, module_name::in, bool::out, io::di, io::uo) is det. % do_compile_c_file(Globals, ErrorStream, PIC, CFile, ObjFile, Succeeded, % !IO) % :- pred do_compile_c_file(globals::in, io.output_stream::in, pic::in, string::in, string::in, bool::out, io::di, io::uo) is det. % compile_java_files(Globals, ErrorStream, JavaFiles, Succeeded, !IO) % :- pred compile_java_files(globals::in, io.output_stream::in, list(string)::in, bool::out, io::di, io::uo) is det. % compile_csharp_file(Globals, ErrorStream, CSharpFile, DLLFile, Succeeded, % !IO) % :- pred compile_csharp_file(globals::in, io.output_stream::in, module_and_imports::in, file_name::in, file_name::in, bool::out, io::di, io::uo) is det. % compile_erlang_file(Globals, ErrorStream, ErlangFile, Succeeded, !IO) % :- pred compile_erlang_file(globals::in, io.output_stream::in, file_name::in, bool::out, io::di, io::uo) is det. % make_library_init_file(Globals, ErrorStream, MainModuleName, ModuleNames, % Succeeded, !IO): % % Make the `.init' file for a library containing the given modules. % :- pred make_library_init_file(globals::in, io.output_stream::in, module_name::in, list(module_name)::in, bool::out, io::di, io::uo) is det. % make_erlang_init_library(Globals, ErrorStream, MainModuleName, % ModuleNames, Succeeded, !IO): % % Make the `.init' file for an Erlang library containing the given % modules. % :- pred make_erlang_library_init_file(globals::in, io.output_stream::in, module_name::in, list(module_name)::in, bool::out, io::di, io::uo) is det. % make_init_obj_file(Globals, ErrorStream, MainModuleName, AllModuleNames, % MaybeInitObjFileName) % :- pred make_init_obj_file(globals::in, io.output_stream::in, module_name::in, list(module_name)::in, maybe(file_name)::out, io::di, io::uo) is det. % make_erlang_program_init_file(Globals, ErrorStream, MainModuleName, % AllModuleNames, MaybeInitObjFileName, !IO) % :- pred make_erlang_program_init_file(globals::in, io.output_stream::in, module_name::in, list(module_name)::in, maybe(file_name)::out, io::di, io::uo) is det. % link_module_list(ModulesToLink, ExtraObjFiles, Globals, Succeeded, !IO): % % The elements of ModulesToLink are the output of % `module_name_to_filename(ModuleName, "", no, ModuleToLink)' % for each module in the program. % % The Globals are supplied late to allow mercury_compile.m to partially % apply the ModuleToLink and ExtraObjFiles arguments. % :- pred link_module_list(list(string)::in, list(string)::in, globals::in, bool::out, io::di, io::uo) is det. :- type linked_target_type ---> executable ; static_library ; shared_library ; csharp_executable ; csharp_library ; java_executable ; java_archive ; erlang_launcher ; erlang_archive. % link(Globals, TargetType, MainModuleName, ObjectFileNames, Succeeded, % !IO) % :- pred link(globals::in, io.output_stream::in, linked_target_type::in, module_name::in, list(string)::in, bool::out, io::di, io::uo) is det. % post_link_make_symlink_or_copy(TargetType, MainModuleName, % Globals, Succeeded, MadeSymlinkOrCopy, !IO) % % If `--use-grade-subdirs' is enabled, link or copy the executable or % library into the user's directory after having successfully built it, % if the target does not exist or is not up-to-date. % :- pred post_link_make_symlink_or_copy(globals::in, io.output_stream::in, linked_target_type::in, module_name::in, bool::out, bool::out, io::di, io::uo) is det. % shared_libraries_supported(Globals, SharedLibsSupported) % % Return whether or not shared libraries are supported on the current % platform. % :- pred shared_libraries_supported(globals::in, bool::out) is det. % get_object_code_type(Globals, TargetType, PIC): % % Work out whether we should be generating position-independent % object code. % :- pred get_object_code_type(globals::in, linked_target_type::in, pic::out) is det. % get_linked_target_type(Globals, LinkedTargetType): % % Work out whether we should be generating an executable or a shared % object. % :- pred get_linked_target_type(globals::in, linked_target_type::out) is det. %-----------------------------------------------------------------------------% % make_all_module_command(Globals, CommandName, MainModule, AllModuleNames, % CommandString, !IO): % % Create a command string which passes the source file names % for AllModuleNames to CommandName, with MainModule given first. % :- pred make_all_module_command(globals::in, string::in, module_name::in, list(module_name)::in, string::out, io::di, io::uo) is det. %-----------------------------------------------------------------------------% % maybe_pic_object_file_extension(Globals, PIC, Ext) is true iff % Ext is the extension which should be used on object files according to % the value of PIC. The value of PIC should be obtained from a call to % `get_object_code_type'. In particular, on architectures for which % no special handling for PIC is necessary, only a value of `non_pic' % should be used. The `(in, out, in)' mode guarantees that the returned % value of PIC meets this requirement. % :- pred maybe_pic_object_file_extension(globals, pic, string). :- mode maybe_pic_object_file_extension(in, in, out) is det. :- mode maybe_pic_object_file_extension(in, out, in) is semidet. %-----------------------------------------------------------------------------% % % Stuff used for standalone interfaces. % % make_standalone_interface(Globals, Basename, !IO): % % Create a standalone interface in the current directory. % :- pred make_standalone_interface(globals::in, string::in, io::di, io::uo) is det. % Output the C compiler flags to the given stream. % This predicate is used to implement the `--output-cflags' option. % :- pred output_c_compiler_flags(globals::in, io.output_stream::in, io::di, io::uo) is det. % Output the C compiler flags that define the macros used to specify the % current compilation grade to the given stream. % This predicate is used to implement the `--output-grade-defines' option. % :- pred output_grade_defines(globals::in, io.output_stream::in, io::di, io::uo) is det. % Output the C compiler flags that specify where the C compiler should % search for header files to the given stream. % This predicate is used to implement the `--output-c-include-dir-flags' % option. % :- pred output_c_include_directory_flags(globals::in, io.output_stream::in, io::di, io::uo) is det. % Output the list of flags required to link against the selected set % of Mercury libraries (the standard libraries, plus any other specified % via the --ml option) in the current grade. % This predicate is used to implement the `--output-library-link-flags' % option. % :- pred output_library_link_flags(globals::in, io.output_stream::in, io::di, io::uo) is det. %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% :- implementation. :- import_module libs.compute_grade. :- import_module libs.options. :- import_module libs.trace_params. :- import_module parse_tree.error_util. :- import_module parse_tree.file_names. :- import_module parse_tree.module_cmds. :- import_module parse_tree.prog_data_foreign. :- import_module parse_tree.prog_foreign. :- import_module dir. :- import_module getopt_io. :- import_module multi_map. :- import_module require. :- import_module set. :- import_module string. %-----------------------------------------------------------------------------% % WARNING: The code here duplicates the functionality of scripts/mgnuc.in. % Any changes there may also require changes here, and vice versa. compile_c_file(Globals, ErrorStream, PIC, ModuleName, Succeeded, !IO) :- module_name_to_file_name(Globals, do_create_dirs, ".c", ModuleName, C_File, !IO), maybe_pic_object_file_extension(Globals, PIC, ObjExt), module_name_to_file_name(Globals, do_create_dirs, ObjExt, ModuleName, O_File, !IO), do_compile_c_file(Globals, ErrorStream, PIC, C_File, O_File, Succeeded, !IO). do_compile_c_file(Globals, ErrorStream, PIC, C_File, O_File, Succeeded, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), globals.lookup_string_option(Globals, c_flag_to_name_object_file, NameObjectFile), maybe_write_string(Verbose, "% Compiling `", !IO), maybe_write_string(Verbose, C_File, !IO), maybe_write_string(Verbose, "':\n", !IO), globals.lookup_string_option(Globals, cc, CC), gather_c_compiler_flags(Globals, PIC, AllCFlags), string.append_list([ CC, " ", AllCFlags, " -c ", quote_arg(C_File), " ", NameObjectFile, quote_arg(O_File)], Command), get_maybe_filtercc_command(Globals, MaybeFilterCmd), invoke_system_command_maybe_filter_output(Globals, ErrorStream, cmd_verbose_commands, Command, MaybeFilterCmd, Succeeded, !IO). :- pred gather_c_compiler_flags(globals::in, pic::in, string::out) is det. gather_c_compiler_flags(Globals, PIC, AllCFlags) :- globals.lookup_accumulating_option(Globals, cflags, C_Flags_List), join_string_list(C_Flags_List, "", "", " ", CFLAGS), gather_compiler_specific_flags(Globals, CC_Specific_CFLAGS), globals.lookup_bool_option(Globals, use_subdirs, UseSubdirs), ( UseSubdirs = yes, % The source file (foo.c) will be compiled in a subdirectory % (either Mercury/cs, foo.dir, or Mercury/dirs/foo.dir, depending % on which of these two options is set) so we need to add `-I.' % so it can include header files in the source directory. SubDirInclOpt = "-I. " ; UseSubdirs = no, SubDirInclOpt = "" ), gather_c_include_dir_flags(Globals, InclOpt), get_framework_directories(Globals, FrameworkInclOpt), gather_grade_defines(Globals, GradeDefinesOpts), globals.lookup_bool_option(Globals, gcc_global_registers, GCC_Regs), ( GCC_Regs = yes, globals.lookup_string_option(Globals, cflags_for_regs, CFLAGS_FOR_REGS) ; GCC_Regs = no, CFLAGS_FOR_REGS = "" ), globals.lookup_bool_option(Globals, gcc_non_local_gotos, GCC_Gotos), ( GCC_Gotos = yes, globals.lookup_string_option(Globals, cflags_for_gotos, CFLAGS_FOR_GOTOS) ; GCC_Gotos = no, CFLAGS_FOR_GOTOS = "" ), globals.lookup_bool_option(Globals, parallel, Parallel), ( Parallel = yes, globals.lookup_string_option(Globals, cflags_for_threads, CFLAGS_FOR_THREADS) ; Parallel = no, CFLAGS_FOR_THREADS = "" ), ( PIC = pic, globals.lookup_string_option(Globals, cflags_for_pic, CFLAGS_FOR_PIC) ; PIC = non_pic, CFLAGS_FOR_PIC = "" ), globals.lookup_bool_option(Globals, target_debug, Target_Debug), ( Target_Debug = yes, globals.lookup_string_option(Globals, cflags_for_debug, Target_DebugOpt0), Target_DebugOpt = Target_DebugOpt0 ++ " " ; Target_Debug = no, Target_DebugOpt = "" ), globals.lookup_string_option(Globals, cflags_for_sanitizers, SanitizerOpts), globals.lookup_bool_option(Globals, use_trail, UseTrail), ( UseTrail = yes, % With tagged trail entries function trailing will not work unless the % C functions stored on the trail are aligned on word boundaries (or a % multiple thereof). The assemblers on some systems, and some gcc % optimisation settings, do not align functions, so we need to % explicitly pass -falign-functions in trailing grades to ensure that % C functions are appropriately aligned. % % Note that this will also affect the untagged version of the trail, % but that shouldn't matter. % globals.get_c_compiler_type(Globals, C_CompilerType), ( C_CompilerType = cc_gcc(_, _, _), globals.lookup_int_option(Globals, bytes_per_word, BytesPerWord), C_FnAlignOpt = string.format("-falign-functions=%d ", [i(BytesPerWord)]) ; % XXX Check whether we need to do anything for these C compilers? ( C_CompilerType = cc_clang(_) ; C_CompilerType = cc_cl(_) ), C_FnAlignOpt = "" ; C_CompilerType = cc_unknown, C_FnAlignOpt = "" ) ; UseTrail = no, C_FnAlignOpt = "" ), globals.lookup_bool_option(Globals, type_layout, TypeLayoutOption), ( TypeLayoutOption = no, TypeLayoutOpt = "-DMR_NO_TYPE_LAYOUT " ; TypeLayoutOption = yes, TypeLayoutOpt = "" ), globals.lookup_bool_option(Globals, c_optimize, C_optimize), ( C_optimize = yes, globals.lookup_string_option(Globals, cflags_for_optimization, OptimizeOpt) ; C_optimize = no, OptimizeOpt = "" ), globals.lookup_bool_option(Globals, ansi_c, Ansi), ( Ansi = yes, globals.lookup_string_option(Globals, cflags_for_ansi, AnsiOpt) ; Ansi = no, AnsiOpt = "" ), globals.lookup_bool_option(Globals, inline_alloc, InlineAlloc), ( InlineAlloc = yes, % XXX disabled because inline allocation is broken in gc7.0 alpha6. % InlineAllocOpt = "-DMR_INLINE_ALLOC " InlineAllocOpt = "" ; InlineAlloc = no, InlineAllocOpt = "" ), globals.lookup_bool_option(Globals, warn_target_code, Warn), ( Warn = yes, globals.lookup_string_option(Globals, cflags_for_warnings, WarningOpt) ; Warn = no, WarningOpt = "" ), % The -floop-optimize option is incompatible with the global % register code we generate on Darwin PowerPC. % See the hard_coded/ppc_bug test case for an example % program which fails with this optimization. globals.lookup_string_option(Globals, target_arch, TargetArch), ( if globals.lookup_bool_option(Globals, highlevel_code, no), globals.lookup_bool_option(Globals, gcc_global_registers, yes), string.prefix(TargetArch, "powerpc-apple-darwin") then AppleGCCRegWorkaroundOpt = "-fno-loop-optimize " else AppleGCCRegWorkaroundOpt = "" ), % Workaround performance problem(s) with gcc that causes the C files % generated in debugging grades to compile very slowly at -O1 and above. % (Changes here need to be reflected in scripts/mgnuc.in.) ( if globals.lookup_bool_option(Globals, exec_trace, yes), arch_is_apple_darwin(TargetArch) then OverrideOpts = "-O0" else OverrideOpts = "" ), % Be careful with the order here! Some options override others, % e.g. CFLAGS_FOR_REGS must come after OptimizeOpt so that % it can override -fomit-frame-pointer with -fno-omit-frame-pointer. % Also be careful that each option is separated by spaces. % % In general, user supplied C compiler flags, i.e. CFLAGS and % CC_Specific_CFLAGS below, should be able to override those introduced by % the Mercury compiler. % In some circumstances we want to prevent the user doing this, typically % where we know the behaviour of a particular C compiler is buggy; the % last option, OverrideOpts, does this -- because of this it must be % listed after CFLAGS and CC_Specific_CFLAGS. % string.append_list([ SubDirInclOpt, InclOpt, " ", FrameworkInclOpt, " ", OptimizeOpt, " ", GradeDefinesOpts, CFLAGS_FOR_REGS, " ", CFLAGS_FOR_GOTOS, " ", CFLAGS_FOR_THREADS, " ", CFLAGS_FOR_PIC, " ", Target_DebugOpt, SanitizerOpts, " ", TypeLayoutOpt, InlineAllocOpt, AnsiOpt, " ", AppleGCCRegWorkaroundOpt, C_FnAlignOpt, WarningOpt, " ", CFLAGS, " ", CC_Specific_CFLAGS, " ", OverrideOpts], AllCFlags). %-----------------------------------------------------------------------------% :- pred gather_grade_defines(globals::in, string::out) is det. gather_grade_defines(Globals, GradeDefines) :- globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode), ( HighLevelCode = yes, HighLevelCodeOpt = "-DMR_HIGHLEVEL_CODE " ; HighLevelCode = no, HighLevelCodeOpt = "" ), globals.lookup_bool_option(Globals, highlevel_data, HighLevelData), ( HighLevelData = yes, HighLevelDataOpt = "-DMR_HIGHLEVEL_DATA " ; HighLevelData = no, HighLevelDataOpt = "" ), globals.lookup_bool_option(Globals, gcc_global_registers, GCC_Regs), ( GCC_Regs = yes, RegOpt = "-DMR_USE_GCC_GLOBAL_REGISTERS " ; GCC_Regs = no, RegOpt = "" ), globals.lookup_bool_option(Globals, gcc_non_local_gotos, GCC_Gotos), ( GCC_Gotos = yes, GotoOpt = "-DMR_USE_GCC_NONLOCAL_GOTOS " ; GCC_Gotos = no, GotoOpt = "" ), globals.lookup_bool_option(Globals, asm_labels, ASM_Labels), ( ASM_Labels = yes, AsmOpt = "-DMR_USE_ASM_LABELS " ; ASM_Labels = no, AsmOpt = "" ), globals.lookup_bool_option(Globals, parallel, Parallel), ( Parallel = yes, ParallelOpt = "-DMR_THREAD_SAFE " ; Parallel = no, ParallelOpt = "" ), globals.lookup_bool_option(Globals, threadscope, Threadscope), ( Threadscope = yes, ThreadscopeOpt = "-DMR_THREADSCOPE " ; Threadscope = no, ThreadscopeOpt = "" ), globals.get_gc_method(Globals, GC_Method), BoehmGC_Opt = "-DMR_CONSERVATIVE_GC -DMR_BOEHM_GC ", ( GC_Method = gc_automatic, GC_Opt = "" ; GC_Method = gc_none, GC_Opt = "" ; GC_Method = gc_boehm, GC_Opt = BoehmGC_Opt ; GC_Method = gc_boehm_debug, GC_Opt = BoehmGC_Opt ++ "-DMR_BOEHM_GC_DEBUG -DGC_DEBUG -DKEEP_BACKPTRS " ; GC_Method = gc_hgc, GC_Opt = "-DMR_CONSERVATIVE_GC -DMR_HGC " ; GC_Method = gc_accurate, GC_Opt = "-DMR_NATIVE_GC " ), globals.lookup_bool_option(Globals, profile_calls, ProfileCalls), ( ProfileCalls = yes, ProfileCallsOpt = "-DMR_MPROF_PROFILE_CALLS " ; ProfileCalls = no, ProfileCallsOpt = "" ), globals.lookup_bool_option(Globals, profile_time, ProfileTime), ( ProfileTime = yes, ProfileTimeOpt = "-DMR_MPROF_PROFILE_TIME " ; ProfileTime = no, ProfileTimeOpt = "" ), globals.lookup_bool_option(Globals, profile_memory, ProfileMemory), ( ProfileMemory = yes, ProfileMemoryOpt = "-DMR_MPROF_PROFILE_MEMORY " ; ProfileMemory = no, ProfileMemoryOpt = "" ), globals.lookup_bool_option(Globals, profile_deep, ProfileDeep), ( ProfileDeep = yes, ProfileDeepOpt = "-DMR_DEEP_PROFILING " ; ProfileDeep = no, ProfileDeepOpt = "" ), globals.lookup_bool_option(Globals, record_term_sizes_as_words, RecordTermSizesAsWords), globals.lookup_bool_option(Globals, record_term_sizes_as_cells, RecordTermSizesAsCells), ( RecordTermSizesAsWords = yes, RecordTermSizesAsCells = yes, % This should have been caught in handle_options. unexpected($module, $pred, "inconsistent record term size options") ; RecordTermSizesAsWords = yes, RecordTermSizesAsCells = no, RecordTermSizesOpt = "-DMR_RECORD_TERM_SIZES " ; RecordTermSizesAsWords = no, RecordTermSizesAsCells = yes, RecordTermSizesOpt = "-DMR_RECORD_TERM_SIZES " ++ "-DMR_RECORD_TERM_SIZES_AS_CELLS " ; RecordTermSizesAsWords = no, RecordTermSizesAsCells = no, RecordTermSizesOpt = "" ), globals.lookup_int_option(Globals, num_ptag_bits, NumPtagBits), string.int_to_string(NumPtagBits, NumPtagBitsString), NumPtagBitsOpt = "-DMR_TAGBITS=" ++ NumPtagBitsString ++ " ", globals.lookup_bool_option(Globals, decl_debug, DeclDebug), ( DeclDebug = yes, DeclDebugOpt = "-DMR_DECL_DEBUG " ; DeclDebug = no, DeclDebugOpt = "" ), globals.lookup_bool_option(Globals, source_to_source_debug, SourceDebug), ( SourceDebug = yes, SourceDebugOpt = "-DMR_SS_DEBUG " ; SourceDebug = no, SourceDebugOpt = "" ), globals.lookup_bool_option(Globals, exec_trace, ExecTrace), ( ExecTrace = yes, ExecTraceOpt = "-DMR_EXEC_TRACE " ; ExecTrace = no, ExecTraceOpt = "" ), globals.lookup_bool_option(Globals, extend_stacks_when_needed, Extend), globals.lookup_bool_option(Globals, stack_segments, StackSegments), ( Extend = yes, StackSegments = no, ExtendOpt = "-DMR_EXTEND_STACKS_WHEN_NEEDED " ; Extend = no, StackSegments = yes, ExtendOpt = "-DMR_STACK_SEGMENTS " ; Extend = no, StackSegments = no, ExtendOpt = "" ; Extend = yes, StackSegments = yes, ExtendOpt = unexpected($module, $pred, "--extend-stacks-when-needed and --stack-segments") ), globals.lookup_bool_option(Globals, low_level_debug, LL_Debug), ( LL_Debug = yes, % This is grade option tells the C compiler to turn on the generation % of debugging symbols and to disable the optimizations that % would make the executable harder to debug in a C debugger % such as gdb. However, here we gather only *macro* definitions, % not general compiler flags. LL_DebugOpt = "-DMR_LL_DEBUG " ; LL_Debug = no, LL_DebugOpt = "" ), globals.lookup_bool_option(Globals, use_trail, UseTrail), ( UseTrail = yes, UseTrailOpt = "-DMR_USE_TRAIL ", globals.lookup_bool_option(Globals, trail_segments, TrailSegments), ( TrailSegments = yes, TrailSegOpt = "-DMR_TRAIL_SEGMENTS " ; TrailSegments = no, TrailSegOpt = "" ) ; UseTrail = no, UseTrailOpt = "", TrailSegOpt = "" ), globals.lookup_bool_option(Globals, use_minimal_model_stack_copy, MinimalModelStackCopy), globals.lookup_bool_option(Globals, use_minimal_model_own_stacks, MinimalModelOwnStacks), ( MinimalModelStackCopy = yes, MinimalModelOwnStacks = yes, % this should have been caught in handle_options unexpected($module, $pred, "inconsistent minimal model options") ; MinimalModelStackCopy = yes, MinimalModelOwnStacks = no, MinimalModelBaseOpt = "-DMR_USE_MINIMAL_MODEL_STACK_COPY " ; MinimalModelStackCopy = no, MinimalModelOwnStacks = yes, MinimalModelBaseOpt = "-DMR_USE_MINIMAL_MODEL_OWN_STACKS " ; MinimalModelStackCopy = no, MinimalModelOwnStacks = no, MinimalModelBaseOpt = "" ), globals.lookup_bool_option(Globals, minimal_model_debug, MinimalModelDebug), ( MinimalModelDebug = yes, ( if MinimalModelBaseOpt = "" then % We ignore the debug flag unless one of the base flags is set. MinimalModelOpt = MinimalModelBaseOpt else MinimalModelOpt = MinimalModelBaseOpt ++ "-DMR_MINIMAL_MODEL_DEBUG " ) ; MinimalModelDebug = no, MinimalModelOpt = MinimalModelBaseOpt ), globals.lookup_bool_option(Globals, pregenerated_dist, PregeneratedDist), ( PregeneratedDist = yes, PregeneratedDistOpt = "-DMR_PREGENERATED_DIST " ; PregeneratedDist = no, PregeneratedDistOpt = "" ), globals.lookup_bool_option(Globals, single_prec_float, SinglePrecFloat), ( SinglePrecFloat = yes, SinglePrecFloatOpt = "-DMR_USE_SINGLE_PREC_FLOAT " ; SinglePrecFloat = no, SinglePrecFloatOpt = "" ), globals.lookup_bool_option(Globals, use_regions, UseRegions), ( UseRegions = yes, UseRegionsOpt0 = "-DMR_USE_REGIONS ", globals.lookup_bool_option(Globals, use_regions_debug, UseRegionsDebug), ( UseRegionsDebug = yes, UseRegionsOpt1 = UseRegionsOpt0 ++ "-DMR_RBMM_DEBUG " ; UseRegionsDebug = no, UseRegionsOpt1 = UseRegionsOpt0 ), globals.lookup_bool_option(Globals, use_regions_profiling, UseRegionsProfiling), ( UseRegionsProfiling = yes, UseRegionsOpt = UseRegionsOpt1 ++ "-DMR_RBMM_PROFILING " ; UseRegionsProfiling = no, UseRegionsOpt = UseRegionsOpt1 ) ; UseRegions = no, UseRegionsOpt = "" ), string.append_list([ HighLevelCodeOpt, HighLevelDataOpt, RegOpt, GotoOpt, AsmOpt, ParallelOpt, ThreadscopeOpt, GC_Opt, ProfileCallsOpt, ProfileTimeOpt, ProfileMemoryOpt, ProfileDeepOpt, RecordTermSizesOpt, NumPtagBitsOpt, ExtendOpt, LL_DebugOpt, DeclDebugOpt, SourceDebugOpt, ExecTraceOpt, UseTrailOpt, TrailSegOpt, MinimalModelOpt, PregeneratedDistOpt, SinglePrecFloatOpt, UseRegionsOpt], GradeDefines). %-----------------------------------------------------------------------------% :- pred gather_c_include_dir_flags(globals::in, string::out) is det. gather_c_include_dir_flags(Globals, InclOpt) :- globals.lookup_accumulating_option(Globals, c_include_directory, C_Incl_Dirs), InclOpt = string.append_list(list.condense(list.map( (func(C_INCL) = ["-I", quote_arg(C_INCL), " "]), C_Incl_Dirs))). %-----------------------------------------------------------------------------% :- pred gather_compiler_specific_flags(globals::in, string::out) is det. gather_compiler_specific_flags(Globals, Flags) :- globals.get_c_compiler_type(Globals, C_CompilerType), ( C_CompilerType = cc_gcc(_, _, _), globals.lookup_accumulating_option(Globals, gcc_flags, FlagsList) ; C_CompilerType = cc_clang(_), globals.lookup_accumulating_option(Globals, clang_flags, FlagsList) ; C_CompilerType = cc_cl(_), globals.lookup_accumulating_option(Globals, msvc_flags, FlagsList) ; C_CompilerType = cc_unknown, FlagsList = [] ), join_string_list(FlagsList, "", "", " ", Flags). :- pred get_maybe_filtercc_command(globals::in, maybe(string)::out) is det. get_maybe_filtercc_command(Globals, MaybeFilterCmd) :- % At this time we only need to filter the compiler output when using % assembler labels with gcc 4.x. Mercury.config.bootstrap doesn't specify % the gcc version so we don't check for it. ( if globals.lookup_bool_option(Globals, asm_labels, yes), globals.lookup_string_option(Globals, filtercc_command, FilterCmd), FilterCmd \= "" then MaybeFilterCmd = yes(FilterCmd) else MaybeFilterCmd = no ). %-----------------------------------------------------------------------------% compile_java_files(Globals, ErrorStream, JavaFiles, Succeeded, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), ( JavaFiles = [JavaFile | MoreFiles], ( Verbose = yes, io.write_string("% Compiling `", !IO), io.write_string(JavaFile, !IO), ( MoreFiles = [], io.write_string("':\n", !IO) ; MoreFiles = [_ | _], io.write_string("', etc.:\n", !IO) ) ; Verbose = no ) ; JavaFiles = [], unexpected($module, $pred, "empty list") ), globals.lookup_string_option(Globals, java_compiler, JavaCompiler), globals.lookup_accumulating_option(Globals, java_flags, JavaFlagsList), globals.lookup_bool_option(Globals, restricted_command_line, RestrictedCommandLine), ( RestrictedCommandLine = yes, % NOTE: the '-J' flag must not occur inside @files, so we need % to ensure that it is always passed on the command line. list.filter(is_minus_j_flag, JavaFlagsList, JRT_JavaFlagsList, NonJRT_JavaFlagsList), join_string_list(JRT_JavaFlagsList, "", "", " ", NonAtFileJAVAFLAGS), join_string_list(NonJRT_JavaFlagsList, "", "", " ", JAVAFLAGS) ; RestrictedCommandLine = no, join_string_list(JavaFlagsList, "", "", " ", JAVAFLAGS), NonAtFileJAVAFLAGS = "" ), get_mercury_std_libs_for_java(Globals, MercuryStdLibs), globals.lookup_accumulating_option(Globals, java_classpath, UserClasspath), Java_Incl_Dirs = MercuryStdLibs ++ UserClasspath, % We prepend the current CLASSPATH (if any) to preserve the accumulating % nature of this variable. get_env_classpath(EnvClasspath, !IO), ( if EnvClasspath = "" then ClassPathList = Java_Incl_Dirs else ClassPathList = [EnvClasspath | Java_Incl_Dirs] ), ClassPath = string.join_list(java_classpath_separator, ClassPathList), ( if ClassPath = "" then InclOpt = "" else InclOpt = string.append_list([ "-classpath ", quote_arg(ClassPath), " "]) ), globals.lookup_bool_option(Globals, target_debug, Target_Debug), ( Target_Debug = yes, Target_DebugOpt = "-g " ; Target_Debug = no, Target_DebugOpt = "" ), globals.lookup_bool_option(Globals, use_subdirs, UseSubdirs), globals.lookup_bool_option(Globals, use_grade_subdirs, UseGradeSubdirs), globals.lookup_string_option(Globals, target_arch, TargetArch), ( UseSubdirs = yes, ( UseGradeSubdirs = yes, grade_directory_component(Globals, Grade), SourceDirName = "Mercury"/Grade/TargetArch/"Mercury"/"javas", DestDirName = "Mercury"/Grade/TargetArch/"Mercury"/"classs" ; UseGradeSubdirs = no, SourceDirName = "Mercury"/"javas", DestDirName = "Mercury"/"classs" ), % Javac won't create the destination directory for class files, % so we need to do it. dir.make_directory(DestDirName, _, !IO), % Set directories for source and class files. DirOpts = string.append_list([ "-sourcepath ", SourceDirName, " ", "-d ", DestDirName, " " ]) ; UseSubdirs = no, DirOpts = "" ), globals.lookup_string_option(Globals, filterjavac_command, MFilterJavac), ( if MFilterJavac = "" then MaybeMFilterJavac = no else MaybeMFilterJavac = yes(MFilterJavac) ), NonAtFileCommandArgs = NonAtFileJAVAFLAGS, % Be careful with the order here! Some options may override others. % Also be careful that each option is separated by spaces. JoinedJavaFiles = string.join_list(" ", JavaFiles), string.append_list([InclOpt, DirOpts, Target_DebugOpt, JAVAFLAGS, " ", JoinedJavaFiles], CommandArgs), invoke_long_system_command_maybe_filter_output(Globals, ErrorStream, cmd_verbose_commands, JavaCompiler, NonAtFileCommandArgs, CommandArgs, MaybeMFilterJavac, Succeeded, !IO). :- func java_classpath_separator = string. java_classpath_separator = PathSeparator :- ( if ( dir.use_windows_paths ; io.have_cygwin ) then PathSeparator = ";" else PathSeparator = ":" ). :- pred is_minus_j_flag(string::in) is semidet. is_minus_j_flag(FlagStr) :- string.prefix(FlagStr, "-J"). %-----------------------------------------------------------------------------% compile_csharp_file(Globals, ErrorStream, ModuleAndImports, CSharpFileName0, DLLFileName, Succeeded, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), maybe_write_string(Verbose, "% Compiling `", !IO), maybe_write_string(Verbose, CSharpFileName, !IO), maybe_write_string(Verbose, "':\n", !IO), globals.lookup_string_option(Globals, csharp_compiler, CSC), globals.lookup_accumulating_option(Globals, csharp_flags, CSCFlagsList), join_string_list(CSCFlagsList, "", "", " ", CSCFlags), % XXX This is because the MS C# compiler doesn't understand % / as a directory separator. CSharpFileName = string.replace_all(CSharpFileName0, "/", "\\\\"), globals.lookup_bool_option(Globals, target_debug, Debug), ( Debug = yes, % XXX This needs testing before it can be enabled (see the comments % for install_debug_library in library/Mmakefile). % DebugOpt = "-debug+ -debug:full " DebugOpt = "" ; Debug = no, DebugOpt = "" ), % XXX Should we use a separate dll_directories options? % NOTE: we use the -option style options in preference to the /option % style in order to avoid problems with POSIX style shells. globals.lookup_accumulating_option(Globals, link_library_directories, DLLDirs), DLLDirOpts = "-lib:Mercury/dlls " ++ string.append_list(list.condense(list.map( (func(DLLDir) = ["-lib:", DLLDir, " "]), DLLDirs))), ModuleName = ModuleAndImports ^ mai_module_name, ( if mercury_std_library_module_name(ModuleName) then Prefix = "-addmodule:" else Prefix = "-r:" ), ForeignImportModules = ModuleAndImports ^ mai_foreign_import_modules, ForeignDeps = list.map( (func(FI) = foreign_import_module_name_from_module(FI, ModuleName)), set.to_sorted_list( get_all_foreign_import_module_infos(ForeignImportModules))), IntDeps = set.sorted_list_to_set( multi_map.keys(ModuleAndImports ^ mai_int_deps)), ImpDeps = set.sorted_list_to_set( multi_map.keys(ModuleAndImports ^ mai_imp_deps)), set.union(IntDeps, ImpDeps, IntImpDeps), set.insert_list(ForeignDeps, IntImpDeps, IntImpForeignDeps), ReferencedDlls = referenced_dlls(ModuleName, IntImpForeignDeps), list.map_foldl( ( pred(Mod::in, Result::out, IO0::di, IO::uo) is det :- module_name_to_file_name(Globals, do_not_create_dirs, ".dll", Mod, FileName, IO0, IO), Result = [Prefix, FileName, " "] ), set.to_sorted_list(ReferencedDlls), ReferencedDllsList, !IO), ReferencedDllsStr = string.append_list( list.condense(ReferencedDllsList)), string.append_list([CSC, DebugOpt, " -t:library ", DLLDirOpts, CSCFlags, ReferencedDllsStr, " -out:", DLLFileName, " ", CSharpFileName], Command), invoke_system_command(Globals, ErrorStream, cmd_verbose_commands, Command, Succeeded, !IO). % Generate the list of .NET DLLs which could be referred to by this module % (including the module itself). % % If we are compiling a module within the standard library we should % reference the runtime DLLs and all other library DLLs. If we are % outside the library we should just reference mercury.dll (which will % contain all the DLLs). % :- func referenced_dlls(module_name, set(module_name)) = set(module_name). referenced_dlls(Module, DepModules0) = Modules :- set.insert(Module, DepModules0, DepModules), % If we are not compiling a module in the mercury std library, then % replace all the std library dlls with one reference to mercury.dll. ( if mercury_std_library_module_name(Module) then % In the standard library we need to add the runtime dlls. AddedModules = [unqualified("mercury_dotnet"), unqualified("mercury_il")], set.insert_list(AddedModules, DepModules, Modules) else F = ( func(M) = ( if mercury_std_library_module_name(M) then unqualified("mercury") else % A sub module is located in the top level assembly. unqualified(outermost_qualifier(M)) ) ), Modules = set.map(F, DepModules) ). %-----------------------------------------------------------------------------% compile_erlang_file(Globals, ErrorStream, ErlangFile, Succeeded, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), maybe_write_string(Verbose, "% Compiling `", !IO), maybe_write_string(Verbose, ErlangFile, !IO), maybe_write_string(Verbose, "':\n", !IO), globals.lookup_string_option(Globals, erlang_compiler, ErlangCompiler), globals.lookup_accumulating_option(Globals, erlang_flags, ErlangFlagsList0), globals.lookup_bool_option(Globals, erlang_native_code, ErlangNativeCode), globals.lookup_bool_option(Globals, erlang_inhibit_trivial_warnings, ErlangInhibitTrivialWarnings), ( ErlangNativeCode = yes, ErlangFlagsList1 = ["+native" | ErlangFlagsList0] ; ErlangNativeCode = no, ErlangFlagsList1 = ErlangFlagsList0 ), ( ErlangInhibitTrivialWarnings = yes, ErlangFlagsList = ["+nowarn_unused_vars", "+nowarn_unused_function" | ErlangFlagsList1] ; ErlangInhibitTrivialWarnings = no, ErlangFlagsList = ErlangFlagsList1 ), ERLANGFLAGS = string.join_list(" ", ErlangFlagsList), globals.lookup_accumulating_option(Globals, erlang_include_directory, Erlang_Incl_Dirs), InclOpt = string.append_list(list.condense(list.map( (func(E_INCL) = ["-I", quote_arg(E_INCL), " "]), Erlang_Incl_Dirs))), globals.lookup_bool_option(Globals, use_subdirs, UseSubdirs), globals.lookup_bool_option(Globals, use_grade_subdirs, UseGradeSubdirs), globals.lookup_string_option(Globals, target_arch, TargetArch), ( UseSubdirs = yes, ( UseGradeSubdirs = yes, grade_directory_component(Globals, Grade), DirName = "Mercury"/Grade/TargetArch/"Mercury"/"beams" ; UseGradeSubdirs = no, DirName = "Mercury"/"beams" ), % Create the destination directory. dir.make_directory(DirName, _, !IO), % Set destination directory for .beam files. DestDir = "-o " ++ DirName ++ " " ; UseSubdirs = no, DestDir = "" ), string.append_list([ErlangCompiler, " ", InclOpt, DestDir, ERLANGFLAGS, " ", ErlangFile], Command), invoke_system_command(Globals, ErrorStream, cmd_verbose_commands, Command, Succeeded, !IO). %-----------------------------------------------------------------------------% make_library_init_file(Globals, ErrorStream, MainModuleName, AllModules, Succeeded, !IO) :- globals.lookup_string_option(Globals, mkinit_command, MkInit), make_library_init_file_2(Globals, ErrorStream, MainModuleName, AllModules, ".c", MkInit, Succeeded, !IO). make_erlang_library_init_file(Globals, ErrorStream, MainModuleName, AllModules, Succeeded, !IO) :- globals.lookup_string_option(Globals, mkinit_erl_command, MkInit), make_library_init_file_2(Globals, ErrorStream, MainModuleName, AllModules, ".erl", MkInit, Succeeded, !IO). :- pred make_library_init_file_2(globals::in, io.output_stream::in, module_name::in, list(module_name)::in, string::in, string::in, bool::out, io::di, io::uo) is det. make_library_init_file_2(Globals, ErrorStream, MainModuleName, AllModules, TargetExt, MkInit, Succeeded, !IO) :- module_name_to_file_name(Globals, do_create_dirs, ".init.tmp", MainModuleName, TmpInitFileName, !IO), io.open_output(TmpInitFileName, InitFileRes, !IO), ( InitFileRes = ok(InitFileStream), list.map_foldl( module_name_to_file_name(Globals, do_not_create_dirs, TargetExt), AllModules, AllTargetFilesList, !IO), invoke_mkinit(Globals, InitFileStream, cmd_verbose_commands, MkInit, " -k ", AllTargetFilesList, MkInitOK, !IO), ( MkInitOK = yes, globals.lookup_maybe_string_option(Globals, extra_init_command, MaybeInitFileCommand), ( MaybeInitFileCommand = yes(InitFileCommand), make_all_module_command(Globals, InitFileCommand, MainModuleName, AllModules, CommandString, !IO), invoke_system_command(Globals, InitFileStream, cmd_verbose_commands, CommandString, Succeeded0, !IO) ; MaybeInitFileCommand = no, Succeeded0 = yes ) ; MkInitOK = no, Succeeded0 = no ), io.close_output(InitFileStream, !IO), module_name_to_file_name(Globals, do_create_dirs, ".init", MainModuleName, InitFileName, !IO), update_interface_return_succeeded(Globals, InitFileName, Succeeded1, !IO), Succeeded2 = Succeeded0 `and` Succeeded1, ( Succeeded2 = yes, % Symlink or copy the .init files to the user's directory % if --use-grade-subdirs is enabled. globals.lookup_bool_option(Globals, use_grade_subdirs, UseGradeSubDirs), ( UseGradeSubDirs = yes, io.set_output_stream(ErrorStream, OutputStream, !IO), globals.set_option(use_subdirs, bool(no), Globals, NoSubdirGlobals0), globals.set_option(use_grade_subdirs, bool(no), NoSubdirGlobals0, NoSubdirGlobals), module_name_to_file_name(NoSubdirGlobals, do_not_create_dirs, ".init", MainModuleName, UserDirFileName, !IO), % Remove the target of the symlink/copy in case it already % exists. io.remove_file(UserDirFileName, _, !IO), make_symlink_or_copy_file(Globals, InitFileName, UserDirFileName, Succeeded, !IO), io.set_output_stream(OutputStream, _, !IO) ; UseGradeSubDirs = no, Succeeded = yes ) ; Succeeded2 = no, Succeeded = no ) ; InitFileRes = error(Error), io.progname_base("mercury_compile", ProgName, !IO), io.write_string(ErrorStream, ProgName, !IO), io.write_string(ErrorStream, ": can't open `", !IO), io.write_string(ErrorStream, TmpInitFileName, !IO), io.write_string(ErrorStream, "' for output:\n", !IO), io.nl(ErrorStream, !IO), io.write_string(ErrorStream, io.error_message(Error), !IO), io.nl(ErrorStream, !IO), Succeeded = no ). :- pred invoke_mkinit(globals::in, io.output_stream::in, command_verbosity::in, string::in, string::in, list(file_name)::in, bool::out, io::di, io::uo) is det. invoke_mkinit(Globals, InitFileStream, Verbosity, MkInit, Args, FileNames, MkInitOK, !IO) :- % mkinit expects unquoted file names. join_string_list(FileNames, "", "\n", "", TargetFileNames), open_temp_output(TmpFileResult, !IO), ( TmpFileResult = ok({TmpFile, TmpStream}), io.write_string(TmpStream, TargetFileNames, !IO), io.close_output(TmpStream, !IO), MkInitCmd = string.append_list([MkInit, " ", Args, " -f ", TmpFile]), invoke_system_command(Globals, InitFileStream, Verbosity, MkInitCmd, MkInitOK0, !IO), io.remove_file(TmpFile, RemoveResult, !IO), ( RemoveResult = ok, MkInitOK = MkInitOK0 ; RemoveResult = error(_), MkInitOK = no ) ; TmpFileResult = error(ErrorMessage), io.write_string(io.stderr_stream, ErrorMessage, !IO), io.nl(!IO), MkInitOK = no ). %-----------------------------------------------------------------------------% make_init_obj_file(Globals, ErrorStream, ModuleName, ModuleNames, Result, !IO) :- globals.lookup_bool_option(Globals, rebuild, MustCompile), do_make_init_obj_file(Globals, ErrorStream, MustCompile, ModuleName, ModuleNames, Result, !IO). % WARNING: The code here duplicates the functionality of scripts/c2init.in. % Any changes there may also require changes here, and vice versa. % The code of make_standalone_interface/3 may also require updating. :- pred do_make_init_obj_file(globals::in, io.output_stream::in, bool::in, module_name::in, list(module_name)::in, maybe(file_name)::out, io::di, io::uo) is det. do_make_init_obj_file(Globals, ErrorStream, MustCompile, ModuleName, ModuleNames, Result, !IO) :- globals.lookup_maybe_string_option(Globals, mercury_standard_library_directory, MaybeStdLibDir), grade_directory_component(Globals, GradeDir), ( MaybeStdLibDir = yes(StdLibDir), ToGradeInit = (func(File) = StdLibDir / "modules" / GradeDir / File), StdInitFileNames = [ ToGradeInit("mer_rt.init"), ToGradeInit("mer_std.init") ], StdTraceInitFileNames = [ ToGradeInit("mer_browser.init"), ToGradeInit("mer_mdbcomp.init") ], SourceDebugInitFileNames = [ ToGradeInit("mer_ssdb.init") ] ; MaybeStdLibDir = no, StdInitFileNames = [], StdTraceInitFileNames = [], SourceDebugInitFileNames = [] ), globals.lookup_string_option(Globals, mkinit_command, MkInit), make_init_target_file(Globals, ErrorStream, MkInit, ModuleName, ModuleNames, ".c", StdInitFileNames, StdTraceInitFileNames, SourceDebugInitFileNames, "", MaybeInitTargetFile, !IO), get_object_code_type(Globals, executable, PIC), maybe_pic_object_file_extension(Globals, PIC, ObjExt), module_name_to_file_name(Globals, do_create_dirs, "_init" ++ ObjExt, ModuleName, InitObjFileName, !IO), CompileCInitFile = (pred(InitTargetFileName::in, Res::out, IO0::di, IO::uo) is det :- do_compile_c_file(Globals, ErrorStream, PIC, InitTargetFileName, InitObjFileName, Res, IO0, IO) ), maybe_compile_init_obj_file(Globals, MaybeInitTargetFile, MustCompile, CompileCInitFile, InitObjFileName, Result, !IO). make_erlang_program_init_file(Globals, ErrorStream, ModuleName, ModuleNames, Result, !IO) :- globals.lookup_bool_option(Globals, rebuild, MustCompile), globals.lookup_maybe_string_option(Globals, mercury_standard_library_directory, MaybeStdLibDir), grade_directory_component(Globals, GradeDir), ( MaybeStdLibDir = yes(StdLibDir), StdInitFileNames = [ StdLibDir / "modules" / GradeDir / "mer_std.init" ], SourceDebugInitFileNames = [ StdLibDir / "modules" / GradeDir / "mer_ssdb.init" ] ; MaybeStdLibDir = no, StdInitFileNames = [], SourceDebugInitFileNames = [] ), % Tracing is not supported in Erlang backend. StdTraceInitFileNames = [], % We need to pass the module name to mkinit_erl. ErlangModuleName = qualify_mercury_std_library_module_name(ModuleName), ModuleNameStr = sym_name_to_string_sep(ErlangModuleName, "__") ++ "_init", ModuleNameOption = " -m " ++ quote_arg(ModuleNameStr), globals.lookup_string_option(Globals, mkinit_erl_command, MkInitErl), make_init_target_file(Globals, ErrorStream, MkInitErl, ModuleName, ModuleNames, ".erl", StdInitFileNames, StdTraceInitFileNames, SourceDebugInitFileNames, ModuleNameOption, MaybeInitTargetFile, !IO), module_name_to_file_name(Globals, do_create_dirs, "_init.beam", ModuleName, InitObjFileName, !IO), CompileErlangInitFile = (pred(InitTargetFileName::in, Res::out, IO0::di, IO::uo) is det :- compile_erlang_file(Globals, ErrorStream, InitTargetFileName, Res, IO0, IO) ), maybe_compile_init_obj_file(Globals, MaybeInitTargetFile, MustCompile, CompileErlangInitFile, InitObjFileName, Result, !IO). :- pred make_init_target_file(globals::in, io.output_stream::in, string::in, module_name::in, list(module_name)::in, string::in, list(file_name)::in, list(file_name)::in, list(file_name)::in, string::in, maybe(file_name)::out, io::di, io::uo) is det. make_init_target_file(Globals, ErrorStream, MkInit, ModuleName, ModuleNames, TargetExt, StdInitFileNames, StdTraceInitFileNames, SourceDebugInitFileNames, ModuleNameOption, MaybeInitTargetFile, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), globals.lookup_bool_option(Globals, statistics, Stats), maybe_write_string(Verbose, "% Creating initialization file...\n", !IO), compute_grade(Globals, Grade), module_name_to_file_name(Globals, do_create_dirs, "_init" ++ TargetExt, ModuleName, InitTargetFileName, !IO), list.map_foldl( module_name_to_file_name(Globals, do_not_create_dirs, TargetExt), ModuleNames, TargetFileNameList, !IO), globals.lookup_accumulating_option(Globals, init_file_directories, InitFileDirsList), join_quoted_string_list(InitFileDirsList, "-I ", "", " ", InitFileDirs), globals.lookup_accumulating_option(Globals, init_files, InitFileNamesList0), % If we pass the same .init file to mkinit multiple times we will end % up with multiple calls to the functions that write out proc statics % in deep profiling grades. This will cause the runtime code that writes % out the profile to abort. list.remove_dups(InitFileNamesList0, InitFileNamesList1), globals.lookup_accumulating_option(Globals, trace_init_files, TraceInitFileNamesList0), InitFileNamesList2 = StdInitFileNames ++ InitFileNamesList1, TraceInitFileNamesList = StdTraceInitFileNames ++ TraceInitFileNamesList0, globals.get_trace_level(Globals, TraceLevel), TraceLevelIsNone = given_trace_level_is_none(TraceLevel), ( TraceLevelIsNone = no, TraceOpt = "-t", InitFileNamesList3 = InitFileNamesList2 ++ TraceInitFileNamesList ; TraceLevelIsNone = yes, TraceOpt = "", InitFileNamesList3 = InitFileNamesList2 ), globals.lookup_bool_option(Globals, link_ssdb_libs, SourceDebug), ( SourceDebug = yes, InitFileNamesList = InitFileNamesList3 ++ SourceDebugInitFileNames ; SourceDebug = no, InitFileNamesList = InitFileNamesList3 ), globals.lookup_accumulating_option(Globals, runtime_flags, RuntimeFlagsList), join_quoted_string_list(RuntimeFlagsList, "-r ", "", " ", RuntimeFlags), globals.lookup_bool_option(Globals, extra_initialization_functions, ExtraInits), ( ExtraInits = yes, ExtraInitsOpt = "-x" ; ExtraInits = no, ExtraInitsOpt = "" ), globals.lookup_bool_option(Globals, main, Main), ( Main = no, NoMainOpt = "-l" ; Main = yes, NoMainOpt = "" ), globals.lookup_string_option(Globals, experimental_complexity, ExperimentalComplexity), ( if ExperimentalComplexity = "" then ExperimentalComplexityOpt = "" else ExperimentalComplexityOpt = "-X " ++ ExperimentalComplexity ), TmpInitTargetFileName = InitTargetFileName ++ ".tmp", MkInitArgs = string.append_list( [ " -g ", Grade, " ", TraceOpt, " ", ExtraInitsOpt, " ", NoMainOpt, " ", ExperimentalComplexityOpt, " ", RuntimeFlags, " -o ", quote_arg(TmpInitTargetFileName), " ", InitFileDirs, ModuleNameOption ]), invoke_mkinit(Globals, ErrorStream, cmd_verbose_commands, MkInit, MkInitArgs, TargetFileNameList ++ InitFileNamesList, MkInitOk, !IO), maybe_report_stats(Stats, !IO), ( MkInitOk = yes, update_interface_return_succeeded(Globals, InitTargetFileName, UpdateOk, !IO), ( UpdateOk = yes, MaybeInitTargetFile = yes(InitTargetFileName) ; UpdateOk = no, MaybeInitTargetFile = no ) ; MkInitOk = no, MaybeInitTargetFile = no ). :- pred maybe_compile_init_obj_file(globals::in, maybe(file_name)::in, bool::in, compile_init_file_pred::in(compile_init_file_pred), file_name::in, maybe(file_name)::out, io::di, io::uo) is det. :- type compile_init_file_pred == pred(file_name, bool, io, io). :- inst compile_init_file_pred == (pred(in, out, di, uo) is det). maybe_compile_init_obj_file(Globals, MaybeInitTargetFile, MustCompile, Compile, InitObjFileName, Result, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), globals.lookup_bool_option(Globals, statistics, Stats), ( MaybeInitTargetFile = yes(InitTargetFileName), file_as_new_as(InitObjFileName, Rel, InitTargetFileName, !IO), ( if ( MustCompile = yes ; Rel = is_not_as_new_as ; Rel = missing_timestamp ) then maybe_write_string(Verbose, "% Compiling initialization file...\n", !IO), Compile(InitTargetFileName, CompileOk, !IO), maybe_report_stats(Stats, !IO), ( CompileOk = yes, Result = yes(InitObjFileName) ; CompileOk = no, Result = no ) else Result = yes(InitObjFileName) ) ; MaybeInitTargetFile = no, Result = no ). % file_as_new_as(FileNameA, Rel, FileNameB, !IO) % % Check if file A has a timestamp at least as new as the timestamp of % file B. Rel is `missing_timestamp' iff either timestamp could not % be retrieved. % :- pred file_as_new_as(file_name::in, is_as_new_as::out, file_name::in, io::di, io::uo) is det. :- type is_as_new_as ---> is_as_new_as ; is_not_as_new_as ; missing_timestamp. file_as_new_as(FileNameA, Rel, FileNameB, !IO) :- compare_file_timestamps(FileNameA, FileNameB, MaybeCompare, !IO), ( ( MaybeCompare = yes(=) ; MaybeCompare = yes(>) ), Rel = is_as_new_as ; MaybeCompare = yes(<), Rel = is_not_as_new_as ; MaybeCompare = no, Rel = missing_timestamp ). :- pred compare_file_timestamps(file_name::in, file_name::in, maybe(comparison_result)::out, io::di, io::uo) is det. compare_file_timestamps(FileNameA, FileNameB, MaybeCompare, !IO) :- io.file_modification_time(FileNameA, TimeResultA, !IO), io.file_modification_time(FileNameB, TimeResultB, !IO), ( if TimeResultA = ok(TimeA), TimeResultB = ok(TimeB) then compare(Compare, TimeA, TimeB), MaybeCompare = yes(Compare) else MaybeCompare = no ). %-----------------------------------------------------------------------------% link_module_list(Modules, ExtraObjFiles, Globals, Succeeded, !IO) :- globals.lookup_string_option(Globals, output_file_name, OutputFileName0), ( if OutputFileName0 = "" then ( Modules = [Module | _], OutputFileName = Module ; Modules = [], unexpected($module, $pred, "no modules") ) else OutputFileName = OutputFileName0 ), file_name_to_module_name(OutputFileName, MainModuleName), globals.lookup_bool_option(Globals, compile_to_shared_lib, CompileToSharedLib), ( CompileToSharedLib = yes, TargetType = shared_library ; CompileToSharedLib = no, TargetType = executable ), get_object_code_type(Globals, TargetType, PIC), maybe_pic_object_file_extension(Globals, PIC, Obj), io.output_stream(OutputStream, !IO), join_module_list(Globals, Modules, Obj, ObjectsList, !IO), ( TargetType = executable, list.map( (pred(ModuleStr::in, ModuleName::out) is det :- file_name_to_module_name(dir.det_basename(ModuleStr), ModuleName) ), Modules, ModuleNames), MustCompile = yes, do_make_init_obj_file(Globals, OutputStream, MustCompile, MainModuleName, ModuleNames, InitObjResult, !IO) ; TargetType = shared_library, InitObjResult = yes("") ), ( InitObjResult = yes(InitObjFileName), globals.lookup_accumulating_option(Globals, link_objects, ExtraLinkObjectsList), AllObjects0 = ObjectsList ++ ExtraLinkObjectsList ++ ExtraObjFiles, ( if InitObjFileName = "" then AllObjects = AllObjects0 else AllObjects = [InitObjFileName | AllObjects0] ), link(Globals, OutputStream, TargetType, MainModuleName, AllObjects, Succeeded, !IO) ; InitObjResult = no, Succeeded = no ). %-----------------------------------------------------------------------------% % WARNING: The code here duplicates the functionality of scripts/ml.in. % Any changes there may also require changes here, and vice versa. link(Globals, ErrorStream, LinkTargetType, ModuleName, ObjectsList, Succeeded, !IO) :- globals.lookup_bool_option(Globals, verbose, Verbose), globals.lookup_bool_option(Globals, statistics, Stats), maybe_write_string(Verbose, "% Linking...\n", !IO), link_output_filename(Globals, LinkTargetType, ModuleName, _Ext, OutputFileName, !IO), ( LinkTargetType = executable, link_exe_or_shared_lib(Globals, ErrorStream, LinkTargetType, ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO) ; LinkTargetType = static_library, create_archive(Globals, ErrorStream, OutputFileName, yes, ObjectsList, LinkSucceeded, !IO) ; LinkTargetType = shared_library, link_exe_or_shared_lib(Globals, ErrorStream, LinkTargetType, ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO) ; ( LinkTargetType = csharp_executable ; LinkTargetType = csharp_library ), % XXX C# see also older predicate compile_csharp_file create_csharp_exe_or_lib(Globals, ErrorStream, LinkTargetType, ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO) ; ( LinkTargetType = java_executable ; LinkTargetType = java_archive ), create_java_exe_or_lib(Globals, ErrorStream, LinkTargetType, ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO) ; LinkTargetType = erlang_launcher, create_erlang_shell_script(Globals, ModuleName, LinkSucceeded, !IO) ; LinkTargetType = erlang_archive, create_erlang_archive(Globals, ErrorStream, ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO) ), maybe_report_stats(Stats, !IO), ( LinkSucceeded = yes, post_link_make_symlink_or_copy(Globals, ErrorStream, LinkTargetType, ModuleName, Succeeded, _MadeSymlinkOrCopy, !IO) ; LinkSucceeded = no, Succeeded = no ). :- pred link_output_filename(globals::in, linked_target_type::in, module_name::in, string::out, string::out, io::di, io::uo) is det. link_output_filename(Globals, LinkTargetType, ModuleName, Ext, OutputFileName, !IO) :- ( LinkTargetType = executable, globals.lookup_string_option(Globals, executable_file_extension, Ext), module_name_to_file_name(Globals, do_create_dirs, Ext, ModuleName, OutputFileName, !IO) ; LinkTargetType = static_library, globals.lookup_string_option(Globals, library_extension, Ext), module_name_to_lib_file_name(Globals, "lib", ModuleName, Ext, do_create_dirs, OutputFileName, !IO) ; LinkTargetType = shared_library, globals.lookup_string_option(Globals, shared_library_extension, Ext), module_name_to_lib_file_name(Globals, "lib", ModuleName, Ext, do_create_dirs, OutputFileName, !IO) ; LinkTargetType = csharp_executable, Ext = ".exe", module_name_to_file_name(Globals, do_create_dirs, Ext, ModuleName, OutputFileName, !IO) ; LinkTargetType = csharp_library, Ext = ".dll", module_name_to_file_name(Globals, do_create_dirs, Ext, ModuleName, OutputFileName, !IO) ; LinkTargetType = erlang_launcher, Ext = get_launcher_script_extension(Globals), module_name_to_file_name(Globals, do_create_dirs, Ext, ModuleName, OutputFileName, !IO) ; LinkTargetType = java_executable, Ext = ".jar", module_name_to_file_name(Globals, do_create_dirs, Ext, ModuleName, OutputFileName, !IO) ; LinkTargetType = java_archive, Ext = ".jar", module_name_to_file_name(Globals, do_create_dirs, Ext, ModuleName, OutputFileName, !IO) ; LinkTargetType = erlang_archive, Ext = ".beams", module_name_to_lib_file_name(Globals, "lib", ModuleName, Ext, do_create_dirs, OutputFileName, !IO) ). :- func get_launcher_script_extension(globals) = string. get_launcher_script_extension(Globals) = Ext :- globals.get_target_env_type(Globals, TargetEnvType), ( % XXX we should actually generate a .ps1 file for PowerShell. ( TargetEnvType = env_type_win_cmd ; TargetEnvType = env_type_powershell ), Ext = ".bat" ; ( TargetEnvType = env_type_posix ; TargetEnvType = env_type_cygwin ; TargetEnvType = env_type_msys ), Ext = "" ). :- pred link_exe_or_shared_lib(globals::in, io.output_stream::in, linked_target_type::in(bound(executable ; shared_library)), module_name::in, file_name::in, list(string)::in, bool::out, io::di, io::uo) is det. link_exe_or_shared_lib(Globals, ErrorStream, LinkTargetType, ModuleName, OutputFileName, ObjectsList, Succeeded, !IO) :- ( LinkTargetType = shared_library, CommandOpt = link_shared_lib_command, RpathFlagOpt = shlib_linker_rpath_flag, RpathSepOpt = shlib_linker_rpath_separator, LDFlagsOpt = ld_libflags, ThreadFlagsOpt = shlib_linker_thread_flags, DebugFlagsOpt = shlib_linker_debug_flags, TraceFlagsOpt = shlib_linker_trace_flags, globals.lookup_bool_option(Globals, allow_undefined, AllowUndef), ( AllowUndef = yes, globals.lookup_string_option(Globals, linker_allow_undefined_flag, UndefOpt) ; AllowUndef = no, globals.lookup_string_option(Globals, linker_error_undefined_flag, UndefOpt) ), ReserveStackSizeOpt = "" ; LinkTargetType = executable, CommandOpt = link_executable_command, RpathFlagOpt = linker_rpath_flag, RpathSepOpt = linker_rpath_separator, LDFlagsOpt = ld_flags, ThreadFlagsOpt = linker_thread_flags, DebugFlagsOpt = linker_debug_flags, TraceFlagsOpt = linker_trace_flags, UndefOpt = "", ReserveStackSizeOpt = reserve_stack_size_flags(Globals) ), % Should the executable be stripped? globals.lookup_bool_option(Globals, strip, Strip), ( if LinkTargetType = executable, Strip = yes then globals.lookup_string_option(Globals, linker_strip_flag, LinkerStripOpt), globals.lookup_string_option(Globals, strip_executable_command, StripExeCommand), globals.lookup_string_option(Globals, mercury_linkage, MercuryLinkage), ( if MercuryLinkage = "shared" then StripExeFlagsOpt = strip_executable_shared_flags else StripExeFlagsOpt = strip_executable_static_flags ), globals.lookup_string_option(Globals, StripExeFlagsOpt, StripExeFlags) else LinkerStripOpt = "", StripExeCommand = "", StripExeFlags = "" ), globals.lookup_bool_option(Globals, target_debug, TargetDebug), ( TargetDebug = yes, globals.lookup_string_option(Globals, DebugFlagsOpt, DebugOpts) ; TargetDebug = no, DebugOpts = "" ), globals.lookup_string_option(Globals, linker_sanitizer_flags, SanitizerOpts), % Should the executable be statically linked? globals.lookup_string_option(Globals, linkage, Linkage), ( if LinkTargetType = executable, Linkage = "static" then globals.lookup_string_option(Globals, linker_static_flags, StaticOpts) else StaticOpts = "" ), % Are the thread libraries needed? use_thread_libs(Globals, UseThreadLibs), ( UseThreadLibs = yes, globals.lookup_string_option(Globals, ThreadFlagsOpt, ThreadOpts), % Determine which options are needed to link to libhwloc, if % libhwloc is not used then the string option will be empty. ( if Linkage = "shared" then HwlocFlagsOpt = hwloc_libs else if Linkage = "static" then HwlocFlagsOpt = hwloc_static_libs else unexpected($module, $pred, "Invalid linkage") ), globals.lookup_string_option(Globals, HwlocFlagsOpt, HwlocOpts) ; UseThreadLibs = no, ThreadOpts = "", HwlocOpts = "" ), % Find the Mercury standard libraries. get_mercury_std_libs(Globals, LinkTargetType, MercuryStdLibs), % Find which system libraries are needed. get_system_libs(Globals, LinkTargetType, SystemLibs), % With --restricted-command-line we may need to some additional % options to the linker. % (See the comment above get_restricted_command_lin_link_opts/3 for % details.) get_restricted_command_line_link_opts(Globals, LinkTargetType, ResCmdLinkOpts), globals.lookup_accumulating_option(Globals, LDFlagsOpt, LDFlagsList), join_string_list(LDFlagsList, "", "", " ", LDFlags), globals.lookup_accumulating_option(Globals, link_library_directories, LinkLibraryDirectoriesList), globals.lookup_string_option(Globals, linker_path_flag, LinkerPathFlag), join_quoted_string_list(LinkLibraryDirectoriesList, LinkerPathFlag, "", " ", LinkLibraryDirectories), % Set up the runtime library path. get_runtime_library_path_opts(Globals, LinkTargetType, RpathFlagOpt, RpathSepOpt, RpathOpts), % Set up any framework search paths. get_framework_directories(Globals, FrameworkDirectories), % Set up the install name for shared libraries. globals.lookup_bool_option(Globals, shlib_linker_use_install_name, UseInstallName), ( if UseInstallName = yes, LinkTargetType = shared_library then % NOTE: `ShLibFileName' must *not* be prefixed with a directory. % get_install_name_option will prefix it with the correct % directory which is the one where the library is going to % be installed, *not* where it is going to be built. % BaseFileName = sym_name_to_string(ModuleName), globals.lookup_string_option(Globals, shared_library_extension, SharedLibExt), ShLibFileName = "lib" ++ BaseFileName ++ SharedLibExt, get_install_name_option(Globals, ShLibFileName, InstallNameOpt) else InstallNameOpt = "" ), globals.get_trace_level(Globals, TraceLevel), TraceLevelIsNone = given_trace_level_is_none(TraceLevel), ( TraceLevelIsNone = yes, TraceOpts = "" ; TraceLevelIsNone = no, globals.lookup_string_option(Globals, TraceFlagsOpt, TraceOpts) ), get_frameworks(Globals, Frameworks), get_link_libraries(Globals, MaybeLinkLibraries, !IO), globals.lookup_string_option(Globals, linker_opt_separator, LinkOptSep), ( MaybeLinkLibraries = yes(LinkLibrariesList), join_quoted_string_list(LinkLibrariesList, "", "", " ", LinkLibraries), globals.lookup_bool_option(Globals, restricted_command_line, RestrictedCommandLine), ( % If we have a restricted command line then it's possible % that following link command will call sub-commands itself % and thus overflow the command line, so in this case % we first create an archive of all of the object files. RestrictedCommandLine = yes, globals.lookup_string_option(Globals, library_extension, LibExt), io.get_temp_directory(TempDir, !IO), io.make_temp_file(TempDir, "mtmp", LibExt, TmpArchiveResult, !IO), ( TmpArchiveResult = ok(TmpArchive), % Only include actual object files in the temporary archive, % not other files such as other archives. filter_object_files(Globals, ObjectsList, ProperObjectFiles, NonObjectFiles), % Delete the currently empty output file first, otherwise ar % will fail to recognise its file format. remove_file(TmpArchive, _, !IO), create_archive(Globals, ErrorStream, TmpArchive, yes, ProperObjectFiles, ArchiveSucceeded, !IO), MaybeDeleteTmpArchive = yes(TmpArchive), join_quoted_string_list([TmpArchive | NonObjectFiles], "", "", " ", Objects) ; TmpArchiveResult = error(Error), io.format(stderr_stream, "Could not create temporary file: %s\n", [s(error_message(Error))], !IO), ArchiveSucceeded = no, MaybeDeleteTmpArchive = no, join_quoted_string_list(ObjectsList, "", "", " ", Objects) ) ; RestrictedCommandLine = no, ArchiveSucceeded = yes, MaybeDeleteTmpArchive = no, join_quoted_string_list(ObjectsList, "", "", " ", Objects) ), ( ArchiveSucceeded = yes, % Note that LDFlags may contain `-l' options so it should come % after Objects. globals.lookup_string_option(Globals, CommandOpt, Command), get_linker_output_option(Globals, LinkTargetType, OutputOpt), string.append_list([ Command, " ", StaticOpts, " ", LinkerStripOpt, " ", UndefOpt, " ", ThreadOpts, " ", TraceOpts, " ", ReserveStackSizeOpt, " ", OutputOpt, quote_arg(OutputFileName), " ", Objects, " ", LinkOptSep, " ", LinkLibraryDirectories, " ", RpathOpts, " ", FrameworkDirectories, " ", InstallNameOpt, " ", DebugOpts, " ", SanitizerOpts, " ", Frameworks, " ", ResCmdLinkOpts, " ", LDFlags, " ", LinkLibraries, " ", MercuryStdLibs, " ", HwlocOpts, " ", SystemLibs], LinkCmd), globals.lookup_bool_option(Globals, demangle, Demangle), ( Demangle = yes, globals.lookup_string_option(Globals, demangle_command, DemangleCmd), MaybeDemangleCmd = yes(DemangleCmd) ; Demangle = no, MaybeDemangleCmd = no ), invoke_system_command_maybe_filter_output(Globals, ErrorStream, cmd_verbose_commands, LinkCmd, MaybeDemangleCmd, LinkSucceeded, !IO), % Invoke strip utility separately if required. ( if LinkSucceeded = yes, LinkerStripOpt = "", StripExeCommand \= "" then string.append_list([ StripExeCommand, " ", StripExeFlags, " ", quote_arg(OutputFileName) ], StripCmd), invoke_system_command_maybe_filter_output(Globals, ErrorStream, cmd_verbose_commands, StripCmd, no, Succeeded, !IO) else Succeeded = LinkSucceeded ) ; ArchiveSucceeded = no, Succeeded = no ), ( MaybeDeleteTmpArchive = yes(FileToDelete), io.remove_file(FileToDelete, _, !IO) ; MaybeDeleteTmpArchive = no ) ; MaybeLinkLibraries = no, Succeeded = no ). % Find the standard Mercury libraries, and the system % libraries needed by them. % Return the empty string if --mercury-standard-library-directory % is not set. % NOTE: changes here may require changes to get_mercury_std_libs_for_java. % :- pred get_mercury_std_libs(globals::in, linked_target_type::in, string::out) is det. get_mercury_std_libs(Globals, TargetType, StdLibs) :- globals.lookup_maybe_string_option(Globals, mercury_standard_library_directory, MaybeStdlibDir), ( MaybeStdlibDir = yes(StdLibDir), globals.get_gc_method(Globals, GCMethod), ( ( TargetType = executable ; TargetType = static_library ; TargetType = shared_library ), globals.lookup_string_option(Globals, library_extension, LibExt), globals.lookup_string_option(Globals, mercury_linkage, MercuryLinkage) ; ( TargetType = csharp_executable ; TargetType = csharp_library ), LibExt = ".dll", MercuryLinkage = "csharp" ; ( TargetType = java_executable ; TargetType = java_archive ; TargetType = erlang_launcher ; TargetType = erlang_archive ), unexpected($module, $pred, string(TargetType)) ), grade_directory_component(Globals, GradeDir), % GC libraries. % We always compile with hgc since it's home-grown and very small. ( GCMethod = gc_automatic, StaticGCLibs = "", SharedGCLibs = "" ; GCMethod = gc_none, StaticGCLibs = "", SharedGCLibs = "" ; GCMethod = gc_hgc, StaticGCLibs = "", SharedGCLibs = "" ; ( GCMethod = gc_boehm, GCGrade0 = "gc" ; GCMethod = gc_boehm_debug, GCGrade0 = "gc_debug" ), globals.lookup_bool_option(Globals, low_level_debug, LLDebug), ( LLDebug = yes, GCGrade1 = GCGrade0 ++ "_ll_debug" ; LLDebug = no, GCGrade1 = GCGrade0 ), globals.lookup_bool_option(Globals, profile_time, ProfTime), globals.lookup_bool_option(Globals, profile_deep, ProfDeep), ( if ( ProfTime = yes ; ProfDeep = yes ) then GCGrade2 = GCGrade1 ++ "_prof" else GCGrade2 = GCGrade1 ), globals.lookup_bool_option(Globals, parallel, Parallel), ( Parallel = yes, GCGrade = "par_" ++ GCGrade2 ; Parallel = no, GCGrade = GCGrade2 ), link_lib_args(Globals, TargetType, StdLibDir, "", LibExt, GCGrade, StaticGCLibs, SharedGCLibs) ; GCMethod = gc_accurate, StaticGCLibs = "", SharedGCLibs = "" ), % Trace libraries. globals.get_trace_level(Globals, TraceLevel), TraceLevelIsNone = given_trace_level_is_none(TraceLevel), ( TraceLevelIsNone = yes, StaticTraceLibs = "", SharedTraceLibs = "" ; TraceLevelIsNone = no, link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_trace", StaticTraceLib, TraceLib), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_eventspec", StaticEventSpecLib, EventSpecLib), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_browser", StaticBrowserLib, BrowserLib), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_mdbcomp", StaticMdbCompLib, MdbCompLib), StaticTraceLibs = string.join_list(" ", [StaticTraceLib, StaticEventSpecLib, StaticBrowserLib, StaticMdbCompLib]), SharedTraceLibs = string.join_list(" ", [TraceLib, EventSpecLib, BrowserLib, MdbCompLib]) ), % Source-to-source debugging libraries. globals.lookup_bool_option(Globals, link_ssdb_libs, SourceDebug), ( SourceDebug = yes, link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_ssdb", StaticSsdbLib, SsdbLib), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_browser", StaticBrowserLib2, BrowserLib2), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_mdbcomp", StaticMdbCompLib2, MdbCompLib2), StaticSourceDebugLibs = string.join_list(" ", [StaticSsdbLib, StaticBrowserLib2, StaticMdbCompLib2]), SharedSourceDebugLibs = string.join_list(" ", [SsdbLib, BrowserLib2, MdbCompLib2]) ; SourceDebug = no, StaticSourceDebugLibs = "", SharedSourceDebugLibs = "" ), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_std", StaticStdLib, StdLib), link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, "mer_rt", StaticRuntimeLib, RuntimeLib), ( if MercuryLinkage = "static" then StdLibs = string.join_list(" ", [ StaticTraceLibs, StaticSourceDebugLibs, StaticStdLib, StaticRuntimeLib, StaticGCLibs ]) else if MercuryLinkage = "shared" then StdLibs = string.join_list(" ", [ SharedTraceLibs, SharedSourceDebugLibs, StdLib, RuntimeLib, SharedGCLibs ]) else if MercuryLinkage = "csharp" then StdLibs = string.join_list(" ", [ SharedTraceLibs, SharedSourceDebugLibs, StdLib ]) else unexpected($module, $pred, "unknown linkage " ++ MercuryLinkage) ) ; MaybeStdlibDir = no, StdLibs = "" ). :- pred link_lib_args(globals::in, linked_target_type::in, string::in, string::in, string::in, string::in, string::out, string::out) is det. link_lib_args(Globals, TargetType, StdLibDir, GradeDir, LibExt, Name, StaticArg, SharedArg) :- ( ( TargetType = executable ; TargetType = shared_library ; TargetType = static_library ), LibPrefix = "lib" ; ( TargetType = csharp_executable ; TargetType = csharp_library ), LibPrefix = "" ; ( TargetType = java_executable ; TargetType = java_archive ; TargetType = erlang_launcher ; TargetType = erlang_archive ), unexpected($module, $pred, string(TargetType)) ), StaticLibName = LibPrefix ++ Name ++ LibExt, StaticArg = quote_arg(StdLibDir/"lib"/GradeDir/StaticLibName), make_link_lib(Globals, TargetType, Name, SharedArg). % Pass either `-llib' or `PREFIX/lib/GRADE/liblib.a', depending on % whether we are linking with static or shared Mercury libraries. % :- pred get_link_libraries(globals::in, maybe(list(string))::out, io::di, io::uo) is det. get_link_libraries(Globals, MaybeLinkLibraries, !IO) :- globals.lookup_accumulating_option(Globals, mercury_library_directories, MercuryLibDirs0), grade_directory_component(Globals, GradeDir), MercuryLibDirs = list.map((func(LibDir) = LibDir/"lib"/GradeDir), MercuryLibDirs0), globals.lookup_accumulating_option(Globals, link_libraries, LinkLibrariesList0), list.map_foldl2(process_link_library(Globals, MercuryLibDirs), LinkLibrariesList0, LinkLibrariesList, yes, LibrariesSucceeded, !IO), ( LibrariesSucceeded = yes, MaybeLinkLibraries = yes(LinkLibrariesList) ; LibrariesSucceeded = no, MaybeLinkLibraries = no ). :- pred make_link_lib(globals::in, linked_target_type::in, string::in, string::out) is det. make_link_lib(Globals, TargetType, LibName, LinkOpt) :- ( ( TargetType = executable, LinkLibFlag = linker_link_lib_flag, LinkLibSuffix = linker_link_lib_suffix ; TargetType = shared_library, LinkLibFlag = shlib_linker_link_lib_flag, LinkLibSuffix = shlib_linker_link_lib_suffix ), globals.lookup_string_option(Globals, LinkLibFlag, LinkLibOpt), globals.lookup_string_option(Globals, LinkLibSuffix, Suffix), LinkOpt = quote_arg(LinkLibOpt ++ LibName ++ Suffix) ; ( TargetType = csharp_executable ; TargetType = csharp_library ), LinkLibOpt = "-r:", Suffix = ".dll", LinkOpt = quote_arg(LinkLibOpt ++ LibName ++ Suffix) ; ( TargetType = static_library ; TargetType = java_executable ; TargetType = java_archive ; TargetType = erlang_launcher ; TargetType = erlang_archive ), unexpected($module, $pred, string(TargetType)) ). :- pred get_runtime_library_path_opts(globals::in, linked_target_type::in, option::in(bound(shlib_linker_rpath_flag ; linker_rpath_flag)), option::in(bound(shlib_linker_rpath_separator ; linker_rpath_separator)), string::out) is det. get_runtime_library_path_opts(Globals, LinkTargetType, RpathFlagOpt, RpathSepOpt, RpathOpts) :- globals.lookup_bool_option(Globals, shlib_linker_use_install_name, UseInstallName), shared_libraries_supported(Globals, SharedLibsSupported), globals.lookup_string_option(Globals, linkage, Linkage), ( if UseInstallName = no, SharedLibsSupported = yes, ( Linkage = "shared" ; LinkTargetType = shared_library ) then globals.lookup_accumulating_option(Globals, runtime_link_library_directories, RpathDirs0), RpathDirs = list.map(quote_arg, RpathDirs0), ( RpathDirs = [], RpathOpts = "" ; RpathDirs = [_ | _], globals.lookup_string_option(Globals, RpathSepOpt, RpathSep), globals.lookup_string_option(Globals, RpathFlagOpt, RpathFlag), RpathOpts0 = string.join_list(RpathSep, RpathDirs), RpathOpts = RpathFlag ++ RpathOpts0 ) else RpathOpts = "" ). :- pred get_system_libs(globals::in, linked_target_type::in, string::out) is det. get_system_libs(Globals, TargetType, SystemLibs) :- % System libraries used when tracing. globals.get_trace_level(Globals, TraceLevel), TraceLevelIsNone = given_trace_level_is_none(TraceLevel), ( TraceLevelIsNone = yes, SystemTraceLibs = "" ; TraceLevelIsNone = no, globals.lookup_string_option(Globals, trace_libs, SystemTraceLibs0), globals.lookup_bool_option(Globals, use_readline, UseReadline), ( UseReadline = yes, globals.lookup_string_option(Globals, readline_libs, ReadlineLibs), SystemTraceLibs = SystemTraceLibs0 ++ " " ++ ReadlineLibs ; UseReadline = no, SystemTraceLibs = SystemTraceLibs0 ) ), % Thread libraries use_thread_libs(Globals, UseThreadLibs), ( UseThreadLibs = yes, globals.lookup_string_option(Globals, thread_libs, ThreadLibs) ; UseThreadLibs = no, ThreadLibs = "" ), % Other system libraries. ( TargetType = shared_library, globals.lookup_string_option(Globals, shared_libs, OtherSystemLibs) ; TargetType = executable, globals.lookup_string_option(Globals, math_lib, OtherSystemLibs) ; ( TargetType = static_library ; TargetType = csharp_executable ; TargetType = csharp_library ; TargetType = java_executable ; TargetType = java_archive ; TargetType = erlang_launcher ; TargetType = erlang_archive ), unexpected($module, $pred, string(TargetType)) ), SystemLibs = string.join_list(" ", [SystemTraceLibs, OtherSystemLibs, ThreadLibs]). :- pred use_thread_libs(globals::in, bool::out) is det. use_thread_libs(Globals, UseThreadLibs) :- globals.lookup_bool_option(Globals, parallel, UseThreadLibs). % When using --restricted-command-line with Visual C we add all the object % files to a temporary archive before linking an executable. % However, if only .lib files are given on the command line then % the linker needs to manually told some details that it usually infers % from the object files, for example the program entry point and the % target machine type. % :- pred get_restricted_command_line_link_opts(globals::in, linked_target_type::in, string::out) is det. get_restricted_command_line_link_opts(Globals, LinkTargetType, ResCmdLinkOpts) :- globals.lookup_bool_option(Globals, restricted_command_line, RestrictedCommandLine), ( RestrictedCommandLine = yes, ( LinkTargetType = executable, get_c_compiler_type(Globals, C_CompilerType), ( C_CompilerType = cc_cl(_), % XXX WIN64 - this will need to be revisited when we begin % supporting 64-bit Windows. ResCmdLinkFlags = [ "-nologo", "-subsystem:console", "-machine:x86", "-entry:wmainCRTStartup", "-defaultlib:libcmt" ], join_string_list(ResCmdLinkFlags, "", "", " ", ResCmdLinkOpts) ; ( C_CompilerType = cc_gcc(_, _, _) ; C_CompilerType = cc_clang(_) ; C_CompilerType = cc_unknown ), ResCmdLinkOpts = "" ) ; ( LinkTargetType = static_library ; LinkTargetType = shared_library ; LinkTargetType = csharp_executable ; LinkTargetType = csharp_library ; LinkTargetType = java_executable ; LinkTargetType = java_archive ; LinkTargetType = erlang_launcher ; LinkTargetType = erlang_archive ), ResCmdLinkOpts = "" ) ; RestrictedCommandLine = no, ResCmdLinkOpts = "" ). % Filter list of files into those with and without known object file % extensions. % :- pred filter_object_files(globals::in, list(string)::in, list(string)::out, list(string)::out) is det. filter_object_files(Globals, Files, ObjectFiles, NonObjectFiles) :- globals.lookup_string_option(Globals, object_file_extension, ObjExt), globals.lookup_string_option(Globals, pic_object_file_extension, PicObjExt), list.filter(has_object_file_extension(ObjExt, PicObjExt), Files, ObjectFiles, NonObjectFiles). :- pred has_object_file_extension(string::in, string::in, string::in) is semidet. has_object_file_extension(ObjExt, PicObjExt, FileName) :- ( string.suffix(FileName, ObjExt) ; string.suffix(FileName, PicObjExt) ). post_link_make_symlink_or_copy(Globals, ErrorStream, LinkTargetType, ModuleName, Succeeded, MadeSymlinkOrCopy, !IO) :- globals.lookup_bool_option(Globals, use_grade_subdirs, UseGradeSubdirs), ( UseGradeSubdirs = yes, link_output_filename(Globals, LinkTargetType, ModuleName, Ext, OutputFileName, !IO), % Link/copy the executable into the user's directory. globals.set_option(use_subdirs, bool(no), Globals, NoSubdirGlobals0), globals.set_option(use_grade_subdirs, bool(no), NoSubdirGlobals0, NoSubdirGlobals), ( ( LinkTargetType = executable ; LinkTargetType = csharp_executable ; LinkTargetType = csharp_library ; LinkTargetType = java_executable ; LinkTargetType = java_archive ; LinkTargetType = erlang_launcher ), module_name_to_file_name(NoSubdirGlobals, do_not_create_dirs, Ext, ModuleName, UserDirFileName, !IO) ; ( LinkTargetType = static_library ; LinkTargetType = shared_library ; LinkTargetType = erlang_archive ), module_name_to_lib_file_name(NoSubdirGlobals, "lib", ModuleName, Ext, do_not_create_dirs, UserDirFileName, !IO) ), same_timestamp(OutputFileName, UserDirFileName, SameTimestamp, !IO), ( SameTimestamp = yes, Succeeded0 = yes, MadeSymlinkOrCopy = no ; SameTimestamp = no, io.set_output_stream(ErrorStream, OutputStream, !IO), % Remove the target of the symlink/copy in case it already exists. io.remove_file_recursively(UserDirFileName, _, !IO), % Erlang "archives" are just directories of .beam files, % so we need to copy them as directories rather than files % (on systems on which symbolic links are not available). ( if LinkTargetType = erlang_archive then make_symlink_or_copy_dir(Globals, OutputFileName, UserDirFileName, Succeeded0, !IO) else make_symlink_or_copy_file(Globals, OutputFileName, UserDirFileName, Succeeded0, !IO) ), io.set_output_stream(OutputStream, _, !IO), MadeSymlinkOrCopy = yes ), % For the Java and C# grades we also need to symlink or copy the % launcher scripts or batch files. ( if Succeeded0 = yes, ( LinkTargetType = csharp_executable, % NOTE: we don't generate a launcher script for C# executables % on Windows -- it isn't necessary since they can be executed % directly. globals.get_target_env_type(Globals, TargetEnvType), TargetEnvType = env_type_posix ; LinkTargetType = java_executable ) then ScriptExt = get_launcher_script_extension(Globals), module_name_to_file_name(Globals, do_not_create_dirs, ScriptExt, ModuleName, OutputScriptName, !IO), module_name_to_file_name(NoSubdirGlobals, do_not_create_dirs, ScriptExt, ModuleName, UserDirScriptName, !IO), same_timestamp(OutputScriptName, UserDirScriptName, ScriptSameTimestamp, !IO), ( ScriptSameTimestamp = yes, Succeeded = yes ; ScriptSameTimestamp = no, io.set_output_stream(ErrorStream, ScriptOutputStream, !IO), % Remove the target of the symlink/copy in case % it already exists. io.remove_file_recursively(UserDirScriptName, _, !IO), make_symlink_or_copy_file(Globals, OutputScriptName, UserDirScriptName, Succeeded, !IO), io.set_output_stream(ScriptOutputStream, _, !IO) ) else Succeeded = Succeeded0 ) ; UseGradeSubdirs = no, Succeeded = yes, MadeSymlinkOrCopy = no ). :- pred get_framework_directories(globals::in, string::out) is det. get_framework_directories(Globals, FrameworkDirs) :- globals.lookup_accumulating_option(Globals, framework_directories, FrameworkDirs0), join_quoted_string_list(FrameworkDirs0, "-F", "", " ", FrameworkDirs). :- pred get_frameworks(globals::in, string::out) is det. get_frameworks(Globals, FrameworkOpts) :- globals.lookup_accumulating_option(Globals, frameworks, Frameworks), join_quoted_string_list(Frameworks, "-framework ", "", " ", FrameworkOpts). :- pred same_timestamp(string::in, string::in, bool::out, io::di, io::uo) is det. same_timestamp(FileNameA, FileNameB, SameTimestamp, !IO) :- compare_file_timestamps(FileNameA, FileNameB, MaybeCompare, !IO), ( if MaybeCompare = yes(=) then SameTimestamp = yes else SameTimestamp = no ). shared_libraries_supported(Globals, Supported) :- % XXX This seems to be the standard way to check whether shared libraries % are supported but it's not very nice. globals.lookup_string_option(Globals, library_extension, LibExt), globals.lookup_string_option(Globals, shared_library_extension, SharedLibExt), Supported = (if LibExt \= SharedLibExt then yes else no). :- pred get_linker_output_option(globals::in, linked_target_type::in, string::out) is det. get_linker_output_option(Globals, LinkTargetType, OutputOpt) :- get_c_compiler_type(Globals, C_CompilerType), % XXX we should allow the user to override the compiler's choice of % output option here. % NOTE: the spacing around the value of OutputOpt here is important. % Any changes should be reflected in predicate link_exe_or_shared_lib/9. ( C_CompilerType = cc_cl(_), ( if LinkTargetType = executable then % NOTE: -Fe _must not_ be separated from its argument by any % whitspace; the lack of a trailing space in the following % is deliberate. OutputOpt = " -Fe" else % XXX This is almost certainly wrong, but we don't currently % support building shared libraries with mmc on Windows % anyway. OutputOpt = " -o " ) ; ( C_CompilerType = cc_gcc(_, _, _) ; C_CompilerType = cc_clang(_) ; C_CompilerType = cc_unknown ), OutputOpt = " -o " ). :- func reserve_stack_size_flags(globals) = string. reserve_stack_size_flags(Globals) = Flags :- globals.lookup_int_option(Globals, cstack_reserve_size, ReserveStackSize), ( if ReserveStackSize = -1 then Flags = "" else get_c_compiler_type(Globals, C_CompilerType), ( ( C_CompilerType = cc_gcc(_, _, _) ; C_CompilerType = cc_clang(_) ; C_CompilerType = cc_unknown ), string.format("-Wl,--stack=%d", [i(ReserveStackSize)], Flags) ; C_CompilerType = cc_cl(_), string.format("-stack:%d", [i(ReserveStackSize)], Flags) ) ). %-----------------------------------------------------------------------------% :- pred process_link_library(globals::in, list(dir_name)::in, string::in, string::out, bool::in, bool::out, io::di, io::uo) is det. process_link_library(Globals, MercuryLibDirs, LibName, LinkerOpt, !Succeeded, !IO) :- globals.get_target(Globals, Target), ( Target = target_c, globals.lookup_string_option(Globals, mercury_linkage, MercuryLinkage), globals.get_c_compiler_type(Globals, CCompilerType), ( ( CCompilerType = cc_gcc(_, _, _) ; CCompilerType = cc_clang(_) ; CCompilerType = cc_unknown ), LinkOpt = "-l", LibSuffix = "" ; CCompilerType = cc_cl(_), LinkOpt = "", LibSuffix = ".lib" ) ; Target = target_csharp, MercuryLinkage = "shared", LinkOpt = "-r:", LibSuffix = ".dll" ; Target = target_java, unexpected($module, $pred, "target_java") ; Target = target_erlang, unexpected($module, $pred, "target_erlang") ), globals.lookup_accumulating_option(Globals, mercury_libraries, MercuryLibs), ( if MercuryLinkage = "static", list.member(LibName, MercuryLibs) then % If we are linking statically with Mercury libraries, pass the % absolute pathname of the `.a' file for the library. file_name_to_module_name(LibName, LibModuleName), globals.lookup_string_option(Globals, library_extension, LibExt), globals.set_option(use_grade_subdirs, bool(no), Globals, NoSubDirGlobals), module_name_to_lib_file_name(NoSubDirGlobals, "lib", LibModuleName, LibExt, do_not_create_dirs, LibFileName, !IO), search_for_file_returning_dir(MercuryLibDirs, LibFileName, MaybeDirName, !IO), ( MaybeDirName = ok(DirName), LinkerOpt = DirName/LibFileName ; MaybeDirName = error(Error), LinkerOpt = "", write_error_pieces_maybe_with_context(Globals, no, 0, [words(Error)], !IO), !:Succeeded = no ) else LinkerOpt = LinkOpt ++ LibName ++ LibSuffix ). :- pred create_archive(globals::in, io.output_stream::in, file_name::in, bool::in, list(file_name)::in, bool::out, io::di, io::uo) is det. create_archive(Globals, ErrorStream, LibFileName, Quote, ObjectList, Succeeded, !IO) :- globals.lookup_string_option(Globals, create_archive_command, ArCmd), globals.lookup_accumulating_option(Globals, create_archive_command_flags, ArFlagsList), join_string_list(ArFlagsList, "", "", " ", ArFlags), globals.lookup_string_option(Globals, create_archive_command_output_flag, ArOutputFlag), globals.lookup_string_option(Globals, ranlib_command, RanLib), ( Quote = yes, join_quoted_string_list(ObjectList, "", "", " ", Objects) ; Quote = no, % Elements of ObjectList may contain shell wildcards, which % are intended to cause the element to expand to several words. % Quoting would prevent that. join_string_list(ObjectList, "", "", " ", Objects) ), % NOTE: when using the Windows Library Manager Tool (lib) there must % _not_ be a space between the -OUT: option and its argument. % XXX we actually check the C compiler type here since that is more % robust than using the values of the configuration options used for % archive creation. get_c_compiler_type(Globals, C_CompilerType), ( % If we are using Visual C then we must be using the Microsoft % lib tool. C_CompilerType = cc_cl(_), ArOutputSpace = "" ; ( C_CompilerType = cc_gcc(_, _, _) ; C_CompilerType = cc_clang(_) ; C_CompilerType = cc_unknown ), ArOutputSpace = " " ), MakeLibCmdArgs = string.append_list([ ArFlags, " ", ArOutputFlag, ArOutputSpace, LibFileName, " ", Objects] ), invoke_long_system_command(Globals, ErrorStream, cmd_verbose_commands, ArCmd, MakeLibCmdArgs, MakeLibCmdSucceeded, !IO), ( if ( RanLib = "" ; MakeLibCmdSucceeded = no ) then Succeeded = MakeLibCmdSucceeded else RanLibCmd = string.append_list([RanLib, " ", LibFileName]), invoke_system_command(Globals, ErrorStream, cmd_verbose_commands, RanLibCmd, Succeeded, !IO) ). :- pred create_csharp_exe_or_lib(globals::in, io.output_stream::in, linked_target_type::in, module_name::in, file_name::in, list(file_name)::in, bool::out, io::di, io::uo) is det. create_csharp_exe_or_lib(Globals, ErrorStream, LinkTargetType, MainModuleName, OutputFileName0, SourceList0, Succeeded, !IO) :- get_system_env_type(Globals, EnvType), get_csharp_compiler_type(Globals, CSharpCompilerType), OutputFileName = csharp_file_name(EnvType, CSharpCompilerType, OutputFileName0), SourceList = list.map(csharp_file_name(EnvType, CSharpCompilerType), SourceList0), % Suppress the MS C# compiler's banner message. ( CSharpCompilerType = csharp_microsoft, NoLogoOpt = "-nologo " ; ( CSharpCompilerType = csharp_mono ; CSharpCompilerType = csharp_unknown ), NoLogoOpt = "" ), globals.lookup_bool_option(Globals, line_numbers, LineNumbers), ( % If we output line numbers, the mono C# compiler outputs lots of % spurious warnings about unused variables and unreachable code, % so disable these warnings. It also confuses #pragma warning, % which is why we make the options global. LineNumbers = yes, NoWarnLineNumberOpt = "-nowarn:162,219 " ; LineNumbers = no, NoWarnLineNumberOpt = "" ), % NOTE: we use the -option style options in preference to the /option % style in order to avoid problems with POSIX style shells. globals.lookup_string_option(Globals, csharp_compiler, CSharpCompiler), globals.lookup_bool_option(Globals, highlevel_data, HighLevelData), ( HighLevelData = yes, HighLevelDataOpt = "-define:MR_HIGHLEVEL_DATA" ; HighLevelData = no, HighLevelDataOpt = "" ), globals.lookup_bool_option(Globals, target_debug, Debug), ( Debug = yes, DebugOpt = "-debug " ; Debug = no, DebugOpt = "" ), globals.lookup_accumulating_option(Globals, csharp_flags, CSCFlagsList), ( LinkTargetType = csharp_executable, TargetOption = "-target:exe", SignAssemblyOpt = "" ; LinkTargetType = csharp_library, TargetOption = "-target:library", globals.lookup_string_option(Globals, sign_assembly, KeyFile), ( if KeyFile = "" then SignAssemblyOpt = "" else SignAssemblyOpt = "-keyfile:" ++ KeyFile ++ " " ) ; ( LinkTargetType = executable ; LinkTargetType = static_library ; LinkTargetType = shared_library ; LinkTargetType = java_executable ; LinkTargetType = java_archive ; LinkTargetType = erlang_launcher ; LinkTargetType = erlang_archive ), unexpected($module, $pred, "wrong target type") ), globals.lookup_accumulating_option(Globals, link_library_directories, LinkLibraryDirectoriesList0), LinkLibraryDirectoriesList = list.map(csharp_file_name(EnvType, CSharpCompilerType), LinkLibraryDirectoriesList0), LinkerPathFlag = "-lib:", join_quoted_string_list(LinkLibraryDirectoriesList, LinkerPathFlag, "", " ", LinkLibraryDirectories), get_link_libraries(Globals, MaybeLinkLibraries, !IO), ( MaybeLinkLibraries = yes(LinkLibrariesList0), LinkLibrariesList = list.map(csharp_file_name(EnvType, CSharpCompilerType), LinkLibrariesList0), join_quoted_string_list(LinkLibrariesList, "", "", " ", LinkLibraries) ; MaybeLinkLibraries = no, LinkLibraries = "" ), get_mercury_std_libs(Globals, LinkTargetType, MercuryStdLibs), Cmd = CSharpCompiler, CmdArgs = string.join_list(" ", [ NoLogoOpt, NoWarnLineNumberOpt, HighLevelDataOpt, DebugOpt, TargetOption, "-out:" ++ OutputFileName, SignAssemblyOpt, LinkLibraryDirectories, LinkLibraries, MercuryStdLibs] ++ CSCFlagsList ++ SourceList), invoke_long_system_command(Globals, ErrorStream, cmd_verbose_commands, Cmd, CmdArgs, Succeeded0, !IO), % Also create a shell script to launch it if necessary. globals.get_target_env_type(Globals, TargetEnvType), globals.lookup_string_option(Globals, cli_interpreter, CLI), ( if Succeeded0 = yes, LinkTargetType = csharp_executable, CLI \= "", TargetEnvType = env_type_posix then create_launcher_shell_script(Globals, MainModuleName, write_cli_shell_script(Globals, OutputFileName), Succeeded, !IO) else Succeeded = Succeeded0 ). % Converts the given filename into a format acceptable to the C# compiler. % % Older MS C# compilers only allowed \ as the path separator, so we convert % all / into \ when using an MS C# compiler on Windows. % :- func csharp_file_name(env_type, csharp_compiler_type, file_name) = file_name. csharp_file_name(env_type_posix, csharp_microsoft, FileName) = FileName. csharp_file_name(env_type_posix, csharp_mono, Filename) = Filename. csharp_file_name(env_type_posix, csharp_unknown, Filename) = Filename. csharp_file_name(env_type_cygwin, csharp_microsoft, Filename) = convert_to_windows_path_format(Filename). csharp_file_name(env_type_cygwin, csharp_mono, Filename) = Filename. csharp_file_name(env_type_cygwin, csharp_unknown, Filename) = Filename. csharp_file_name(env_type_msys, csharp_microsoft, Filename) = convert_to_windows_path_format(Filename). csharp_file_name(env_type_msys, csharp_mono, Filename) = Filename. csharp_file_name(env_type_msys, csharp_unknown, Filename) = Filename. csharp_file_name(env_type_win_cmd, csharp_microsoft, Filename) = convert_to_windows_path_format(Filename). csharp_file_name(env_type_win_cmd, csharp_mono, Filename) = Filename. csharp_file_name(env_type_win_cmd, csharp_unknown, Filename) = convert_to_windows_path_format(Filename). csharp_file_name(env_type_powershell, csharp_microsoft, Filename) = convert_to_windows_path_format(Filename). csharp_file_name(env_type_powershell, csharp_mono, Filename) = Filename. csharp_file_name(env_type_powershell, csharp_unknown, Filename) = convert_to_windows_path_format(Filename). :- func convert_to_windows_path_format(file_name) = file_name. convert_to_windows_path_format(FileName) = string.replace_all(FileName, "/", "\\\\"). :- pred write_cli_shell_script(globals::in, string::in, io.output_stream::in, io::di, io::uo) is det. write_cli_shell_script(Globals, ExeFileName, Stream, !IO) :- globals.lookup_string_option(Globals, cli_interpreter, CLI), globals.lookup_accumulating_option(Globals, link_library_directories, LinkLibraryDirectoriesList), join_quoted_string_list(LinkLibraryDirectoriesList, "", "", ":", LinkLibraryDirectories), list.foldl(io.write_string(Stream), [ "#!/bin/sh\n", "DIR=${0%/*}\n", "MONO_PATH=$MONO_PATH:", LinkLibraryDirectories, "\n", "export MONO_PATH\n", "CLI_INTERPRETER=${CLI_INTERPRETER:-", CLI, "}\n", "exec \"$CLI_INTERPRETER\" \"$DIR/", ExeFileName, "\" \"$@\"\n" ], !IO). %-----------------------------------------------------------------------------% % % Create Java "executables" or archives. % :- pred create_java_exe_or_lib(globals::in, io.text_output_stream::in, linked_target_type::in, module_name::in, file_name::in, list(file_name)::in, bool::out, io::di, io::uo) is det. create_java_exe_or_lib(Globals, ErrorStream, LinkTargetType, MainModuleName, JarFileName, ObjectList, Succeeded, !IO) :- globals.lookup_string_option(Globals, java_archive_command, Jar), list_class_files_for_jar(Globals, ObjectList, ClassSubDir, ListClassFiles, !IO), ( ListClassFiles = [], unexpected($module, $pred, "empty list of .class files") ; ListClassFiles = [_ | _] ), % Write the list of class files to a temporary file and pass the name of % the temporary file to jar using @syntax. The list of class files can be % extremely long. We create the temporary file in the current directory to % avoid problems under Cygwin, where absolute paths will be interpreted % incorrectly when passed to a non-Cygwin jar program. open_temp_output(".", "mtmp", "", TempFileResult, !IO), ( TempFileResult = ok({TempFileName, Stream}), list.foldl(write_jar_class_argument(Stream, ClassSubDir), ListClassFiles, !IO), io.close_output(Stream, !IO), Cmd = string.append_list( [Jar, " cf ", JarFileName, " @", TempFileName]), invoke_system_command(Globals, ErrorStream, cmd_verbose_commands, Cmd, Succeeded0, !IO), io.remove_file(TempFileName, _, !IO), ( Succeeded0 = yes ; Succeeded0 = no, io.remove_file(JarFileName, _, !IO) ) ; TempFileResult = error(ErrorMessage), io.write_string(ErrorStream, ErrorMessage, !IO), io.nl(!IO), Succeeded0 = no ), ( if Succeeded0 = yes, LinkTargetType = java_executable then create_java_shell_script(Globals, MainModuleName, Succeeded, !IO) else Succeeded = Succeeded0 ). :- pred write_jar_class_argument(io.output_stream::in, string::in, string::in, io::di, io::uo) is det. write_jar_class_argument(Stream, ClassSubDir, ClassFileName, !IO) :- ( if dir.path_name_is_absolute(ClassFileName) then true else io.write_string(Stream, "-C ", !IO), io.write_string(Stream, ClassSubDir, !IO), io.write_string(Stream, " ", !IO) ), io.write_string(Stream, ClassFileName, !IO), io.nl(Stream, !IO). %-----------------------------------------------------------------------------% % Create an "Erlang archive", which is simply a directory containing % `.beam' files. % :- pred create_erlang_archive(globals::in, io.output_stream::in, module_name::in, file_name::in, list(file_name)::in, bool::out, io::di, io::uo) is det. create_erlang_archive(Globals, ErrorStream, _ModuleName, ErlangArchiveFileName, ObjectList, Succeeded, !IO) :- % Delete anything in the way first. io.remove_file_recursively(ErlangArchiveFileName, _, !IO), dir.make_directory(ErlangArchiveFileName, Res, !IO), ( Res = ok, copy_erlang_archive_files(Globals, ErrorStream, ErlangArchiveFileName, ObjectList, Succeeded, !IO) ; Res = error(Error), io.write_string(ErrorStream, "Error creating `", !IO), io.write_string(ErrorStream, ErlangArchiveFileName, !IO), io.write_string(ErrorStream, "': ", !IO), io.write_string(ErrorStream, io.error_message(Error), !IO), io.nl(ErrorStream, !IO), Succeeded = no ). :- pred copy_erlang_archive_files(globals::in, io.output_stream::in, file_name::in, list(file_name)::in, bool::out, io::di, io::uo) is det. copy_erlang_archive_files(_Globals, _ErrorStream, _ErlangArchiveFileName, [], yes, !IO). copy_erlang_archive_files(Globals, ErrorStream, ErlangArchiveFileName, [Obj | Objs], Succeeded, !IO) :- copy_file(Globals, Obj, ErlangArchiveFileName, Res, !IO), ( Res = ok, copy_erlang_archive_files(Globals, ErrorStream, ErlangArchiveFileName, Objs, Succeeded, !IO) ; Res = error(Error), io.write_string(ErrorStream, "Error copying `", !IO), io.write_string(ErrorStream, Obj, !IO), io.write_string(ErrorStream, "': ", !IO), io.write_string(ErrorStream, io.error_message(Error), !IO), io.nl(ErrorStream, !IO), Succeeded = no ). %-----------------------------------------------------------------------------% get_object_code_type(Globals, FileType, ObjectCodeType) :- ( ( FileType = executable ; FileType = static_library ; FileType = csharp_executable ; FileType = csharp_library ; FileType = java_executable ; FileType = java_archive ; FileType = erlang_launcher ; FileType = erlang_archive ), ObjectCodeType = non_pic ; FileType = shared_library, globals.lookup_string_option(Globals, pic_object_file_extension, PicObjExt), globals.lookup_string_option(Globals, object_file_extension, ObjExt), ObjectCodeType = ( if PicObjExt = ObjExt then non_pic else pic ) ). get_linked_target_type(Globals, LinkedTargetType) :- globals.lookup_bool_option(Globals, compile_to_shared_lib, MakeSharedLib), ( MakeSharedLib = yes, LinkedTargetType = shared_library ; MakeSharedLib = no, LinkedTargetType = executable ). %-----------------------------------------------------------------------------% % join_string_list(Strings, Prefix, Suffix, Separator, Result): % % Appends the strings in the list `Strings' together into the string % Result. Each string is prefixed by Prefix, suffixed by Suffix and % separated by Separator. % :- pred join_string_list(list(string)::in, string::in, string::in, string::in, string::out) is det. join_string_list([], _Prefix, _Suffix, _Separator, ""). join_string_list([String | Strings], Prefix, Suffix, Separator, Result) :- ( Strings = [], string.append_list([Prefix, String, Suffix], Result) ; Strings = [_ | _], join_string_list(Strings, Prefix, Suffix, Separator, Result0), string.append_list([Prefix, String, Suffix, Separator, Result0], Result) ). % As above, but quote the strings first. Note that the strings in values % of the *flags options are already quoted. % :- pred join_quoted_string_list(list(string)::in, string::in, string::in, string::in, string::out) is det. join_quoted_string_list(Strings, Prefix, Suffix, Separator, Result) :- join_string_list(map(quote_arg, Strings), Prefix, Suffix, Separator, Result). % join_module_list(Globals, ModuleNames, Extension, Result, !IO): % % The list of strings `Result' is computed from the list of strings % `ModuleNames', by removing any directory paths, and converting the % strings to file names and then back, adding the specified Extension. % (This conversion ensures that we follow the usual file naming % conventions.) % :- pred join_module_list(globals::in, list(string)::in, string::in, list(string)::out, io::di, io::uo) is det. join_module_list(_Globals, [], _Extension, [], !IO). join_module_list(Globals, [Module | Modules], Extension, [FileName | FileNames], !IO) :- file_name_to_module_name(dir.det_basename(Module), ModuleName), module_name_to_file_name(Globals, do_not_create_dirs, Extension, ModuleName, FileName, !IO), join_module_list(Globals, Modules, Extension, FileNames, !IO). %-----------------------------------------------------------------------------% make_all_module_command(Globals, Command0, MainModule, AllModules, Command, !IO) :- % Pass the main module first. list.map_foldl( module_name_to_file_name(Globals, do_not_create_dirs, ".m"), [MainModule | list.delete_all(AllModules, MainModule)], ModuleNameStrings, !IO), Command = string.join_list(" ", list.map(quote_arg, [Command0 | ModuleNameStrings])). %-----------------------------------------------------------------------------% :- pragma promise_equivalent_clauses(maybe_pic_object_file_extension/3). maybe_pic_object_file_extension(Globals::in, PIC::in, Ext::out) :- ( PIC = non_pic, globals.lookup_string_option(Globals, object_file_extension, Ext) ; PIC = pic, globals.lookup_string_option(Globals, pic_object_file_extension, Ext) ). maybe_pic_object_file_extension(Globals::in, PIC::out, Ext::in) :- ( if % This test must come first -- if the architecture doesn't need % special treatment for PIC, we should always return `non_pic'. % `mmc --make' depends on this. globals.lookup_string_option(Globals, object_file_extension, Ext) then PIC = non_pic else if globals.lookup_string_option(Globals, pic_object_file_extension, Ext) then PIC = pic else fail ). %-----------------------------------------------------------------------------% % % Standalone interfaces. % % NOTE: the following code is similar to that of make_init_obj/7. % Any changes here may need to be reflected there. make_standalone_interface(Globals, Basename, !IO) :- make_standalone_int_header(Basename, HdrSucceeded, !IO), ( HdrSucceeded = yes, make_standalone_int_body(Globals, Basename, !IO) ; HdrSucceeded = no ). :- pred make_standalone_int_header(string::in, bool::out, io::di, io::uo) is det. make_standalone_int_header(Basename, Succeeded, !IO) :- HdrFileName = Basename ++ ".h", io.open_output(HdrFileName, OpenResult, !IO), ( OpenResult = ok(HdrFileStream), io.write_strings(HdrFileStream, [ "#ifndef ", to_upper(Basename), "_H\n", "#define ", to_upper(Basename), "_H\n", "\n", "#ifdef __cplusplus\n", "extern \"C\" {\n", "#endif\n", "\n", "extern void\n", "mercury_init(int argc, char **argv, void *stackbottom);\n", "\n", "extern int\n", "mercury_terminate(void);\n", "\n", "#ifdef __cplusplus\n", "}\n", "#endif\n", "\n", "#endif /* ", to_upper(Basename), "_H */\n"], !IO), io.close_output(HdrFileStream, !IO), Succeeded = yes ; OpenResult = error(Error), unable_to_open_file(HdrFileName, Error, !IO), Succeeded = no ). :- pred make_standalone_int_body(globals::in, string::in, io::di, io::uo) is det. make_standalone_int_body(Globals, Basename, !IO) :- globals.lookup_accumulating_option(Globals, init_files, InitFiles0), % See the similar code in make_init_target_file for an explanation % of why we must remove duplicates from this list. list.remove_dups(InitFiles0, InitFiles1), globals.lookup_accumulating_option(Globals, trace_init_files, TraceInitFiles0), globals.lookup_maybe_string_option(Globals, mercury_standard_library_directory, MaybeStdLibDir), grade_directory_component(Globals, GradeDir), ( MaybeStdLibDir = yes(StdLibDir), InitFiles2 = [ StdLibDir / "modules" / GradeDir / "mer_rt.init", StdLibDir / "modules" / GradeDir / "mer_std.init" | InitFiles1 ], TraceInitFiles = [ StdLibDir / "modules" / GradeDir / "mer_browser.init", StdLibDir / "modules" / GradeDir / "mer_mdbcomp.init" | TraceInitFiles0 ], SourceDebugInitFiles = [ StdLibDir / "modules" / GradeDir / "mer_ssdb.init" ] ; % Supporting `--no-mercury-standard-library-directory' is necessary % in order to use `--generate-standalone-interface' with the % the lmc script. MaybeStdLibDir = no, InitFiles2 = InitFiles1, TraceInitFiles = TraceInitFiles0, SourceDebugInitFiles = [] ), globals.get_trace_level(Globals, TraceLevel), TraceLevelIsNone = given_trace_level_is_none(TraceLevel), ( TraceLevelIsNone = no, TraceOpt = "-t", InitFiles3 = InitFiles2 ++ TraceInitFiles ; TraceLevelIsNone = yes, TraceOpt = "", InitFiles3 = InitFiles2 ), globals.lookup_bool_option(Globals, link_ssdb_libs, SourceDebug), ( SourceDebug = yes, InitFiles = InitFiles3 ++ SourceDebugInitFiles ; SourceDebug = no, InitFiles = InitFiles3 ), globals.lookup_accumulating_option(Globals, runtime_flags, RuntimeFlagsList), join_quoted_string_list(RuntimeFlagsList, "-r ", "", " ", RuntimeFlags), globals.lookup_accumulating_option(Globals, init_file_directories, InitFileDirsList), join_quoted_string_list(InitFileDirsList, "-I ", "", " ", InitFileDirs), globals.lookup_string_option(Globals, experimental_complexity, ExperimentalComplexity), ( if ExperimentalComplexity = "" then ExperimentalComplexityOpt = "" else ExperimentalComplexityOpt = "-X " ++ ExperimentalComplexity ), compute_grade(Globals, Grade), globals.lookup_string_option(Globals, mkinit_command, MkInit), CFileName = Basename ++ ".c", io.output_stream(ErrorStream, !IO), MkInitArgs = string.append_list( [ " -g ", Grade, " ", TraceOpt, " ", ExperimentalComplexityOpt, " ", RuntimeFlags, " -o ", quote_arg(CFileName), " ", InitFileDirs, " -s " ]), invoke_mkinit(Globals, ErrorStream, cmd_verbose_commands, MkInit, MkInitArgs, InitFiles, MkInitCmdOk, !IO), ( MkInitCmdOk = yes, get_object_code_type(Globals, executable, PIC), maybe_pic_object_file_extension(Globals, PIC, ObjExt), ObjFileName = Basename ++ ObjExt, do_compile_c_file(Globals, ErrorStream, PIC, CFileName, ObjFileName, CompileOk, !IO), ( CompileOk = yes ; CompileOk = no, io.set_exit_status(1, !IO), io.write_string("mercury_compile: error while compiling ", !IO), io.write_string("standalone interface in `", !IO), io.write_string(CFileName, !IO), io.write_string("'\n", !IO) ) ; MkInitCmdOk = no, io.set_exit_status(1, !IO), io.write_string("mercury_compile: error while creating ", !IO), io.write_string("standalone interface in `", !IO), io.write_string(CFileName, !IO), io.write_string("'\n", !IO) ). %-----------------------------------------------------------------------------% % invoke_long_system_command attempts to use the @file style of % calling to avoid command line length arguments on various systems. % If the underlying tool chain doesn't support this it just calls % the normal invoke_system_command and hopes the command isn't too % long. % :- pred invoke_long_system_command(globals::in, io.output_stream::in, command_verbosity::in, string::in, string::in, bool::out, io::di, io::uo) is det. invoke_long_system_command(Globals, ErrorStream, Verbosity, Cmd, Args, Succeeded, !IO) :- invoke_long_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity, Cmd, "", Args, no, Succeeded, !IO). :- pred invoke_long_system_command_maybe_filter_output(globals::in, io.output_stream::in, command_verbosity::in, string::in, string::in, string::in, maybe(string)::in, bool::out, io::di, io::uo) is det. invoke_long_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity, Cmd, NonAtArgs, Args, MaybeProcessOutput, Succeeded, !IO) :- globals.lookup_bool_option(Globals, restricted_command_line, RestrictedCommandLine), ( RestrictedCommandLine = yes, % Avoid generating very long command lines by using @files. open_temp_output(TmpFileResult, !IO), ( TmpFileResult = ok({TmpFile, TmpStream}), % We need to escape any \ before writing them to the file, % otherwise we lose them. TmpFileArgs = string.replace_all(Args, "\\", "\\\\"), io.write_string(TmpStream, TmpFileArgs, !IO), io.close_output(TmpStream, !IO), globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), AtFileName = at_file_name(Globals, TmpFile), ( VeryVerbose = yes, io.format("%% Args placed in %s: `%s'\n", [s(AtFileName), s(TmpFileArgs)], !IO), io.flush_output(!IO) ; VeryVerbose = no ), ( if NonAtArgs = "" then FullCmd = Cmd ++ " " ++ AtFileName else string.append_list([Cmd, " ", NonAtArgs, " ", AtFileName], FullCmd) ), invoke_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity, FullCmd, MaybeProcessOutput, Succeeded0, !IO), io.remove_file(TmpFile, RemoveResult, !IO), ( RemoveResult = ok, Succeeded = Succeeded0 ; RemoveResult = error(_), Succeeded = no ) ; TmpFileResult = error(ErrorMessage), io.write_string(stderr_stream, ErrorMessage, !IO), io.nl(!IO), Succeeded = no ) ; RestrictedCommandLine = no, ( if NonAtArgs = "" then FullCmd = Cmd ++ " " ++ Args else string.append_list([Cmd, " ", NonAtArgs, " ", Args], FullCmd) ), invoke_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity, FullCmd, MaybeProcessOutput, Succeeded, !IO) ). % Form the name of an @file given a file name. % On some systems we need to escape the `@' character. % :- func at_file_name(globals, string) = string. at_file_name(Globals, FileName) = AtFileName :- get_system_env_type(Globals, EnvType), ( EnvType = env_type_powershell, AtFileName = "`@" ++ FileName ; ( EnvType = env_type_posix ; EnvType = env_type_cygwin ; EnvType = env_type_msys ; EnvType = env_type_win_cmd ), AtFileName = "@" ++ FileName ). %-----------------------------------------------------------------------------% % % C compiler flags. % output_c_compiler_flags(Globals, Stream, !IO) :- get_object_code_type(Globals, executable, PIC), gather_c_compiler_flags(Globals, PIC, CFlags), io.write_string(Stream, CFlags, !IO). %-----------------------------------------------------------------------------% % % Grade defines flags. % output_grade_defines(Globals, Stream, !IO) :- get_object_code_type(Globals, executable, _PIC), gather_grade_defines(Globals, GradeDefines), io.write_string(Stream, GradeDefines, !IO), io.nl(Stream, !IO). %-----------------------------------------------------------------------------% % % C include directory flags. % output_c_include_directory_flags(Globals, Stream, !IO) :- gather_c_include_dir_flags(Globals, InclOpts), io.write_string(Stream, InclOpts, !IO), io.nl(Stream, !IO). %-----------------------------------------------------------------------------% % % Library link flags. % output_library_link_flags(Globals, Stream, !IO) :- % We output the library link flags as they are for when we are linking % an executable. LinkTargetType = executable, RpathFlagOpt = linker_rpath_flag, RpathSepOpt = linker_rpath_separator, globals.lookup_accumulating_option(Globals, link_library_directories, LinkLibraryDirectoriesList), globals.lookup_string_option(Globals, linker_path_flag, LinkerPathFlag), join_quoted_string_list(LinkLibraryDirectoriesList, LinkerPathFlag, "", " ", LinkLibraryDirectories), get_runtime_library_path_opts(Globals, LinkTargetType, RpathFlagOpt, RpathSepOpt, RpathOpts), get_link_libraries(Globals, MaybeLinkLibraries, !IO), ( MaybeLinkLibraries = yes(LinkLibrariesList), join_string_list(LinkLibrariesList, "", "", " ", LinkLibraries) ; MaybeLinkLibraries = no, LinkLibraries = "" ), % Find the Mercury standard libraries. get_mercury_std_libs(Globals, LinkTargetType, MercuryStdLibs), get_system_libs(Globals, LinkTargetType, SystemLibs), string.append_list([ LinkLibraryDirectories, " ", RpathOpts, " ", LinkLibraries, " ", MercuryStdLibs, " ", SystemLibs], LinkFlags), io.write_string(Stream, LinkFlags, !IO), io.nl(Stream, !IO). %-----------------------------------------------------------------------------% % Succeeds if the configuration name for this machine matches % *-apple-darwin*, i.e. its an x86 / x86_64 / ppc machine with Mac OS X. % :- pred arch_is_apple_darwin(string::in) is semidet. arch_is_apple_darwin(FullArch) :- ArchComponents = string.split_at_char(('-'), FullArch), % See the comments at the head of config.sub for details of how autoconf % handles configuration names. ArchComponents = [_CPU, Mfr, OS], Mfr = "apple", string.prefix(OS, "darwin"). %-----------------------------------------------------------------------------% :- end_module backend_libs.compile_target_code. %-----------------------------------------------------------------------------%