mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-23 13:23:47 +00:00
The previous change to how detecting stdlib grade works exposed
an ordering problem. The base of the problem was that
- we had to detect the grades in which the standard library is installed
*first*, so that
- we could convert this set to a sequence of compiler-generated
--libgrade options, so they could then be updated by
- later, user-specified --libgrade options.
All this happened before we constructed the first globals structure. The
problem is that the code in handle_options.m that finds out what stdlib
location we want to use *needs* the first globals structure. The result
of this computation worked correctly only if the initial, effectively guessed
stdlib location we used in step 1 above happened to be the final chosen
stdlib location.
The root cause of the problem was the use of --libgrade options
for two related but nevertheless conceptually separate things:
- the set grades in which the Mercury standard libray is installed
in its chosen location, and
- the set of grade in which the user wants to install his/her libraries.
The latter defaults to the former, but user options can tell the compiler
to deviate from that default.
The main part of the fix is to use --libgrade options only for the
second notion (which it is already named after), and to store the
set of stdlib grades not in option values at all, but in a new field
in the globals structure. This field can then be filled with meaningful data
*after* we have chosen the location of the Mercury stdlib.
The main complication with using this approach is that mmc --make
currently rebuilds the globals structure from scratch before building
each target. (I am not sure whether this is necessary, but it does seem
a nontrivial thing to change.) Doing this naively would cause us to detect
the stdlib's grades once per target, which is obviously wasteful.
The old code handled this by adding a --libgrade option for each stdlib grade
to the list of options we build each target with; the new code passes
the value in the new globals field instead.
compiler/options.m:
doc/user_guide.texi:
Change the --detect-libgrades option name to --detect-stdlib-grades.
Unlike the old name, the new name is not misleading. Leave the old
name as a synonym, for backward compatibility.
XXX Do we need backward compatibility in such a rarely-used option?
NEWS:
Document the new option name, XXX as either a rename or a new synonym.
compiler/options.m:
Set the initial value of the libgrades option to "stdlib".
compiler/handle_options.m:
If the initial value of the libgrades option to "stdlib", then replace
that value with the set of detected stdlib grades. Do this after either
- detecting those grades in the chosen stdlib dir, or
- just recalling the result of that computation, if it has been done
before.
Thread a variable through the code of this module that specifies
whether we have does this before.
compiler/globals.m:
Add a slot to the globals structure where handle_options.m can record
the result of detecting stdlib grades.
compiler/mercury_compile_args.m:
compiler/mercury_compile_main.m:
Stop passing around stdlib grade flags in code that we execute
before we construct the first globals, since we now do this job
after we construct the first globals.
Delete the code in mercury_compile_args.m that used to do this job;
its updated version is now in handle_options.m.
Change the code in mercury_compile_main.m that implements the
--output-stdlib-grade option. Instead of invoking check_libgrades.m,
we can now just look up the result we got when handle_options.m
did that same invocation.
compiler/check_libgrades.m:
Make the code detecting stdlib grades take a globals as an argument,
since it is now invoked when we *have* a globals structure.
compiler/compute_grade.m:
Give a predicate a more descriptive name.
Add an XXX.
compiler/make.make_info.m:
Replace the field of the make_info that contained the set of stdlib grades
in the form of an option list with a field that contains that same info
as a bespoke structure.
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:
Conform to the changes above.
1133 lines
47 KiB
Mathematica
1133 lines
47 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2002-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2017, 2019-2024 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.module_target.m.
|
|
% Main author: stayl.
|
|
%
|
|
% Build targets which relate to a single module (e.g. C code, object code,
|
|
% interface files).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module make.module_target.
|
|
:- interface.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.compile_target_code.
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.maybe_util.
|
|
:- import_module make.make_info.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.module_dep_info.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% make_module_target(ExtraOpts, ProgressStream, Globals, Target,
|
|
% Succeeded, !Info, !IO):
|
|
%
|
|
% Make a target corresponding to a single module, possibly with
|
|
% extra command line options.
|
|
%
|
|
% ExtraOpts must be the first argument, because we curry it.
|
|
%
|
|
:- pred make_module_target(list(string)::in, io.text_output_stream::in,
|
|
globals::in, dependency_file::in, maybe_succeeded::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% record_made_target(ProgressStream, Globals, TargetFile,
|
|
% TargetFileName, Task, MakeSucceeded, !Info, !IO):
|
|
%
|
|
% Record whether building a target succeeded or not.
|
|
% Makes sure any timestamps for files which may have changed
|
|
% in building the target are recomputed next time they are needed.
|
|
% Exported for use by make.module_dep_file.write_module_dep_file.
|
|
%
|
|
:- pred record_made_target(io.text_output_stream::in, globals::in,
|
|
target_file::in, file_name::in,
|
|
compilation_task_type::in, maybe_succeeded::in,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
:- type foreign_code_file
|
|
---> foreign_code_file(
|
|
fcf_foreign_language :: foreign_language,
|
|
|
|
% Name of the file produced by the Mercury compiler,
|
|
% e.g. module_c_code.c.
|
|
fcf_target_file :: file_name,
|
|
|
|
% Name of the file produced by the foreign language compiler,
|
|
% e.g. module_c_code.o.
|
|
fcf_object_file :: file_name
|
|
).
|
|
|
|
% Find the foreign code files generated for fact tables
|
|
% when a module is processed. For now, the result will be the empty list
|
|
% for any target language other than C.
|
|
%
|
|
:- pred get_any_fact_table_object_code_files(globals::in, pic::in,
|
|
module_dep_info::in, list(foreign_code_file)::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.options.
|
|
:- import_module libs.process_util.
|
|
:- import_module libs.shell_util.
|
|
:- import_module libs.system_cmds.
|
|
:- import_module libs.timestamp.
|
|
:- import_module make.build.
|
|
:- import_module make.check_up_to_date.
|
|
:- import_module make.dependencies.
|
|
:- import_module make.file_names.
|
|
:- import_module make.get_module_dep_info.
|
|
:- import_module make.util.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.module_baggage.
|
|
:- import_module parse_tree.module_cmds.
|
|
:- import_module parse_tree.write_error_spec.
|
|
:- import_module top_level. % XXX unwanted dependency
|
|
:- import_module top_level.mercury_compile_main. % XXX unwanted dependency
|
|
|
|
:- import_module bool.
|
|
:- import_module float.
|
|
:- import_module int.
|
|
:- import_module io.environment.
|
|
:- 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_module_target(ExtraOptions, ProgressStream, Globals, Dep, Succeeded,
|
|
!Info, !IO) :-
|
|
get_dependency_file_status(ProgressStream, Globals, Dep, StatusResult,
|
|
!Info, !IO),
|
|
StatusResult = dependency_status_result(_Dep, _DepFileName, Status),
|
|
(
|
|
Status = deps_status_error,
|
|
Succeeded = did_not_succeed
|
|
;
|
|
Status = deps_status_up_to_date,
|
|
Succeeded = succeeded
|
|
;
|
|
Status = deps_status_not_considered,
|
|
(
|
|
Dep = dep_file(_),
|
|
Succeeded = succeeded
|
|
;
|
|
Dep = dep_target(TargetFile),
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
get_maybe_module_dep_info(ProgressStream, Globals,
|
|
ModuleName, MaybeModuleDepInfo, !Info, !IO),
|
|
(
|
|
MaybeModuleDepInfo = no_module_dep_info,
|
|
Succeeded = did_not_succeed,
|
|
DepStatusMap0 = make_info_get_dep_file_status_map(!.Info),
|
|
version_hash_table.set(Dep, deps_status_error,
|
|
DepStatusMap0, DepStatusMap),
|
|
make_info_set_dep_file_status_map(DepStatusMap, !Info)
|
|
;
|
|
MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo),
|
|
get_compilation_task_and_options(TargetType,
|
|
CompilationTaskAndOptions),
|
|
module_dep_info_get_source_file_module_name(ModuleDepInfo,
|
|
SourceFileModuleName),
|
|
CompilationTaskAndOptions =
|
|
task_and_options(CompilationTaskType, _),
|
|
( if
|
|
% For a target built by processing a Mercury source file,
|
|
% the target for a nested submodule is produced as
|
|
% a side effect of making the target for the top-level
|
|
% module in the file.
|
|
CompilationTaskType = process_module(_),
|
|
SourceFileModuleName \= ModuleName
|
|
then
|
|
MainTargetFile =
|
|
target_file(SourceFileModuleName, TargetType),
|
|
% Recursive call. We should not recurse more than once.
|
|
make_module_target(ExtraOptions, ProgressStream, Globals,
|
|
dep_target(MainTargetFile), Succeeded, !Info, !IO)
|
|
else
|
|
make_module_target_file_main_path(ExtraOptions,
|
|
ProgressStream, Globals, TargetFile,
|
|
CompilationTaskAndOptions, ModuleDepInfo,
|
|
Succeeded, !Info, !IO)
|
|
)
|
|
)
|
|
)
|
|
;
|
|
Status = deps_status_being_built,
|
|
(
|
|
Dep = dep_file(_),
|
|
Succeeded = succeeded
|
|
;
|
|
Dep = dep_target(_),
|
|
unexpected($pred, "target being built, circular dependencies?")
|
|
)
|
|
).
|
|
|
|
:- pred make_module_target_file_main_path(list(string)::in,
|
|
io.text_output_stream::in, globals::in,
|
|
target_file::in, compilation_task_type_and_options::in,
|
|
module_dep_info::in, maybe_succeeded::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
make_module_target_file_main_path(ExtraOptions, ProgressStream, Globals,
|
|
TargetFile, CompilationTaskAndOptions, ModuleDepInfo, Succeeded,
|
|
!Info, !IO) :-
|
|
% NOTE Our caller computes CompilationTaskAndOptions from the target_type
|
|
% component of the TargetFile argument, which means that the only valid
|
|
% target type/compilation task pairs are those that occur in the
|
|
% predicate that does that computation, get_compilation_task_and_options.
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
% XXX LEGACY
|
|
module_target_file_to_file_name(Globals, $pred,
|
|
TargetFile, TargetFileName, _TargetFileNameProposed, !IO),
|
|
CompilationTaskAndOptions = task_and_options(CompilationTaskType, _),
|
|
find_lhs_files_of_task(ProgressStream, Globals, TargetFile,
|
|
CompilationTaskType, MakeLhsFiles, !Info, !IO),
|
|
MakeLhsFiles =
|
|
make_lhs_files(DatelessLhsTargetFiles, DatedLhsTargetFiles, _, _),
|
|
list.foldl(update_target_status(deps_status_being_built),
|
|
DatelessLhsTargetFiles, !Info),
|
|
list.foldl(update_target_status(deps_status_being_built),
|
|
DatedLhsTargetFiles, !Info),
|
|
|
|
debug_make_msg(Globals,
|
|
string.format("%s: checking dependencies\n", [s(TargetFileName)]),
|
|
CheckingMsg),
|
|
maybe_write_msg(ProgressStream, CheckingMsg, !IO),
|
|
|
|
find_direct_prereqs_of_target_file(ProgressStream, Globals,
|
|
CompilationTaskType, ModuleDepInfo, TargetFile, RhsResult,
|
|
!Info, !IO),
|
|
|
|
KeepGoing = make_info_get_keep_going(!.Info),
|
|
( if
|
|
RhsResult = could_not_find_some_prereqs(_),
|
|
KeepGoing = do_not_keep_going
|
|
then
|
|
LhsResult = rhs_error
|
|
else
|
|
( RhsResult = could_not_find_some_prereqs(RhsDepFileSet)
|
|
; RhsResult = found_all_prereqs(RhsDepFileSet)
|
|
),
|
|
% XXX MAKE sort
|
|
RhsDepFiles = set.to_sorted_list(RhsDepFileSet),
|
|
% Build the files on the rhs.
|
|
foldl2_make_module_targets(KeepGoing, [], ProgressStream, Globals,
|
|
RhsDepFiles, MakeRhsFilesSucceeded, !Info, !IO),
|
|
(
|
|
MakeRhsFilesSucceeded = did_not_succeed,
|
|
debug_make_msg(Globals,
|
|
string.format("%s: error making prerequisites\n",
|
|
[s(TargetFileName)]),
|
|
RhsErrorDebugMsg),
|
|
maybe_write_msg(ProgressStream, RhsErrorDebugMsg, !IO),
|
|
LhsResult = rhs_error
|
|
;
|
|
MakeRhsFilesSucceeded = succeeded,
|
|
% We succeeded in making RhsDepFiles. However, if there are
|
|
% prerequisities that we could not find, then we cannot build
|
|
% the lhs files.
|
|
(
|
|
RhsResult = found_all_prereqs(_),
|
|
must_or_should_we_rebuild_lhs(ProgressStream, Globals,
|
|
TargetFile, TargetFileName, MakeLhsFiles, RhsDepFiles,
|
|
LhsResult, !Info, !IO)
|
|
;
|
|
RhsResult = could_not_find_some_prereqs(_),
|
|
LhsResult = rhs_error
|
|
)
|
|
)
|
|
),
|
|
(
|
|
LhsResult = rhs_error,
|
|
Succeeded = did_not_succeed,
|
|
LhsTargetFiles = DatelessLhsTargetFiles ++ DatedLhsTargetFiles,
|
|
list.foldl(update_target_status(deps_status_error),
|
|
LhsTargetFiles, !Info)
|
|
;
|
|
LhsResult = can_rebuild_lhs(some_lhs_file_needs_rebuilding),
|
|
Targets0 = make_info_get_command_line_targets(!.Info),
|
|
set.delete(top_target_file(ModuleName, module_target(TargetType)),
|
|
Targets0, Targets),
|
|
make_info_set_command_line_targets(Targets, !Info),
|
|
build_target(ProgressStream, Globals, CompilationTaskAndOptions,
|
|
TargetFile, TargetFileName, ModuleDepInfo, MakeLhsFiles,
|
|
ExtraOptions, Succeeded, !Info, !IO)
|
|
;
|
|
LhsResult = can_rebuild_lhs(all_lhs_files_up_to_date),
|
|
TopTargetFile = top_target_file(ModuleName, module_target(TargetType)),
|
|
maybe_warn_up_to_date_target_msg(Globals, TopTargetFile,
|
|
TargetFileName, !Info, UpToDateMsg),
|
|
maybe_write_msg(ProgressStream, UpToDateMsg, !IO),
|
|
debug_make_msg(Globals,
|
|
string.format("%s: up to date\n", [s(TargetFileName)]),
|
|
UpToDateDebugMsg),
|
|
maybe_write_msg(ProgressStream, UpToDateDebugMsg, !IO),
|
|
Succeeded = succeeded,
|
|
LhsTargetFiles = DatelessLhsTargetFiles ++ DatedLhsTargetFiles,
|
|
list.foldl(update_target_status(deps_status_up_to_date),
|
|
[TargetFile | LhsTargetFiles], !Info)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred build_target(io.text_output_stream::in, globals::in,
|
|
compilation_task_type_and_options::in,
|
|
target_file::in, file_name::in, module_dep_info::in, make_lhs_files::in,
|
|
list(string)::in, maybe_succeeded::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
build_target(ProgressStream, Globals, CompilationTask,
|
|
TargetFile, TargetFileName, ModuleDepInfo, MakeLhsFiles,
|
|
ExtraOptions, Succeeded, !Info, !IO) :-
|
|
maybe_making_filename_msg(Globals, TargetFileName, MakingMsg),
|
|
maybe_write_msg(ProgressStream, MakingMsg, !IO),
|
|
TargetFile = target_file(ModuleName, _TargetType),
|
|
CompilationTask = task_and_options(Task, TaskOptions),
|
|
ExtraAndTaskOptions = ExtraOptions ++ TaskOptions,
|
|
( if
|
|
Task = process_module(ModuleTask),
|
|
do_task_in_separate_process(ModuleTask) = yes,
|
|
not can_fork
|
|
then
|
|
% If we want to perform a compilation task in a separate process,
|
|
% but fork() is unavailable, then we will invoke the Mercury compiler
|
|
% with a bunch of command line arguments. On Windows, the command line
|
|
% is likely to exceed that maximum command line length, so we need to
|
|
% pass the command line arguments via a temporary file, created here
|
|
% (not in invoke_mmc) so it can be cleaned up by
|
|
% teardown_checking_for_interrupt.
|
|
io.file.make_temp_file(ArgFileNameResult, !IO),
|
|
(
|
|
ArgFileNameResult = ok(ArgFileName),
|
|
MaybeArgFileName = yes(ArgFileName),
|
|
ArgFileNameRes = ok : io.res
|
|
;
|
|
ArgFileNameResult = error(Error),
|
|
MaybeArgFileName = no,
|
|
ArgFileNameRes = error(Error)
|
|
)
|
|
else
|
|
MaybeArgFileName = no,
|
|
ArgFileNameRes = ok
|
|
),
|
|
|
|
(
|
|
ArgFileNameRes = ok,
|
|
get_real_milliseconds(StartTimeMs, !IO),
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
Cleanup = cleanup_files(ProgressStream, Globals, MaybeArgFileName,
|
|
MakeLhsFiles),
|
|
setup_checking_for_interrupt(Cookie, !IO),
|
|
get_default_options(Globals, DefaultOptionTable),
|
|
MaybeStdLibGrades = make_info_get_maybe_stdlib_grades(!.Info),
|
|
EnvOptFileVariables = make_info_get_env_optfile_variables(!.Info),
|
|
EnvVarArgs = make_info_get_env_var_args(!.Info),
|
|
OptionArgs = make_info_get_option_args(!.Info),
|
|
setup_for_build_with_module_options(ProgressStream, DefaultOptionTable,
|
|
MaybeStdLibGrades, invoked_by_mmc_make, ModuleName,
|
|
EnvOptFileVariables, EnvVarArgs, OptionArgs,
|
|
ExtraAndTaskOptions, MayBuild, !IO),
|
|
(
|
|
MayBuild = may_build(AllOptionArgs, BuildGlobals),
|
|
open_module_error_stream(ProgressStream, Globals, ModuleName,
|
|
MaybeErrorStream, !Info, !IO),
|
|
(
|
|
MaybeErrorStream = es_error_already_reported,
|
|
Succeeded0 = did_not_succeed
|
|
;
|
|
MaybeErrorStream = es_ok(MESI, ErrorStream),
|
|
build_target_2(ProgressStream, ErrorStream, BuildGlobals,
|
|
Task, ModuleName, ModuleDepInfo,
|
|
MaybeArgFileName, AllOptionArgs, Succeeded0, !Info, !IO),
|
|
close_module_error_stream_handle_errors(ProgressStream,
|
|
Globals, ModuleName, MESI, ErrorStream, !Info, !IO)
|
|
)
|
|
;
|
|
MayBuild = may_not_build(Specs),
|
|
write_error_specs(ProgressStream, Globals, Specs, !IO),
|
|
Succeeded0 = did_not_succeed
|
|
),
|
|
teardown_checking_for_interrupt(VeryVerbose, Cookie, Cleanup,
|
|
Succeeded0, Succeeded, !Info, !IO),
|
|
record_made_target_given_make_lhs_files(ProgressStream, Globals,
|
|
Succeeded, TargetFile, TargetFileName, MakeLhsFiles, !Info, !IO),
|
|
get_real_milliseconds(EndTimeMs, !IO),
|
|
|
|
globals.lookup_bool_option(Globals, show_make_times, ShowMakeTimes),
|
|
(
|
|
ShowMakeTimes = yes,
|
|
DiffSecs = float(EndTimeMs - StartTimeMs) / 1000.0,
|
|
% Avoid cluttering the screen with short running times.
|
|
( if DiffSecs >= 0.5 then
|
|
io.format(ProgressStream, "Making %s took %.2fs\n",
|
|
[s(TargetFileName), f(DiffSecs)], !IO)
|
|
else
|
|
true
|
|
)
|
|
;
|
|
ShowMakeTimes = no
|
|
)
|
|
;
|
|
ArgFileNameRes = error(ArgFileError),
|
|
io.format(ProgressStream, "Could not create temporary file: %s\n",
|
|
[s(error_message(ArgFileError))], !IO),
|
|
Succeeded = did_not_succeed
|
|
).
|
|
|
|
:- pred build_target_2( io.text_output_stream::in, io.text_output_stream::in,
|
|
globals::in, compilation_task_type::in, module_name::in,
|
|
module_dep_info::in, maybe(file_name)::in, list(string)::in,
|
|
maybe_succeeded::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
build_target_2(ProgressStream, ErrorStream, Globals, Task, ModuleName,
|
|
ModuleDepInfo, ArgFileName, AllOptionArgs, Succeeded, !Info, !IO) :-
|
|
(
|
|
Task = process_module(ModuleTask),
|
|
ModuleArg = sym_name_to_string(ModuleName),
|
|
|
|
globals.lookup_bool_option(Globals, verbose_commands, Verbose),
|
|
(
|
|
Verbose = yes,
|
|
AllArgs = AllOptionArgs ++ [ModuleArg],
|
|
% XXX XXX Don't write the options whose values are the same
|
|
% as the defaults.
|
|
AllArgStrs = list.map(quote_shell_cmd_arg, AllArgs),
|
|
AllArgsStr = string.join_list(" ", AllArgStrs),
|
|
io.format(ProgressStream, "Invoking self `mmc %s'\n",
|
|
[s(AllArgsStr)], !IO)
|
|
;
|
|
Verbose = no
|
|
),
|
|
|
|
% Run some tasks in a separate process instead of within the mmc --make
|
|
% process. This avoids problems with the Boehm GC retaining memory by
|
|
% scanning too much of the Mercury stacks. If the compilation is run
|
|
% in a separate process, it is also easier to kill if an interrupt
|
|
% arrives.
|
|
% XXX The above comment is likely to be quite out-of-date.
|
|
CallInSeparateProcess = do_task_in_separate_process(ModuleTask),
|
|
(
|
|
CallInSeparateProcess = yes,
|
|
call_in_forked_process_with_backup(
|
|
call_mercury_compile_main(ProgressStream, ErrorStream, Globals,
|
|
[ModuleArg]),
|
|
invoke_mmc(Globals, ProgressStream, ErrorStream,
|
|
ArgFileName, AllOptionArgs ++ [ModuleArg]),
|
|
CompileSucceeded, !IO)
|
|
;
|
|
CallInSeparateProcess = no,
|
|
call_mercury_compile_main(ProgressStream, ErrorStream, Globals,
|
|
[ModuleArg], CompileSucceeded, !IO)
|
|
),
|
|
|
|
( if
|
|
( ModuleTask = task_compile_to_c
|
|
; ModuleTask = task_compile_to_java
|
|
; ModuleTask = task_compile_to_csharp
|
|
; ModuleTask = task_errorcheck
|
|
)
|
|
then
|
|
% The `.err_date' file is needed because the `.err' file is touched
|
|
% by all phases of compilation, including writing interfaces.
|
|
touch_module_ext_datestamp(Globals, ProgressStream,
|
|
ModuleName, ext_cur_ngs_gs(ext_cur_ngs_gs_misc_err_date),
|
|
TouchSucceeded, !IO),
|
|
Succeeded = CompileSucceeded `and` TouchSucceeded
|
|
else
|
|
Succeeded = CompileSucceeded
|
|
)
|
|
;
|
|
Task = target_code_to_object_code(PIC),
|
|
globals.get_target(Globals, CompilationTarget),
|
|
% Run the compilation in a child process, so it can be killed
|
|
% if an interrupt arrives.
|
|
call_in_forked_process(
|
|
build_object_code(ProgressStream, ErrorStream, Globals,
|
|
CompilationTarget, PIC, ModuleName, ModuleDepInfo),
|
|
Succeeded, !IO)
|
|
;
|
|
Task = fact_table_code_to_object_code(PIC, FactTableFileName),
|
|
get_object_extension(Globals, PIC, ObjExt),
|
|
get_fact_table_foreign_code_file(Globals, do_create_dirs,
|
|
ext_cur_ngs_gas(ObjExt),
|
|
FactTableFileName, FactTableForeignCode, !IO),
|
|
% Run the compilation in a child process, so it can be killed
|
|
% if an interrupt arrives.
|
|
call_in_forked_process(
|
|
compile_foreign_code_file(Globals, ProgressStream, PIC,
|
|
ModuleDepInfo, FactTableForeignCode),
|
|
Succeeded, !IO)
|
|
).
|
|
|
|
:- pred build_object_code(io.text_output_stream::in, io.text_output_stream::in,
|
|
globals::in, compilation_target::in, pic::in, module_name::in,
|
|
module_dep_info::in, maybe_succeeded::out, io::di, io::uo) is det.
|
|
|
|
build_object_code(ProgressStream, ErrorStream, Globals, Target, PIC,
|
|
ModuleName, _ModuleDepInfo, Succeeded, !IO) :-
|
|
(
|
|
Target = target_c,
|
|
compile_c_file(Globals, ProgressStream, PIC, ModuleName,
|
|
Succeeded, !IO)
|
|
;
|
|
Target = target_java,
|
|
% XXX LEGACY
|
|
module_name_to_file_name_create_dirs(Globals, $pred,
|
|
ext_cur_ngs_gs_java(ext_cur_ngs_gs_java_java),
|
|
ModuleName, JavaFile, _JavaFileProposed, !IO),
|
|
compile_java_files(Globals, ProgressStream, JavaFile, [],
|
|
Succeeded, !IO)
|
|
;
|
|
Target = target_csharp,
|
|
% XXX LEGACY
|
|
module_name_to_file_name_create_dirs(Globals, $pred,
|
|
ext_cur_ngs_gs(ext_cur_ngs_gs_target_cs),
|
|
ModuleName, CsharpFile, _CsharpFileProposed, !IO),
|
|
compile_target_code.link(Globals, ProgressStream, csharp_library,
|
|
ModuleName, [CsharpFile], Specs, Succeeded, !IO),
|
|
% XXX MAKE This predicate, build_object_code, is invoked only as the
|
|
% top call in a newly-spawned-off process. We cannot return Specs
|
|
% to our caller, because our caller is in a separate process.
|
|
write_error_specs(ErrorStream, Globals, Specs, !IO)
|
|
).
|
|
|
|
:- pred compile_foreign_code_file(globals::in, io.text_output_stream::in,
|
|
pic::in, module_dep_info::in, foreign_code_file::in, maybe_succeeded::out,
|
|
io::di, io::uo) is det.
|
|
|
|
compile_foreign_code_file(Globals, ProgressStream, PIC, ModuleDepInfo,
|
|
ForeignCodeFile, Succeeded, !IO) :-
|
|
(
|
|
ForeignCodeFile = foreign_code_file(lang_c, CFile, ObjFile),
|
|
do_compile_c_file(Globals, ProgressStream, PIC,
|
|
CFile, ObjFile, Succeeded, !IO)
|
|
;
|
|
ForeignCodeFile = foreign_code_file(lang_java, JavaFile, _ClassFile),
|
|
compile_java_files(Globals, ProgressStream, JavaFile, [],
|
|
Succeeded, !IO)
|
|
;
|
|
ForeignCodeFile = foreign_code_file(lang_csharp, CSharpFile, DLLFile),
|
|
compile_csharp_file(Globals, ProgressStream, ModuleDepInfo,
|
|
CSharpFile, DLLFile, Succeeded, !IO)
|
|
).
|
|
|
|
:- func do_task_in_separate_process(module_compilation_task_type) = bool.
|
|
|
|
do_task_in_separate_process(task_errorcheck) = no.
|
|
do_task_in_separate_process(task_make_int0) = no.
|
|
do_task_in_separate_process(task_make_int12) = no.
|
|
do_task_in_separate_process(task_make_int3) = no.
|
|
do_task_in_separate_process(task_make_opt) = yes.
|
|
do_task_in_separate_process(task_make_analysis_registry) = yes.
|
|
do_task_in_separate_process(task_compile_to_c) = yes.
|
|
do_task_in_separate_process(task_compile_to_java) = yes.
|
|
do_task_in_separate_process(task_compile_to_csharp) = yes.
|
|
do_task_in_separate_process(task_make_xml_doc) = yes.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred cleanup_files(io.text_output_stream::in, globals::in,
|
|
maybe(string)::in, make_lhs_files::in,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
cleanup_files(ProgressStream, Globals, MaybeArgFileName, MakeLhsFiles,
|
|
!MakeInfo, !IO) :-
|
|
MakeLhsFiles = make_lhs_files(DatelessLhsTargetFiles, DatedLhsTargetFiles,
|
|
LhsDateFileNames, LhsForeignCodeFileNames),
|
|
% XXX Remove `.int.tmp' files.
|
|
list.foldl2(
|
|
remove_make_target_file(ProgressStream, Globals, $pred,
|
|
very_verbose),
|
|
DatelessLhsTargetFiles, !MakeInfo, !IO),
|
|
list.foldl2(
|
|
remove_make_target_file(ProgressStream, Globals, $pred,
|
|
very_verbose),
|
|
DatedLhsTargetFiles, !MakeInfo, !IO),
|
|
list.foldl2(remove_file_for_make(ProgressStream, Globals, very_verbose),
|
|
LhsDateFileNames, !MakeInfo, !IO),
|
|
list.foldl2(remove_file_for_make(ProgressStream, Globals, very_verbose),
|
|
LhsForeignCodeFileNames, !MakeInfo, !IO),
|
|
(
|
|
MaybeArgFileName = yes(ArgFileName),
|
|
io.file.remove_file(ArgFileName, _, !IO)
|
|
;
|
|
MaybeArgFileName = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred get_object_extension(globals::in, pic::in, ext_cur_ngs_gas::out) is det.
|
|
|
|
get_object_extension(Globals, PIC, ExtObj) :-
|
|
globals.get_target(Globals, CompilationTarget),
|
|
(
|
|
CompilationTarget = target_c,
|
|
maybe_pic_object_file_extension(PIC, ExtObj, _)
|
|
;
|
|
CompilationTarget = target_csharp,
|
|
sorry($pred, "object extension for csharp")
|
|
;
|
|
CompilationTarget = target_java,
|
|
sorry($pred, "object extension for java")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred call_mercury_compile_main(io.text_output_stream::in,
|
|
io.text_output_stream::in, globals::in, list(string)::in,
|
|
maybe_succeeded::out, io::di, io::uo) is det.
|
|
|
|
call_mercury_compile_main(ProgressStream, ErrorStream, Globals, Args,
|
|
Succeeded, !IO) :-
|
|
io.get_exit_status(Status0, !IO),
|
|
io.set_exit_status(0, !IO),
|
|
mercury_compile_main.main_for_make(ProgressStream, ErrorStream, Globals,
|
|
Args, !IO),
|
|
io.get_exit_status(Status, !IO),
|
|
Succeeded = ( if Status = 0 then succeeded else did_not_succeed ),
|
|
io.set_exit_status(Status0, !IO).
|
|
|
|
:- pred invoke_mmc(globals::in,
|
|
io.text_output_stream::in, io.text_output_stream::in,
|
|
maybe(file_name)::in, list(string)::in, maybe_succeeded::out,
|
|
io::di, io::uo) is det.
|
|
|
|
invoke_mmc(Globals, ProgressStream, ErrorStream, MaybeArgFileName, Args,
|
|
Succeeded, !IO) :-
|
|
io.progname("", ProgName, !IO),
|
|
( if
|
|
% NOTE: If the compiler is built in the Java grade, then ProgName will
|
|
% be set to "top_level" (which was the name compiled into the module
|
|
% containing the predicate main/2). We don't want to attempt to
|
|
% invoke an executable named "top_level" however, since the wrapper
|
|
% script will have been renamed to "mercury_compile" by the Mmakefile
|
|
% in the compiler directory.
|
|
( ProgName = ""
|
|
; target_is_java
|
|
)
|
|
then
|
|
io.environment.get_environment_var("MERCURY_COMPILER",
|
|
MaybeMercuryCompiler, !IO),
|
|
(
|
|
MaybeMercuryCompiler = yes(MercuryCompiler)
|
|
;
|
|
MaybeMercuryCompiler = no,
|
|
MercuryCompiler = "mmc"
|
|
)
|
|
else
|
|
MercuryCompiler = ProgName
|
|
),
|
|
|
|
QuotedArgs = list.map(quote_shell_cmd_arg, Args),
|
|
|
|
% Some operating systems (e.g. Windows) have shells with ludicrously
|
|
% short limits on the length of command lines, so we need to write the
|
|
% arguments to a file which will be read by the child mmc process.
|
|
% This code is only called if fork() doesn't work, so there is no point
|
|
% checking whether the shell actually has this limitation.
|
|
% The temporary file is created by the caller so that it will be removed
|
|
% by teardown_checking_for_interrupt if an interrupt occurs.
|
|
(
|
|
MaybeArgFileName = yes(ArgFileName)
|
|
;
|
|
MaybeArgFileName = no,
|
|
unexpected($pred, "argument file not created")
|
|
),
|
|
|
|
io.open_output(ArgFileName, ArgFileOpenRes, !IO),
|
|
(
|
|
ArgFileOpenRes = ok(ArgFileStream),
|
|
io.format(ArgFileStream, "MCFLAGS = %s\n",
|
|
[s(string.join_list(" ", QuotedArgs))], !IO),
|
|
io.close_output(ArgFileStream, !IO),
|
|
|
|
Command = string.format("%s --arg-file %s", [
|
|
s(quote_shell_cmd_arg(MercuryCompiler)),
|
|
s(quote_shell_cmd_arg(ArgFileName))]),
|
|
|
|
% We have already written the command.
|
|
CommandVerbosity = cmd_verbose,
|
|
invoke_system_command(Globals, ProgressStream, ErrorStream,
|
|
CommandVerbosity, Command, Succeeded, !IO)
|
|
;
|
|
ArgFileOpenRes = error(Error),
|
|
Succeeded = did_not_succeed,
|
|
io.error_message(Error, ErrorMsg),
|
|
io.format(ProgressStream, "Error opening `%s' for output: %s\n",
|
|
[s(ArgFileName), s(ErrorMsg)], !IO)
|
|
),
|
|
io.file.remove_file(ArgFileName, _, !IO).
|
|
|
|
:- pred target_is_java is semidet.
|
|
|
|
:- pragma foreign_proc("Java",
|
|
target_is_java,
|
|
[promise_pure, will_not_call_mercury, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
target_is_java :-
|
|
semidet_fail.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
record_made_target(ProgressStream, Globals, TargetFile, TargetFileName,
|
|
CompilationTask, Succeeded, !Info, !IO) :-
|
|
find_lhs_files_of_task(ProgressStream, Globals, TargetFile,
|
|
CompilationTask, MakeLhsFiles, !Info, !IO),
|
|
record_made_target_given_make_lhs_files(ProgressStream, Globals,
|
|
Succeeded, TargetFile, TargetFileName, MakeLhsFiles, !Info, !IO).
|
|
|
|
:- pred record_made_target_given_make_lhs_files(io.text_output_stream::in,
|
|
globals::in, maybe_succeeded::in, target_file::in, string::in,
|
|
make_lhs_files::in, make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
record_made_target_given_make_lhs_files(ProgressStream, Globals,
|
|
Succeeded, TargetFile, TargetFileName, MakeLhsFiles, !Info, !IO) :-
|
|
(
|
|
Succeeded = succeeded,
|
|
TargetStatus = deps_status_up_to_date
|
|
;
|
|
Succeeded = did_not_succeed,
|
|
TargetStatus = deps_status_error,
|
|
file_error_msg(TargetFileName, ErrorMsg),
|
|
maybe_write_msg_locked(ProgressStream, !.Info, ErrorMsg, !IO)
|
|
),
|
|
|
|
MakeLhsFiles = make_lhs_files(DatelessLhsTargetFiles, DatedLhsTargetFiles,
|
|
LhsDateFiles, LhsForeignCodeFileNames),
|
|
list.foldl(update_target_status(TargetStatus),
|
|
DatelessLhsTargetFiles, !Info),
|
|
list.foldl(update_target_status(TargetStatus),
|
|
DatedLhsTargetFiles, !Info),
|
|
|
|
% XXX LEGACY
|
|
list.map2_foldl2(
|
|
module_maybe_nested_target_file_to_file_name(ProgressStream, Globals,
|
|
$pred),
|
|
DatelessLhsTargetFiles,
|
|
DatelessLhsFileNames, _DatelessLhsFileNamesProposed, !Info, !IO),
|
|
list.map2_foldl2(
|
|
module_maybe_nested_target_file_to_file_name(ProgressStream, Globals,
|
|
$pred),
|
|
DatedLhsTargetFiles,
|
|
DatedLhsFileNames, _DatedLhsFileNamesProposed, !Info, !IO),
|
|
|
|
some [!FileTimestampMap] (
|
|
!:FileTimestampMap = make_info_get_file_timestamp_map(!.Info),
|
|
list.foldl(delete_timestamp(ProgressStream, Globals),
|
|
DatelessLhsFileNames, !FileTimestampMap),
|
|
list.foldl(delete_timestamp(ProgressStream, Globals),
|
|
DatedLhsFileNames, !FileTimestampMap),
|
|
list.foldl(delete_timestamp(ProgressStream, Globals),
|
|
LhsDateFiles, !FileTimestampMap),
|
|
list.foldl(delete_timestamp(ProgressStream, Globals),
|
|
LhsForeignCodeFileNames, !FileTimestampMap),
|
|
|
|
% When an .analysis file is made, that potentially invalidates other
|
|
% .analysis files so we have to delete their timestamps. The exact list
|
|
% of files which might be affected can be found by reading the
|
|
% corresponding .imdg file. But it is simpler to just delete the
|
|
% timestamps of all the .analysis files that we know about.
|
|
( if TargetFile = target_file(_, module_target_analysis_registry) then
|
|
map.foldl(
|
|
delete_analysis_registry_timestamps(ProgressStream, Globals),
|
|
!.FileTimestampMap, !FileTimestampMap)
|
|
else
|
|
true
|
|
),
|
|
make_info_set_file_timestamp_map(!.FileTimestampMap, !Info)
|
|
),
|
|
|
|
TargetFileTimestampMap0 = make_info_get_target_file_timestamp_map(!.Info),
|
|
list.foldl(version_hash_table.delete, DatelessLhsTargetFiles,
|
|
TargetFileTimestampMap0, TargetFileTimestampMap1),
|
|
list.foldl(version_hash_table.delete, DatedLhsTargetFiles,
|
|
TargetFileTimestampMap1, TargetFileTimestampMap),
|
|
make_info_set_target_file_timestamp_map(TargetFileTimestampMap, !Info).
|
|
|
|
:- pred update_target_status(dependency_status::in, target_file::in,
|
|
make_info::in, make_info::out) is det.
|
|
|
|
update_target_status(TargetStatus, TargetFile, !Info) :-
|
|
Dep = dep_target(TargetFile),
|
|
DepStatusMap0 = make_info_get_dep_file_status_map(!.Info),
|
|
version_hash_table.set(Dep, TargetStatus, DepStatusMap0, DepStatusMap),
|
|
make_info_set_dep_file_status_map(DepStatusMap, !Info).
|
|
|
|
:- pred delete_analysis_registry_timestamps(io.text_output_stream::in,
|
|
globals::in, string::in, {list(dir_name), maybe_error(timestamp)}::in,
|
|
file_timestamp_map::in, file_timestamp_map::out) is det.
|
|
|
|
delete_analysis_registry_timestamps(ProgressStream, Globals, FileName, _,
|
|
!TimestampMap) :-
|
|
( if string.suffix(FileName, ".analysis") then
|
|
delete_timestamp(ProgressStream, Globals, FileName, !TimestampMap)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred delete_timestamp(io.text_output_stream::in, globals::in, string::in,
|
|
file_timestamp_map::in, file_timestamp_map::out) is det.
|
|
|
|
delete_timestamp(ProgressStream, Globals, TouchedFile, !TimestampMap) :-
|
|
trace [io(!IO)] (
|
|
debug_make_msg(Globals,
|
|
string.format("Deleting timestamp for %s\n", [s(TouchedFile)]),
|
|
DebugMsg),
|
|
maybe_write_msg(ProgressStream, DebugMsg, !IO)
|
|
),
|
|
map.delete(TouchedFile, !TimestampMap).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type compilation_task_type_and_options
|
|
---> task_and_options(
|
|
compilation_task_type,
|
|
list(string)
|
|
).
|
|
|
|
:- pred get_compilation_task_and_options(module_target_type::in,
|
|
compilation_task_type_and_options::out) is det.
|
|
|
|
get_compilation_task_and_options(Target, Result) :-
|
|
(
|
|
( Target = module_target_source
|
|
; Target = module_target_track_flags
|
|
),
|
|
unexpected($pred, "compilation_task")
|
|
;
|
|
Target = module_target_errors,
|
|
Result = task_and_options(process_module(task_errorcheck),
|
|
["--errorcheck-only"])
|
|
;
|
|
Target = module_target_int0,
|
|
Result = task_and_options(process_module(task_make_int0),
|
|
["--make-private-interface"])
|
|
;
|
|
( Target = module_target_int1
|
|
; Target = module_target_int2
|
|
),
|
|
Result = task_and_options(process_module(task_make_int12),
|
|
["--make-interface"])
|
|
;
|
|
Target = module_target_int3,
|
|
Result = task_and_options(process_module(task_make_int3),
|
|
["--make-short-interface"])
|
|
;
|
|
Target = module_target_opt,
|
|
Result = task_and_options(process_module(task_make_opt),
|
|
["--make-optimization-interface"])
|
|
;
|
|
Target = module_target_analysis_registry,
|
|
Result = task_and_options(process_module(task_make_analysis_registry),
|
|
["--make-analysis-registry"])
|
|
;
|
|
( Target = module_target_c_header(_)
|
|
; Target = module_target_c_code
|
|
),
|
|
Result = task_and_options(process_module(task_compile_to_c),
|
|
["--compile-to-c"])
|
|
;
|
|
Target = module_target_csharp_code,
|
|
Result = task_and_options(process_module(task_compile_to_csharp),
|
|
["--csharp-only"])
|
|
;
|
|
Target = module_target_java_code,
|
|
Result = task_and_options(process_module(task_compile_to_java),
|
|
["--java-only"])
|
|
;
|
|
Target = module_target_java_class_code,
|
|
Result = task_and_options(target_code_to_object_code(non_pic), [])
|
|
;
|
|
Target = module_target_object_code(PIC),
|
|
Result = task_and_options(target_code_to_object_code(PIC), [])
|
|
;
|
|
Target = module_target_fact_table_object(PIC, FactTable),
|
|
Result = task_and_options(
|
|
fact_table_code_to_object_code(PIC, FactTable), [])
|
|
;
|
|
Target = module_target_xml_doc,
|
|
Result = task_and_options(process_module(task_make_xml_doc),
|
|
["--make-xml-doc"])
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Find the files which could be touched by a compilation task.
|
|
%
|
|
:- pred find_lhs_files_of_task(io.text_output_stream::in,
|
|
globals::in, target_file::in, compilation_task_type::in,
|
|
make_lhs_files::out, make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
find_lhs_files_of_task(ProgressStream, Globals, TargetFile, Task, MakeLhsFiles,
|
|
!Info, !IO) :-
|
|
% NOTE Our ancestor computes Task from the target_type component
|
|
% of the TargetFile argument, which means that the only valid
|
|
% target type/compilation task pairs are those that occur in the
|
|
% predicate that does that computation, get_compilation_task_and_options.
|
|
(
|
|
Task = process_module(ModuleTask),
|
|
find_lhs_files_of_process_module(ProgressStream, Globals,
|
|
TargetFile, ModuleTask, MakeLhsFiles, !Info, !IO)
|
|
;
|
|
Task = target_code_to_object_code(_),
|
|
MakeLhsFiles = make_lhs_files([TargetFile], [], [], [])
|
|
;
|
|
Task = fact_table_code_to_object_code(PIC, FactTableName),
|
|
get_object_extension(Globals, PIC, ObjExt),
|
|
% XXX LEGACY
|
|
fact_table_file_name_return_dirs(Globals, $pred,
|
|
ext_cur_ngs_gas(ObjExt), FactTableName,
|
|
FactTableDirs, _FactTableDirsProposed,
|
|
FactTableObjectFileName, _FactTableObjectFileNameProposed),
|
|
create_any_dirs_on_path(FactTableDirs, !IO),
|
|
% Fact table object files (.o or .pic_o) don't have date files.
|
|
% XXX MAKE double inclusion in first and last fields
|
|
MakeLhsFiles = make_lhs_files([TargetFile], [], [],
|
|
[FactTableObjectFileName])
|
|
).
|
|
|
|
:- pred find_lhs_files_of_process_module(io.text_output_stream::in,
|
|
globals::in, target_file::in, module_compilation_task_type::in,
|
|
make_lhs_files::out, make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
find_lhs_files_of_process_module(ProgressStream, Globals, TargetFile, Task,
|
|
MakeLhsFiles, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
get_maybe_module_dep_info(ProgressStream, Globals,
|
|
ModuleName, MaybeModuleDepInfo, !Info, !IO),
|
|
(
|
|
MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo)
|
|
;
|
|
MaybeModuleDepInfo = no_module_dep_info,
|
|
% This error should have been caught earlier. We should not be
|
|
% attempting to build a target if we couldn't find the dependencies
|
|
% for the module.
|
|
unexpected($pred, "no module dependencies")
|
|
),
|
|
|
|
module_dep_info_get_maybe_top_module(ModuleDepInfo, MaybeTopModule),
|
|
NestedSubModules = get_nested_children_list_of_top_module(MaybeTopModule),
|
|
SourceFileModuleNames = [ModuleName | NestedSubModules],
|
|
|
|
list.map_foldl2(get_maybe_module_dep_info(ProgressStream, Globals),
|
|
NestedSubModules, MaybeNestedModuleDepInfos, !Info, !IO),
|
|
( if
|
|
list.map((pred(some_module_dep_info(MDI)::in, MDI::out) is semidet),
|
|
MaybeNestedModuleDepInfos, NestedModuleDepInfos)
|
|
then
|
|
ModuleDepInfos = [ModuleDepInfo | NestedModuleDepInfos]
|
|
else
|
|
% This error should have been caught earlier. We should not be
|
|
% attempting to build a target if we couldn't find the dependencies
|
|
% for the module or its nested sub-modules.
|
|
unexpected($pred, "no nested module dependencies")
|
|
),
|
|
|
|
(
|
|
Task = task_compile_to_c,
|
|
DirectLhsTargetFiles =
|
|
make_target_file_list(SourceFileModuleNames, TargetType),
|
|
% Find out what header files are generated.
|
|
TargetPIC = target_type_to_pic(TargetType),
|
|
list.map_foldl(
|
|
get_any_fact_table_object_code_files(Globals, TargetPIC),
|
|
ModuleDepInfos, ForeignCodeFiles, !IO),
|
|
LhsForeignCodeFileNames =
|
|
list.map((func(ForeignFile) = ForeignFile ^ fcf_target_file),
|
|
list.condense(ForeignCodeFiles)),
|
|
MhTargetFiles = make_target_file_list(SourceFileModuleNames,
|
|
module_target_c_header(header_mh)),
|
|
globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
|
|
(
|
|
HighLevelCode = yes,
|
|
% When compiling to high-level C, we always generate both
|
|
% a .mih file and a .mh file.
|
|
MihTargetFiles = make_target_file_list(SourceFileModuleNames,
|
|
module_target_c_header(header_mih)),
|
|
LhsTargetFiles = DirectLhsTargetFiles ++ MhTargetFiles ++
|
|
MihTargetFiles
|
|
;
|
|
HighLevelCode = no,
|
|
% When compiling to high-level C, we generate only a .mh file.
|
|
LhsTargetFiles = DirectLhsTargetFiles ++ MhTargetFiles
|
|
)
|
|
;
|
|
( Task = task_compile_to_java
|
|
; Task = task_compile_to_csharp
|
|
),
|
|
% We do not generate any kind of header file for either Java or C#.
|
|
LhsTargetFiles =
|
|
make_target_file_list(SourceFileModuleNames, TargetType),
|
|
% We support fact tables only when targeting C, not when targeting
|
|
% Java or C#.
|
|
LhsForeignCodeFileNames = []
|
|
;
|
|
Task = task_make_int0,
|
|
% LhsTargetFiles must only include modules with children,
|
|
% as we no longer write out .int0 files for modules without children.
|
|
list.filter_map(is_ancestor_module, ModuleDepInfos, AncestorModules),
|
|
LhsTargetFiles = make_target_file_list(AncestorModules, TargetType),
|
|
LhsForeignCodeFileNames = []
|
|
;
|
|
Task = task_make_int12,
|
|
% "mmc --make-interface" generates both .int and .int2 files.
|
|
LhsTargetFiles =
|
|
make_target_file_list(SourceFileModuleNames, module_target_int1) ++
|
|
make_target_file_list(SourceFileModuleNames, module_target_int2),
|
|
LhsForeignCodeFileNames = []
|
|
;
|
|
( Task = task_errorcheck
|
|
; Task = task_make_int3
|
|
; Task = task_make_opt
|
|
; Task = task_make_analysis_registry
|
|
; Task = task_make_xml_doc
|
|
),
|
|
LhsTargetFiles =
|
|
make_target_file_list(SourceFileModuleNames, TargetType),
|
|
LhsForeignCodeFileNames = []
|
|
),
|
|
split_dateless_dated_target_files(Globals, LhsTargetFiles,
|
|
DatelessLhsTargetFiles, DatedLhsTargetFiles, LhsDateFileNames),
|
|
MakeLhsFiles = make_lhs_files(DatelessLhsTargetFiles, DatedLhsTargetFiles,
|
|
LhsDateFileNames, LhsForeignCodeFileNames).
|
|
|
|
:- pred is_ancestor_module(module_dep_info::in, module_name::out) is semidet.
|
|
|
|
is_ancestor_module(ModuleDepInfo, ModuleName) :-
|
|
module_dep_info_get_children(ModuleDepInfo, Children),
|
|
not set.is_empty(Children),
|
|
module_dep_info_get_module_name(ModuleDepInfo, ModuleName).
|
|
|
|
:- pred split_dateless_dated_target_files(globals::in, list(target_file)::in,
|
|
list(target_file)::out, list(target_file)::out, list(file_name)::out)
|
|
is det.
|
|
|
|
split_dateless_dated_target_files(_Globals, [], [], [], []).
|
|
split_dateless_dated_target_files(Globals,
|
|
[LhsTargetFile | LhsTargetFiles],
|
|
!:DatelessLhsTargetFiles, !:DatedLhsTargetFiles, !:LhsDateFileNames) :-
|
|
% The list of target files our caller calls us with
|
|
% typically contains one or two entries for each (sub)module
|
|
% contained in a source file. This means that almost all lists
|
|
% will be very short, so using accumulators to make this predicate
|
|
% tail recursive would have bigger costs than benefits.
|
|
split_dateless_dated_target_files(Globals, LhsTargetFiles,
|
|
!:DatelessLhsTargetFiles, !:DatedLhsTargetFiles, !:LhsDateFileNames),
|
|
LhsTargetFile = target_file(ModuleName, TargetType),
|
|
( if date_file_extension(TargetType, DateFileExt) then
|
|
!:DatedLhsTargetFiles = [LhsTargetFile | !.DatedLhsTargetFiles],
|
|
% XXX LEGACY
|
|
module_name_to_file_name(Globals, $pred, DateFileExt,
|
|
ModuleName, LhsDateFileName, _LhsDateFileNamePropoposed),
|
|
!:LhsDateFileNames = [LhsDateFileName | !.LhsDateFileNames]
|
|
else
|
|
!:DatelessLhsTargetFiles = [LhsTargetFile | !.DatelessLhsTargetFiles]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_any_fact_table_object_code_files(Globals, PIC, ModuleDepInfo,
|
|
ForeignFiles, !IO) :-
|
|
% Find externally compiled foreign code files for
|
|
% `:- pragma foreign_proc' declarations.
|
|
%
|
|
% Any changes here may require corresponding changes in
|
|
% get_foreign_object_targets.
|
|
globals.get_target(Globals, CompilationTarget),
|
|
|
|
% None of the current backends require externally compiled foreign code,
|
|
% except the C backend for fact tables.
|
|
(
|
|
CompilationTarget = target_c,
|
|
maybe_pic_object_file_extension(PIC, ObjExt, _),
|
|
module_dep_info_get_fact_tables(ModuleDepInfo, FactTableFiles),
|
|
list.map_foldl(
|
|
get_fact_table_foreign_code_file(Globals, do_not_create_dirs,
|
|
ext_cur_ngs_gas(ObjExt)),
|
|
set.to_sorted_list(FactTableFiles), FactTableForeignFiles, !IO),
|
|
ForeignFiles = FactTableForeignFiles
|
|
;
|
|
( CompilationTarget = target_java
|
|
; CompilationTarget = target_csharp
|
|
),
|
|
ForeignFiles = []
|
|
).
|
|
|
|
:- pred get_fact_table_foreign_code_file(globals::in, maybe_create_dirs::in,
|
|
ext::in, file_name::in, foreign_code_file::out, io::di, io::uo) is det.
|
|
|
|
get_fact_table_foreign_code_file(Globals, Mkdir, ObjExt,
|
|
FactTableFileName, ForeignCodeFile, !IO) :-
|
|
% XXX EXT Neither of these calls should be needed.
|
|
% XXX LEGACY
|
|
fact_table_file_name_return_dirs(Globals, $pred,
|
|
ext_cur_ngs_gs(ext_cur_ngs_gs_target_c), FactTableFileName,
|
|
FactTableDirsC, _FactTableDirsCProposed,
|
|
FactTableCFileName, _FactTableCFileNameProposed),
|
|
maybe_create_any_dirs_on_path(Mkdir, FactTableDirsC, !IO),
|
|
% XXX LEGACY
|
|
fact_table_file_name_return_dirs(Globals, $pred, ObjExt, FactTableFileName,
|
|
FactTableDirsO, _FactTableDirsOProposed,
|
|
FactTableObjFileName, _FactTableObjFileNameProposed),
|
|
maybe_create_any_dirs_on_path(Mkdir, FactTableDirsO, !IO),
|
|
ForeignCodeFile =
|
|
foreign_code_file(lang_c, FactTableCFileName, FactTableObjFileName).
|
|
|
|
:- func target_type_to_pic(module_target_type) = pic.
|
|
|
|
target_type_to_pic(TargetType) = Result :-
|
|
(
|
|
TargetType = module_target_object_code(PIC),
|
|
Result = PIC
|
|
;
|
|
( TargetType = module_target_source
|
|
; TargetType = module_target_errors
|
|
; TargetType = module_target_int0
|
|
; TargetType = module_target_int1
|
|
; TargetType = module_target_int2
|
|
; TargetType = module_target_int3
|
|
; TargetType = module_target_opt
|
|
; TargetType = module_target_analysis_registry
|
|
; TargetType = module_target_track_flags
|
|
; TargetType = module_target_c_header(_)
|
|
; TargetType = module_target_c_code
|
|
; TargetType = module_target_csharp_code
|
|
; TargetType = module_target_java_code
|
|
; TargetType = module_target_java_class_code
|
|
; TargetType = module_target_fact_table_object(_, _)
|
|
; TargetType = module_target_xml_doc
|
|
),
|
|
Result = non_pic
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module make.module_target.
|
|
%---------------------------------------------------------------------------%
|