%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2002-2012 The University of Melbourne. % Copyright (C) 2013-2017, 2019-2023 The Mercury team. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: make.program_target.m. % Main author: stayl. % % Build targets which relate to whole programs or libraries. % %---------------------------------------------------------------------------% :- module make.program_target. :- interface. :- import_module libs. :- import_module libs.globals. :- import_module libs.maybe_util. :- import_module make.make_info. :- import_module mdbcomp. :- import_module mdbcomp.sym_name. :- import_module parse_tree. :- import_module parse_tree.error_spec. :- import_module io. :- import_module list. :- import_module pair. %---------------------------------------------------------------------------% % make_linked_target(Globals, Target, Succeeded, !Info, !Specs, !IO): % % Build a library or an executable. % :- pred make_linked_target(io.text_output_stream::in, globals::in, linked_target_file::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. % make_misc_target(Globals, Target, Succeeded, !Info, !Specs, !IO): % % Handle miscellaneous target types, including clean-up, library % installation, and building all files of a given type for all % modules in the program. % :- pred make_misc_target(io.text_output_stream::in, globals::in, pair(module_name, misc_target_type)::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. % install_library_grade(LinkSucceeded0, ModuleName, AllModules, % ProgressStream, Globals, Grade, Succeeded, !Info, !IO) % :- pred install_library_grade(maybe_succeeded::in, module_name::in, list(module_name)::in, io.text_output_stream::in, globals::in, string::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module analysis. :- import_module backend_libs. :- import_module backend_libs.compile_target_code. :- import_module libs.check_libgrades. :- import_module libs.compute_grade. :- import_module libs.file_util. :- import_module libs.handle_options. :- import_module libs.options. :- import_module libs.process_util. :- import_module libs.shell_util. :- import_module libs.timestamp. :- import_module make.build. :- import_module make.check_up_to_date. :- import_module make.find_local_modules. :- import_module make.get_module_dep_info. :- import_module make.hash. :- import_module make.module_target. :- import_module make.options_file. :- import_module make.timestamp. :- import_module make.util. :- import_module parse_tree.file_names. :- import_module parse_tree.maybe_error. :- import_module parse_tree.module_baggage. :- import_module parse_tree.module_cmds. :- import_module parse_tree.module_dep_info. :- import_module parse_tree.module_deps_graph. :- import_module parse_tree.parse_tree_out_sym_name. :- import_module parse_tree.prog_foreign. :- import_module parse_tree.write_error_spec. :- import_module transform_hlds. :- import_module transform_hlds.mmc_analysis. :- import_module bool. :- import_module cord. :- import_module digraph. :- import_module dir. :- import_module getopt. :- import_module int. :- import_module io.file. :- import_module map. :- import_module maybe. :- import_module require. :- import_module set. :- import_module string. :- import_module version_hash_table. %---------------------------------------------------------------------------% make_linked_target(ProgressStream, Globals, LinkedTargetFile, LinkedTargetSucceeded, !Info, !Specs, !IO) :- LinkedTargetFile = linked_target_file(_MainModuleName, FileType), ( FileType = shared_library, ExtraOptions = ["--compile-to-shared-lib"] ; ( FileType = executable ; FileType = static_library ; FileType = csharp_executable ; FileType = csharp_library ; FileType = java_executable ; FileType = java_archive ), ExtraOptions = [] ), globals.lookup_accumulating_option(Globals, lib_linkages, LibLinkages), ( if ( FileType = static_library, not list.member("static", LibLinkages) ; FileType = shared_library, not list.member("shared", LibLinkages) ) then LinkedTargetSucceeded = succeeded else maybe_check_libraries_are_installed(Globals, LibgradeCheckSpecs, !IO), ( LibgradeCheckSpecs = [], maybe_with_analysis_cache_dir_3(ProgressStream, Globals, make_linked_target_1(Globals, LinkedTargetFile, ExtraOptions), LinkedTargetSucceeded, !Info, !Specs, !IO) ; LibgradeCheckSpecs = [_ | _], !:Specs = LibgradeCheckSpecs ++ !.Specs, LinkedTargetSucceeded = did_not_succeed ) ). % The form of the argument list is dictated by the build3 type. % :- pred make_linked_target_1(globals::in, linked_target_file::in, list(string)::in, io.text_output_stream::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. make_linked_target_1(Globals, LinkedTargetFile, ExtraOptions, ProgressStream, Succeeded, !Info, !Specs, !IO) :- LinkedTargetFile = linked_target_file(MainModuleName, _FileType), % When using `--intermodule-analysis', perform an analysis pass first. % The analysis of one module may invalidate the results of a module % we analysed earlier, so this step must be carried out until all the % `.analysis' files are in a valid state before we can continue. globals.lookup_bool_option(Globals, intermodule_analysis, IntermodAnalysis), ( IntermodAnalysis = yes, make_misc_target_builder(ProgressStream, Globals, MainModuleName, misc_target_build_analyses, IntermodAnalysisSucceeded, !Info, !Specs, !IO) ; IntermodAnalysis = no, IntermodAnalysisSucceeded = succeeded ), ( IntermodAnalysisSucceeded = succeeded, get_default_options(Globals, DefaultOptionTable), DetectedGradeFlags = make_info_get_detected_grade_flags(!.Info), OptionVariables = make_info_get_options_variables(!.Info), OptionArgs = make_info_get_option_args(!.Info), setup_for_build_with_module_options(ProgressStream, DefaultOptionTable, invoked_by_mmc_make, MainModuleName, DetectedGradeFlags, OptionVariables, OptionArgs, ExtraOptions, MayBuild, !IO), ( MayBuild = may_build(_AllOptionArgs, BuildGlobals), make_linked_target_2(ProgressStream, BuildGlobals, LinkedTargetFile, Succeeded, !Info, !IO) ; MayBuild = may_not_build(Specs), get_error_output_stream(Globals, MainModuleName, ErrorStream, !IO), write_error_specs(ErrorStream, Globals, Specs, !IO), Succeeded = did_not_succeed ) ; IntermodAnalysisSucceeded = did_not_succeed, Succeeded = did_not_succeed ). :- pred make_linked_target_2(io.text_output_stream::in, globals::in, linked_target_file::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. make_linked_target_2(ProgressStream, Globals, LinkedTargetFile, Succeeded, !Info, !IO) :- LinkedTargetFile = linked_target_file(MainModuleName, FileType), find_reachable_local_modules(ProgressStream, Globals, MainModuleName, DepsSucceeded, AllModules, !Info, !IO), KeepGoing = make_info_get_keep_going(!.Info), ( if DepsSucceeded = did_not_succeed, KeepGoing = do_not_keep_going then Succeeded = did_not_succeed else get_object_code_type(Globals, FileType, PIC), % Build the `.c' files first so that errors are reported % as soon as possible. globals.get_target(Globals, CompilationTarget), ( CompilationTarget = target_c, IntermediateTargetType = module_target_c_code, ObjectTargetType = module_target_object_code(PIC) ; CompilationTarget = target_csharp, IntermediateTargetType = module_target_csharp_code, ObjectTargetType = module_target_csharp_code ; CompilationTarget = target_java, IntermediateTargetType = module_target_java_code, ObjectTargetType = module_target_java_class_code ), AllModulesList = set.to_sorted_list(AllModules), % XXX This assignment to ObjModulesAlpha represents inlining % get_target_modules(Globals, IntermediateTargetType, AllModulesList, % ObjModulesAlpha, !Info, !IO), % knowing that IntermediateTargetType cannot be module_target_errors. ObjModulesAlpha = AllModulesList, order_target_modules(ProgressStream, Globals, ObjModulesAlpha, ObjModules, !Info, !IO), filter_out_nested_modules(ProgressStream, Globals, ObjModules, ObjModulesNonnested, !Info, !IO), IntermediateTargetsNonnested = make_dependency_list(ObjModulesNonnested, IntermediateTargetType), ObjTargets = make_dependency_list(ObjModules, ObjectTargetType), list.map_foldl2( get_foreign_object_targets(ProgressStream, Globals, PIC), ObjModules, ForeignObjTargetsList, !Info, !IO), ForeignObjTargets = list.condense(ForeignObjTargetsList), % Ensure all interface files are present before continuing. % This prevents a problem when two parallel branches try to generate % the same missing interface file later. make_all_interface_files(ProgressStream, Globals, AllModulesList, IntsSucceeded, !Info, !IO), ( if IntsSucceeded = did_not_succeed, KeepGoing = do_not_keep_going then BuildDepsSucceeded = did_not_succeed else foldl2_make_module_targets_maybe_parallel(KeepGoing, [], ProgressStream, Globals, IntermediateTargetsNonnested, BuildDepsSucceeded0, !Info, !IO), ( BuildDepsSucceeded0 = succeeded, ( if ObjectTargetType = module_target_java_class_code then make_java_files(ProgressStream, Globals, MainModuleName, ObjModules, BuildJavaSucceeded, !Info, !IO), ( BuildJavaSucceeded = succeeded, % Disable the `--rebuild' option during this pass, % otherwise all the Java classes will be built again. globals.set_option(rebuild, bool(no), Globals, NoRebuildGlobals), foldl2_make_module_targets_maybe_parallel(KeepGoing, [], ProgressStream, NoRebuildGlobals, ObjTargets, BuildDepsSucceeded1, !Info, !IO) ; BuildJavaSucceeded = did_not_succeed, BuildDepsSucceeded1 = did_not_succeed ) else foldl2_make_module_targets_maybe_parallel(KeepGoing, [], ProgressStream, Globals, ObjTargets, BuildDepsSucceeded1, !Info, !IO) ) ; BuildDepsSucceeded0 = did_not_succeed, BuildDepsSucceeded1 = did_not_succeed ), ( BuildDepsSucceeded1 = succeeded, foldl2_make_module_targets(KeepGoing, [], ProgressStream, Globals, ForeignObjTargets, BuildDepsSucceeded, !Info, !IO) ; BuildDepsSucceeded1 = did_not_succeed, BuildDepsSucceeded = did_not_succeed ) ), linked_target_file_name_full_curdir(Globals, MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, !IO), get_file_timestamp([dir.this_directory], FullMainModuleLinkedFileName, MaybeTimestamp, !Info, !IO), check_dependencies(ProgressStream, Globals, FullMainModuleLinkedFileName, MaybeTimestamp, BuildDepsSucceeded, ObjTargets, BuildDepsResult, !Info, !IO), ( if DepsSucceeded = succeeded, BuildDepsResult \= deps_error then globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), setup_checking_for_interrupt(Cookie, !IO), open_module_error_stream(Globals, MainModuleName, ProgressStream, MaybeErrorStream, !Info, !IO), ( MaybeErrorStream = es_error_already_reported, Succeeded0 = did_not_succeed ; MaybeErrorStream = es_ok(MESI, ErrorStream), build_linked_target(MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, MaybeTimestamp, AllModules, ObjModules, CompilationTarget, PIC, DepsSucceeded, BuildDepsResult, ProgressStream, ErrorStream, Globals, Succeeded0, !Info, !IO), close_module_error_stream_handle_errors(Globals, MainModuleName, ProgressStream, MESI, ErrorStream, !Info, !IO) ), Cleanup = linked_target_cleanup(ProgressStream, Globals, MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName), teardown_checking_for_interrupt(VeryVerbose, Cookie, Cleanup, Succeeded0, Succeeded, !Info, !IO) else Succeeded = did_not_succeed ) ). :- pred order_target_modules(io.text_output_stream::in, globals::in, list(module_name)::in, list(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. order_target_modules(ProgressStream, Globals, Modules, OrderedModules, !Info, !IO) :- globals.lookup_bool_option(Globals, order_make_by_timestamp, OrderByTimestamp), ( OrderByTimestamp = yes, list.map_foldl2(pair_module_with_timestamp(ProgressStream, Globals), Modules, PairedModules, !Info, !IO), list.sort(compare_paired_modules, PairedModules, RevOrderedPairs), % More recently touched files, i.e. files with *larger* timestamps, % should appear *earlier* in the list. list.reverse(RevOrderedPairs, OrderedPairs), list.map(pair.snd, OrderedPairs, OrderedModules) ; OrderByTimestamp = no, list.map(pair_module_with_name, Modules, PairedModules), list.sort(compare_paired_modules, PairedModules, OrderedPairs), list.map(pair.snd, OrderedPairs, OrderedModules) ). :- pred pair_module_with_timestamp(io.text_output_stream::in, globals::in, module_name::in, pair(timestamp, module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. pair_module_with_timestamp(ProgressStream, Globals, Module, Timestamp - Module, !Info, !IO) :- Search = do_not_search, Target = target_file(Module, module_target_source), get_target_timestamp(ProgressStream, Globals, Search, Target, MaybeTimestamp, !Info, !IO), ( MaybeTimestamp = ok(Timestamp) ; MaybeTimestamp = error(_), Timestamp = oldest_timestamp ). :- pred pair_module_with_name(module_name::in, pair(string, module_name)::out) is det. pair_module_with_name(Module, Name - Module) :- Name = sym_name_to_string(Module). :- pred compare_paired_modules(pair(T, module_name)::in, pair(T, module_name)::in, comparison_result::out) is det. compare_paired_modules(KeyA - ModuleA, KeyB - ModuleB, Result) :- compare(KeyResult, KeyA, KeyB), % More recently touched files should appear earlier in the list. ( ( KeyResult = (<) ; KeyResult = (>) ), Result = KeyResult ; KeyResult = (=), ModuleAStr = sym_name_to_string(ModuleA), ModuleBStr = sym_name_to_string(ModuleB), compare(Result, ModuleAStr, ModuleBStr) ). :- pred get_foreign_object_targets(io.text_output_stream::in, globals::in, pic::in, module_name::in, list(dependency_file)::out, make_info::in, make_info::out, io::di, io::uo) is det. get_foreign_object_targets(ProgressStream, Globals, PIC, ModuleName, ObjectTargets, !Info, !IO) :- % Find externally compiled foreign code files for % `:- pragma foreign_proc' declarations. % % Any changed here may require corresponding changes in % external_foreign_code_files. globals.get_target(Globals, CompilationTarget), get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo) ; MaybeModuleDepInfo = no_module_dep_info, unexpected($pred, "unknown imports") ), % None of the current backends require externally compiled foreign % code, except the C backend for fact tables. ( CompilationTarget = target_c, FactFileToTarget = ( func(FactFile) = dep_target(target_file(ModuleName, module_target_fact_table_object(PIC, FactFile))) ), module_dep_info_get_fact_tables(ModuleDepInfo, FactTableFiles), FactObjectTargets = list.map(FactFileToTarget, set.to_sorted_list(FactTableFiles)), ObjectTargets = FactObjectTargets ; ( CompilationTarget = target_java ; CompilationTarget = target_csharp ), ObjectTargets = [] ). :- pred build_linked_target(module_name::in, linked_target_type::in, file_name::in, file_name::in, maybe_error(timestamp)::in, set(module_name)::in, list(module_name)::in, compilation_target::in, pic::in, maybe_succeeded::in, dependencies_result::in, io.text_output_stream::in, io.text_output_stream::in, globals::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_linked_target(MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, MaybeTimestamp, AllModules, ObjModules, CompilationTarget, PIC, DepsSucceeded, BuildDepsResult, ProgressStream, ErrorStream, Globals, Succeeded, !Info, !IO) :- globals.lookup_maybe_string_option(Globals, pre_link_command, MaybePreLinkCommand), ( MaybePreLinkCommand = yes(PreLinkCommand), make_all_module_command(PreLinkCommand, MainModuleName, set.to_sorted_list(AllModules), CommandString, !IO), OutputStream = ProgressStream, invoke_system_command(Globals, ProgressStream, ErrorStream, OutputStream, cmd_verbose, CommandString, PreLinkSucceeded, !IO) ; MaybePreLinkCommand = no, PreLinkSucceeded = succeeded ), ( PreLinkSucceeded = succeeded, build_linked_target_2(ProgressStream, Globals, MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, MaybeTimestamp, AllModules, ObjModules, CompilationTarget, PIC, DepsSucceeded, BuildDepsResult, ErrorStream, Succeeded, !Info, !IO) ; PreLinkSucceeded = did_not_succeed, Succeeded = did_not_succeed ). :- pred build_linked_target_2(io.text_output_stream::in, globals::in, module_name::in, linked_target_type::in, file_name::in, file_name::in, maybe_error(timestamp)::in, set(module_name)::in, list(module_name)::in, compilation_target::in, pic::in, maybe_succeeded::in, dependencies_result::in, io.text_output_stream::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_linked_target_2(ProgressStream, Globals, MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, MaybeTimestamp, AllModules, ObjModules, CompilationTarget, PIC, DepsSucceeded, BuildDepsResult, ErrorStream, Succeeded, !Info, !IO) :- % Clear the option -- we will pass the list of files directly. globals.lookup_accumulating_option(Globals, link_objects, LinkObjects), globals.set_option(link_objects, accumulating([]), Globals, NoLinkObjsGlobals), % Remake the `_init.o' file. % XXX We should probably make a `_init.o' file for shared % libraries linked using dlopen(). AllModulesList = set.to_sorted_list(AllModules), ( FileType = executable, make_init_obj_file(NoLinkObjsGlobals, ProgressStream, ErrorStream, MainModuleName, AllModulesList, InitObjectResult, !IO), MaybeInitObjectResult = yes(InitObjectResult) ; ( FileType = static_library ; FileType = shared_library ; FileType = csharp_executable ; FileType = csharp_library ; FileType = java_executable ; FileType = java_archive ), MaybeInitObjectResult = no ), ( MaybeInitObjectResult = yes(InitObjectResult1), ( InitObjectResult1 = yes(InitObject), % We may need to update the timestamp of the `_init.o' file. FileTimestamps0 = make_info_get_file_timestamps(!.Info), map.delete(InitObject, FileTimestamps0, FileTimestamps1), make_info_set_file_timestamps(FileTimestamps1, !Info), % There is no module_target_type for the `_init.o' file, % so mki_target_file_timestamps should not contain anything % that needs to be invalidated. InitObjects = [InitObject], DepsResult2 = BuildDepsResult ; InitObjectResult1 = no, DepsResult2 = deps_error, InitObjects = [] ) ; MaybeInitObjectResult = no, DepsResult2 = BuildDepsResult, InitObjects = [] ), ObjectsToCheck = InitObjects ++ LinkObjects, % Report errors if any of the extra objects aren't present. ObjectsToCheckDepFiles = list.map((func(F) = dep_file(F)), ObjectsToCheck), list.map_foldl2(get_dependency_status(ProgressStream, NoLinkObjsGlobals), ObjectsToCheckDepFiles, ExtraObjTuples, !Info, !IO), ( if some [ExtraObjTuple] ( list.member(ExtraObjTuple, ExtraObjTuples), ExtraObjTuple = dependency_status_result(_, _, deps_status_error) ) then DepsResult3 = deps_error else DepsResult3 = DepsResult2 ), BuildDepsSucceeded = ( if DepsResult3 = deps_error then did_not_succeed else succeeded ), list.map_foldl2(get_file_timestamp([dir.this_directory]), ObjectsToCheck, ExtraObjectTimestamps, !Info, !IO), check_dependency_timestamps(ProgressStream, NoLinkObjsGlobals, FullMainModuleLinkedFileName, MaybeTimestamp, BuildDepsSucceeded, ExtraObjTuples, ExtraObjectTimestamps, ExtraObjectDepsResult, !IO), ( DepsSucceeded = succeeded, DepsResult4 = DepsResult3 ; DepsSucceeded = did_not_succeed, DepsResult4 = deps_error ), ( DepsResult4 = deps_error, DepsResult = DepsResult4 ; DepsResult4 = deps_out_of_date, DepsResult = DepsResult4 ; DepsResult4 = deps_up_to_date, DepsResult = ExtraObjectDepsResult ), ( DepsResult = deps_error, file_error_msg(FullMainModuleLinkedFileName, ErrorMsg), maybe_write_msg_locked(ProgressStream, !.Info, ErrorMsg, !IO), Succeeded = did_not_succeed ; DepsResult = deps_up_to_date, MainModuleLinkedTarget = top_target_file(MainModuleName, linked_target(FileType)), ( if FullMainModuleLinkedFileName = CurDirMainModuleLinkedFileName then maybe_warn_up_to_date_target_msg(NoLinkObjsGlobals, MainModuleLinkedTarget, FullMainModuleLinkedFileName, !Info, UpToDateMsg), maybe_write_msg(ProgressStream, UpToDateMsg, !IO), Succeeded = succeeded else post_link_maybe_make_symlink_or_copy(NoLinkObjsGlobals, ProgressStream, ErrorStream, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, MainModuleName, FileType, Succeeded, MadeSymlinkOrCopy, !IO), ( MadeSymlinkOrCopy = yes, maybe_symlink_or_copy_linked_target_msg(NoLinkObjsGlobals, FullMainModuleLinkedFileName, LinkMsg), maybe_write_msg(ProgressStream, LinkMsg, !IO) ; MadeSymlinkOrCopy = no, maybe_warn_up_to_date_target_msg(NoLinkObjsGlobals, MainModuleLinkedTarget, FullMainModuleLinkedFileName, !Info, UpToDateMsg), maybe_write_msg(ProgressStream, UpToDateMsg, !IO) ) ) ; DepsResult = deps_out_of_date, maybe_making_filename_msg(NoLinkObjsGlobals, FullMainModuleLinkedFileName, MakingMsg), maybe_write_msg(ProgressStream, MakingMsg, !IO), % Find the extra object files for externally compiled foreign % procedures and fact tables. We don't need to include these in the % timestamp checking above -- they will have been checked when the % module's object file was built. list.map_foldl2( get_module_foreign_object_files(ProgressStream, Globals, PIC), AllModulesList, ForeignObjectFileLists, !Info, !IO), ForeignObjects = list.condense(ForeignObjectFileLists), ( CompilationTarget = target_c, maybe_pic_object_file_extension(PIC, ObjExt, _), Ext = ext_cur_ngs_gs(ObjExt) ; CompilationTarget = target_csharp, % There is no separate object code step. Ext = ext_cur_ngs_gs(ext_cur_ngs_gs_target_cs) ; CompilationTarget = target_java, Ext = ext_cur_ngs_gs_java(ext_cur_ngs_gs_java_class) ), list.map( module_name_to_file_name(NoLinkObjsGlobals, $pred, Ext), ObjModules, ObjList), % LinkObjects may contain `.a' files which must come % after all the object files on the linker command line. AllObjects = InitObjects ++ ObjList ++ ForeignObjects ++ LinkObjects, ( ( CompilationTarget = target_c ; CompilationTarget = target_java ; CompilationTarget = target_csharp ), % Run the link in a separate process so it can be killed % if an interrupt is received. call_in_forked_process( compile_target_code.link(NoLinkObjsGlobals, ProgressStream, ErrorStream, FileType, MainModuleName, AllObjects), Succeeded, !IO) ), CmdLineTargets0 = make_info_get_command_line_targets(!.Info), set.delete(top_target_file(MainModuleName, linked_target(FileType)), CmdLineTargets0, CmdLineTargets), make_info_set_command_line_targets(CmdLineTargets, !Info), ( Succeeded = succeeded, FileTimestamps2 = make_info_get_file_timestamps(!.Info), map.delete(FullMainModuleLinkedFileName, FileTimestamps2, FileTimestamps), make_info_set_file_timestamps(FileTimestamps, !Info) % There is no module_target_type for the linked target, % so mki_target_file_timestamps should not contain anything % that needs to be invalidated. ; Succeeded = did_not_succeed, file_error_msg(FullMainModuleLinkedFileName, ErrorMsg), maybe_write_msg_locked(ProgressStream, !.Info, ErrorMsg, !IO) ) ). :- pred get_module_foreign_object_files(io.text_output_stream::in, globals::in, pic::in, module_name::in, list(file_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. get_module_foreign_object_files(ProgressStream, Globals, PIC, ModuleName, ForeignObjectFiles, !MakeInfo, !IO) :- get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !MakeInfo, !IO), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo), external_foreign_code_files(Globals, PIC, ModuleDepInfo, ForeignFiles, !IO), ForeignObjectFiles = list.map( (func(foreign_code_file(_, _, ObjFile)) = ObjFile), ForeignFiles) ; MaybeModuleDepInfo = no_module_dep_info, % This error should have been detected earlier. unexpected($pred, "error in dependencies") ). :- pred linked_target_cleanup(io.text_output_stream::in, globals::in, module_name::in, linked_target_type::in, file_name::in, file_name::in, make_info::in, make_info::out, io::di, io::uo) is det. linked_target_cleanup(ProgressStream, Globals, MainModuleName, FileType, FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, !Info, !IO) :- remove_file_for_make(ProgressStream, Globals, verbose_make, FullMainModuleLinkedFileName, !Info, !IO), ( if FullMainModuleLinkedFileName = CurDirMainModuleLinkedFileName then true else remove_file_for_make(ProgressStream, Globals, verbose_make, CurDirMainModuleLinkedFileName, !Info, !IO) ), ( FileType = executable, remove_init_files(ProgressStream, Globals, verbose_make, MainModuleName, !Info, !IO) ; ( FileType = static_library ; FileType = shared_library ; FileType = csharp_executable ; FileType = csharp_library ; FileType = java_executable ; FileType = java_archive ) ). %---------------------------------------------------------------------------% % When compiling to Java we want to invoke `javac' just once, passing it a % list of all out-of-date `.java' files. This is a lot quicker than % compiling each Java file individually. % :- pred make_java_files(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. make_java_files(ProgressStream, Globals, MainModuleName, ObjModules, Succeeded, !Info, !IO) :- out_of_date_java_modules(ProgressStream, Globals, ObjModules, OutOfDateModules, !Info, !IO), ( OutOfDateModules = [], Succeeded = succeeded ; OutOfDateModules = [_ | _], build_java_files(ProgressStream, Globals, MainModuleName, OutOfDateModules, Succeeded, !Info, !IO), % javac might write more `.class' files than we anticipated (though % it probably won't) so clear out all the timestamps which might be % affected. Timestamps0 = make_info_get_file_timestamps(!.Info), map.foldl(delete_java_class_timestamps, Timestamps0, map.init, Timestamps), make_info_set_file_timestamps(Timestamps, !Info), % For simplicity, clear out all target file timestamps. make_info_set_target_file_timestamps(init_target_file_timestamps, !Info) ). :- pred out_of_date_java_modules(io.text_output_stream::in, globals::in, list(module_name)::in, list(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. out_of_date_java_modules(ProgressStream, Globals, ObjModules, OutOfDateModules, !Info, !IO) :- ( ObjModules = [], OutOfDateModules = [] ; ObjModules = [ModuleName | ModuleNames], out_of_date_java_modules(ProgressStream, Globals, ModuleNames, OutOfDateModules0, !Info, !IO), JavaTarget = target_file(ModuleName, module_target_java_code), ClassTarget = target_file(ModuleName, module_target_java_class_code), get_target_timestamp(ProgressStream, Globals, do_not_search, JavaTarget, MaybeJavaTimestamp, !Info, !IO), get_target_timestamp(ProgressStream, Globals, do_not_search, ClassTarget, MaybeClassTimestamp, !Info, !IO), ( if MaybeJavaTimestamp = ok(JavaTimestamp), MaybeClassTimestamp = ok(ClassTimestamp), ClassTimestamp @>= JavaTimestamp then OutOfDateModules = OutOfDateModules0 else OutOfDateModules = [ModuleName | OutOfDateModules0] ) ). :- pred build_java_files(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_java_files(ProgressStream, Globals, MainModuleName, ModuleNames, Succeeded, !Info, !IO) :- verbose_make_one_part_msg(Globals, "Making Java class files", MakingMsg), maybe_write_msg(ProgressStream, MakingMsg, !IO), % XXX FILE_NAMES list.map_foldl( module_name_to_file_name_create_dirs(Globals, $pred, ext_cur_ngs_gs_java(ext_cur_ngs_gs_java_java)), ModuleNames, JavaFiles, !IO), % We redirect errors to a file named after the main module. open_module_error_stream(Globals, MainModuleName, ProgressStream, MaybeErrorStream, !Info, !IO), ( MaybeErrorStream = es_error_already_reported, Succeeded = did_not_succeed ; MaybeErrorStream = es_ok(MESI, ErrorStream), build_java_files_2(ProgressStream, ErrorStream, Globals, JavaFiles, Succeeded, !Info, !IO), close_module_error_stream_handle_errors(Globals, MainModuleName, ProgressStream, MESI, ErrorStream, !Info, !IO) ). :- pred build_java_files_2(io.text_output_stream::in, io.text_output_stream::in, globals::in, list(string)::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_java_files_2(ProgressStream, ErrorStream, Globals, JavaFiles, Succeeded, !Info, !IO) :- list.det_head_tail(JavaFiles, HeadJavaFile, TailJavaFiles), call_in_forked_process( compile_java_files(Globals, ProgressStream, ErrorStream, HeadJavaFile, TailJavaFiles), Succeeded, !IO). :- pred delete_java_class_timestamps(string::in, maybe_error(timestamp)::in, file_timestamps::in, file_timestamps::out) is det. delete_java_class_timestamps(FileName, MaybeTimestamp, !Timestamps) :- ( if string.suffix(FileName, ".class") then true else map.det_insert(FileName, MaybeTimestamp, !Timestamps) ). %---------------------------------------------------------------------------% make_misc_target(ProgressStream, Globals, MainModuleName - TargetType, Succeeded, !Info, !Specs, !IO) :- get_default_options(Globals, DefaultOptionTable), DetectedGradeFlags = make_info_get_detected_grade_flags(!.Info), OptionVariables = make_info_get_options_variables(!.Info), OptionArgs = make_info_get_option_args(!.Info), ExtraOptions = [], setup_for_build_with_module_options(ProgressStream, DefaultOptionTable, invoked_by_mmc_make, MainModuleName, DetectedGradeFlags, OptionVariables, OptionArgs, ExtraOptions, MayBuild, !IO), ( MayBuild = may_build(_AllOptionArgs, BuildGlobals), make_misc_target_builder(ProgressStream, BuildGlobals, MainModuleName, TargetType, Succeeded, !Info, !Specs, !IO) ; MayBuild = may_not_build(Specs), % XXX MAKE_STREAM get_error_output_stream(Globals, MainModuleName, ErrorStream, !IO), write_error_specs(ErrorStream, Globals, Specs, !IO), Succeeded = did_not_succeed ). :- pred make_misc_target_builder(io.text_output_stream::in, globals::in, module_name::in, misc_target_type::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. make_misc_target_builder(ProgressStream, Globals, MainModuleName, TargetType, Succeeded, !Info, !Specs, !IO) :- % Don't rebuild .module_dep files when cleaning up. RebuildModuleDeps = make_info_get_rebuild_module_deps(!.Info), ( if ( TargetType = misc_target_clean ; TargetType = misc_target_realclean ) then make_info_set_rebuild_module_deps(do_not_rebuild_module_deps, !Info) else true ), find_reachable_local_modules(ProgressStream, Globals, MainModuleName, Succeeded0, AllModulesSet, !Info, !IO), make_info_set_rebuild_module_deps(RebuildModuleDeps, !Info), AllModules = set.to_sorted_list(AllModulesSet), ( TargetType = misc_target_clean, Succeeded = succeeded, list.foldl2(make_module_clean(ProgressStream, Globals), AllModules, !Info, !IO), remove_init_files(ProgressStream, Globals, very_verbose, MainModuleName, !Info, !IO) ; TargetType = misc_target_realclean, Succeeded = succeeded, make_main_module_realclean(ProgressStream, Globals, MainModuleName, !Info, !IO), list.foldl2(make_module_realclean(ProgressStream, Globals), AllModules, !Info, !IO) ; TargetType = misc_target_build_all(ModuleTargetType), get_target_modules(ProgressStream, Globals, ModuleTargetType, AllModules, TargetModules, !Info, !IO), KeepGoing = make_info_get_keep_going(!.Info), ( if Succeeded0 = did_not_succeed, KeepGoing = do_not_keep_going then Succeeded = did_not_succeed else % Ensure all interface files are present before continuing. % This prevents a problem when two parallel branches % try to generate the same missing interface file later. make_all_interface_files(ProgressStream, Globals, AllModules, Succeeded1, !Info, !IO), ( if Succeeded1 = did_not_succeed, KeepGoing = do_not_keep_going then Succeeded = did_not_succeed else maybe_with_analysis_cache_dir_2(ProgressStream, Globals, foldl2_make_module_targets_maybe_parallel_build2(KeepGoing, [], Globals, make_dependency_list(TargetModules, ModuleTargetType)), Succeeded2, !Info, !IO), Succeeded = Succeeded0 `and` Succeeded1 `and` Succeeded2 ) ) ; TargetType = misc_target_build_analyses, maybe_with_analysis_cache_dir_2(ProgressStream, Globals, build_analysis_files(Globals, MainModuleName, AllModules, Succeeded0), Succeeded, !Info, !IO) ; TargetType = misc_target_build_library, make_all_interface_files(ProgressStream, Globals, AllModules, IntSucceeded, !Info, !IO), ( IntSucceeded = succeeded, maybe_with_analysis_cache_dir_3(ProgressStream, Globals, build_library(MainModuleName, AllModules, Globals), Succeeded, !Info, !Specs, !IO) ; IntSucceeded = did_not_succeed, Succeeded = did_not_succeed ) ; TargetType = misc_target_install_library, make_misc_target(ProgressStream, Globals, MainModuleName - misc_target_build_library, LibSucceeded, !Info, !Specs, !IO), ( LibSucceeded = succeeded, install_library(ProgressStream, Globals, MainModuleName, Succeeded, !Info, !IO) ; LibSucceeded = did_not_succeed, Succeeded = did_not_succeed ) ; TargetType = misc_target_build_xml_docs, get_target_modules(ProgressStream, Globals, module_target_xml_doc, AllModules, TargetModules, !Info, !IO), KeepGoing = make_info_get_keep_going(!.Info), ( if Succeeded0 = did_not_succeed, KeepGoing = do_not_keep_going then Succeeded = did_not_succeed else foldl2_make_module_targets(KeepGoing, [], ProgressStream, Globals, make_dependency_list(TargetModules, module_target_xml_doc), Succeeded1, !Info, !IO), Succeeded = Succeeded0 `and` Succeeded1 ) ). %---------------------------------------------------------------------------% :- pred make_all_interface_files(io.text_output_stream::in, globals::in, list(module_name)::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. make_all_interface_files(ProgressStream, Globals, AllModules0, Succeeded, !Info, !IO) :- filter_out_nested_modules(ProgressStream, Globals, AllModules0, NonnestedModules, !Info, !IO), list.foldl3(collect_modules_with_children(ProgressStream, Globals), NonnestedModules, [], ParentModules, !Info, !IO), Int3s = make_dependency_list(NonnestedModules, module_target_int3), Int0s = make_dependency_list(ParentModules, module_target_int0), Int1s = make_dependency_list(NonnestedModules, module_target_int1), globals.get_any_intermod(Globals, AnyIntermod), ( AnyIntermod = yes, Opts = make_dependency_list(NonnestedModules, module_target_opt) ; AnyIntermod = no, Opts = [] ), KeepGoing = make_info_get_keep_going(!.Info), % Private interfaces (.int0) need to be made before building long interface % files in parallel, otherwise two processes may try to build the same % private interface file. foldl2_make_module_targets_maybe_parallel(KeepGoing, [], ProgressStream, Globals, Int3s, Succeeded0, !Info, !IO), ( Succeeded0 = succeeded, foldl2_make_module_targets(KeepGoing, [], ProgressStream, Globals, Int0s, Succeeded1, !Info, !IO), ( Succeeded1 = succeeded, foldl2_make_module_targets_maybe_parallel(KeepGoing, [], ProgressStream, Globals, Int1s, Succeeded2, !Info, !IO), ( Succeeded2 = succeeded, foldl2_make_module_targets_maybe_parallel(KeepGoing, [], ProgressStream, Globals, Opts, Succeeded, !Info, !IO) ; Succeeded2 = did_not_succeed, Succeeded = did_not_succeed ) ; Succeeded1 = did_not_succeed, Succeeded = did_not_succeed ) ; Succeeded0 = did_not_succeed, Succeeded = did_not_succeed ). :- pred collect_modules_with_children(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, list(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. collect_modules_with_children(ProgressStream, Globals, ModuleName, !ParentModules, !Info, !IO) :- get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo), module_dep_info_get_children(ModuleDepInfo, Children), ( if set.is_empty(Children) then true else !:ParentModules = [ModuleName | !.ParentModules] ) ; MaybeModuleDepInfo = no_module_dep_info ). %---------------------------------------------------------------------------% :- type build2(Info) == pred(io.text_output_stream, maybe_succeeded, Info, Info, io, io). :- inst build2 == (pred(in, out, in, out, di, uo) is det). % If `--analysis-file-cache' is enabled, create a temporary directory for % holding analysis cache files and pass that to child processes. % After P is finished, remove the cache directory completely. % :- pred maybe_with_analysis_cache_dir_2(io.text_output_stream::in, globals::in, build2(make_info)::in(build2), maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. maybe_with_analysis_cache_dir_2(ProgressStream, Globals, P, Succeeded, !Info, !IO) :- should_we_use_analysis_cache_dir(ProgressStream, Globals, !.Info, UseAnalysisCacheDir, !IO), ( UseAnalysisCacheDir = do_not_use_analysis_cache_dir, P(ProgressStream, Succeeded, !Info, !IO) ; UseAnalysisCacheDir = use_analysis_cache_dir(CacheDir, CacheDirOption), OrigOptionArgs = make_info_get_option_args(!.Info), % Pass the name of the cache directory to child processes. make_info_set_option_args(OrigOptionArgs ++ [CacheDirOption, CacheDir], !Info), globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), setup_checking_for_interrupt(Cookie, !IO), P(ProgressStream, Succeeded1, !Info, !IO), Cleanup = remove_cache_dir(ProgressStream, Globals, CacheDir), teardown_checking_for_interrupt(VeryVerbose, Cookie, Cleanup, Succeeded1, Succeeded, !Info, !IO), remove_cache_dir(ProgressStream, Globals, CacheDir, !Info, !IO), make_info_set_option_args(OrigOptionArgs, !Info) ; UseAnalysisCacheDir = analysis_cache_dir_create_failed, Succeeded = did_not_succeed ). %---------------------% :- type build3(Info) == pred(io.text_output_stream, maybe_succeeded, Info, Info, list(error_spec), list(error_spec), io, io). :- inst build3 == (pred(in, out, in, out, in, out, di, uo) is det). % If `--analysis-file-cache' is enabled, create a temporary directory for % holding analysis cache files and pass that to child processes. % After P is finished, remove the cache directory completely. % :- pred maybe_with_analysis_cache_dir_3(io.text_output_stream::in, globals::in, build3(make_info)::in(build3), maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. maybe_with_analysis_cache_dir_3(ProgressStream, Globals, P, Succeeded, !Info, !Specs, !IO) :- should_we_use_analysis_cache_dir(ProgressStream, Globals, !.Info, UseAnalysisCacheDir, !IO), ( UseAnalysisCacheDir = do_not_use_analysis_cache_dir, P(ProgressStream, Succeeded, !Info, !Specs, !IO) ; UseAnalysisCacheDir = use_analysis_cache_dir(CacheDir, CacheDirOption), OrigOptionArgs = make_info_get_option_args(!.Info), % Pass the name of the cache directory to child processes. make_info_set_option_args(OrigOptionArgs ++ [CacheDirOption, CacheDir], !Info), globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), setup_checking_for_interrupt(Cookie, !IO), P(ProgressStream, Succeeded1, !Info, !Specs, !IO), Cleanup = remove_cache_dir(ProgressStream, Globals, CacheDir), teardown_checking_for_interrupt(VeryVerbose, Cookie, Cleanup, Succeeded1, Succeeded, !Info, !IO), remove_cache_dir(ProgressStream, Globals, CacheDir, !Info, !IO), make_info_set_option_args(OrigOptionArgs, !Info) ; UseAnalysisCacheDir = analysis_cache_dir_create_failed, Succeeded = did_not_succeed ). %---------------------% :- type maybe_use_analysis_cache_dir ---> do_not_use_analysis_cache_dir ; use_analysis_cache_dir(string, string) ; analysis_cache_dir_create_failed. % If `--analysis-file-cache' is enabled, create a temporary directory for % holding analysis cache files. % :- pred should_we_use_analysis_cache_dir(io.text_output_stream::in, globals::in, make_info::in, maybe_use_analysis_cache_dir::out, io::di, io::uo) is det. should_we_use_analysis_cache_dir(ProgressStream, Globals, Info, UseAnalysisCacheDir, !IO) :- globals.lookup_bool_option(Globals, intermodule_analysis, IntermodAnalysis), globals.lookup_bool_option(Globals, analysis_file_cache, Caching), globals.lookup_string_option(Globals, analysis_file_cache_dir, CacheDir0), CacheDirOption = "--analysis-file-cache-dir", ( if ( IntermodAnalysis = no ; Caching = no ; % Cache directory given on command line. CacheDir0 \= "" ; % Analysis file cache directory already set up in a parent call. list.member(CacheDirOption, make_info_get_option_args(Info)) ) then UseAnalysisCacheDir = do_not_use_analysis_cache_dir else create_analysis_cache_dir(ProgressStream, Globals, Succeeded, CacheDir, !IO), ( Succeeded = succeeded, UseAnalysisCacheDir = use_analysis_cache_dir(CacheDir, CacheDirOption) ; Succeeded = did_not_succeed, UseAnalysisCacheDir = analysis_cache_dir_create_failed ) ). %---------------------% :- pred create_analysis_cache_dir(io.text_output_stream::in, globals::in, maybe_succeeded::out, string::out, io::di, io::uo) is det. create_analysis_cache_dir(ProgressStream, Globals, Succeeded, CacheDir, !IO) :- analysis_cache_dir_name(Globals, CacheDir), verbose_make_two_part_msg(Globals, "Creating", CacheDir, CreatingMsg), maybe_write_msg(ProgressStream, CreatingMsg, !IO), dir.make_directory(CacheDir, MakeRes, !IO), ( MakeRes = ok, Succeeded = succeeded ; MakeRes = error(Error), io.format(ProgressStream, "Error: making directory %s: %s\n", [s(CacheDir), s(io.error_message(Error))], !IO), Succeeded = did_not_succeed ). :- pred remove_cache_dir(io.text_output_stream::in, globals::in, string::in, make_info::in, make_info::out, io::di, io::uo) is det. remove_cache_dir(ProgressStream, Globals, CacheDir, !Info, !IO) :- verbose_make_two_part_msg(Globals, "Removing", CacheDir, RemovingMsg), maybe_write_msg(ProgressStream, RemovingMsg, !IO), io.file.remove_file_recursively(CacheDir, _, !IO). %---------------------------------------------------------------------------% % The form of the argument list is dictated by the build2 type. % :- pred build_analysis_files(globals::in, module_name::in, list(module_name)::in, maybe_succeeded::in, io.text_output_stream::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_analysis_files(Globals, MainModuleName, AllModules, Succeeded0, ProgressStream, Succeeded, !Info, !IO) :- KeepGoing = make_info_get_keep_going(!.Info), ( if Succeeded0 = did_not_succeed, KeepGoing = do_not_keep_going then Succeeded = did_not_succeed else % Ensure all interface files are present before continuing. % This prevents a problem when two parallel branches try to generate % the same missing interface file later. % (Although we can't actually build analysis files in parallel yet.) make_all_interface_files(ProgressStream, Globals, AllModules, Succeeded1, !Info, !IO), ( if Succeeded1 = did_not_succeed, KeepGoing = do_not_keep_going then Succeeded = did_not_succeed else build_analysis_files_1(ProgressStream, Globals, MainModuleName, AllModules, Succeeded, !Info, !IO) ) ). :- pred build_analysis_files_1(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_analysis_files_1(ProgressStream, Globals, MainModuleName, AllModules, Succeeded, !Info, !IO) :- get_target_modules(ProgressStream, Globals, module_target_analysis_registry, AllModules, TargetModules0, !Info, !IO), get_reverse_ordered_modules(make_info_get_module_dependencies(!.Info), TargetModules0, TargetModules1), % Filter out the non-local modules so we don't try to reanalyse them. list.filter(list.contains(AllModules), TargetModules1, TargetModules), make_local_module_id_options(ProgressStream, Globals, MainModuleName, Succeeded0, LocalModulesOpts, !Info, !IO), ( Succeeded0 = succeeded, build_analysis_files_2(ProgressStream, Globals, MainModuleName, TargetModules, LocalModulesOpts, Succeeded0, Succeeded, !Info, !IO) ; Succeeded0 = did_not_succeed, Succeeded = did_not_succeed ). :- pred build_analysis_files_2(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, list(string)::in, maybe_succeeded::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. build_analysis_files_2(ProgressStream, Globals, MainModuleName, TargetModules, LocalModulesOpts, Succeeded0, Succeeded, !Info, !IO) :- KeepGoing = make_info_get_keep_going(!.Info), foldl2_make_module_targets(KeepGoing, LocalModulesOpts, ProgressStream, Globals, make_dependency_list(TargetModules, module_target_analysis_registry), Succeeded1, !Info, !IO), % Maybe we should have an option to reanalyse cliques before moving % upwards in the dependency graph? % Find which module analysis files are suboptimal or invalid. % If there are any invalid files then we repeat the analysis pass. % If there are only suboptimal files then we repeat the analysis up % to the number of times given by the user. ReanalysisPasses = make_info_get_reanalysis_passes(!.Info), ReanalyseSuboptimal = (if ReanalysisPasses > 1 then yes else no), modules_needing_reanalysis(ReanalyseSuboptimal, Globals, TargetModules, InvalidModules, SuboptimalModules, !IO), ( if list.is_not_empty(InvalidModules) then maybe_reanalyse_modules_msg(Globals, ReanalysingMsg), maybe_write_msg(ProgressStream, ReanalysingMsg, !IO), list.foldl(reset_analysis_registry_dependency_status, InvalidModules, !Info), build_analysis_files_2(ProgressStream, Globals, MainModuleName, TargetModules, LocalModulesOpts, Succeeded0, Succeeded, !Info, !IO) else if list.is_not_empty(SuboptimalModules) then list.foldl(reset_analysis_registry_dependency_status, SuboptimalModules, !Info), make_info_set_reanalysis_passes(ReanalysisPasses - 1, !Info), maybe_reanalyse_modules_msg(Globals, ReanalysingMsg), maybe_write_msg(ProgressStream, ReanalysingMsg, !IO), build_analysis_files_2(ProgressStream, Globals, MainModuleName, TargetModules, LocalModulesOpts, Succeeded0, Succeeded, !Info, !IO) else Succeeded = Succeeded0 `and` Succeeded1 ). %---------------------------------------------------------------------------% :- pred get_target_modules(io.text_output_stream::in, globals::in, module_target_type::in, list(module_name)::in, list(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. get_target_modules(ProgressStream, Globals, TargetType, AllModules, TargetModules, !Info, !IO) :- ( if TargetType = module_target_errors then % `.err' files are only produced for the top-level module % in each source file. list.foldl3( get_non_nested_target_modules(ProgressStream, Globals), AllModules, cord.init, TargetModulesCord, !Info, !IO), TargetModules = cord.list(TargetModulesCord) else TargetModules = AllModules ). :- pred get_non_nested_target_modules(io.text_output_stream::in, globals::in, module_name::in, cord(module_name)::in, cord(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. get_non_nested_target_modules(ProgressStream, Globals, ModuleName, !TargetModulesCord, !Info, !IO) :- get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( if MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo), module_dep_info_get_source_file_module_name(ModuleDepInfo, SourceFileModuleName), ModuleName = SourceFileModuleName then cord.snoc(ModuleName, !TargetModulesCord) else true ). %---------------------------------------------------------------------------% % Return a list of modules in reverse order of their dependencies, i.e. % the list is the module dependency graph from bottom-up. Mutually % dependent modules (modules which form a clique in the dependency graph) % are returned adjacent in the list in arbitrary order. % :- pred get_reverse_ordered_modules( map(module_name, maybe_module_dep_info)::in, list(module_name)::in, list(module_name)::out) is det. get_reverse_ordered_modules(ModuleDeps, Modules0, Modules) :- list.foldl2( add_module_relations(lookup_module_dep_info_in_maybe_map(ModuleDeps)), Modules0, digraph.init, _IntDepsGraph, digraph.init, ImpDepsGraph), SccSets = digraph.return_sccs_in_to_from_order(ImpDepsGraph), list.map(set.to_sorted_list, SccSets, SccLists), list.condense(SccLists, Modules). % add_module_relations(LookupModuleImports, ModuleName, % !IntDepsRel, !ImplDepsRel) % % Add a module's interface and implementation dependencies to IntDepsRel % and ImplDepsRel respectively. Dependencies are found using the % LookupModuleImports function. % :- pred add_module_relations(lookup_module_dep_info::in, module_name::in, digraph(module_name)::in, digraph(module_name)::out, digraph(module_name)::in, digraph(module_name)::out) is det. add_module_relations(LookupModuleImports, ModuleName, !IntDepsGraph, !ImplDepsGraph) :- ModuleDepInfo = LookupModuleImports(ModuleName), add_module_dep_info_to_deps_graph(ModuleDepInfo, LookupModuleImports, !IntDepsGraph, !ImplDepsGraph). %---------------------------------------------------------------------------% :- func lookup_module_dep_info_in_maybe_map( map(module_name, maybe_module_dep_info), module_name) = module_dep_info. lookup_module_dep_info_in_maybe_map(ModuleDeps, ModuleName) = ModuleDepInfo :- map.lookup(ModuleDeps, ModuleName, MaybeModuleDepInfo), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo) ; MaybeModuleDepInfo = no_module_dep_info, unexpected($pred, "MaybeModuleDepInfo = no") ). :- pred modules_needing_reanalysis(bool::in, globals::in, list(module_name)::in, list(module_name)::out, list(module_name)::out, io::di, io::uo) is det. modules_needing_reanalysis(_, _, [], [], [], !IO). modules_needing_reanalysis(ReanalyseSuboptimal, Globals, [Module | Modules], InvalidModules, SuboptimalModules, !IO) :- do_read_module_overall_status(mmc, Globals, Module, ModuleStatus, !IO), ( ModuleStatus = optimal, modules_needing_reanalysis(ReanalyseSuboptimal, Globals, Modules, InvalidModules, SuboptimalModules, !IO) ; ModuleStatus = suboptimal, modules_needing_reanalysis(ReanalyseSuboptimal, Globals, Modules, InvalidModules, SuboptimalModules0, !IO), ( ReanalyseSuboptimal = yes, SuboptimalModules = [Module | SuboptimalModules0] ; ReanalyseSuboptimal = no, SuboptimalModules = SuboptimalModules0 ) ; ModuleStatus = invalid, modules_needing_reanalysis(ReanalyseSuboptimal, Globals, Modules, InvalidModules0, SuboptimalModules, !IO), InvalidModules = [Module | InvalidModules0] ). :- pred reset_analysis_registry_dependency_status(module_name::in, make_info::in, make_info::out) is det. reset_analysis_registry_dependency_status(ModuleName, !Info) :- Dep = dep_target(target_file(ModuleName, module_target_analysis_registry)), DepStatusMap0 = make_info_get_dependency_status(!.Info), version_hash_table.set(Dep, deps_status_not_considered, DepStatusMap0, DepStatusMap), make_info_set_dependency_status(DepStatusMap, !Info). %---------------------------------------------------------------------------% % The form of the argument list is dictated by the build3 type. % :- pred build_library(module_name::in, list(module_name)::in, globals::in, io.text_output_stream::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. build_library(MainModuleName, AllModules, Globals, ProgressStream, Succeeded, !Info, !Specs, !IO) :- globals.get_target(Globals, Target), ( Target = target_c, build_c_library(ProgressStream, Globals, MainModuleName, AllModules, Succeeded, !Info, !Specs, !IO) ; Target = target_csharp, build_csharp_library(ProgressStream, Globals, MainModuleName, Succeeded, !Info, !Specs, !IO) ; Target = target_java, build_java_library(ProgressStream, Globals, MainModuleName, Succeeded, !Info, !Specs, !IO) ). :- pred build_c_library(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. build_c_library(ProgressStream, Globals, MainModuleName, AllModules, Succeeded, !Info, !Specs, !IO) :- make_linked_target(ProgressStream, Globals, linked_target_file(MainModuleName, static_library), StaticSucceeded, !Info, !Specs, !IO), shared_libraries_supported(Globals, SharedLibsSupported), ( StaticSucceeded = succeeded, ( SharedLibsSupported = yes, make_linked_target(ProgressStream, Globals, linked_target_file(MainModuleName, shared_library), SharedLibsSucceeded, !Info, !Specs, !IO) ; SharedLibsSupported = no, SharedLibsSucceeded = succeeded ), % We can only build the .init file if we have succesfully built % the .c files. ( SharedLibsSucceeded = succeeded, % Errors while making the .init file should be very rare. % XXX MAKE_STREAM This preserves old behavior, but our caller % should pass to us a progress stream *explicitly*. ErrorStream = ProgressStream, make_library_init_file(Globals, ProgressStream, ErrorStream, MainModuleName, AllModules, Succeeded, !IO) ; SharedLibsSucceeded = did_not_succeed, Succeeded = did_not_succeed ) ; StaticSucceeded = did_not_succeed, Succeeded = did_not_succeed ). :- pred build_csharp_library(io.text_output_stream::in, globals::in, module_name::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. build_csharp_library(ProgressStream, Globals, MainModuleName, Succeeded, !Info, !Specs, !IO) :- make_linked_target(ProgressStream, Globals, linked_target_file(MainModuleName, csharp_library), Succeeded, !Info, !Specs, !IO). :- pred build_java_library(io.text_output_stream::in, globals::in, module_name::in, maybe_succeeded::out, make_info::in, make_info::out, list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det. build_java_library(ProgressStream, Globals, MainModuleName, Succeeded, !Info, !Specs, !IO) :- make_linked_target(ProgressStream, Globals, linked_target_file(MainModuleName, java_archive), Succeeded, !Info, !Specs, !IO). %---------------------------------------------------------------------------% :- pred install_library(io.text_output_stream::in, globals::in, module_name::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. install_library(ProgressStream, Globals, MainModuleName, Succeeded, !Info, !IO) :- find_reachable_local_modules(ProgressStream, Globals, MainModuleName, DepsSucceeded, AllModules0, !Info, !IO), AllModules = set.to_sorted_list(AllModules0), make_install_dirs(ProgressStream, Globals, DirSucceeded, LinkSucceeded, !IO), ( if DepsSucceeded = succeeded, DirSucceeded = succeeded then list.map_foldl2( install_ints_and_headers(ProgressStream, Globals, LinkSucceeded), AllModules, IntsSucceeded, !Info, !IO), install_extra_headers(ProgressStream, Globals, ExtraHdrsSucceeded, !IO), grade_directory_component(Globals, Grade), install_library_grade_files(ProgressStream, Globals, LinkSucceeded, Grade, MainModuleName, AllModules, GradeSucceeded, !Info, !IO), ( if and_list([ExtraHdrsSucceeded | IntsSucceeded]) = succeeded, GradeSucceeded = succeeded then KeepGoing = make_info_get_keep_going(!.Info), % XXX With Mmake, LIBGRADES is target-specific. globals.lookup_accumulating_option(Globals, libgrades, LibGrades0), LibGrades = list.delete_all(LibGrades0, Grade), foldl2_install_library_grades(KeepGoing, LinkSucceeded, MainModuleName, AllModules, ProgressStream, Globals, LibGrades, Succeeded, !Info, !IO) else Succeeded = did_not_succeed ) else Succeeded = did_not_succeed ). :- pred install_ints_and_headers(io.text_output_stream::in, globals::in, maybe_succeeded::in, module_name::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. install_ints_and_headers(ProgressStream, Globals, SubdirLinkSucceeded, ModuleName, Succeeded, !Info, !IO) :- get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo), % We always install the `.int0' files for a library even though they % are only required by the `.opt' files. This is because when building % a program with --intermodule-optimization enabled, the compiler will % look for `.int0' files of any libraries the program uses. It will do % this even for libraries that were not installed with % --intermodule-optimization enabled, returning an error if it cannot % find the `.int0' file. module_dep_info_get_children(ModuleDepInfo, Children), ( if set.is_empty(Children) then Exts0 = [] else Exts0 = [{ext_cur_ngs(ext_cur_ngs_int_int0), "int0s"}] ), globals.get_any_intermod(Globals, AnyIntermod), ( AnyIntermod = yes, Exts1 = [{ext_cur_ngs_gs_max_ngs(ext_cur_ngs_gs_max_ngs_opt_plain), "opts"} | Exts0] ; AnyIntermod = no, Exts1 = Exts0 ), Exts = [{ext_cur_ngs(ext_cur_ngs_int_int1), "ints"}, {ext_cur_ngs(ext_cur_ngs_int_int2), "int2s"}, {ext_cur_ngs(ext_cur_ngs_int_int3), "int3s"}, {ext_cur_ngs(ext_cur_ngs_misc_module_dep), "module_deps"} | Exts1], globals.lookup_string_option(Globals, install_prefix, Prefix), LibDir = Prefix/"lib"/"mercury", list.map_foldl( install_subdir_file(ProgressStream, Globals, SubdirLinkSucceeded, LibDir/"ints", ModuleName), Exts, Results, !IO), globals.get_target(Globals, Target), ( % `.mh' files are (were) only generated for modules containing % `:- pragma foreign_export' declarations. % But `.mh' files are expected by Mmake so always generate them, % otherwise there is trouble using libraries installed by % `mmc --make' with Mmake. % XXX If we ever phase out mmake we could revert this behaviour. Target = target_c, % XXX Should we test % ModuleDepInfo ^ contains_foreign_export % = contains_foreign_export? module_name_to_file_name(Globals, $pred, ext_cur(ext_cur_mh), ModuleName, FileName), install_file(ProgressStream, Globals, FileName, LibDir/"inc", HeaderSucceeded1, !IO), % This is needed so that the file will be found in Mmake's VPATH. install_subdir_file(ProgressStream, Globals, SubdirLinkSucceeded, LibDir/"ints", ModuleName, {ext_cur(ext_cur_mh), "mhs"}, HeaderSucceeded2, !IO), HeaderSucceeded = HeaderSucceeded1 `and` HeaderSucceeded2 ; ( Target = target_java ; Target = target_csharp ), HeaderSucceeded = succeeded ), Succeeded = and_list([HeaderSucceeded | Results]) ; MaybeModuleDepInfo = no_module_dep_info, Succeeded = did_not_succeed ). :- pred install_extra_headers(io.text_output_stream::in, globals::in, maybe_succeeded::out, io::di, io::uo) is det. install_extra_headers(ProgressStream, Globals, ExtraHdrsSucceeded, !IO) :- globals.lookup_accumulating_option(Globals, extra_library_header, ExtraHdrs), globals.lookup_string_option(Globals, install_prefix, Prefix), IncDir = Prefix / "lib" / "mercury" / "inc", list.foldl2(install_extra_header(ProgressStream, Globals, IncDir), ExtraHdrs, succeeded, ExtraHdrsSucceeded, !IO). :- pred install_extra_header(io.text_output_stream::in, globals::in, dir_name::in, string::in, maybe_succeeded::in, maybe_succeeded::out, io::di, io::uo) is det. install_extra_header(ProgressStream, Globals, IncDir, FileName, !Succeeded, !IO) :- install_file(ProgressStream, Globals, FileName, IncDir, InstallSucceeded, !IO), !:Succeeded = !.Succeeded `and` InstallSucceeded. install_library_grade(LinkSucceeded0, ModuleName, AllModules, ProgressStream, Globals, Grade, Succeeded, !Info, !IO) :- % Only remove grade-dependent files after installing if % --use-grade-subdirs is not specified by the user. globals.get_subdir_setting(Globals, SubdirSetting), ( ( SubdirSetting = use_cur_dir ; SubdirSetting = use_cur_ngs_subdir ), CleanAfter = yes ; SubdirSetting = use_cur_ngs_gs_subdir, CleanAfter = no ), % Set up so that grade-dependent files for the current grade % don't overwrite the files for the default grade. OptionArgs0 = make_info_get_option_args(!.Info), OptionArgs = OptionArgs0 ++ ["--grade", Grade, "--use-grade-subdirs"], verbose_make_two_part_msg(Globals, "Installing grade", Grade, InstallMsg), maybe_write_msg(ProgressStream, InstallMsg, !IO), lookup_mmc_options(make_info_get_options_variables(!.Info), MaybeMCFlags), ( MaybeMCFlags = ok1(MCFlags), get_default_options(Globals, DefaultOptionTable), DetectedGradeFlags = make_info_get_detected_grade_flags(!.Info), AllFlags = DetectedGradeFlags ++ MCFlags ++ OptionArgs, handle_given_options(ProgressStream, DefaultOptionTable, AllFlags, _, _, OptionsSpecs, LibGlobals, !IO) ; MaybeMCFlags = error1(LookupSpecs), write_error_specs(ProgressStream, Globals, LookupSpecs, !IO), % Errors should have been caught before. unexpected($pred, "bad DEFAULT_MCFLAGS") ), ( OptionsSpecs = [_ | _], get_error_output_stream(Globals, ModuleName, ErrorStream, !IO), usage_errors(ErrorStream, Globals, OptionsSpecs, !IO), Succeeded = did_not_succeed ; OptionsSpecs = [], % Remove the grade-dependent targets from the status map % (we need to rebuild them in the new grade). % XXX version_hash_table.delete is not working properly so just clear % the dependency status cache completely. % % StatusMap0 = !.Info ^ dependency_status, % StatusMap = version_hash_table.fold(remove_grade_dependent_targets, % StatusMap0, StatusMap0), StatusMap = version_hash_table.init_default(dependency_file_hash), make_info_set_dependency_status(StatusMap, !Info), make_info_set_option_args(OptionArgs, !Info), % Building the library in the new grade is done in a separate process % to make it easier to stop and clean up on an interrupt. globals.lookup_bool_option(LibGlobals, very_verbose, VeryVerbose), setup_checking_for_interrupt(Cookie, !IO), call_in_forked_process( install_library_grade_2(ProgressStream, LibGlobals, LinkSucceeded0, ModuleName, AllModules, !.Info, CleanAfter), Succeeded0, !IO), Cleanup = maybe_make_grade_clean(ProgressStream, LibGlobals, CleanAfter, ModuleName, AllModules), teardown_checking_for_interrupt(VeryVerbose, Cookie, Cleanup, Succeeded0, Succeeded, !Info, !IO) ). % XXX Move out of between install_library_grade and install_library_grade_2. :- func remove_grade_dependent_targets(dependency_file, dependency_status, version_hash_table(dependency_file, dependency_status)) = version_hash_table(dependency_file, dependency_status). % See the comment above for the reason why we don't use this function % (that should be a predicate anyway). :- pragma consider_used(func(remove_grade_dependent_targets/3)). remove_grade_dependent_targets(File, _Status, StatusMap0) = StatusMap :- ( if File = dep_target(target_file(_, Target)), target_is_grade_or_arch_dependent(Target) then StatusMap = delete(StatusMap0, File) else StatusMap = StatusMap0 ). :- pred install_library_grade_2(io.text_output_stream::in, globals::in, maybe_succeeded::in, module_name::in, list(module_name)::in, make_info::in, bool::in, maybe_succeeded::out, io::di, io::uo) is det. install_library_grade_2(ProgressStream, Globals, LinkSucceeded0, ModuleName, AllModules, Info0, CleanAfter, Succeeded, !IO) :- make_misc_target(ProgressStream, Globals, ModuleName - misc_target_build_library, LibSucceeded, Info0, Info1, [], Specs, !IO), ( LibSucceeded = succeeded, % `GradeDir' differs from `Grade' in that it is in canonical form. grade_directory_component(Globals, GradeDir), install_library_grade_files(ProgressStream, Globals, LinkSucceeded0, GradeDir, ModuleName, AllModules, Succeeded, Info1, Info2, !IO), maybe_make_grade_clean(ProgressStream, Globals, CleanAfter, ModuleName, AllModules, Info2, _Info, !IO) ; LibSucceeded = did_not_succeed, % XXX MAKE_STREAM io.output_stream(ErrorStream, !IO), write_error_specs(ErrorStream, Globals, Specs, !IO), Succeeded = did_not_succeed ). % Install the `.a', `.so', `.jar', `.opt' and `.mih' files for % the current grade. % % NOTE: changes here may require changes to % file_util.get_install_name_option/4. % :- pred install_library_grade_files(io.text_output_stream::in, globals::in, maybe_succeeded::in, string::in, module_name::in, list(module_name)::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. install_library_grade_files(ProgressStream, Globals, LinkSucceeded0, GradeDir, ModuleName, AllModules, Succeeded, !Info, !IO) :- make_grade_install_dirs(ProgressStream, Globals, GradeDir, DirResult, LinkSucceeded1, !IO), LinkSucceeded = LinkSucceeded0 `and` LinkSucceeded1, ( DirResult = succeeded, linked_target_file_name(Globals, ModuleName, static_library, LibFileName, !IO), linked_target_file_name(Globals, ModuleName, shared_library, SharedLibFileName, !IO), linked_target_file_name(Globals, ModuleName, csharp_library, DllFileName, !IO), linked_target_file_name(Globals, ModuleName, java_archive, JarFileName, !IO), globals.lookup_string_option(Globals, install_prefix, Prefix), ( if string.prefix(GradeDir, "csharp") then GradeLibDir = Prefix/"lib"/"mercury"/"lib"/GradeDir, install_file(ProgressStream, Globals, DllFileName, GradeLibDir, LibsSucceeded, !IO), InitSucceeded = succeeded else if string.prefix(GradeDir, "java") then GradeLibDir = Prefix/"lib"/"mercury"/"lib"/GradeDir, install_file(ProgressStream, Globals, JarFileName, GradeLibDir, LibsSucceeded, !IO), InitSucceeded = succeeded else GradeLibDir = Prefix/"lib"/"mercury"/"lib"/GradeDir, maybe_install_library_file(ProgressStream, Globals, "static", LibFileName, GradeLibDir, LibSucceeded0, !IO), ( if LibFileName = SharedLibFileName then LibsSucceeded = LibSucceeded0 else maybe_install_library_file(ProgressStream, Globals, "shared", SharedLibFileName, GradeLibDir, SharedLibSucceeded, !IO), LibsSucceeded = LibSucceeded0 `and` SharedLibSucceeded ), install_grade_init(ProgressStream, Globals, GradeDir, ModuleName, InitSucceeded, !IO) ), list.map_foldl2( install_grade_ints_and_headers(ProgressStream, Globals, LinkSucceeded, GradeDir), AllModules, IntsHeadersSucceeded, !Info, !IO), Succeeded = and_list( [LibsSucceeded, InitSucceeded | IntsHeadersSucceeded]) ; DirResult = did_not_succeed, Succeeded = did_not_succeed ). % Install the `.init' file for the current grade. % :- pred install_grade_init(io.text_output_stream::in, globals::in, string::in, module_name::in, maybe_succeeded::out, io::di, io::uo) is det. install_grade_init(ProgressStream, Globals, GradeDir, ModuleName, Succeeded, !IO) :- globals.lookup_string_option(Globals, install_prefix, Prefix), GradeModulesDir = Prefix / "lib" / "mercury" / "modules" / GradeDir, module_name_to_file_name(Globals, $pred, ext_cur_gs(ext_cur_gs_lib_init), ModuleName, InitFileName), install_file(ProgressStream, Globals, InitFileName, GradeModulesDir, Succeeded, !IO). % Install the `.opt', `.analysis' and `.mih' files for the current grade. % :- pred install_grade_ints_and_headers(io.text_output_stream::in, globals::in, maybe_succeeded::in, string::in, module_name::in, maybe_succeeded::out, make_info::in, make_info::out, io::di, io::uo) is det. install_grade_ints_and_headers(ProgressStream, Globals, LinkSucceeded, GradeDir, ModuleName, Succeeded, !Info, !IO) :- get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( MaybeModuleDepInfo = some_module_dep_info(_ModuleDepInfo), globals.lookup_string_option(Globals, install_prefix, Prefix), LibDir = Prefix/"lib"/"mercury", globals.get_target(Globals, Target), globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode), ( if Target = target_c, HighLevelCode = yes then GradeIncDir = LibDir/"lib"/GradeDir/"inc", install_subdir_file(ProgressStream, Globals, LinkSucceeded, GradeIncDir, ModuleName, {ext_cur_ngs_gs_max_cur(ext_cur_ngs_gs_max_cur_mih), "mihs"}, HeaderSucceeded1, !IO), % This is needed so that the file will be found in Mmake's VPATH. IntDir = LibDir/"ints", install_subdir_file(ProgressStream, Globals, LinkSucceeded, IntDir, ModuleName, {ext_cur_ngs_gs_max_cur(ext_cur_ngs_gs_max_cur_mih), "mihs"}, HeaderSucceeded2, !IO), HeaderSucceeded = HeaderSucceeded1 `and` HeaderSucceeded2 else HeaderSucceeded = succeeded ), GradeIntDir = LibDir/"ints"/GradeDir, globals.get_any_intermod(Globals, AnyIntermod), ( AnyIntermod = yes, install_subdir_file(ProgressStream, Globals, LinkSucceeded, GradeIntDir, ModuleName, {ext_cur_ngs_gs_max_ngs(ext_cur_ngs_gs_max_ngs_opt_plain), "opts"}, OptSucceeded, !IO) ; AnyIntermod = no, OptSucceeded = succeeded ), globals.lookup_bool_option(Globals, intermodule_analysis, IntermodAnalysis), ( IntermodAnalysis = yes, install_subdir_file(ProgressStream, Globals, LinkSucceeded, GradeIntDir, ModuleName, {ext_cur_ngs_gs_max_ngs(ext_cur_ngs_gs_max_ngs_an_analysis), "analyses"}, IntermodAnalysisSucceeded, !IO) ; IntermodAnalysis = no, IntermodAnalysisSucceeded = succeeded ), Succeeded = HeaderSucceeded `and` OptSucceeded `and` IntermodAnalysisSucceeded ; MaybeModuleDepInfo = no_module_dep_info, Succeeded = did_not_succeed ). % Install a file in the given directory, and in directory/Mercury/exts % if the symlinks for the subdirectories couldn't be created % (e.g. on Windows). % :- pred install_subdir_file(io.text_output_stream::in, globals::in, maybe_succeeded::in, dir_name::in, module_name::in, {ext, string}::in, maybe_succeeded::out, io::di, io::uo) is det. install_subdir_file(ProgressStream, Globals, SubdirLinkSucceeded, InstallDir, ModuleName, {Ext, Exts}, Succeeded, !IO) :- module_name_to_file_name(Globals, $pred, Ext, ModuleName, FileName), install_file(ProgressStream, Globals, FileName, InstallDir, Succeeded1, !IO), ( SubdirLinkSucceeded = did_not_succeed, install_file(ProgressStream, Globals, FileName, InstallDir/"Mercury"/Exts, Succeeded2, !IO), Succeeded = Succeeded1 `and` Succeeded2 ; SubdirLinkSucceeded = succeeded, Succeeded = Succeeded1 ). :- pred maybe_install_library_file(io.text_output_stream::in, globals::in, string::in, file_name::in, dir_name::in, maybe_succeeded::out, io::di, io::uo) is det. maybe_install_library_file(ProgressStream, Globals, Linkage, FileName, InstallDir, Succeeded, !IO) :- globals.lookup_accumulating_option(Globals, lib_linkages, LibLinkages), ( if list.member(Linkage, LibLinkages) then install_file(ProgressStream, Globals, FileName, InstallDir, Succeeded0, !IO), % We need to update the archive index after we copy a .a file to % the installation directory because the linkers on some OSs % complain if we don't. ( if Linkage = "static", Succeeded0 = succeeded then % Since mmc --make uses --use-subdirs the above FileName will % be directory qualified. We don't care about the build % directory here so we strip that qualification off. BaseFileName = dir.det_basename(FileName), generate_archive_index(ProgressStream, Globals, BaseFileName, InstallDir, Succeeded, !IO) else Succeeded = Succeeded0 ) else Succeeded = succeeded ). :- pred install_file(io.text_output_stream::in, globals::in, file_name::in, dir_name::in, maybe_succeeded::out, io::di, io::uo) is det. install_file(ProgressStream, Globals, FileName, InstallDir, Succeeded, !IO) :- % XXX MAKE_STREAM ErrorStream = ProgressStream, OutputStream = ProgressStream, verbose_make_four_part_msg(Globals, "Installing file", FileName, "in", InstallDir, InstallMsg), maybe_write_msg(ProgressStream, InstallMsg, !IO), Command = make_install_file_command(Globals, FileName, InstallDir), invoke_system_command(Globals, ProgressStream, ErrorStream, OutputStream, cmd_verbose, Command, Succeeded, !IO). :- pred install_directory(io.text_output_stream::in, globals::in, dir_name::in, dir_name::in, maybe_succeeded::out, io::di, io::uo) is det. :- pragma consider_used(pred(install_directory/7)). install_directory(ProgressStream, Globals, SourceDirName, InstallDir, Succeeded, !IO) :- % XXX MAKE_STREAM ErrorStream = ProgressStream, OutputStream = ProgressStream, verbose_make_four_part_msg(Globals, "Installing directory", SourceDirName, "in", InstallDir, InstallMsg), maybe_write_msg(ProgressStream, InstallMsg, !IO), Command = make_install_dir_command(Globals, SourceDirName, InstallDir), invoke_system_command(Globals, ProgressStream, ErrorStream, OutputStream, cmd_verbose, Command, Succeeded, !IO). :- pred make_install_dirs(io.text_output_stream::in, globals::in, maybe_succeeded::out, maybe_succeeded::out, io::di, io::uo) is det. make_install_dirs(ProgressStream, Globals, Result, LinkResult, !IO) :- globals.lookup_string_option(Globals, install_prefix, Prefix), LibDir = Prefix/"lib"/"mercury", make_directory(LibDir/"inc", Result1, !IO), make_directory(LibDir/"modules", Result2, !IO), IntsSubdir = LibDir/"ints"/"Mercury", make_directory(IntsSubdir, Result3, !IO), Results0 = [Result1, Result2, Result3], Subdirs = ["int0", "int", "int2", "int3", "opt", "trans_opt", "mh", "mih", "module_dep"], list.map_foldl(make_install_symlink(Globals, IntsSubdir), Subdirs, LinkResults, !IO), LinkResult = and_list(LinkResults), ( LinkResult = succeeded, Results = Results0 ; LinkResult = did_not_succeed, list.map_foldl( ( pred(Ext::in, MkDirResult::out, !.IO::di, !:IO::uo) is det:- make_directory(IntsSubdir/(Ext ++ "s"), MkDirResult, !IO) ), Subdirs, MkDirResults, !IO), Results = Results0 ++ MkDirResults ), print_mkdir_errors(ProgressStream, Results, Result, !IO). :- pred make_grade_install_dirs(io.text_output_stream::in, globals::in, string::in, maybe_succeeded::out, maybe_succeeded::out, io::di, io::uo) is det. make_grade_install_dirs(ProgressStream, Globals, Grade, Result, LinkResult, !IO) :- globals.lookup_string_option(Globals, install_prefix, Prefix), LibDir = Prefix/"lib"/"mercury", GradeIntsSubdir = LibDir/"ints"/Grade/"Mercury", make_directory(GradeIntsSubdir, Result1, !IO), GradeIncSubdir = LibDir/"lib"/Grade/"inc"/"Mercury", make_directory(GradeIncSubdir, Result2, !IO), GradeModuleSubdir = LibDir/"modules"/Grade, make_directory(GradeModuleSubdir, Result3, !IO), Results0 = [Result1, Result2, Result3], make_install_symlink(Globals, GradeIncSubdir, "mih", LinkResult0, !IO), list.map_foldl(make_install_symlink(Globals, GradeIntsSubdir), ["opt", "trans_opt", "analysis"], LinkResults, !IO), LinkResult = and_list([LinkResult0 | LinkResults]), ( LinkResult = succeeded, Results = Results0 ; LinkResult = did_not_succeed, make_directory(GradeIncSubdir/"mihs", Result4, !IO), make_directory(GradeIntsSubdir/"opts", Result5, !IO), make_directory(GradeIntsSubdir/"trans_opts", Result6, !IO), make_directory(GradeIntsSubdir/"analyses", Result7, !IO), Results = [Result4, Result5, Result6, Result7 | Results0] ), print_mkdir_errors(ProgressStream, Results, Result, !IO). :- pred print_mkdir_errors(io.text_output_stream::in, list(io.res)::in, maybe_succeeded::out, io::di, io::uo) is det. print_mkdir_errors(_ProgressStream, [], succeeded, !IO). print_mkdir_errors(ProgressStream, [Result | Results], Succeeded, !IO) :- ( Result = ok, print_mkdir_errors(ProgressStream, Results, Succeeded, !IO) ; Result = error(Error), io.format(ProgressStream, "Error creating installation directories: %s\n", [s(io.error_message(Error))], !IO), print_mkdir_errors(ProgressStream, Results, _, !IO), Succeeded = did_not_succeed ). :- pred make_install_symlink(globals::in, string::in, string::in, maybe_succeeded::out, io::di, io::uo) is det. make_install_symlink(Globals, Subdir, Ext, Succeeded, !IO) :- LinkName = Subdir/(Ext ++ "s"), maybe_make_symlink(Globals, "..", LinkName, Succeeded, !IO). % Generate (or update) the index for an archive file, % i.e. run ranlib on a .a file. % :- pred generate_archive_index(io.text_output_stream::in, globals::in, file_name::in, dir_name::in, maybe_succeeded::out, io::di, io::uo) is det. generate_archive_index(ProgressStream, Globals, FileName, InstallDir, Succeeded, !IO) :- verbose_make_four_part_msg(Globals, "Generating archive index for file", FileName, "in", InstallDir, InstallMsg), maybe_write_msg(ProgressStream, InstallMsg, !IO), globals.lookup_string_option(Globals, ranlib_command, RanLibCommand), globals.lookup_string_option(Globals, ranlib_flags, RanLibFlags), % XXX What is the point of using more than one space? Command = string.join_list(" ", [ quote_shell_cmd_arg(RanLibCommand), RanLibFlags, quote_shell_cmd_arg(InstallDir / FileName) ]), % XXX MAKE_STREAM ErrorStream = ProgressStream, CmdOutputStream = ProgressStream, invoke_system_command(Globals, ProgressStream, ErrorStream, CmdOutputStream, cmd_verbose, Command, Succeeded, !IO). %---------------------------------------------------------------------------% :- pred maybe_make_grade_clean(io.text_output_stream::in, globals::in, bool::in, module_name::in, list(module_name)::in, make_info::in, make_info::out, io::di, io::uo) is det. maybe_make_grade_clean(ProgressStream, Globals, Clean, ModuleName, AllModules, !Info, !IO) :- ( Clean = yes, make_grade_clean(ProgressStream, Globals, ModuleName, AllModules, !Info, !IO) ; Clean = no ). % Clean up grade-dependent files. % :- pred make_grade_clean(io.text_output_stream::in, globals::in, module_name::in, list(module_name)::in, make_info::in, make_info::out, io::di, io::uo) is det. make_grade_clean(ProgressStream, Globals, ModuleName, AllModules, !Info, !IO) :- grade_directory_component(Globals, Grade), % XXX MAKE_EXTRA_PERIOD string.format("Cleaning up grade-dependent files for `%s' in grade %s.", [s(escaped_sym_name_to_string(ModuleName)), s(Grade)], Part1), verbose_make_one_part_msg(Globals, Part1, CleaningMsg), maybe_write_msg(ProgressStream, CleaningMsg, !IO), make_main_module_realclean(ProgressStream, Globals, ModuleName, !Info, !IO), list.foldl2(make_module_clean(ProgressStream, Globals), AllModules, !Info, !IO). :- pred make_main_module_realclean(io.text_output_stream::in, globals::in, module_name::in, make_info::in, make_info::out, io::di, io::uo) is det. make_main_module_realclean(ProgressStream, Globals, ModuleName, !Info, !IO) :- % XXX MAKE_EXTRA_PERIOD string.format("Removing executable and library files for `%s'.", [s(escaped_sym_name_to_string(ModuleName))], Part1), verbose_make_one_part_msg(Globals, Part1, CleaningMsg), maybe_write_msg(ProgressStream, CleaningMsg, !IO), LinkedTargetTypes = [ executable, static_library, shared_library, csharp_executable, csharp_library, java_executable, java_archive ], list.map2_foldl(linked_target_file_name_full_curdir(Globals, ModuleName), LinkedTargetTypes, FileNames, ThisDirFileNames, !IO), % Remove the symlinks created for `--use-grade-subdirs'. % XXX This symlink should not be necessary anymore for `mmc --make'. module_name_to_file_name_full_curdir(Globals, $pred, ext_cur_gs(ext_cur_gs_lib_init), ModuleName, FullInitFileName, ThisDirInitFileName), FilesToRemove = FileNames ++ ThisDirFileNames ++ [FullInitFileName, ThisDirInitFileName], list.foldl2(remove_file_for_make(ProgressStream, Globals, very_verbose), FilesToRemove, !Info, !IO), remove_init_files(ProgressStream, Globals, very_verbose, ModuleName, !Info, !IO). :- pred remove_init_files(io.text_output_stream::in, globals::in, option::in, module_name::in, make_info::in, make_info::out, io::di, io::uo) is det. remove_init_files(ProgressStream, Globals, Verbose, ModuleName, !Info, !IO) :- remove_module_file_for_make(ProgressStream, Globals, Verbose, ModuleName, ext_cur_ngs_gs(ext_cur_ngs_gs_init_c), !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, Verbose, ModuleName, ext_cur_ngs_gs(ext_cur_ngs_gs_init_obj_obj_opt), !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, Verbose, ModuleName, ext_cur_ngs_gs(ext_cur_ngs_gs_init_obj_pic_obj_opt), !Info, !IO). %---------------------------------------------------------------------------% :- pred make_module_clean(io.text_output_stream::in, globals::in, module_name::in, make_info::in, make_info::out, io::di, io::uo) is det. make_module_clean(ProgressStream, Globals, ModuleName, !Info, !IO) :- % XXX MAKE_EXTRA_PERIOD string.format("Cleaning up target files for module `%s'.", [s(escaped_sym_name_to_string(ModuleName))], Part1), verbose_make_one_part_msg(Globals, Part1, CleaningMsg), maybe_write_msg(ProgressStream, CleaningMsg, !IO), list.foldl2( remove_make_target_file_by_name(ProgressStream, Globals, $pred, very_verbose, ModuleName), [module_target_errors, module_target_c_code, module_target_c_header(header_mih), module_target_csharp_code, module_target_java_code, module_target_java_class_code], !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, very_verbose, ModuleName, ext_cur_ngs_gs(ext_cur_ngs_gs_misc_used), !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, very_verbose, ModuleName, ext_cur_ngs(ext_cur_ngs_misc_prof), !Info, !IO), get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo), module_dep_info_get_fact_tables(ModuleDepInfo, FactTableFilesSet), set.to_sorted_list(FactTableFilesSet, FactTableFiles) ; MaybeModuleDepInfo = no_module_dep_info, FactTableFiles = [] ), list.foldl2(remove_fact_table_c_file(ProgressStream, Globals), FactTableFiles, !Info, !IO), foreign_language_module_name(ModuleName, lang_c, CCodeModule), remove_make_target_file_by_name(ProgressStream, Globals, $pred, very_verbose, CCodeModule, module_target_c_code, !Info, !IO), remove_object_and_assembler_files(ProgressStream, Globals, ModuleName, pic, FactTableFiles, !Info, !IO), remove_object_and_assembler_files(ProgressStream, Globals, ModuleName, non_pic, FactTableFiles, !Info, !IO). :- pred remove_fact_table_c_file(io.text_output_stream::in, globals::in, string::in, make_info::in, make_info::out, io::di, io::uo) is det. remove_fact_table_c_file(ProgressStream, Globals, FactTableFile, !Info, !IO) :- fact_table_file_name_return_dirs(Globals, $pred, ext_cur_ngs_gs(ext_cur_ngs_gs_target_c), FactTableFile, _FactTableDirs, FactTableCFile), remove_file_for_make(ProgressStream, Globals, very_verbose, FactTableCFile, !Info, !IO). :- pred remove_object_and_assembler_files(io.text_output_stream::in, globals::in, module_name::in, pic::in, list(file_name)::in, make_info::in, make_info::out, io::di, io::uo) is det. remove_object_and_assembler_files(ProgressStream, Globals, ModuleName, PIC, FactTableFiles, !Info, !IO) :- remove_make_target_file_by_name(ProgressStream, Globals, $pred, very_verbose, ModuleName, module_target_object_code(PIC), !Info, !IO), remove_make_target_file_by_name(ProgressStream, Globals, $pred, very_verbose, ModuleName, module_target_foreign_object(PIC, lang_c), !Info, !IO), list.foldl2( remove_fact_table_object_and_assembler_files(ProgressStream, Globals, ModuleName, PIC), FactTableFiles, !Info, !IO). :- pred remove_fact_table_object_and_assembler_files(io.text_output_stream::in, globals::in, module_name::in, pic::in, file_name::in, make_info::in, make_info::out, io::di, io::uo) is det. remove_fact_table_object_and_assembler_files(ProgressStream, Globals, ModuleName, PIC, FactTableFile, !Info, !IO) :- remove_make_target_file_by_name(ProgressStream, Globals, $pred, very_verbose, ModuleName, module_target_fact_table_object(PIC, FactTableFile), !Info, !IO). %---------------------------------------------------------------------------% :- pred make_module_realclean(io.text_output_stream::in, globals::in, module_name::in, make_info::in, make_info::out, io::di, io::uo) is det. make_module_realclean(ProgressStream, Globals, ModuleName, !Info, !IO) :- make_module_clean(ProgressStream, Globals, ModuleName, !Info, !IO), string.format("Cleaning up interface files for module `%s'", [s(escaped_sym_name_to_string(ModuleName))], Part1), verbose_make_one_part_msg(Globals, Part1, CleaningMsg), maybe_write_msg(ProgressStream, CleaningMsg, !IO), Targets = [module_target_int0, module_target_int1, module_target_int2, module_target_int3, module_target_opt, module_target_analysis_registry, module_target_c_header(header_mh), module_target_track_flags], list.foldl2( remove_make_target_file_by_name(ProgressStream, Globals, $pred, very_verbose, ModuleName), Targets, !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, very_verbose, ModuleName, ext_cur_ngs(ext_cur_ngs_misc_module_dep), !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, very_verbose, ModuleName, ext_cur_ngs_gs_max_ngs(ext_cur_ngs_gs_max_ngs_an_imdg), !Info, !IO), remove_module_file_for_make(ProgressStream, Globals, very_verbose, ModuleName, ext_cur_ngs_gs_max_ngs(ext_cur_ngs_gs_max_ngs_an_request), !Info, !IO). %---------------------------------------------------------------------------% % Remove all nested modules from a list of modules. % :- pred filter_out_nested_modules(io.text_output_stream::in, globals::in, list(module_name)::in, list(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. filter_out_nested_modules(ProgressStream, Globals, Modules0, Modules, !Info, !IO) :- list.foldl3(collect_nested_modules(ProgressStream, Globals), Modules0, set.init, NestedModules, !Info, !IO), list.negated_filter(set.contains(NestedModules), Modules0, Modules). :- pred collect_nested_modules(io.text_output_stream::in, globals::in, module_name::in, set(module_name)::in, set(module_name)::out, make_info::in, make_info::out, io::di, io::uo) is det. collect_nested_modules(ProgressStream, Globals, ModuleName, !NestedModules, !Info, !IO) :- get_maybe_module_dep_info(ProgressStream, Globals, ModuleName, MaybeModuleDepInfo, !Info, !IO), ( MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo), module_dep_info_get_maybe_top_module(ModuleDepInfo, MaybeTopModule), NestedSubModules = get_nested_children_of_top_module(MaybeTopModule), set.union(NestedSubModules, !NestedModules) ; MaybeModuleDepInfo = no_module_dep_info ). %---------------------------------------------------------------------------% % Find all modules in the current directory which are reachable (by import) % from the given module. Return a list of `--local-module-id' options % suitable for the command line. % :- pred make_local_module_id_options(io.text_output_stream::in, globals::in, module_name::in, maybe_succeeded::out, list(string)::out, make_info::in, make_info::out, io::di, io::uo) is det. make_local_module_id_options(ProgressStream, Globals, ModuleName, Succeeded, Options, !Info, !IO) :- find_reachable_local_modules(ProgressStream, Globals, ModuleName, Succeeded, LocalModules, !Info, !IO), set.fold(make_local_module_id_option, LocalModules, [], Options). :- pred make_local_module_id_option(module_name::in, list(string)::in, list(string)::out) is det. make_local_module_id_option(ModuleName, Opts0, Opts) :- ModuleNameStr = sym_name_to_string(ModuleName), Opts = ["--local-module-id", ModuleNameStr | Opts0]. %---------------------------------------------------------------------------% :- end_module make.program_target. %---------------------------------------------------------------------------%