Files
mercury/compiler/make.program_target.m
2026-02-04 04:16:37 +11:00

1803 lines
76 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2002-2012 The University of Melbourne.
% Copyright (C) 2013-2017, 2019-2026 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.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module analysis.
:- import_module analysis.framework.
:- import_module analysis.operations.
:- import_module backend_libs.
:- import_module backend_libs.compile_target_code.
:- import_module backend_libs.link_target_code.
:- import_module libs.check_libgrades.
:- import_module libs.file_util.
:- import_module libs.options.
:- import_module libs.process_util.
:- import_module libs.system_cmds.
:- import_module libs.timestamp.
:- import_module make.build.
:- import_module make.check_up_to_date.
:- import_module make.clean.
:- import_module make.find_local_modules.
:- import_module make.get_module_dep_info.
:- import_module make.library_install.
:- 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.find_module.
:- import_module parse_tree.module_baggage.
:- import_module parse_tree.module_dep_info.
:- import_module parse_tree.module_deps_graph.
:- 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, LinkedTargetType),
(
LinkedTargetType = shared_library,
ExtraOptions = ["--compile-to-shared-lib"]
;
( LinkedTargetType = executable
; LinkedTargetType = static_library
; LinkedTargetType = csharp_executable
; LinkedTargetType = csharp_library
; LinkedTargetType = java_executable
; LinkedTargetType = java_archive
),
ExtraOptions = []
),
globals.get_library_install_linkages(Globals, LibraryInstallLinkages),
( if
(
LinkedTargetType = static_library,
not set.member(sos_static, LibraryInstallLinkages)
;
LinkedTargetType = shared_library,
not set.member(sos_shared, LibraryInstallLinkages)
)
then
% XXX What is the justification for this?
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, _LinkedTargetType),
% 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),
MaybeStdLibGrades = make_info_get_maybe_stdlib_grades(!.Info),
Params = make_info_get_compiler_params(!.Info),
setup_for_build_with_module_options(ProgressStream, DefaultOptionTable,
MaybeStdLibGrades, invoked_by_mmc_make, MainModuleName,
Params, ExtraOptions, MayBuild, !IO),
(
MayBuild = may_build(_AllOptionArgs, BuildGlobals),
make_linked_target_2(ProgressStream, BuildGlobals,
LinkedTargetFile, Succeeded, !Info, !IO)
;
MayBuild = may_not_build(Specs),
% The errors we can get here report problems we encounter
% while trying to set up to compile a module, and not specific
% to the Mercury module itself, except insofar as the problem
% may be caused by e.g. an unrecognized option name in a
% module-specific MCFLAGS make variable.
write_error_specs(ProgressStream, 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, LinkedTargetType),
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, LinkedTargetType, 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 ProgModulesAlpha represents inlining
% get_target_modules(Globals, IntermediateTargetType, AllModulesList,
% ProgModulesAlpha, !Info, !IO),
% knowing that IntermediateTargetType cannot be module_target_errors.
ProgModulesAlpha = AllModulesList,
order_target_modules(ProgressStream, Globals,
ProgModulesAlpha, ProgModules, !Info, !IO),
filter_out_nested_modules(ProgressStream, Globals,
ProgModules, ProgModulesNonnested, !Info, !IO),
IntermediateTargetsNonnested =
make_target_id_list(ProgModulesNonnested, IntermediateTargetType),
ObjTargets = make_target_id_list(ProgModules, ObjectTargetType),
list.map_foldl2(
get_foreign_object_targets(ProgressStream, Globals, PIC),
ProgModules, 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.
build_int_opt_files(ProgressStream, Globals,
build_all_ints_opts, 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,
(
ObjectTargetType = module_target_java_class_code,
make_class_files_for_all_program_modules(ProgressStream,
Globals, MainModuleName, ProgModules,
BuildJavaSucceeded, !Info, !IO),
(
BuildJavaSucceeded = succeeded,
% Disable the `--rebuild' option during this pass,
% otherwise all the Java classes will be built again.
globals.set_option(part_opmode_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
)
;
( ObjectTargetType = module_target_object_code(_)
; ObjectTargetType = module_target_csharp_code
),
foldl2_make_module_targets_maybe_parallel(KeepGoing, [],
ProgressStream, Globals, ObjTargets,
BuildDepsSucceeded1, !Info, !IO)
)
;
BuildDepsSucceeded0 = did_not_succeed,
BuildDepsSucceeded1 = did_not_succeed
),
(
BuildDepsSucceeded1 = succeeded,
% We expect ForeignObjTargets to be the empty list during
% most compiler invocations, and to be a very short list during
% most of the remaining compiler invocations. This means that
% parallelism here would probably incur more cost in overhead
% than it could possibly gain back.
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, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName, !IO),
get_file_timestamp(search_auth_cur_dir, FullMainModuleLinkedFileName,
_SearchDirs, MaybeTimestamp, !Info, !IO),
(
MaybeTimestamp = error(_),
MaybeOldestLhsTimestamp = some_lhs_file_is_missing
;
MaybeTimestamp = ok(LinkedFileTimestamp),
MaybeOldestLhsTimestamp =
all_lhs_files_exist_oldest_timestamp(LinkedFileTimestamp)
),
% XXX We pass BuildDepsSucceeded here, but BuildDepsSucceeded being
% did_not_succeed does not prevent LhsResult being can_rebuild_lhs(_).
% This means that we can go on to attempt to (re)build the target
% even if we couldn't build all its prerequisites. To me (zs),
% that looks like a bug, even if the keep_going flag is set.
should_we_rebuild_lhs(ProgressStream, Globals,
FullMainModuleLinkedFileName, MaybeOldestLhsTimestamp,
BuildDepsSucceeded, ObjTargets, LhsResult, !Info, !IO),
( if
DepsSucceeded = succeeded,
LhsResult = can_rebuild_lhs(ShouldRebuildLhs)
then
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
setup_checking_for_interrupt(Cookie, !IO),
open_module_error_stream(ProgressStream, Globals, !.Info,
MainModuleName, MaybeErrorStream, !IO),
(
MaybeErrorStream = es_error_already_reported,
Succeeded0 = did_not_succeed
;
MaybeErrorStream = es_ok(MESI, ErrorStream),
build_linked_target_with_any_prelink(ProgressStream, Globals,
MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName,
CurDirMainModuleLinkedFileName, MaybeOldestLhsTimestamp,
AllModules, ProgModules, CompilationTarget, PIC,
ShouldRebuildLhs, Succeeded0, !Info, !IO),
close_module_error_stream_handle_errors(ProgressStream,
Globals, MESI, ErrorStream, !.Info, !IO)
),
CleanupPred = linked_target_cleanup(ProgressStream, Globals,
MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName),
teardown_checking_for_interrupt(VeryVerbose, Cookie, CleanupPred,
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) :-
Target = target_file(Module, module_target_source),
get_target_timestamp(ProgressStream, Globals, 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)
).
%---------------------%
% 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
).
%---------------------%
:- pred get_foreign_object_targets(io.text_output_stream::in, globals::in,
pic::in, module_name::in, list(target_id)::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) =
merc_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_with_any_prelink(io.text_output_stream::in,
globals::in, module_name::in, linked_target_type::in,
file_name::in, file_name::in, maybe_oldest_lhs_file::in,
set(module_name)::in, list(module_name)::in,
compilation_target::in, pic::in, should_rebuild_lhs::in,
maybe_succeeded::out, make_info::in, make_info::out,
io::di, io::uo) is det.
build_linked_target_with_any_prelink(ProgressStream,
Globals, MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName,
MaybeOldestLhsTimestamp, AllModules, ProgModules,
CompilationTarget, PIC, ShouldRebuildLhs, Succeeded, !Info, !IO) :-
globals.lookup_maybe_string_option(Globals, make_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, OutputStream,
cmd_verbose, CommandString, PreLinkSucceeded, !IO)
;
MaybePreLinkCommand = no,
PreLinkSucceeded = succeeded
),
(
PreLinkSucceeded = succeeded,
maybe_build_linked_target(ProgressStream, Globals,
MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName,
MaybeOldestLhsTimestamp, AllModules, ProgModules,
CompilationTarget, PIC, ShouldRebuildLhs, Succeeded, !Info, !IO)
;
PreLinkSucceeded = did_not_succeed,
Succeeded = did_not_succeed
).
:- pred maybe_build_linked_target(io.text_output_stream::in, globals::in,
module_name::in, linked_target_type::in, file_name::in, file_name::in,
maybe_oldest_lhs_file::in, set(module_name)::in, list(module_name)::in,
compilation_target::in, pic::in, should_rebuild_lhs::in,
maybe_succeeded::out, make_info::in, make_info::out,
io::di, io::uo) is det.
maybe_build_linked_target(ProgressStream,
Globals0, MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName,
MaybeOldestLhsTimestamp, AllModules, ProgModules,
CompilationTarget, PIC, ShouldRebuildLhs, Succeeded, !Info, !IO) :-
% Clear the link_objects option -- we will pass the list of files directly.
globals.lookup_accumulating_option(Globals0, link_objects, LinkObjects),
globals.set_option(link_objects, accumulating([]),
Globals0, 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),
(
LinkedTargetType = executable,
make_init_obj_file_check_result(ProgressStream, NoLinkObjsGlobals,
MainModuleName, AllModulesList, InitObjSucceeded, InitObjects,
!Info, !IO)
;
( LinkedTargetType = static_library
; LinkedTargetType = shared_library
; LinkedTargetType = csharp_executable
; LinkedTargetType = csharp_library
; LinkedTargetType = java_executable
; LinkedTargetType = java_archive
),
InitObjSucceeded = succeeded,
InitObjects = []
),
% Report errors if any of the extra objects aren't present.
ObjectsToCheck = InitObjects ++ LinkObjects,
ObjectsToCheckTargets =
list.map((func(F) = non_merc_target(F)), ObjectsToCheck),
list.map_foldl2(
get_target_id_status(ProgressStream, NoLinkObjsGlobals),
ObjectsToCheckTargets, ExtraObjTargetStatuses, !Info, !IO),
( if
some [ExtraObjTargetStatus] (
list.member(ExtraObjTargetStatus, ExtraObjTargetStatuses),
ExtraObjTargetStatus =
target_status_result(_, _, target_status_error)
)
then
ExtraObjSucceeded = did_not_succeed
else
ExtraObjSucceeded = succeeded
),
BuildObjsSucceeded = InitObjSucceeded `and` ExtraObjSucceeded,
list.map2_foldl2(get_file_timestamp(search_auth_cur_dir),
ObjectsToCheck, _SearchDirs, ExtraObjectTimestamps, !Info, !IO),
% XXX We pass BuildObjsSucceeded here, but BuildObjsSucceeded being
% did_not_succeed does not prevent LhsResult being can_rebuild_lhs(_).
% This means that we can go on to attempt to (re)build the target
% even if we couldn't build all its prerequisites. To me (zs),
% that looks like a bug, even if the keep_going flag is set.
should_we_rebuild_lhs_given_timestamps(ProgressStream, NoLinkObjsGlobals,
FullMainModuleLinkedFileName, MaybeOldestLhsTimestamp,
BuildObjsSucceeded, ExtraObjTargetStatuses, ExtraObjectTimestamps,
ExtraObjectLhsResult, !IO),
(
ExtraObjectLhsResult = rhs_error,
% XXX We should get here if BuildDepsSucceeded = did_not_succeed,
% as well as if ExtraObjectDepsResult = rhs_error.
file_error_msg(FullMainModuleLinkedFileName, ErrorMsg),
maybe_write_msg_locked(ProgressStream, !.Info, ErrorMsg, !IO),
Succeeded = did_not_succeed
;
ExtraObjectLhsResult = can_rebuild_lhs(ExtraObjShouldRebuildLhs),
% Our caller has checked whether the lhs is up to date with respect
% to *some* files on the rhs, and just above we checked whether they
% are up to date with respect to *other* files on the rhs.
% XXX This seems like a clumsy arrangement.
( if
ShouldRebuildLhs = all_lhs_files_up_to_date,
ExtraObjShouldRebuildLhs = all_lhs_files_up_to_date
then
maybe_warn_linked_target_up_to_date(ProgressStream,
NoLinkObjsGlobals, MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName,
Succeeded, !Info, !IO)
else
build_linked_target(ProgressStream, NoLinkObjsGlobals,
MainModuleName, LinkedTargetType, FullMainModuleLinkedFileName,
AllModulesList, ProgModules, InitObjects, LinkObjects,
CompilationTarget, PIC, Succeeded, !Info, !IO)
)
).
:- pred make_init_obj_file_check_result(io.text_output_stream::in, globals::in,
module_name::in, list(module_name)::in,
maybe_succeeded::out, list(file_name)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
make_init_obj_file_check_result(ProgressStream, NoLinkObjsGlobals,
MainModuleName, AllModulesList, Succeeded, InitObjects, !Info, !IO) :-
globals.lookup_bool_option(NoLinkObjsGlobals, part_opmode_rebuild,
MustRecompile),
make_init_obj_file(ProgressStream, NoLinkObjsGlobals, MustRecompile,
MainModuleName, AllModulesList, InitObjectResult, !IO),
(
InitObjectResult = yes(InitObject),
% We may need to update the timestamp of the `_init.o' file.
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
map.delete(InitObject, FileTimestampMap0, FileTimestampMap1),
make_info_set_file_timestamp_map(FileTimestampMap1, !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.
Succeeded = succeeded,
InitObjects = [InitObject]
;
InitObjectResult = no,
Succeeded = did_not_succeed,
InitObjects = []
).
:- pred maybe_warn_linked_target_up_to_date(io.text_output_stream::in,
globals::in, module_name::in, linked_target_type::in,
file_name::in, file_name::in, maybe_succeeded::out,
make_info::in, make_info::out, io::di, io::uo) is det.
maybe_warn_linked_target_up_to_date(ProgressStream,
NoLinkObjsGlobals, MainModuleName, LinkedTargetType,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName,
Succeeded, !Info, !IO) :-
MainModuleLinkedTarget =
top_target_file(MainModuleName, linked_target(LinkedTargetType)),
( 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
% We did not just perform a link, but check whether the other copy
% of the would-have-been-linked-if-that-were-needed file has to be
% re-copied.
post_link_maybe_make_symlink_or_copy(NoLinkObjsGlobals, ProgressStream,
FullMainModuleLinkedFileName, CurDirMainModuleLinkedFileName,
MainModuleName, LinkedTargetType, 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)
)
).
:- pred build_linked_target(io.text_output_stream::in, globals::in,
module_name::in, linked_target_type::in, file_name::in,
list(module_name)::in, list(module_name)::in,
list(file_name)::in, list(file_name)::in, compilation_target::in, pic::in,
maybe_succeeded::out, make_info::in, make_info::out,
io::di, io::uo) is det.
build_linked_target(ProgressStream, NoLinkObjsGlobals,
MainModuleName, LinkedTargetType, FullMainModuleLinkedFileName,
AllModulesList, ProgModules, InitObjectFileNames, LinkObjectFileNames,
CompilationTarget, PIC, Succeeded, !Info, !IO) :-
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, NoLinkObjsGlobals,
PIC),
AllModulesList, ForeignObjectFileNameLists, !Info, !IO),
ForeignObjectFileNames = list.condense(ForeignObjectFileNameLists),
(
CompilationTarget = target_c,
maybe_pic_object_file_extension(PIC, ObjExt, _),
Ext = ext_cur_ngs_gas(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)
),
% XXX LEGACY
list.map2(module_name_to_file_name(NoLinkObjsGlobals, $pred, Ext),
ProgModules, ProgModuleObjFileNames, _ProgModuleObjFileNamesProposed),
% LinkObjectFileNames may contain `.a' files which must come
% after all the object files on the linker command line.
AllObjects = InitObjectFileNames ++ ProgModuleObjFileNames ++
ForeignObjectFileNames ++ LinkObjectFileNames,
(
( 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(
link_and_write_error_specs(NoLinkObjsGlobals, ProgressStream,
LinkedTargetType, MainModuleName, AllObjects),
Succeeded, !IO)
),
CmdLineTargets0 = make_info_get_command_line_targets(!.Info),
set.delete(
top_target_file(MainModuleName, linked_target(LinkedTargetType)),
CmdLineTargets0, CmdLineTargets),
make_info_set_command_line_targets(CmdLineTargets, !Info),
(
Succeeded = succeeded,
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
map.delete(FullMainModuleLinkedFileName,
FileTimestampMap0, FileTimestampMap),
make_info_set_file_timestamp_map(FileTimestampMap, !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),
get_any_fact_table_object_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 link_and_write_error_specs(globals::in, io.text_output_stream::in,
linked_target_type::in, module_name::in, list(string)::in,
maybe_succeeded::out, io::di, io::uo) is det.
link_and_write_error_specs(Globals, ProgressStream,
LinkTargetType, ModuleName, ObjectsList, Succeeded, !IO) :-
link_files_into_executable_or_library_for_c_cs_java(ProgressStream,
Globals, LinkTargetType, ModuleName, ObjectsList,
Specs, Succeeded, !IO),
% The errors we can get here are not specific to any Mercury module.
write_error_specs(ProgressStream, Globals, Specs, !IO).
:- 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, LinkedTargetType,
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)
),
(
LinkedTargetType = executable,
remove_init_files(ProgressStream, Globals, verbose_make,
MainModuleName, !Info, !IO)
;
( LinkedTargetType = static_library
; LinkedTargetType = shared_library
; LinkedTargetType = csharp_executable
; LinkedTargetType = csharp_library
; LinkedTargetType = java_executable
; LinkedTargetType = java_archive
)
).
%---------------------------------------------------------------------------%
% In java grades, we want to invoke `javac' just once, passing it
% a list of all the program's out-of-date `.java' files. This is
% a lot quicker than compiling each Java file individually.
% (For the reason, see the comment on the module_target_java_class_code
% arm of the big switch in the find_direct_prereqs_of_module_target
% predicate in make.prereqs.m.)
%
:- pred make_class_files_for_all_program_modules(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_class_files_for_all_program_modules(ProgressStream, Globals,
MainModuleName, ProgModules, Succeeded, !Info, !IO) :-
find_java_files_to_recompile(ProgressStream, Globals,
ProgModules, JavaFilesToRecompile, !Info, !IO),
(
JavaFilesToRecompile = [],
Succeeded = succeeded
;
JavaFilesToRecompile = [HeadJavaFile | TailJavaFiles],
recompile_given_java_files(ProgressStream, Globals,
MainModuleName, HeadJavaFile, TailJavaFiles,
Succeeded, !.Info, !IO),
(
Succeeded = did_not_succeed
;
Succeeded = succeeded,
% javac may write not just the .class files corresponding to
% JavaFilesToRecompile, but also other .class files.
% Since we have no simple, efficient way to predict what .class
% files the recompilation may have affected, we intentionally
% forget all our previously-recorded timestamps for .class files.
TimestampMap0 = make_info_get_file_timestamp_map(!.Info),
map.foldl(reinsert_timestamps_for_non_class_files, TimestampMap0,
map.init, TimestampMap),
make_info_set_file_timestamp_map(TimestampMap, !Info),
% For simplicity, clear out all target file timestamps.
make_info_set_target_file_timestamp_map(
init_target_file_timestamp_map, !Info)
)
).
:- pred find_java_files_to_recompile(io.text_output_stream::in,
globals::in, list(module_name)::in, list(file_name)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
find_java_files_to_recompile(_, _, [], [], !Info, !IO).
find_java_files_to_recompile(ProgressStream, Globals,
[HeadModuleName | TailModuleNames], JavaFilesToRecompile,
!Info, !IO) :-
find_java_files_to_recompile(ProgressStream, Globals,
TailModuleNames, TailJavaFilesToRecompile, !Info, !IO),
JavaTarget = target_file(HeadModuleName, module_target_java_code),
ClassTarget = target_file(HeadModuleName, module_target_java_class_code),
get_target_timestamp(ProgressStream, Globals,
JavaTarget, MaybeJavaTimestamp, !Info, !IO),
get_target_timestamp(ProgressStream, Globals,
ClassTarget, MaybeClassTimestamp, !Info, !IO),
( if
MaybeJavaTimestamp = ok(JavaTimestamp),
MaybeClassTimestamp = ok(ClassTimestamp),
ClassTimestamp @>= JavaTimestamp
then
JavaFilesToRecompile = TailJavaFilesToRecompile
else
% XXX Note that get_target_timestamp *also* converts HeadModuleName
% to HeadJavaFile, but *only* if the timestamp of JavaTarget
% is not in the cache.
%
% XXX FILE_NAMES
% XXX LEGACY
JavaExt = ext_cur_ngs_gs_java(ext_cur_ngs_gs_java_java),
module_name_to_file_name(Globals, $pred, JavaExt, HeadModuleName,
HeadJavaFile, _HeadJavaFileProposed),
JavaFilesToRecompile = [HeadJavaFile | TailJavaFilesToRecompile]
).
:- pred recompile_given_java_files(io.text_output_stream::in, globals::in,
module_name::in, file_name::in, list(file_name)::in, maybe_succeeded::out,
make_info::in, io::di, io::uo) is det.
recompile_given_java_files(ProgressStream, Globals, MainModuleName,
HeadJavaFile, TailJavaFiles, Succeeded, Info, !IO) :-
verbose_make_one_part_msg(Globals, "Making Java class files", MakingMsg),
maybe_write_msg(ProgressStream, MakingMsg, !IO),
% We redirect errors to a file named after the main module.
open_module_error_stream(ProgressStream, Globals, Info, MainModuleName,
MaybeErrorStream, !IO),
(
MaybeErrorStream = es_error_already_reported,
Succeeded = did_not_succeed
;
MaybeErrorStream = es_ok(MESI, ErrorStream),
call_in_forked_process(
compile_java_files(Globals, ProgressStream,
HeadJavaFile, TailJavaFiles),
Succeeded, !IO),
close_module_error_stream_handle_errors(ProgressStream, Globals,
MESI, ErrorStream, Info, !IO)
).
:- pred reinsert_timestamps_for_non_class_files(string::in,
{list(dir_name), maybe_error(timestamp)}::in,
file_timestamp_map::in, file_timestamp_map::out) is det.
reinsert_timestamps_for_non_class_files(FileName, DirNamesMaybeTimestamp,
!TimestampMap) :-
( if string.suffix(FileName, ".class") then
true
else
map.det_insert(FileName, DirNamesMaybeTimestamp, !TimestampMap)
).
%---------------------------------------------------------------------------%
make_misc_target(ProgressStream, Globals, MainModuleName - TargetType,
Succeeded, !Info, !Specs, !IO) :-
get_default_options(Globals, DefaultOptionTable),
MaybeStdLibGrades = make_info_get_maybe_stdlib_grades(!.Info),
Params = make_info_get_compiler_params(!.Info),
ExtraOptions = [],
setup_for_build_with_module_options(ProgressStream, DefaultOptionTable,
MaybeStdLibGrades, invoked_by_mmc_make, MainModuleName,
Params, 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) :-
find_reachable_local_modules_for_misc(ProgressStream, Globals,
MainModuleName, TargetType, Succeeded0, AllModuleNames, !Info, !IO),
(
TargetType = misc_target_clean,
Succeeded = succeeded,
list.foldl2(make_module_clean(ProgressStream, Globals),
AllModuleNames, !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),
AllModuleNames, !Info, !IO)
;
TargetType = misc_target_build_all(ModuleTargetType),
get_target_modules(ProgressStream, Globals, ModuleTargetType,
AllModuleNames, 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 EARLIER THAN ModuleTargetType
% are present before continuing.
% This prevents a problem when two parallel branches
% try to generate the same missing interface file later.
(
( ModuleTargetType = module_target_source
; ModuleTargetType = module_target_track_flags
; ModuleTargetType = module_target_int3
),
% There are no earlier interface files.
Succeeded1 = succeeded
;
ModuleTargetType = module_target_int0,
build_int_opt_files(ProgressStream, Globals,
build_int3s, AllModuleNames, Succeeded1, !Info, !IO)
;
( ModuleTargetType = module_target_int1
; ModuleTargetType = module_target_int2
),
build_int_opt_files(ProgressStream, Globals,
build_int3s_int0s, AllModuleNames, Succeeded1, !Info, !IO)
;
ModuleTargetType = module_target_opt,
build_int_opt_files(ProgressStream, Globals,
build_all_ints, AllModuleNames, Succeeded1, !Info, !IO)
;
( ModuleTargetType = module_target_errors
; ModuleTargetType = module_target_analysis_registry
; ModuleTargetType = module_target_c_header(_)
; ModuleTargetType = module_target_c_code
; ModuleTargetType = module_target_csharp_code
; ModuleTargetType = module_target_java_code
; ModuleTargetType = module_target_java_class_code
; ModuleTargetType = module_target_object_code(_)
; ModuleTargetType = module_target_fact_table_object(_, _)
; ModuleTargetType = module_target_xml_doc
),
build_int_opt_files(ProgressStream, Globals,
build_all_ints_opts, AllModuleNames, Succeeded1,
!Info, !IO)
),
( if
Succeeded1 = did_not_succeed,
KeepGoing = do_not_keep_going
then
Succeeded = did_not_succeed
else
Targets = make_target_id_list(TargetModules, ModuleTargetType),
maybe_with_analysis_cache_dir_2(ProgressStream, Globals,
foldl2_make_module_targets_maybe_parallel_build2(KeepGoing,
[], Globals, Targets),
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,
AllModuleNames, Succeeded0),
Succeeded, !Info, !IO)
;
TargetType = misc_target_build_library,
build_int_opt_files(ProgressStream, Globals, build_all_ints_opts,
AllModuleNames, IntSucceeded, !Info, !IO),
(
IntSucceeded = succeeded,
maybe_with_analysis_cache_dir_3(ProgressStream, Globals,
build_library(MainModuleName, AllModuleNames, Globals),
Succeeded, !Info, !Specs, !IO)
;
IntSucceeded = did_not_succeed,
Succeeded = did_not_succeed
)
;
( TargetType = misc_target_install_library
; TargetType = misc_target_install_library_gs_gas
),
make_misc_target(ProgressStream, Globals,
MainModuleName - misc_target_build_library, LibSucceeded,
!Info, !Specs, !IO),
SucceededSoFar = Succeeded0 `and` LibSucceeded,
(
SucceededSoFar = succeeded,
(
TargetType = misc_target_install_library,
install_library(ProgressStream, Globals,
MainModuleName, AllModuleNames, Succeeded, !Info, !IO)
;
TargetType = misc_target_install_library_gs_gas,
install_library_gs_gas(ProgressStream, Globals,
MainModuleName, AllModuleNames, Succeeded, !Info, !IO)
)
;
SucceededSoFar = did_not_succeed,
Succeeded = did_not_succeed
)
;
TargetType = misc_target_build_xml_docs,
get_target_modules(ProgressStream, Globals, module_target_xml_doc,
AllModuleNames, 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
XmlDocs =
make_target_id_list(TargetModules, module_target_xml_doc),
foldl2_make_module_targets(KeepGoing, [],
ProgressStream, Globals, XmlDocs, Succeeded1, !Info, !IO),
Succeeded = Succeeded0 `and` Succeeded1
)
).
:- pred find_reachable_local_modules_for_misc(io.text_output_stream::in,
globals::in, module_name::in, misc_target_type::in,
maybe_succeeded::out, list(module_name)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
find_reachable_local_modules_for_misc(ProgressStream, Globals,
MainModuleName, TargetType, Succeeded, AllModuleNames, !Info, !IO) :-
(
( TargetType = misc_target_clean
; TargetType = misc_target_realclean
),
% Don't rebuild .module_dep files when cleaning up.
RebuildModuleDeps = make_info_get_rebuild_module_deps(!.Info),
make_info_set_rebuild_module_deps(do_not_rebuild_module_deps, !Info),
find_reachable_local_modules(ProgressStream, Globals, MainModuleName,
Succeeded, AllModuleNamesSet, !Info, !IO),
make_info_set_rebuild_module_deps(RebuildModuleDeps, !Info)
;
( TargetType = misc_target_build_all(_)
; TargetType = misc_target_build_analyses
; TargetType = misc_target_build_library
; TargetType = misc_target_install_library
; TargetType = misc_target_install_library_gs_gas
; TargetType = misc_target_build_xml_docs
),
find_reachable_local_modules(ProgressStream, Globals, MainModuleName,
Succeeded, AllModuleNamesSet, !Info, !IO)
),
AllModuleNames = set.to_sorted_list(AllModuleNamesSet).
%---------------------------------------------------------------------------%
:- type build_what
---> build_int3s
; build_int3s_int0s
; build_all_ints
; build_all_ints_opts.
:- pred build_int_opt_files(io.text_output_stream::in, globals::in,
build_what::in, list(module_name)::in, maybe_succeeded::out,
make_info::in, make_info::out, io::di, io::uo) is det.
build_int_opt_files(ProgressStream, Globals, BuildWhat, AllModules0,
Succeeded, !Info, !IO) :-
get_nonnested_and_parent_modules(ProgressStream, Globals, AllModules0,
NonnestedModules, ParentModules, !Info, !IO),
Int3s = make_target_id_list(NonnestedModules, module_target_int3),
Int0s = make_target_id_list(ParentModules, module_target_int0),
Int1s = make_target_id_list(NonnestedModules, module_target_int1),
globals.get_any_intermod(Globals, AnyIntermod),
(
AnyIntermod = yes,
Opts = make_target_id_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),
( if
( Succeeded0 = did_not_succeed
; BuildWhat = build_int3s
)
then
Succeeded = Succeeded0
else
foldl2_make_module_targets(KeepGoing, [],
ProgressStream, Globals, Int0s, Succeeded1, !Info, !IO),
( if
( Succeeded1 = did_not_succeed
; BuildWhat = build_int3s_int0s
)
then
Succeeded = Succeeded1
else
foldl2_make_module_targets_maybe_parallel(KeepGoing, [],
ProgressStream, Globals, Int1s, Succeeded2, !Info, !IO),
( if
( Succeeded2 = did_not_succeed
; BuildWhat = build_all_ints
)
then
Succeeded = Succeeded2
else
foldl2_make_module_targets_maybe_parallel(KeepGoing, [],
ProgressStream, Globals, Opts, Succeeded, !Info, !IO)
)
)
).
%---------------------------------------------------------------------------%
:- type build2 == pred(io.text_output_stream, maybe_succeeded,
make_info, make_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 Pred is finished, remove the cache directory completely.
%
:- pred maybe_with_analysis_cache_dir_2(io.text_output_stream::in, globals::in,
build2::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, Pred, Succeeded,
!Info, !IO) :-
should_we_use_analysis_cache_dir(ProgressStream, Globals, !.Info,
UseAnalysisCacheDir, !IO),
(
UseAnalysisCacheDir = do_not_use_analysis_cache_dir,
Pred(ProgressStream, Succeeded, !Info, !IO)
;
UseAnalysisCacheDir = use_analysis_cache_dir(CacheDir, CacheDirOption),
OrigParams = make_info_get_compiler_params(!.Info),
OrigOptionArgs = OrigParams ^ cp_option_args,
% Pass the name of the cache directory to child processes.
NewOptionArgs = OrigOptionArgs ++ [CacheDirOption, CacheDir],
NewParams = OrigParams ^ cp_option_args := NewOptionArgs,
make_info_set_compiler_params(NewParams, !Info),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
setup_checking_for_interrupt(Cookie, !IO),
Pred(ProgressStream, TaskSucceeded, !Info, !IO),
CleanupPred = remove_cache_dir(ProgressStream, Globals, CacheDir),
teardown_checking_for_interrupt(VeryVerbose, Cookie, CleanupPred,
TaskSucceeded, Succeeded, !Info, !IO),
remove_cache_dir(ProgressStream, Globals, CacheDir, !Info, !IO),
make_info_set_compiler_params(OrigParams, !Info)
;
UseAnalysisCacheDir = analysis_cache_dir_create_failed,
Succeeded = did_not_succeed
).
%---------------------%
:- type build3 == pred(io.text_output_stream, maybe_succeeded,
make_info, make_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 Pred is finished, remove the cache directory completely.
%
:- pred maybe_with_analysis_cache_dir_3(io.text_output_stream::in, globals::in,
build3::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, Pred, Succeeded,
!Info, !Specs, !IO) :-
should_we_use_analysis_cache_dir(ProgressStream, Globals, !.Info,
UseAnalysisCacheDir, !IO),
(
UseAnalysisCacheDir = do_not_use_analysis_cache_dir,
Pred(ProgressStream, Succeeded, !Info, !Specs, !IO)
;
UseAnalysisCacheDir = use_analysis_cache_dir(CacheDir, CacheDirOption),
OrigParams = make_info_get_compiler_params(!.Info),
OrigOptionArgs = OrigParams ^ cp_option_args,
% Pass the name of the cache directory to child processes.
NewOptionArgs = OrigOptionArgs ++ [CacheDirOption, CacheDir],
NewParams = OrigParams ^ cp_option_args := NewOptionArgs,
make_info_set_compiler_params(NewParams, !Info),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
setup_checking_for_interrupt(Cookie, !IO),
Pred(ProgressStream, TaskSucceeded, !Info, !Specs, !IO),
CleanupPred = remove_cache_dir(ProgressStream, Globals, CacheDir),
teardown_checking_for_interrupt(VeryVerbose, Cookie, CleanupPred,
TaskSucceeded, Succeeded, !Info, !IO),
remove_cache_dir(ProgressStream, Globals, CacheDir, !Info, !IO),
make_info_set_compiler_params(OrigParams, !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.
%
% XXX This oversimplifies the logic in the code.
%
:- 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.
% XXX I (zs) find it strange to take the cache directory's name
% being specified on the command line as a sign that
% we should NOT use an analysis cache directory,
% but that is what we do.
CacheDir0 \= ""
;
% Analysis file cache directory already set up in a parent call.
% XXX The comment just above applies here as well.
Params = make_info_get_compiler_params(Info),
Params = compiler_params(_, _, OpttonArgs),
list.member(CacheDirOption, OpttonArgs)
)
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) :-
% XXX LEGACY
analysis_cache_dir_name(Globals, CacheDir, _CacheDirProposed),
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, Info, !IO) :-
% The unnecessary Info arguments are required by the (current)
% interface of teardown_checking_for_interrupt.
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 .intN and .opt files are present before continuing.
% This prevents a problem when two parallel branches try to generate
% the same missing file later.
% (Although we can't actually build analysis files in parallel yet.)
build_int_opt_files(ProgressStream, Globals, build_all_ints_opts,
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_bottom_up_ordered_modules(
make_info_get_maybe_module_dep_info_map(!.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),
Registries =
make_target_id_list(TargetModules, module_target_analysis_registry),
foldl2_make_module_targets(KeepGoing, LocalModulesOpts, ProgressStream,
Globals, Registries, 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_bottom_up_ordered_modules(
map(module_name, maybe_module_dep_info)::in,
list(module_name)::in, list(module_name)::out) is det.
get_bottom_up_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, !ImpDepsRel)
%
% Add a module's interface and implementation dependencies to IntDepsRel
% and ImpDepsRel respectively. Dependencies are found using the
% LookupModuleImports function.
%
:- pred add_module_relations(lookup_module_dep_info_func::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(LookupModuleImportsFunc, ModuleName,
!IntDepsGraph, !ImpDepsGraph) :-
ModuleDepInfo = LookupModuleImportsFunc(ModuleName),
add_module_dep_info_to_deps_graph(ModuleDepInfo, LookupModuleImportsFunc,
!IntDepsGraph, !ImpDepsGraph).
%---------------------------------------------------------------------------%
:- 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) :-
TargetFile = target_file(ModuleName, module_target_analysis_registry),
TargetId = merc_target(TargetFile),
TargetStatusMap0 = make_info_get_target_status_map(!.Info),
version_hash_table.set(TargetId, target_status_not_considered,
TargetStatusMap0, TargetStatusMap),
make_info_set_target_status_map(TargetStatusMap, !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),
are_shared_libraries_supported(Globals, SharedLibsSupported),
(
StaticSucceeded = succeeded,
(
SharedLibsSupported = shared_libraries_supported,
make_linked_target(ProgressStream, Globals,
linked_target_file(MainModuleName, shared_library),
SharedLibsSucceeded, !Info, !Specs, !IO)
;
SharedLibsSupported = shared_libraries_not_supported,
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.
make_library_init_file(Globals, ProgressStream,
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 get_nonnested_and_parent_modules(io.text_output_stream::in,
globals::in, list(module_name)::in,
list(module_name)::out, list(module_name)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
get_nonnested_and_parent_modules(ProgressStream, Globals, ModuleNames,
NonnestedModules, ParentModules, !Info, !IO) :-
list.foldl4(
acc_nonnested_and_parent_modules(ProgressStream, Globals),
ModuleNames,
[], NonnestedModules, [], ParentModules, !Info, !IO).
:- pred acc_nonnested_and_parent_modules(io.text_output_stream::in,
globals::in, module_name::in,
list(module_name)::in, list(module_name)::out,
list(module_name)::in, list(module_name)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
acc_nonnested_and_parent_modules(ProgressStream, Globals, ModuleName,
!NonnestedModules, !ParentModules, !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),
(
MaybeTopModule = top_module(_NestedSubModules),
% don't include in NestedModules
% which means DO include in NonnestedModules
!:NonnestedModules = [ModuleName | !.NonnestedModules],
module_dep_info_get_children(ModuleDepInfo, Children),
( if set.is_empty(Children) then
true
else
!:ParentModules = [ModuleName | !.ParentModules]
)
;
MaybeTopModule = not_top_module
% do include in NestedModules
% which means DO NOT include in NonnestedModules
% which means DO NOT include in ParentModules
)
;
MaybeModuleDepInfo = no_module_dep_info,
% don't include in NestedModules
% which means DO include in NonnestedModules
% do not include in ParentModules
% due to absence of info about any children
!:NonnestedModules = [ModuleName | !.NonnestedModules]
).
%---------------------------------------------------------------------------%
% 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.
%---------------------------------------------------------------------------%