Files
mercury/compiler/make.top_level.m
Zoltan Somogyi 7ebb01cf12 Pass some related data structures together ...
... because they are almost always passed together, and grouping them
makes this clear. Also, code with shorter argument lists is more readable.

compiler/options_file.m:
    Define the types of the collective structures. Defined them here
    because one of them is defined here, and all the others are
    lists of strings.

compiler/make.make_info.m:
    Store one of its collective structures instead of its components.

compiler/make.build.m:
compiler/make.get_module_dep_info.m:
compiler/make.library_install.m:
compiler/make.module_target.m:
compiler/make.program_target.m:
compiler/make.top_level.m:
compiler/make.track_flags.m:
compiler/mercury_compile_args.m:
compiler/mercury_compile_main.m:
    Conform to the changes above.
2025-12-13 09:56:08 +11:00

657 lines
23 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2002-2012 The University of Melbourne.
% Copyright (C) 2013-2025 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.top_level.m.
% Main author: stayl.
%
% The top level of mmc --make.
%
% TODO:
% - distributed builds
%
%---------------------------------------------------------------------------%
:- module make.top_level.
:- interface.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.maybe_util.
:- import_module make.make_info.
:- import_module make.options_file.
:- import_module io.
:- import_module list.
%---------------------------------------------------------------------------%
% make_process_compiler_args(ProgressStream, Globals, ArgPack, !IO):
%
% Build the targets specified by the non-option arguments in ArgPack,
% obeying the options in the rest of ArgPack.
%
:- pred make_process_compiler_args(io.text_output_stream::in, globals::in,
compiler_arg_pack::in, io::di, io::uo) is det.
:- pred make_top_targets(io.text_output_stream::in, globals::in,
maybe_keep_going::in, list(top_target_file)::in,
maybe_succeeded::in, maybe_succeeded::out,
make_info::in, make_info::out, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.compile_target_code.
:- import_module backend_libs.link_target_code.
:- import_module libs.options.
:- import_module libs.timestamp.
:- import_module make.hash.
:- import_module make.index_set.
:- import_module make.module_target.
:- import_module make.program_target.
:- import_module make.timestamp.
:- import_module make.track_flags.
:- import_module make.util.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.error_spec.
:- import_module parse_tree.file_names.
:- import_module parse_tree.maybe_error.
:- import_module parse_tree.module_cmds.
:- import_module parse_tree.write_error_spec.
:- import_module bool.
:- import_module dir.
:- import_module map.
:- import_module pair.
:- import_module set.
:- import_module string.
:- import_module version_array.
:- import_module version_hash_table.
%---------------------------------------------------------------------------%
make_process_compiler_args(ProgressStream, Globals, ArgPack, !IO) :-
ArgPack = compiler_arg_pack(EnvOptFileVariables, EnvVarArgs,
OptionArgs, NonOptionArgs),
io.progname_base("mercury_compile", ProgName, !IO),
get_main_target_if_needed(ProgName, EnvOptFileVariables,
NonOptionArgs, MaybeTargets0),
report_any_absolute_targets(ProgName, MaybeTargets0, MaybeTargets),
(
MaybeTargets = error1(Specs),
io.stderr_stream(StdErr, !IO),
write_error_specs(StdErr, Globals, Specs, !IO)
;
MaybeTargets = ok1(Targets),
globals.lookup_bool_option(Globals, keep_going, KeepGoingBool),
( KeepGoingBool = no, KeepGoing = do_not_keep_going
; KeepGoingBool = yes, KeepGoing = do_keep_going
),
globals.lookup_int_option(Globals, analysis_repeat, AnalysisRepeat),
HashPredMI = module_name_hash,
ForwardMI = version_hash_table.init_default(HashPredMI),
ReverseMI = version_array.empty,
ModuleIndexMap = module_index_map(ForwardMI, ReverseMI, 0u),
HashPredDI = target_id_module_index_hash,
ForwardDI = version_hash_table.init_default(HashPredDI),
ReverseDI = version_array.empty,
TargetIndexMap = target_id_index_map(ForwardDI, ReverseDI, 0u),
TargetStatusMap = version_hash_table.init_default(target_id_hash),
% Accept and ignore `.depend' targets. `mmc --make' does not need
% a separate make depend step. The dependencies for each module
% are regenerated on demand.
list.filter(
( pred(Target::in) is semidet :-
not string.suffix(Target, ".depend"),
not string.suffix(Target, ".depend_ints")
), Targets, NonDependTargets),
% Classify the remaining targets.
list.map(classify_target(Globals), NonDependTargets,
ClassifiedTargets),
ClassifiedTargetSet = set.list_to_set(ClassifiedTargets),
globals.get_maybe_stdlib_grades(Globals, MaybeStdLibGrades),
Params = compiler_params(EnvOptFileVariables, EnvVarArgs, OptionArgs),
TimestampMap = init_target_file_timestamp_map,
MakeInfo0 = init_make_info(MaybeStdLibGrades, KeepGoing, Params,
ClassifiedTargetSet, AnalysisRepeat, TimestampMap,
ModuleIndexMap, TargetIndexMap, TargetStatusMap),
% Build the targets, stopping on any errors if `--keep-going'
% was not set.
make_top_targets(ProgressStream, Globals, KeepGoing, ClassifiedTargets,
succeeded, Succeeded, MakeInfo0, _MakeInfo, !IO),
maybe_set_exit_status(Succeeded, !IO)
).
%---------------------%
:- pred get_main_target_if_needed(string::in, env_optfile_variables::in,
list(string)::in, maybe1(list(string))::out) is det.
get_main_target_if_needed(ProgName, EnvOptFileVariables, Targets0,
MaybeTargets) :-
(
Targets0 = [_ | _],
MaybeTargets = ok1(Targets0)
;
Targets0 = [],
lookup_main_target(EnvOptFileVariables, MaybeMainTargets),
(
MaybeMainTargets = ok1(MainTargets),
(
MainTargets = [_ | _],
MaybeTargets = ok1(MainTargets)
;
MainTargets = [],
Pieces = [fixed(ProgName), suffix(":"),
words("*** Error: no target or MAIN_TARGET specified."),
nl],
Spec = no_ctxt_spec($pred, severity_error, phase_options,
Pieces),
MaybeTargets = error1([Spec])
)
;
MaybeMainTargets = error1(Specs),
MaybeTargets = error1(Specs)
)
).
%---------------------%
% Ensure none of the targets contains the directory_separator.
% Such targets are not supported by the rest of the code.
%
:- pred report_any_absolute_targets(string::in, maybe1(list(string))::in,
maybe1(list(string))::out) is det.
report_any_absolute_targets(ProgName, MaybeTargets0, MaybeTargets) :-
(
MaybeTargets0 = error1(_),
MaybeTargets = MaybeTargets0
;
MaybeTargets0 = ok1(Targets),
IsAbsoluteFileName =
( pred(Target::in) is semidet :-
string.contains_char(Target, dir.directory_separator)
),
list.filter(IsAbsoluteFileName, Targets, AbsTargets),
(
AbsTargets = [],
MaybeTargets = MaybeTargets0
;
AbsTargets = [_ | _],
AbsTargetSpecs =
list.map(report_target_with_dir_component(ProgName),
AbsTargets),
MaybeTargets = error1(AbsTargetSpecs)
)
).
:- func report_target_with_dir_component(string, string) = error_spec.
report_target_with_dir_component(ProgName, Target) = Spec :-
Pieces = [fixed(ProgName), suffix(":"),
words("a make target may not contain a directory component,"),
words("but"), quote(Target), words("does."), nl],
Spec = no_ctxt_spec($pred, severity_error, phase_make_target, Pieces).
%---------------------%
make_top_targets(_ProgressStream, _Globals, _KeepGoing, [],
!Succeeded, !Info, !IO).
make_top_targets(ProgressStream, Globals, KeepGoing, [Target | Targets],
!Succeeded, !Info, !IO) :-
make_top_target(ProgressStream, Globals, Target, TargetSucceeded,
!Info, !IO),
should_we_stop_or_continue(KeepGoing, TargetSucceeded, StopOrContinue,
!Succeeded),
(
StopOrContinue = soc_stop
;
StopOrContinue = soc_continue,
make_top_targets(ProgressStream, Globals, KeepGoing, Targets,
!Succeeded, !Info, !IO)
).
:- pred make_top_target(io.text_output_stream::in, globals::in,
top_target_file::in, maybe_succeeded::out,
make_info::in, make_info::out, io::di, io::uo) is det.
make_top_target(ProgressStream, Globals, Target, Succeeded, !Info, !IO) :-
Target = top_target_file(ModuleName, TargetType),
globals.lookup_bool_option(Globals, make_track_flags, TrackFlags),
get_error_output_stream(Globals, ModuleName, ErrorStream, !IO),
(
TrackFlags = no,
TrackFlagsSucceeded = succeeded
;
TrackFlags = yes,
make_track_flags_files(ErrorStream, ProgressStream, Globals,
ModuleName, TrackFlagsSucceeded, !Info, !IO)
),
(
TrackFlagsSucceeded = succeeded,
(
TargetType = module_target(ModuleTargetType),
TargetFile = target_file(ModuleName, ModuleTargetType),
make_module_target([], ProgressStream, Globals,
merc_target(TargetFile), Succeeded, !Info, !IO)
;
TargetType = linked_target(LinkedTargetType),
LinkedTargetFile =
linked_target_file(ModuleName, LinkedTargetType),
make_linked_target(ProgressStream, Globals, LinkedTargetFile,
Succeeded, !Info, [], Specs, !IO),
write_error_specs(ErrorStream, Globals, Specs, !IO)
;
TargetType = misc_target(MiscTargetType),
make_misc_target(ProgressStream, Globals,
ModuleName - MiscTargetType, Succeeded, !Info, [], Specs, !IO),
write_error_specs(ErrorStream, Globals, Specs, !IO)
)
;
TrackFlagsSucceeded = did_not_succeed,
Succeeded = did_not_succeed
).
%---------------------------------------------------------------------------%
:- pred classify_target(globals::in, string::in, top_target_file::out) is det.
classify_target(Globals, TargetName, TopTargetFile) :-
( if
string.length(TargetName, NameLength),
search_backwards_for_dot(TargetName, NameLength, DotLocn),
string.split(TargetName, DotLocn, ModuleNameStr0, ExtStr),
classify_extstr_target(Globals, ModuleNameStr0, ExtStr,
TopTargetFilePrime)
then
TopTargetFile = TopTargetFilePrime
else
( if string.remove_prefix("lib", TargetName, ModuleNameStr) then
file_name_to_module_name(ModuleNameStr, ModuleName),
TargetType = misc_target(misc_target_build_library)
else
file_name_to_module_name(TargetName, ModuleName),
TargetType = linked_target(get_executable_type(Globals))
),
TopTargetFile = top_target_file(ModuleName, TargetType)
).
:- pred classify_extstr_target(globals::in, string::in, string::in,
top_target_file::out) is semidet.
classify_extstr_target(Globals, ModuleNameStr0, ExtStr, TopTargetFile) :-
( if
fixed_extension_top_target_file(Globals, ModuleNameStr0, ExtStr,
TopTargetFilePrime)
then
TopTargetFile = TopTargetFilePrime
else
mapped_extension_top_target_file(Globals, ModuleNameStr0, ExtStr,
TopTargetFile)
).
:- pred fixed_extension_top_target_file(globals::in, string::in, string::in,
top_target_file::out) is semidet.
fixed_extension_top_target_file(Globals, ModuleNameStr, ExtStr,
TopTargetFile) :-
(
(
ExtStr = ".m",
ModuleTarget = module_target_source
;
ExtStr = ".err",
ModuleTarget = module_target_errors
;
ExtStr = ".int0",
ModuleTarget = module_target_int0
;
ExtStr = ".int",
ModuleTarget = module_target_int1
;
ExtStr = ".int2",
ModuleTarget = module_target_int2
;
ExtStr = ".int3",
ModuleTarget = module_target_int3
;
ExtStr = ".opt",
ModuleTarget = module_target_opt
;
ExtStr = ".mih",
ModuleTarget = module_target_c_header(header_mih)
;
ExtStr = ".mh",
ModuleTarget = module_target_c_header(header_mh)
;
ExtStr = ".c",
ModuleTarget = module_target_c_code
;
ExtStr = ".cs",
ModuleTarget = module_target_csharp_code
;
ExtStr = ".csharp",
% For a long time, until 2023 oct 5, we treated the ".cs" target
% name as the build-all target for C files, so we accepted
% ".csharp" as the target name for C# files. Keep the synonym
% for a while longer to give people time to update their
% projects and habits.
ModuleTarget = module_target_csharp_code
;
ExtStr = ".java",
ModuleTarget = module_target_java_code
;
ExtStr = ".class",
ModuleTarget = module_target_java_class_code
;
ExtStr = ".target",
grade_specific_target_type(Globals, ModuleTarget)
;
ExtStr = ".track_flags",
ModuleTarget = module_target_track_flags
;
ExtStr = ".xml",
ModuleTarget = module_target_xml_doc
;
ExtStr = ".analysis",
ModuleTarget = module_target_analysis_registry
),
TargetType = module_target(ModuleTarget)
;
% This block of unifications is required by the fact that
% switch detection looks into two levels of disjunctions.
( ExtStr = ".all_ms"
; ExtStr = ".ms"
; ExtStr = ".all_errs"
; ExtStr = ".errs"
; ExtStr = ".all_int0s"
; ExtStr = ".int0s"
; ExtStr = ".all_ints"
; ExtStr = ".ints"
; ExtStr = ".all_int2s"
; ExtStr = ".int2s"
; ExtStr = ".all_int3s"
; ExtStr = ".int3s"
; ExtStr = ".all_opts"
; ExtStr = ".opts"
; ExtStr = ".all_cs"
; ExtStr = ".all_css"
; ExtStr = ".css"
; ExtStr = ".all_csharps"
; ExtStr = ".csharps"
; ExtStr = ".all_javas"
; ExtStr = ".javas"
; ExtStr = ".all_classs"
; ExtStr = ".classs"
; ExtStr = ".targets"
; ExtStr = ".all_targets"
; ExtStr = ".all_track_flagss"
; ExtStr = ".track_flagss"
; ExtStr = ".all_xmls"
; ExtStr = ".xmls"
; ExtStr = ".all_analysiss"
; ExtStr = ".analysiss"
),
(
( ExtStr = ".all_ms"
; ExtStr = ".ms"
),
ModuleTarget = module_target_source
;
( ExtStr = ".all_errs"
; ExtStr = ".errs"
),
ModuleTarget = module_target_errors
;
( ExtStr = ".all_int0s"
; ExtStr = ".int0s"
),
ModuleTarget = module_target_int0
;
( ExtStr = ".all_ints"
; ExtStr = ".ints"
),
ModuleTarget = module_target_int1
;
( ExtStr = ".all_int2s"
; ExtStr = ".int2s"
),
ModuleTarget = module_target_int2
;
( ExtStr = ".all_int3s"
; ExtStr = ".int3s"
),
ModuleTarget = module_target_int3
;
( ExtStr = ".all_opts"
; ExtStr = ".opts"
),
ModuleTarget = module_target_opt
% Not yet implemented. `build_all' targets are only used by
% tools/bootcheck, so it doesn't really matter.
% ;
% ( ExtStr = ".all_mihs"
% ; ExtStr = ".mihs"
% ),
% ModuleTarget = module_target_c_header(header_mih)
% ;
% ( ExtStr = ".all_mhs"
% ; ExtStr = ".mhs"
% ),
% ModuleTarget = module_target_c_header(header_mh)
;
( ExtStr = ".all_cs"
% ; ExtStr = ".cs" % Duplicates C# target.
),
ModuleTarget = module_target_c_code
;
( ExtStr = ".all_css"
; ExtStr = ".css"
),
ModuleTarget = module_target_csharp_code
;
( ExtStr = ".all_csharps"
; ExtStr = ".csharps"
),
% For a long time, until 2023 oct 5, we treated the ".cs" target
% name as the build-all target for C files, so we accepted
% ".csharp" as the target name for C# files. Keep the synonym
% for a while longer to give people time to update their
% projects and habits.
ModuleTarget = module_target_csharp_code
;
( ExtStr = ".all_javas"
; ExtStr = ".javas"
),
ModuleTarget = module_target_java_code
;
% We should s/classs/classes/ here, but this requires
% documentation and announcement, as well as updating all existing
% references to the old spelling in the Mercury system.
( ExtStr = ".all_classs"
; ExtStr = ".classs"
),
ModuleTarget = module_target_java_class_code
;
( ExtStr = ".targets"
; ExtStr = ".all_targets"
),
grade_specific_target_type(Globals, ModuleTarget)
;
( ExtStr = ".all_track_flagss"
; ExtStr = ".track_flagss"
),
ModuleTarget = module_target_track_flags
;
( ExtStr = ".all_xmls"
; ExtStr = ".xmls"
),
ModuleTarget = module_target_xml_doc
;
% We should s/analysiss/analyses/ here, but this requires
% documentation and announcement, as well as updating all existing
% references to the old spelling in the Mercury system.
( ExtStr = ".all_analysiss"
; ExtStr = ".analysiss"
),
ModuleTarget = module_target_analysis_registry
),
TargetType = misc_target(misc_target_build_all(ModuleTarget))
;
(
ExtStr = ".check",
MiscTarget = misc_target_build_all(module_target_errors)
;
ExtStr = ".doc",
MiscTarget = misc_target_build_xml_docs
;
ExtStr = ".analyse",
MiscTarget = misc_target_build_analyses
;
ExtStr = ".clean",
MiscTarget = misc_target_clean
;
ExtStr = ".realclean",
MiscTarget = misc_target_realclean
),
TargetType = misc_target(MiscTarget)
),
file_name_to_module_name(ModuleNameStr, ModuleName),
TopTargetFile = top_target_file(ModuleName, TargetType).
% XXX This predicate is intended to allow the tests in the recompilation
% directory to build target files *without* compiling or linking them,
% thus avoiding the disabling of smart recompilation by the
% maybe_disable_smart_recompilation predicate in handle_options.m.
% However, that would be worthwhile only *after* the infrastructure
% of that test directory has been converted to use mmc --make directly,
% *without* going through mmake.
%
% The extensions that this predicate is used for will need to be documented
% only when that actually happens.
%
% For more info, see the comment in tests/recompilation/Mmakefile
% on the code that disables all the tests there in Java and C# grades.
%
:- pred grade_specific_target_type(globals::in, module_target_type::out)
is det.
grade_specific_target_type(Globals, ModuleTargetType) :-
globals.get_target(Globals, Target),
(
Target = target_c,
ModuleTargetType = module_target_c_code
;
Target = target_java,
ModuleTargetType = module_target_java_code
;
Target = target_csharp,
ModuleTargetType = module_target_csharp_code
).
:- pred mapped_extension_top_target_file(globals::in, string::in, string::in,
top_target_file::out) is semidet.
mapped_extension_top_target_file(Globals, ModuleNameStr0, ExtStr,
TopTargetFile) :-
get_linked_target_ext_map(Globals, LinkedtargetExtMap),
map.search(LinkedtargetExtMap, ExtStr, LinkedTargetExtInfo),
LinkedTargetExtInfo = linked_target_ext_info(_, LinkedTargetKind),
% target_type_to_maybe_target_extension and the following code represent
% the same relationship between targets and suffixes, but in different
% directions, and for slightly different sets of targets. (For example,
% there is no extension that generates module_target_fact_table_object
% as a target.) Where they talk about the same targets, their codes
% should be kept in sync.
(
(
LinkedTargetKind = ltk_object_file,
ModuleTargetType = module_target_object_code(non_pic)
;
LinkedTargetKind = ltk_pic_object_file,
ModuleTargetType = module_target_object_code(pic)
),
ModuleNameStr = ModuleNameStr0,
TargetType = module_target(ModuleTargetType)
;
(
LinkedTargetKind = ltk_all_object_file,
ModuleTargetType = module_target_object_code(non_pic)
;
LinkedTargetKind = ltk_all_pic_object_file,
ModuleTargetType = module_target_object_code(pic)
),
ModuleNameStr = ModuleNameStr0,
TargetType = misc_target(misc_target_build_all(ModuleTargetType))
;
LinkedTargetKind = ltk_executable,
ModuleNameStr = ModuleNameStr0,
TargetType = linked_target(get_executable_type(Globals))
;
(
LinkedTargetKind = ltk_static_library,
TargetType = linked_target(static_library)
;
LinkedTargetKind = ltk_shared_library,
TargetType = linked_target(shared_library)
;
LinkedTargetKind = ltk_library_install,
TargetType = misc_target(misc_target_install_library)
;
LinkedTargetKind = ltk_library_install_gs_gas,
TargetType = misc_target(misc_target_install_library_gs_gas)
),
string.remove_prefix("lib", ModuleNameStr0, ModuleNameStr)
),
file_name_to_module_name(ModuleNameStr, ModuleName),
TopTargetFile = top_target_file(ModuleName, TargetType).
%---------------------------------------------------------------------------%
:- pred search_backwards_for_dot(string::in, int::in, int::out) is semidet.
search_backwards_for_dot(String, Index, DotIndex) :-
string.unsafe_prev_index(String, Index, CharIndex, Char),
( if Char = ('.') then
DotIndex = CharIndex
else
search_backwards_for_dot(String, CharIndex, DotIndex)
).
:- func get_executable_type(globals) = linked_target_type.
get_executable_type(Globals) = ExecutableType :-
globals.get_target(Globals, CompilationTarget),
(
CompilationTarget = target_c,
ExecutableType = executable
;
CompilationTarget = target_csharp,
ExecutableType = csharp_executable
;
CompilationTarget = target_java,
ExecutableType = java_executable
).
%---------------------------------------------------------------------------%
:- end_module make.top_level.
%---------------------------------------------------------------------------%