mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 21:04:00 +00:00
2445 lines
96 KiB
Mathematica
2445 lines
96 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2012 The University of Melbourne.
|
|
% Copyright (C) 2017 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: mercury_compile_main.m.
|
|
% Main authors: fjh, zs.
|
|
%
|
|
% This is the top-level of the Mercury compiler.
|
|
%
|
|
% This module invokes the different passes of the compiler as appropriate.
|
|
% The constraints on pass ordering are documented in
|
|
% compiler/notes/compiler_design.html.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module top_level.mercury_compile_main.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
% This is the main entry point for the Mercury compiler.
|
|
% It is called from mercury_compile.main.
|
|
%
|
|
:- pred real_main(io::di, io::uo) is det.
|
|
|
|
% main_for_make(Globals, Args, !IO) is called from
|
|
% make.module_target.call_mercury_compile_main.
|
|
%
|
|
:- pred main_for_make(globals::in, list(string)::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module analysis.
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.compile_target_code.
|
|
:- import_module backend_libs.export.
|
|
:- import_module check_hlds.
|
|
:- import_module check_hlds.xml_documentation.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_defns.
|
|
:- import_module hlds.hlds_dependency_graph.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.make_hlds.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.compute_grade.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.handle_options.
|
|
:- import_module libs.op_mode.
|
|
:- import_module libs.options.
|
|
:- import_module libs.timestamp.
|
|
:- import_module make.
|
|
:- import_module make.options_file.
|
|
:- import_module make.util.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module mdbcomp.shared_utilities.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.check_raw_comp_unit.
|
|
:- import_module parse_tree.equiv_type.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.file_kind.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.generate_dep_d_files.
|
|
:- import_module parse_tree.module_cmds.
|
|
:- import_module parse_tree.module_imports.
|
|
:- import_module parse_tree.module_qual.
|
|
:- import_module parse_tree.modules.
|
|
:- import_module parse_tree.parse_error.
|
|
:- import_module parse_tree.parse_tree_out.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_event.
|
|
:- import_module parse_tree.prog_data_used_modules.
|
|
:- import_module parse_tree.prog_event.
|
|
:- import_module parse_tree.prog_item.
|
|
:- import_module parse_tree.read_modules.
|
|
:- import_module parse_tree.source_file_map.
|
|
:- import_module parse_tree.split_parse_tree_src.
|
|
:- import_module parse_tree.write_deps_file.
|
|
:- import_module parse_tree.write_module_interface_files.
|
|
:- import_module recompilation.
|
|
:- import_module recompilation.check.
|
|
:- import_module recompilation.usage.
|
|
:- import_module top_level.mercury_compile_erl_back_end.
|
|
:- import_module top_level.mercury_compile_front_end.
|
|
:- import_module top_level.mercury_compile_llds_back_end.
|
|
:- import_module top_level.mercury_compile_middle_passes.
|
|
:- import_module top_level.mercury_compile_mlds_back_end.
|
|
|
|
:- import_module bool.
|
|
:- import_module char.
|
|
:- import_module cord.
|
|
:- import_module dir.
|
|
:- import_module gc.
|
|
:- import_module getopt_io.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module multi_map.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
real_main(!IO) :-
|
|
gc_init(!IO),
|
|
|
|
% All messages go to stderr.
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.set_output_stream(StdErr, _, !IO),
|
|
io.command_line_arguments(CmdLineArgs, !IO),
|
|
|
|
unlimit_stack(!IO),
|
|
|
|
% Replace all @file arguments with the contents of the file.
|
|
expand_at_file_arguments(CmdLineArgs, Res, !IO),
|
|
(
|
|
Res = ok(ExpandedCmdLineArgs),
|
|
real_main_after_expansion(ExpandedCmdLineArgs, !IO)
|
|
;
|
|
Res = error(E),
|
|
io.set_exit_status(1, !IO),
|
|
|
|
io.write_string(io.error_message(E), !IO),
|
|
io.nl(!IO)
|
|
),
|
|
write_translations_record_if_any(!IO).
|
|
|
|
% Expand @File arguments.
|
|
% Each argument in the above form is replaced with a list of arguments
|
|
% where each arg is each line in the file File which is not just
|
|
% whitespace.
|
|
%
|
|
:- pred expand_at_file_arguments(list(string)::in, io.res(list(string))::out,
|
|
io::di, io::uo) is det.
|
|
|
|
expand_at_file_arguments([], ok([]), !IO).
|
|
expand_at_file_arguments([Arg | Args], Result, !IO) :-
|
|
( if string.remove_prefix("@", Arg, File) then
|
|
io.open_input(File, OpenRes, !IO),
|
|
(
|
|
OpenRes = ok(S),
|
|
expand_file_into_arg_list(S, ReadRes, !IO),
|
|
(
|
|
ReadRes = ok(FileArgs),
|
|
expand_at_file_arguments(FileArgs ++ Args, Result, !IO)
|
|
;
|
|
ReadRes = error(E),
|
|
Result = error(at_file_error(File, E))
|
|
)
|
|
;
|
|
OpenRes = error(_E),
|
|
Msg = "mercury_compile: cannot open '" ++ File ++ "'",
|
|
Result = error(io.make_io_error(Msg))
|
|
)
|
|
else
|
|
expand_at_file_arguments(Args, Result0, !IO),
|
|
(
|
|
Result0 = ok(ExpandedArgs),
|
|
Result = ok([Arg | ExpandedArgs])
|
|
;
|
|
Result0 = error(E),
|
|
Result = error(E)
|
|
)
|
|
).
|
|
|
|
:- func at_file_error(string, io.error) = io.error.
|
|
|
|
at_file_error(File, E) =
|
|
io.make_io_error("While attempting to process '" ++ File ++
|
|
"' the following error occurred: " ++ io.error_message(E)).
|
|
|
|
% Read each of the command line arguments from the given input file.
|
|
% Note lines which consist purely of whitespace are ignored.
|
|
%
|
|
:- pred expand_file_into_arg_list(io.input_stream::in,
|
|
io.res(list(string))::out, io::di, io::uo) is det.
|
|
|
|
expand_file_into_arg_list(S, Res, !IO) :-
|
|
io.read_line_as_string(S, LineRes, !IO),
|
|
(
|
|
LineRes = ok(Line),
|
|
expand_file_into_arg_list(S, Res0, !IO),
|
|
(
|
|
Res0 = ok(Lines),
|
|
StrippedLine = strip(Line),
|
|
( if StrippedLine = "" then
|
|
Res = ok(Lines)
|
|
else
|
|
Res = ok([StrippedLine | Lines])
|
|
)
|
|
;
|
|
Res0 = error(_E),
|
|
Res = Res0
|
|
)
|
|
;
|
|
LineRes = eof,
|
|
Res = ok([])
|
|
;
|
|
LineRes = error(E),
|
|
Res = error(E)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred real_main_after_expansion(list(string)::in, io::di, io::uo) is det.
|
|
|
|
real_main_after_expansion(CmdLineArgs, !IO) :-
|
|
% XXX Processing the options up to three times is not what you call
|
|
% elegant.
|
|
( if CmdLineArgs = ["--arg-file", ArgFile | ExtraArgs] then
|
|
% All the configuration and options file options are passed in the
|
|
% given file, which is created by the parent `mmc --make' process.
|
|
% (make.module_target does this to overcome limits on the lengths
|
|
% of command lines on Windows.) The environment is ignored, unlike
|
|
% with @file syntax.
|
|
|
|
% Diagnose bad invocations, e.g. shell redirection operators treated
|
|
% as command line arguments.
|
|
(
|
|
ExtraArgs = []
|
|
;
|
|
ExtraArgs = [_ | _],
|
|
unexpected($module, $pred,
|
|
"extra arguments with --arg-file: " ++ string(ExtraArgs))
|
|
),
|
|
|
|
% Read_args_file may attempt to look up options, so we need
|
|
% to initialize the globals.
|
|
generate_default_globals(DummyGlobals, !IO),
|
|
options_file.read_args_file(DummyGlobals, ArgFile, MaybeArgs1, !IO),
|
|
(
|
|
MaybeArgs1 = yes(Args1),
|
|
separate_option_args(Args1, OptionArgs, NonOptionArgs, !IO)
|
|
;
|
|
MaybeArgs1 = no,
|
|
OptionArgs = [],
|
|
NonOptionArgs = []
|
|
),
|
|
DetectedGradeFlags = [],
|
|
Variables = options_variables_init,
|
|
MaybeMCFlags = yes([])
|
|
else
|
|
% Find out which options files to read.
|
|
% Don't report errors yet, as the errors may no longer exist
|
|
% after we have read in options files.
|
|
handle_given_options(CmdLineArgs, OptionArgs, NonOptionArgs,
|
|
_Errors0, ArgsGlobals, !IO),
|
|
read_options_files(ArgsGlobals, options_variables_init,
|
|
MaybeVariables0, !IO),
|
|
(
|
|
MaybeVariables0 = yes(Variables0),
|
|
lookup_mmc_options(ArgsGlobals, Variables0, MaybeMCFlags0, !IO),
|
|
(
|
|
MaybeMCFlags0 = yes(MCFlags0),
|
|
|
|
% Process the options again to find out which configuration
|
|
% file to read.
|
|
handle_given_options(MCFlags0 ++ CmdLineArgs, _, _,
|
|
FlagsSpecs, FlagsArgsGlobals, !IO),
|
|
(
|
|
FlagsSpecs = [_ | _],
|
|
usage_errors(FlagsArgsGlobals, FlagsSpecs, !IO),
|
|
DetectedGradeFlags = [],
|
|
Variables = options_variables_init,
|
|
MaybeMCFlags = no
|
|
;
|
|
FlagsSpecs = [],
|
|
globals.lookup_maybe_string_option(FlagsArgsGlobals,
|
|
config_file, MaybeConfigFile),
|
|
(
|
|
MaybeConfigFile = yes(ConfigFile),
|
|
read_options_file(FlagsArgsGlobals, ConfigFile,
|
|
Variables0, MaybeVariables, !IO),
|
|
(
|
|
MaybeVariables = yes(Variables),
|
|
lookup_mmc_options(FlagsArgsGlobals, Variables,
|
|
MaybeMCFlags, !IO),
|
|
lookup_mercury_stdlib_dir(FlagsArgsGlobals,
|
|
Variables, MaybeMerStdLibDir, !IO),
|
|
detect_libgrades(FlagsArgsGlobals,
|
|
MaybeMerStdLibDir, DetectedGradeFlags, !IO)
|
|
;
|
|
MaybeVariables = no,
|
|
MaybeMCFlags = no,
|
|
DetectedGradeFlags = [],
|
|
Variables = options_variables_init
|
|
)
|
|
;
|
|
MaybeConfigFile = no,
|
|
DetectedGradeFlags = [],
|
|
Variables = options_variables_init,
|
|
lookup_mmc_options(FlagsArgsGlobals, Variables,
|
|
MaybeMCFlags, !IO)
|
|
)
|
|
)
|
|
;
|
|
MaybeMCFlags0 = no,
|
|
Variables = options_variables_init,
|
|
DetectedGradeFlags = [],
|
|
MaybeMCFlags = no
|
|
)
|
|
;
|
|
MaybeVariables0 = no,
|
|
Variables = options_variables_init,
|
|
DetectedGradeFlags = [],
|
|
MaybeMCFlags = no
|
|
)
|
|
),
|
|
(
|
|
MaybeMCFlags = yes(MCFlags),
|
|
|
|
% NOTE: the order of the flags here is important. It must be:
|
|
%
|
|
% (1) flags for detected library grades
|
|
% (2) flags from Mercury.config and any Mercury.options files
|
|
% (3) flags from any command line options
|
|
%
|
|
% Flags given later in this list will override those given earlier.
|
|
%
|
|
% XXX the relationship between --no-libgrade or --libgrade options set
|
|
% via the DEFAULT_MCFLAGS variable and detected library grades is
|
|
% currently not defined. It does not matter at the moment, since
|
|
% Mercury.config does not contain either of those two flags.
|
|
AllFlags = DetectedGradeFlags ++ MCFlags ++ OptionArgs,
|
|
handle_given_options(AllFlags, _, _, Specs, ActualGlobals, !IO),
|
|
|
|
% When computing the option arguments to pass to `--make', only include
|
|
% the command-line arguments, not the contents of DEFAULT_MCFLAGS.
|
|
(
|
|
Specs = [_ | _],
|
|
usage_errors(ActualGlobals, Specs, !IO)
|
|
;
|
|
Specs = [],
|
|
main_after_setup(ActualGlobals, DetectedGradeFlags, Variables,
|
|
OptionArgs, NonOptionArgs, !IO)
|
|
)
|
|
;
|
|
MaybeMCFlags = no,
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Enable the compile-time trace flag "debug-detect-libgrades" to enable
|
|
% debugging messages for library grade detection in the very verbose output.
|
|
|
|
:- pred detect_libgrades(globals::in, maybe(list(string))::in,
|
|
list(string)::out, io::di, io::uo) is det.
|
|
|
|
detect_libgrades(Globals, MaybeConfigMerStdLibDir, GradeOpts, !IO) :-
|
|
globals.lookup_bool_option(Globals, detect_libgrades, Detect),
|
|
(
|
|
Detect = yes,
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
trace [io(!TIO), compile_time(flag("debug-detect-libgrades"))] (
|
|
maybe_write_string(Verbose, "% Detecting library grades ...\n",
|
|
!TIO)
|
|
),
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
% NOTE: a standard library directory specified on the command line
|
|
% overrides one set using the MERCURY_STDLIB_DIR variable.
|
|
( if
|
|
% Was the standard library directory set on the command line?
|
|
globals.lookup_maybe_string_option(Globals,
|
|
mercury_standard_library_directory, MaybeStdLibDir),
|
|
MaybeStdLibDir = yes(MerStdLibDir)
|
|
then
|
|
do_detect_libgrades(VeryVerbose, MerStdLibDir, GradeOpts, !IO)
|
|
else if
|
|
% Was the standard library directory set using the
|
|
% MERCURY_STDLIB_DIR variable?
|
|
MaybeConfigMerStdLibDir = yes([MerStdLibDir])
|
|
then
|
|
do_detect_libgrades(VeryVerbose, MerStdLibDir, GradeOpts, !IO)
|
|
else
|
|
GradeOpts = []
|
|
),
|
|
trace [io(!TIO), compile_time(flag("debug-detect-libgrades"))] (
|
|
maybe_write_string(Verbose, "% done.\n", !TIO)
|
|
)
|
|
;
|
|
Detect = no,
|
|
GradeOpts = []
|
|
).
|
|
|
|
:- pred do_detect_libgrades(bool::in, string::in, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
do_detect_libgrades(VeryVerbose, StdLibDir, GradeOpts, !IO) :-
|
|
ModulesDir = StdLibDir / "modules",
|
|
dir.foldl2(do_detect_libgrade_using_init_file(VeryVerbose), ModulesDir,
|
|
[], MaybeGradeOpts0, !IO),
|
|
(
|
|
MaybeGradeOpts0 = ok(GradeOpts0),
|
|
LibsDir = StdLibDir / "lib",
|
|
dir.foldl2(do_detect_libgrade_using_lib_file(VeryVerbose), LibsDir,
|
|
GradeOpts0, MaybeGradeOpts, !IO),
|
|
(
|
|
MaybeGradeOpts = ok(GradeOpts)
|
|
;
|
|
MaybeGradeOpts = error(_, _),
|
|
GradeOpts = []
|
|
)
|
|
;
|
|
MaybeGradeOpts0 = error(_, _),
|
|
GradeOpts = []
|
|
).
|
|
|
|
% Test for the presence of an installed grade by looking for mer_std.init.
|
|
% This works for all grades except the C# and Java grades.
|
|
%
|
|
:- pred do_detect_libgrade_using_init_file(bool::in, string::in, string::in,
|
|
io.file_type::in, bool::out, list(string)::in, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
do_detect_libgrade_using_init_file(VeryVerbose, DirName, GradeFileName,
|
|
GradeFileType, Continue, !GradeOpts, !IO) :-
|
|
(
|
|
GradeFileType = directory,
|
|
InitFile = DirName / GradeFileName / "mer_std.init",
|
|
io.check_file_accessibility(InitFile, [read], Result, !IO),
|
|
(
|
|
Result = ok,
|
|
maybe_report_detected_libgrade(VeryVerbose, GradeFileName, !IO),
|
|
!:GradeOpts = ["--libgrade", GradeFileName | !.GradeOpts]
|
|
;
|
|
Result = error(_)
|
|
),
|
|
Continue = yes
|
|
;
|
|
( GradeFileType = regular_file
|
|
; GradeFileType = symbolic_link
|
|
; GradeFileType = named_pipe
|
|
; GradeFileType = socket
|
|
; GradeFileType = character_device
|
|
; GradeFileType = block_device
|
|
; GradeFileType = message_queue
|
|
; GradeFileType = semaphore
|
|
; GradeFileType = shared_memory
|
|
; GradeFileType = unknown
|
|
),
|
|
Continue = yes
|
|
).
|
|
|
|
% Test for the presence of installed Java and C# grades by looking for
|
|
% the standard library JAR or assembly respectively.
|
|
%
|
|
:- pred do_detect_libgrade_using_lib_file(bool::in, string::in, string::in,
|
|
io.file_type::in, bool::out, list(string)::in, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
do_detect_libgrade_using_lib_file(VeryVerbose, DirName, GradeFileName,
|
|
GradeFileType, Continue, !GradeOpts, !IO) :-
|
|
(
|
|
GradeFileType = directory,
|
|
( if
|
|
csharp_or_java_libgrade_target(GradeFileName, LibFile)
|
|
then
|
|
TargetFile = DirName / GradeFileName / LibFile,
|
|
io.check_file_accessibility(TargetFile, [read], Result, !IO),
|
|
(
|
|
Result = ok,
|
|
maybe_report_detected_libgrade(VeryVerbose, GradeFileName,
|
|
!IO),
|
|
!:GradeOpts = ["--libgrade", GradeFileName | !.GradeOpts]
|
|
;
|
|
Result = error(_)
|
|
)
|
|
else
|
|
true
|
|
),
|
|
Continue = yes
|
|
;
|
|
( GradeFileType = regular_file
|
|
; GradeFileType = symbolic_link
|
|
; GradeFileType = named_pipe
|
|
; GradeFileType = socket
|
|
; GradeFileType = character_device
|
|
; GradeFileType = block_device
|
|
; GradeFileType = message_queue
|
|
; GradeFileType = semaphore
|
|
; GradeFileType = shared_memory
|
|
; GradeFileType = unknown
|
|
),
|
|
Continue = yes
|
|
).
|
|
|
|
:- pred csharp_or_java_libgrade_target(string::in, string::out) is semidet.
|
|
|
|
csharp_or_java_libgrade_target(GradeFileName, LibFile) :-
|
|
( if string.prefix(GradeFileName, "csharp") then
|
|
LibFile = "mer_std.dll"
|
|
else if string.prefix(GradeFileName, "java") then
|
|
LibFile = "mer_std.jar"
|
|
else
|
|
false
|
|
).
|
|
|
|
:- pred maybe_report_detected_libgrade(bool::in, string::in,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_report_detected_libgrade(VeryVerbose, GradeStr, !IO) :-
|
|
trace [io(!TIO), compile_time(flag("debug-detect-libgrades"))] (
|
|
(
|
|
VeryVerbose = yes,
|
|
io.format("%% Detected library grade: %s\n", [s(GradeStr)], !TIO)
|
|
;
|
|
VeryVerbose = no
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
main_for_make(Globals, Args, !IO) :-
|
|
main_after_setup(Globals, [], options_variables_init, [], Args, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred main_after_setup(globals::in, list(string)::in, options_variables::in,
|
|
list(string)::in, list(string)::in, io::di, io::uo) is det.
|
|
|
|
main_after_setup(Globals, DetectedGradeFlags, OptionVariables, OptionArgs,
|
|
Args, !IO) :-
|
|
globals.lookup_bool_option(Globals, version, Version),
|
|
globals.lookup_bool_option(Globals, help, Help),
|
|
|
|
% NOTE: --help takes precedence over any other modes of operation as we do
|
|
% not wish to place unnecessary obstacles before users who want help.
|
|
% --version takes precedence over the remaining modes of operation since
|
|
% this behaviour is common in other compilers and command line tools and
|
|
% will be in line with the expectations of at least some users.
|
|
%
|
|
( if Help = yes then
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.set_output_stream(Stdout, OldOutputStream, !IO),
|
|
long_usage(!IO),
|
|
io.set_output_stream(OldOutputStream, _, !IO)
|
|
else if Version = yes then
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.set_output_stream(Stdout, OldOutputStream, !IO),
|
|
display_compiler_version(!IO),
|
|
io.set_output_stream(OldOutputStream, _, !IO)
|
|
else
|
|
globals.get_op_mode(Globals, OpMode),
|
|
do_op_mode(Globals, OpMode, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Args, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred do_op_mode(globals::in, op_mode::in,
|
|
list(string)::in, options_variables::in,
|
|
list(string)::in, list(string)::in, io::di, io::uo) is det.
|
|
|
|
do_op_mode(Globals, OpMode, DetectedGradeFlags, OptionVariables,
|
|
OptionArgs, Args, !IO) :-
|
|
(
|
|
OpMode = opm_top_make,
|
|
make_process_compiler_args(Globals, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Args, !IO)
|
|
;
|
|
OpMode = opm_top_generate_source_file_mapping,
|
|
source_file_map.write_source_file_map(Globals, Args, !IO)
|
|
;
|
|
OpMode = opm_top_generate_standalone_interface(StandaloneIntBasename),
|
|
do_op_mode_standalone_interface(Globals, StandaloneIntBasename, !IO)
|
|
;
|
|
OpMode = opm_top_query(OpModeQuery),
|
|
do_op_mode_query(Globals, OpModeQuery, !IO)
|
|
;
|
|
OpMode = opm_top_args(OpModeArgs),
|
|
do_op_mode_args(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Args, !IO)
|
|
).
|
|
|
|
:- pred do_op_mode_standalone_interface(globals::in, string::in,
|
|
io::di, io::uo) is det.
|
|
|
|
do_op_mode_standalone_interface(Globals, StandaloneIntBasename, !IO) :-
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
( Target = target_csharp
|
|
; Target = target_java
|
|
),
|
|
NotRequiredMsg = [words("Error:"),
|
|
quote("--generate-standalone-interface"),
|
|
words("is not required for target language"),
|
|
words(compilation_target_string(Target)), suffix("."), nl],
|
|
write_error_pieces_plain(Globals, NotRequiredMsg, !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
Target = target_erlang,
|
|
NYIMsg = [words("Sorry,"),
|
|
quote("--generate-standalone-interface"),
|
|
words("is not yet supported with target language"),
|
|
words(compilation_target_string(Target)), suffix("."), nl],
|
|
write_error_pieces_plain(Globals, NYIMsg, !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
Target = target_c,
|
|
make_standalone_interface(Globals, StandaloneIntBasename, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred do_op_mode_query(globals::in, op_mode_query::in,
|
|
io::di, io::uo) is det.
|
|
|
|
do_op_mode_query(Globals, OpModeQuery, !IO) :-
|
|
(
|
|
OpModeQuery = opmq_output_cc,
|
|
globals.lookup_string_option(Globals, cc, CC),
|
|
io.stdout_stream(StdOut, !IO),
|
|
io.write_string(StdOut, CC ++ "\n", !IO)
|
|
;
|
|
OpModeQuery = opmq_output_c_compiler_type,
|
|
globals.lookup_string_option(Globals, c_compiler_type, CC_Type),
|
|
io.stdout_stream(StdOut, !IO),
|
|
io.write_string(StdOut, CC_Type ++ "\n", !IO)
|
|
;
|
|
OpModeQuery = opmq_output_cflags,
|
|
io.stdout_stream(StdOut, !IO),
|
|
output_c_compiler_flags(Globals, StdOut, !IO),
|
|
io.nl(StdOut, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_c_include_directory_flags,
|
|
io.stdout_stream(StdOut, !IO),
|
|
output_c_include_directory_flags(Globals, StdOut, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_csharp_compiler,
|
|
globals.lookup_string_option(Globals, csharp_compiler, CSC),
|
|
io.stdout_stream(StdOut, !IO),
|
|
io.write_string(StdOut, CSC ++ "\n", !IO)
|
|
;
|
|
OpModeQuery = opmq_output_csharp_compiler_type,
|
|
globals.lookup_string_option(Globals, csharp_compiler_type, CSC_Type),
|
|
io.stdout_stream(StdOut, !IO),
|
|
io.write_string(StdOut, CSC_Type ++ "\n", !IO)
|
|
;
|
|
OpModeQuery = opmq_output_grade_defines,
|
|
io.stdout_stream(StdOut, !IO),
|
|
output_grade_defines(Globals, StdOut, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_link_command,
|
|
globals.lookup_string_option(Globals, link_executable_command,
|
|
LinkCommand),
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_string(Stdout, LinkCommand, !IO),
|
|
io.nl(Stdout, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_shared_lib_link_command,
|
|
globals.lookup_string_option(Globals, link_shared_lib_command,
|
|
LinkCommand),
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_string(Stdout, LinkCommand, !IO),
|
|
io.nl(Stdout, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_library_link_flags,
|
|
io.stdout_stream(StdOut, !IO),
|
|
output_library_link_flags(Globals, StdOut, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_class_dir,
|
|
io.stdout_stream(StdOut, !IO),
|
|
get_class_dir_name(Globals, ClassName),
|
|
io.write_string(StdOut, ClassName ++ "\n", !IO)
|
|
;
|
|
OpModeQuery = opmq_output_grade_string,
|
|
% When Mmake asks for the grade, it really wants the directory
|
|
% component to use. This is consistent with scripts/canonical_grade.
|
|
grade_directory_component(Globals, Grade),
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_string(Stdout, Grade, !IO),
|
|
io.nl(Stdout, !IO)
|
|
;
|
|
OpModeQuery = opmq_output_libgrades,
|
|
globals.lookup_accumulating_option(Globals, libgrades, LibGrades),
|
|
(
|
|
LibGrades = []
|
|
;
|
|
LibGrades = [_ | _],
|
|
io.stdout_stream(Stdout, !IO),
|
|
io.write_list(Stdout, LibGrades, "\n", io.write_string, !IO),
|
|
io.nl(Stdout, !IO)
|
|
)
|
|
;
|
|
OpModeQuery = opmq_output_target_arch,
|
|
io.stdout_stream(StdOut, !IO),
|
|
globals.lookup_string_option(Globals, target_arch, TargetArch),
|
|
io.write_string(StdOut, TargetArch ++ "\n", !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Do the modes of operation that process the argument list.
|
|
%
|
|
|
|
:- pred do_op_mode_args(globals::in, op_mode_args::in,
|
|
list(string)::in, options_variables::in,
|
|
list(string)::in, list(string)::in, io::di, io::uo) is det.
|
|
|
|
do_op_mode_args(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Args, !IO) :-
|
|
globals.lookup_bool_option(Globals, filenames_from_stdin,
|
|
FileNamesFromStdin),
|
|
( if
|
|
Args = [],
|
|
FileNamesFromStdin = no
|
|
then
|
|
usage(!IO)
|
|
else
|
|
(
|
|
FileNamesFromStdin = yes,
|
|
process_compiler_stdin_args(Globals, OpModeArgs,
|
|
DetectedGradeFlags, OptionVariables, OptionArgs,
|
|
cord.empty, ModulesToLinkCord,
|
|
cord.empty, ExtraObjFilesCord, !IO)
|
|
;
|
|
FileNamesFromStdin = no,
|
|
process_compiler_cmd_line_args(Globals, OpModeArgs,
|
|
DetectedGradeFlags, OptionVariables, OptionArgs, Args,
|
|
cord.empty, ModulesToLinkCord,
|
|
cord.empty, ExtraObjFilesCord, !IO)
|
|
),
|
|
ModulesToLink = cord.list(ModulesToLinkCord),
|
|
ExtraObjFiles = cord.list(ExtraObjFilesCord),
|
|
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
( if ExitStatus = 0 then
|
|
( if
|
|
OpModeArgs = opma_augment(opmau_generate_code(
|
|
opmcg_target_object_and_executable)),
|
|
ModulesToLink = [FirstModule | _]
|
|
then
|
|
file_name_to_module_name(FirstModule, MainModuleName),
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_java,
|
|
% For Java, at the "link" step we just generate a shell
|
|
% script; the actual linking will be done at runtime by
|
|
% the Java interpreter.
|
|
create_java_shell_script(Globals, MainModuleName,
|
|
Succeeded, !IO)
|
|
;
|
|
( Target = target_c
|
|
; Target = target_csharp
|
|
; Target = target_erlang
|
|
),
|
|
compile_with_module_options(Globals, MainModuleName,
|
|
DetectedGradeFlags, OptionVariables, OptionArgs,
|
|
link_module_list(ModulesToLink, ExtraObjFiles),
|
|
Succeeded, !IO)
|
|
),
|
|
maybe_set_exit_status(Succeeded, !IO)
|
|
else
|
|
true
|
|
)
|
|
else
|
|
% If we suppressed the printing of some errors, then tell the user
|
|
% about this fact, because the absence of any errors being printed
|
|
% during a failing compilation would otherwise be likely to be
|
|
% baffling.
|
|
globals.io_get_some_errors_were_context_limited(Limited, !IO),
|
|
(
|
|
Limited = no
|
|
;
|
|
Limited = yes,
|
|
io.write_string("Some error messages were suppressed " ++
|
|
"by `--limit-error-contexts' options.\n", !IO),
|
|
io.write_string("You can see the suppressed messages " ++
|
|
"if you recompile without these options.\n", !IO)
|
|
),
|
|
|
|
% If we found some errors, but the user didn't enable the `-E'
|
|
% (`--verbose-errors') option, give them a hint about it.
|
|
% Of course, we should only output the hint when we have further
|
|
% information to give the user.
|
|
globals.lookup_bool_option(Globals, verbose_errors, VerboseErrors),
|
|
globals.io_get_extra_error_info(ExtraErrorInfo, !IO),
|
|
(
|
|
VerboseErrors = no,
|
|
(
|
|
ExtraErrorInfo = yes,
|
|
io.write_string("For more information, " ++
|
|
"recompile with `-E'.\n", !IO)
|
|
;
|
|
ExtraErrorInfo = no
|
|
)
|
|
;
|
|
VerboseErrors = yes
|
|
)
|
|
),
|
|
globals.lookup_bool_option(Globals, statistics, Statistics),
|
|
(
|
|
Statistics = yes,
|
|
io.report_stats("full_memory_stats", !IO)
|
|
;
|
|
Statistics = no
|
|
)
|
|
).
|
|
|
|
:- type compile == pred(globals, bool, io, io).
|
|
:- inst compile == (pred(in, out, di, uo) is det).
|
|
|
|
:- pred compile_with_module_options(globals::in, module_name::in,
|
|
list(string)::in, options_variables::in, list(string)::in,
|
|
compile::in(compile), bool::out, io::di, io::uo) is det.
|
|
|
|
compile_with_module_options(Globals, ModuleName, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Compile, Succeeded, !IO) :-
|
|
globals.lookup_bool_option(Globals, invoked_by_mmc_make, InvokedByMake),
|
|
(
|
|
InvokedByMake = yes,
|
|
% `mmc --make' has already set up the options.
|
|
Compile(Globals, Succeeded, !IO)
|
|
;
|
|
InvokedByMake = no,
|
|
Builder =
|
|
(pred(BuildGlobals::in, _::in, Succeeded0::out, X::in, X::out,
|
|
IO0::di, IO::uo) is det :-
|
|
Compile(BuildGlobals, Succeeded0, IO0, IO)
|
|
),
|
|
build_with_module_options_args(Globals, ModuleName, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, [], Builder, Succeeded, unit, _, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred process_compiler_stdin_args(globals::in, op_mode_args::in,
|
|
list(string)::in, options_variables::in, list(string)::in,
|
|
cord(string)::in, cord(string)::out,
|
|
cord(string)::in, cord(string)::out, io::di, io::uo) is det.
|
|
|
|
process_compiler_stdin_args(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, !Modules, !ExtraObjFiles, !IO) :-
|
|
( if is_empty(!.Modules) then
|
|
true
|
|
else
|
|
garbage_collect(!IO)
|
|
),
|
|
io.read_line_as_string(FileResult, !IO),
|
|
(
|
|
FileResult = ok(Line),
|
|
Arg = string.rstrip(Line),
|
|
process_compiler_arg(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Arg, ArgModules, ArgExtraObjFiles,
|
|
!IO),
|
|
!:Modules = !.Modules ++ cord.from_list(ArgModules),
|
|
!:ExtraObjFiles = !.ExtraObjFiles ++ cord.from_list(ArgExtraObjFiles),
|
|
process_compiler_stdin_args(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, !Modules, !ExtraObjFiles, !IO)
|
|
;
|
|
FileResult = eof
|
|
;
|
|
FileResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.write_string("Error reading module name: ", !IO),
|
|
io.write_string(Msg, !IO),
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
:- pred process_compiler_cmd_line_args(globals::in, op_mode_args::in,
|
|
list(string)::in, options_variables::in,
|
|
list(string)::in, list(string)::in,
|
|
cord(string)::in, cord(string)::out,
|
|
cord(string)::in, cord(string)::out, io::di, io::uo) is det.
|
|
|
|
process_compiler_cmd_line_args(_, _, _, _, _, [],
|
|
!Modules, !ExtraObjFiles, !IO).
|
|
process_compiler_cmd_line_args(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, [Arg | Args],
|
|
!Modules, !ExtraObjFiles, !IO) :-
|
|
process_compiler_arg(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Arg, ArgModules, ArgExtraObjFiles, !IO),
|
|
(
|
|
Args = [_ | _],
|
|
garbage_collect(!IO)
|
|
;
|
|
Args = []
|
|
),
|
|
!:Modules = !.Modules ++ cord.from_list(ArgModules),
|
|
!:ExtraObjFiles = !.ExtraObjFiles ++ cord.from_list(ArgExtraObjFiles),
|
|
process_compiler_cmd_line_args(Globals, OpModeArgs, DetectedGradeFlags,
|
|
OptionVariables, OptionArgs, Args, !Modules, !ExtraObjFiles, !IO).
|
|
|
|
% Figure out whether the compiler argument is a module name or a file name.
|
|
% Open the specified file or module, and process it.
|
|
% Return the list of modules (including submodules,
|
|
% if they were compiled to separate object files)
|
|
% that should be linked into the final executable.
|
|
%
|
|
% The actual work is done by do_process_compiler_arg.
|
|
% XXX The job of this predicate is apparently to ensure that
|
|
% we go through the machinery of mmc --make, represented here by
|
|
% build_with_module_options_args, even if we were NOT invoked with --make.
|
|
% This seems strange to me. -zs
|
|
%
|
|
:- pred process_compiler_arg(globals::in, op_mode_args::in,
|
|
list(string)::in, options_variables::in,
|
|
list(string)::in, string::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_compiler_arg(Globals, OpModeArgs, DetectedGradeFlags, OptionVariables,
|
|
OptionArgs, Arg, ModulesToLink, ExtraObjFiles, !IO) :-
|
|
FileOrModule = string_to_file_or_module(Arg),
|
|
globals.lookup_bool_option(Globals, invoked_by_mmc_make, InvokedByMake),
|
|
(
|
|
InvokedByMake = no,
|
|
ExtraOptions = [],
|
|
DummyInput = unit,
|
|
build_with_module_options_args(Globals,
|
|
file_or_module_to_module_name(FileOrModule),
|
|
DetectedGradeFlags, OptionVariables, OptionArgs, ExtraOptions,
|
|
process_compiler_arg_build(OpModeArgs, FileOrModule, OptionArgs),
|
|
_Succeeded, DummyInput, MaybeTuple, !IO),
|
|
% XXX Why do we ignore _Succeeded? We know it is set to `yes'
|
|
% by process_compiler_arg_build, but this could be overridden by
|
|
% build_with_module_options_args if it finds *other* kinds of problems.
|
|
% However, enabling the following line did not yield any aborts.
|
|
% expect(unify(Succeeded, yes), $pred, "Succeeded != yes"),
|
|
(
|
|
MaybeTuple = yes(Tuple),
|
|
Tuple = {ModulesToLink, ExtraObjFiles}
|
|
;
|
|
MaybeTuple = no,
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
)
|
|
;
|
|
InvokedByMake = yes,
|
|
% `mmc --make' has already set up the options.
|
|
do_process_compiler_arg(Globals, OpModeArgs, OptionArgs, FileOrModule,
|
|
ModulesToLink, ExtraObjFiles, !IO)
|
|
).
|
|
|
|
:- pred process_compiler_arg_build(op_mode_args::in, file_or_module::in,
|
|
list(string)::in, globals::in, list(string)::in, bool::out,
|
|
unit::in, {list(string), list(string)}::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_compiler_arg_build(OpModeArgs, FileOrModule, OptionArgs, Globals, _,
|
|
Succeeded, _DummyInput, {Modules, ExtraObjFiles}, !IO) :-
|
|
do_process_compiler_arg(Globals, OpModeArgs, OptionArgs, FileOrModule,
|
|
Modules, ExtraObjFiles, !IO),
|
|
Succeeded = yes.
|
|
|
|
:- func version_numbers_return_timestamp(bool) = maybe_return_timestamp.
|
|
|
|
version_numbers_return_timestamp(no) = dont_return_timestamp.
|
|
version_numbers_return_timestamp(yes) = do_return_timestamp.
|
|
|
|
:- pred do_process_compiler_arg(globals::in, op_mode_args::in,
|
|
list(string)::in, file_or_module::in, list(string)::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
do_process_compiler_arg(Globals0, OpModeArgs, OptionArgs, FileOrModule,
|
|
ModulesToLink, ExtraObjFiles, !IO) :-
|
|
(
|
|
OpModeArgs = opma_generate_dependencies,
|
|
(
|
|
FileOrModule = fm_file(FileName),
|
|
generate_dep_file_for_file(Globals0, FileName, !IO)
|
|
;
|
|
FileOrModule = fm_module(ModuleName),
|
|
generate_dep_file_for_module(Globals0, ModuleName, !IO)
|
|
),
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeArgs = opma_generate_dependency_file,
|
|
(
|
|
FileOrModule = fm_file(FileName),
|
|
generate_d_file_for_file(Globals0, FileName, !IO)
|
|
;
|
|
FileOrModule = fm_module(ModuleName),
|
|
generate_d_file_for_module(Globals0, ModuleName, !IO)
|
|
),
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeArgs = opma_convert_to_mercury,
|
|
HaveReadModuleMaps0 =
|
|
have_read_module_maps(map.init, map.init, map.init),
|
|
read_module_or_file(Globals0, Globals, FileOrModule, ModuleName, _,
|
|
dont_return_timestamp, _, ParseTreeSrc, Specs, Errors,
|
|
HaveReadModuleMaps0, _HaveReadModuleMaps, !IO),
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs, Globals, 0, _NumWarnings, 0, _NumErrors, !IO),
|
|
( if halt_at_module_error(Globals, Errors) then
|
|
true
|
|
else
|
|
module_name_to_file_name(Globals, do_create_dirs, ".ugly",
|
|
ModuleName, OutputFileName, !IO),
|
|
convert_to_mercury_src(Globals, OutputFileName, ParseTreeSrc, !IO)
|
|
),
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
;
|
|
(
|
|
OpModeArgs = opma_make_private_interface,
|
|
ProcessModule = call_make_private_interface(Globals0),
|
|
globals.lookup_bool_option(Globals0, generate_item_version_numbers,
|
|
GenerateVersionNumbers),
|
|
ReturnTimestamp =
|
|
version_numbers_return_timestamp(GenerateVersionNumbers)
|
|
;
|
|
OpModeArgs = opma_make_short_interface,
|
|
ProcessModule = call_make_short_interface(Globals0),
|
|
ReturnTimestamp = dont_return_timestamp
|
|
;
|
|
OpModeArgs = opma_make_interface,
|
|
ProcessModule = call_make_interface(Globals0),
|
|
globals.lookup_bool_option(Globals0, generate_item_version_numbers,
|
|
GenerateVersionNumbers),
|
|
ReturnTimestamp =
|
|
version_numbers_return_timestamp(GenerateVersionNumbers)
|
|
),
|
|
HaveReadModuleMaps0 =
|
|
have_read_module_maps(map.init, map.init, map.init),
|
|
read_module_or_file(Globals0, Globals, FileOrModule,
|
|
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
|
|
ParseTreeSrc, Specs0, Errors,
|
|
HaveReadModuleMaps0, _HaveReadModuleMaps, !IO),
|
|
( if halt_at_module_error(Globals, Errors) then
|
|
true
|
|
else
|
|
split_into_compilation_units_perform_checks(ParseTreeSrc,
|
|
RawCompUnits, Specs0, Specs),
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO),
|
|
list.foldl(
|
|
apply_process_module(ProcessModule, FileName, ModuleName,
|
|
MaybeTimestamp),
|
|
RawCompUnits, !IO)
|
|
),
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeArgs = opma_augment(OpModeAugment),
|
|
globals.lookup_bool_option(Globals0, smart_recompilation, Smart0),
|
|
io_get_disable_smart_recompilation(DisableSmart, !IO),
|
|
(
|
|
DisableSmart = yes,
|
|
globals.set_option(smart_recompilation, bool(no),
|
|
Globals0, Globals),
|
|
Smart = no
|
|
;
|
|
DisableSmart = no,
|
|
Globals = Globals0,
|
|
Smart = Smart0
|
|
),
|
|
(
|
|
Smart = yes,
|
|
(
|
|
FileOrModule = fm_module(ModuleName)
|
|
;
|
|
FileOrModule = fm_file(FileName),
|
|
% XXX This won't work if the module name doesn't match
|
|
% the file name -- such modules will always be recompiled.
|
|
%
|
|
% This problem will be fixed when mmake functionality
|
|
% is moved into the compiler. The file_name->module_name
|
|
% mapping will be explicitly recorded.
|
|
file_name_to_module_name(FileName, ModuleName)
|
|
),
|
|
find_smart_recompilation_target_files(Globals, FindTargetFiles),
|
|
find_timestamp_files(Globals, FindTimestampFiles),
|
|
recompilation.check.should_recompile(Globals, ModuleName,
|
|
FindTargetFiles, FindTimestampFiles, ModulesToRecompile,
|
|
HaveReadModuleMaps, !IO)
|
|
;
|
|
Smart = no,
|
|
HaveReadModuleMaps =
|
|
have_read_module_maps(map.init, map.init, map.init),
|
|
ModulesToRecompile = all_modules
|
|
),
|
|
( if ModulesToRecompile = some_modules([]) then
|
|
% XXX Currently smart recompilation is disabled if mmc is linking
|
|
% the executable because it doesn't know how to check whether
|
|
% all the necessary intermediate files are present and up-to-date.
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
else
|
|
read_augment_and_process_module(Globals, OpModeAugment, OptionArgs,
|
|
FileOrModule, ModulesToRecompile, HaveReadModuleMaps,
|
|
ModulesToLink, ExtraObjFiles, !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Return a closure which will work out what the target files are for
|
|
% a module, so recompilation_check.m can check that they are up-to-date
|
|
% when deciding whether compilation is necessary.
|
|
% Note that `--smart-recompilation' only works with
|
|
% `--target-code-only', which is always set when the compiler is
|
|
% invoked by mmake. Using smart recompilation without using mmake
|
|
% is not a sensible thing to do. handle_options.m will disable smart
|
|
% recompilation if `--target-code-only' is not set.
|
|
%
|
|
:- pred find_smart_recompilation_target_files(globals::in,
|
|
find_target_file_names::out(find_target_file_names)) is det.
|
|
|
|
find_smart_recompilation_target_files(Globals, FindTargetFiles) :-
|
|
globals.get_target(Globals, CompilationTarget),
|
|
( CompilationTarget = target_c, TargetSuffix = ".c"
|
|
; CompilationTarget = target_csharp, TargetSuffix = ".cs"
|
|
; CompilationTarget = target_java, TargetSuffix = ".java"
|
|
; CompilationTarget = target_erlang, TargetSuffix = ".erl"
|
|
),
|
|
FindTargetFiles = usual_find_target_files(Globals, TargetSuffix).
|
|
|
|
:- pred usual_find_target_files(globals::in,
|
|
string::in, module_name::in, list(file_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
usual_find_target_files(Globals, TargetSuffix, ModuleName, TargetFiles, !IO) :-
|
|
% XXX Should we check the generated header files?
|
|
module_name_to_file_name(Globals, do_create_dirs, TargetSuffix,
|
|
ModuleName, FileName, !IO),
|
|
TargetFiles = [FileName].
|
|
|
|
:- pred find_timestamp_files(globals::in,
|
|
find_timestamp_file_names::out(find_timestamp_file_names)) is det.
|
|
|
|
find_timestamp_files(Globals, FindTimestampFiles) :-
|
|
globals.get_target(Globals, CompilationTarget),
|
|
(
|
|
CompilationTarget = target_c,
|
|
TimestampSuffix = ".c_date"
|
|
;
|
|
CompilationTarget = target_csharp,
|
|
TimestampSuffix = ".cs_date"
|
|
;
|
|
CompilationTarget = target_java,
|
|
TimestampSuffix = ".java_date"
|
|
;
|
|
CompilationTarget = target_erlang,
|
|
TimestampSuffix = ".erl_date"
|
|
),
|
|
FindTimestampFiles = find_timestamp_files_2(Globals, TimestampSuffix).
|
|
|
|
:- pred find_timestamp_files_2(globals::in, string::in, module_name::in,
|
|
list(file_name)::out, io::di, io::uo) is det.
|
|
|
|
find_timestamp_files_2(Globals, TimestampSuffix, ModuleName, TimestampFiles,
|
|
!IO) :-
|
|
module_name_to_file_name(Globals, do_create_dirs, TimestampSuffix,
|
|
ModuleName, FileName, !IO),
|
|
TimestampFiles = [FileName].
|
|
|
|
%---------------------%
|
|
|
|
:- pred call_make_interface(globals::in, file_name::in, module_name::in,
|
|
maybe(timestamp)::in, raw_compilation_unit::in, io::di, io::uo) is det.
|
|
|
|
call_make_interface(Globals, SourceFileName, SourceFileModuleName,
|
|
MaybeTimestamp, RawCompUnit, !IO) :-
|
|
write_interface_file(Globals, SourceFileName, SourceFileModuleName,
|
|
RawCompUnit, MaybeTimestamp, !IO).
|
|
|
|
:- pred call_make_short_interface(globals::in, file_name::in, module_name::in,
|
|
maybe(timestamp)::in, raw_compilation_unit::in, io::di, io::uo) is det.
|
|
|
|
call_make_short_interface(Globals, SourceFileName, _, _, RawCompUnit, !IO) :-
|
|
write_short_interface_file(Globals, SourceFileName, RawCompUnit, !IO).
|
|
|
|
:- pred call_make_private_interface(globals::in, file_name::in,
|
|
module_name::in, maybe(timestamp)::in, raw_compilation_unit::in,
|
|
io::di, io::uo) is det.
|
|
|
|
call_make_private_interface(Globals, SourceFileName, SourceFileModuleName,
|
|
MaybeTimestamp, RawCompUnit, !IO) :-
|
|
write_private_interface_file(Globals, SourceFileName, SourceFileModuleName,
|
|
RawCompUnit, MaybeTimestamp, !IO).
|
|
|
|
:- pred apply_process_module(
|
|
pred(file_name, module_name, maybe(timestamp), raw_compilation_unit,
|
|
io, io)::in(pred(in, in, in, in, di, uo) is det),
|
|
file_name::in, module_name::in, maybe(timestamp)::in,
|
|
raw_compilation_unit::in, io::di, io::uo) is det.
|
|
|
|
apply_process_module(ProcessModule, FileName, ModuleName, MaybeTimestamp,
|
|
RawCompUnit, !IO) :-
|
|
ProcessModule(FileName, ModuleName, MaybeTimestamp, RawCompUnit, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred read_augment_and_process_module(globals::in,
|
|
op_mode_augment::in, list(string)::in, file_or_module::in,
|
|
modules_to_recompile::in, have_read_module_maps::in,
|
|
list(string)::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
read_augment_and_process_module(Globals0, OpModeAugment, OptionArgs,
|
|
FileOrModule, MaybeModulesToRecompile, HaveReadModuleMap0,
|
|
ModulesToLink, ExtraObjFiles, !IO) :-
|
|
(
|
|
( OpModeAugment = opmau_make_opt_int
|
|
; OpModeAugment = opmau_make_trans_opt_int
|
|
; OpModeAugment = opmau_make_analysis_registry
|
|
; OpModeAugment = opmau_make_xml_documentation
|
|
)
|
|
;
|
|
( OpModeAugment = opmau_errorcheck_only
|
|
; OpModeAugment = opmau_typecheck_only
|
|
; OpModeAugment = opmau_generate_code(_)
|
|
),
|
|
globals.lookup_bool_option(Globals0, report_cmd_line_args_in_doterr,
|
|
ReportCmdLineArgsDotErr),
|
|
maybe_report_cmd_line(ReportCmdLineArgsDotErr, OptionArgs, [], !IO)
|
|
),
|
|
|
|
read_module_or_file(Globals0, Globals, FileOrModule, ModuleName, FileName,
|
|
do_return_timestamp, MaybeTimestamp, ParseTreeSrc, Specs0, Errors,
|
|
HaveReadModuleMap0, HaveReadModuleMaps, !IO),
|
|
|
|
( if halt_at_module_error(Globals, Errors) then
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs0, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO),
|
|
ModulesToLink = [],
|
|
ExtraObjFiles = []
|
|
else
|
|
split_into_compilation_units_perform_checks(ParseTreeSrc,
|
|
RawCompUnits0, Specs0, Specs1),
|
|
(
|
|
MaybeModulesToRecompile = some_modules(ModulesToRecompile),
|
|
ToRecompile = (pred(RawCompUnit::in) is semidet :-
|
|
RawCompUnit =
|
|
raw_compilation_unit(RawCompUnitModuleName, _, _),
|
|
list.member(RawCompUnitModuleName, ModulesToRecompile)
|
|
),
|
|
list.filter(ToRecompile, RawCompUnits0, RawCompUnitsToCompile)
|
|
;
|
|
MaybeModulesToRecompile = all_modules,
|
|
RawCompUnitsToCompile = RawCompUnits0
|
|
),
|
|
RawCompUnitNames = set.list_to_set(
|
|
list.map(raw_compilation_unit_project_name, RawCompUnits0)),
|
|
set.delete(ModuleName, RawCompUnitNames, NestedCompUnitNames),
|
|
|
|
find_timestamp_files(Globals, FindTimestampFiles),
|
|
|
|
globals.lookup_bool_option(Globals, trace_prof, TraceProf),
|
|
|
|
( if
|
|
non_traced_mercury_builtin_module(ModuleName),
|
|
not (
|
|
ModuleName = mercury_profiling_builtin_module,
|
|
TraceProf = yes
|
|
)
|
|
then
|
|
% Some predicates in the builtin modules are missing typeinfo
|
|
% arguments, which means that execution tracing will not work
|
|
% on them. Predicates defined there should never be part of
|
|
% an execution trace anyway; they are effectively language
|
|
% primitives. (They may still be parts of stack traces.)
|
|
globals.set_option(trace_stack_layout, bool(no),
|
|
Globals, GlobalsNoTrace0),
|
|
globals.set_trace_level_none(
|
|
GlobalsNoTrace0, GlobalsNoTrace),
|
|
GlobalsToUse = GlobalsNoTrace
|
|
else
|
|
GlobalsToUse = Globals
|
|
),
|
|
augment_and_process_all_submodules(GlobalsToUse, OpModeAugment,
|
|
FileName, ModuleName, MaybeTimestamp, NestedCompUnitNames,
|
|
HaveReadModuleMaps, FindTimestampFiles, RawCompUnitsToCompile,
|
|
Specs1, ModulesToLink, ExtraObjFiles, !IO)
|
|
).
|
|
|
|
:- pred maybe_report_cmd_line(bool::in, list(string)::in, list(string)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_report_cmd_line(Report, OptionArgs, Args, !IO) :-
|
|
(
|
|
Report = no
|
|
;
|
|
Report = yes,
|
|
io.format("%% Command line options start\n", [], !IO),
|
|
io.format("%% %s\n", [s(string.join_list("\n% ", OptionArgs ++ Args))],
|
|
!IO),
|
|
io.format("%% Command line options end\n", [], !IO)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type file_or_module
|
|
---> fm_file(file_name)
|
|
; fm_module(module_name).
|
|
|
|
:- func string_to_file_or_module(string) = file_or_module.
|
|
|
|
string_to_file_or_module(String) = FileOrModule :-
|
|
( if string.remove_suffix(String, ".m", FileName) then
|
|
% If the argument name ends in `.m', then we assume it is a file name.
|
|
FileOrModule = fm_file(FileName)
|
|
else
|
|
% If it doesn't end in `.m', then we assume it is a module name.
|
|
% (Is it worth checking that the name doesn't contain directory
|
|
% separators, and issuing a warning or error in that case?)
|
|
file_name_to_module_name(String, ModuleName),
|
|
FileOrModule = fm_module(ModuleName)
|
|
).
|
|
|
|
:- func file_or_module_to_module_name(file_or_module) = module_name.
|
|
|
|
file_or_module_to_module_name(fm_file(FileName)) = ModuleName :-
|
|
% Assume the module name matches the file name.
|
|
file_name_to_module_name(FileName, ModuleName).
|
|
file_or_module_to_module_name(fm_module(ModuleName)) = ModuleName.
|
|
|
|
:- pred read_module_or_file(globals::in, globals::out, file_or_module::in,
|
|
module_name::out, file_name::out,
|
|
maybe_return_timestamp::in, maybe(timestamp)::out,
|
|
parse_tree_src::out, list(error_spec)::out, read_module_errors::out,
|
|
have_read_module_maps::in, have_read_module_maps::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module_or_file(Globals0, Globals, FileOrModuleName,
|
|
ModuleName, SourceFileName, ReturnTimestamp, MaybeTimestamp,
|
|
ParseTreeSrc, Specs, Errors, !HaveReadModuleMaps, !IO) :-
|
|
(
|
|
FileOrModuleName = fm_module(ModuleName),
|
|
globals.lookup_bool_option(Globals0, verbose, Verbose),
|
|
maybe_write_string(Verbose, "% Parsing module `", !IO),
|
|
ModuleNameString = sym_name_to_string(ModuleName),
|
|
maybe_write_string(Verbose, ModuleNameString, !IO),
|
|
maybe_write_string(Verbose, "' and imported interfaces...\n", !IO),
|
|
( if
|
|
% Avoid rereading the module if it was already read
|
|
% by recompilation_version.m.
|
|
find_read_module_src(!.HaveReadModuleMaps ^ hrmm_src, ModuleName,
|
|
ReturnTimestamp, SourceFileNamePrime, MaybeTimestampPrime,
|
|
ParseTreeSrcPrime, SpecsPrime, ErrorsPrime)
|
|
then
|
|
Globals = Globals0,
|
|
% XXX When we have read the module before, it *could* have had
|
|
% problems that should cause smart recompilation to be disabled.
|
|
HaveReadModuleMapSrc0 = !.HaveReadModuleMaps ^ hrmm_src,
|
|
map.delete(have_read_module_key(ModuleName, sfk_src),
|
|
HaveReadModuleMapSrc0, HaveReadModuleMapSrc),
|
|
!HaveReadModuleMaps ^ hrmm_src := HaveReadModuleMapSrc,
|
|
SourceFileName = SourceFileNamePrime,
|
|
MaybeTimestamp = MaybeTimestampPrime,
|
|
ParseTreeSrc = ParseTreeSrcPrime,
|
|
Specs = SpecsPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
% We don't search `--search-directories' for source files
|
|
% because that can result in the generated interface files
|
|
% being created in the wrong directory.
|
|
read_module_src(Globals0, "Reading module",
|
|
do_not_ignore_errors, do_not_search,
|
|
ModuleName, [], SourceFileName,
|
|
always_read_module(ReturnTimestamp), MaybeTimestamp,
|
|
ParseTreeSrc, Specs, Errors, !IO),
|
|
io_get_disable_smart_recompilation(DisableSmart, !IO),
|
|
(
|
|
DisableSmart = yes,
|
|
globals.set_option(smart_recompilation, bool(no),
|
|
Globals0, Globals)
|
|
;
|
|
DisableSmart = no,
|
|
Globals = Globals0
|
|
)
|
|
),
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
FileOrModuleName = fm_file(FileName),
|
|
globals.lookup_bool_option(Globals0, verbose, Verbose),
|
|
maybe_write_string(Verbose, "% Parsing file `", !IO),
|
|
maybe_write_string(Verbose, FileName, !IO),
|
|
maybe_write_string(Verbose, "' and imported interfaces...\n", !IO),
|
|
|
|
file_name_to_module_name(FileName, DefaultModuleName),
|
|
( if
|
|
% Avoid rereading the module if it was already read
|
|
% by recompilation_version.m.
|
|
find_read_module_src(!.HaveReadModuleMaps ^ hrmm_src,
|
|
DefaultModuleName, ReturnTimestamp, _, MaybeTimestampPrime,
|
|
ParseTreeSrcPrime, SpecsPrime, ErrorsPrime)
|
|
then
|
|
Globals = Globals0,
|
|
% XXX When we have read the module before, it *could* have had
|
|
% problems that should cause smart recompilation to be disabled.
|
|
HaveReadModuleMapSrc0 = !.HaveReadModuleMaps ^ hrmm_src,
|
|
map.delete(have_read_module_key(ModuleName, sfk_src),
|
|
HaveReadModuleMapSrc0, HaveReadModuleMapSrc),
|
|
!HaveReadModuleMaps ^ hrmm_src := HaveReadModuleMapSrc,
|
|
ModuleName = DefaultModuleName,
|
|
MaybeTimestamp = MaybeTimestampPrime,
|
|
ParseTreeSrc = ParseTreeSrcPrime,
|
|
Specs = SpecsPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
% We don't search `--search-directories' for source files
|
|
% because that can result in the generated interface files
|
|
% being created in the wrong directory.
|
|
read_module_src_from_file(Globals0, FileName, "Reading file",
|
|
do_not_search,
|
|
always_read_module(ReturnTimestamp), MaybeTimestamp,
|
|
ParseTreeSrc, Specs, Errors, !IO),
|
|
io_get_disable_smart_recompilation(DisableSmart, !IO),
|
|
(
|
|
DisableSmart = yes,
|
|
globals.set_option(smart_recompilation, bool(no),
|
|
Globals0, Globals)
|
|
;
|
|
DisableSmart = no,
|
|
Globals = Globals0
|
|
),
|
|
|
|
% XXX If the module name doesn't match the file name, the compiler
|
|
% won't be able to find the `.used' file (the name of the `.used'
|
|
% file is derived from the module name not the file name).
|
|
% This will be fixed when mmake functionality is moved into
|
|
% the compiler.
|
|
|
|
globals.lookup_bool_option(Globals, smart_recompilation, Smart),
|
|
ParseTreeSrc = parse_tree_src(ModuleName, _, _),
|
|
( if
|
|
Smart = yes,
|
|
ModuleName \= DefaultModuleName
|
|
% We want to give this warning even if smart recompilation
|
|
% was disabled before this.
|
|
then
|
|
globals.lookup_bool_option(Globals, warn_smart_recompilation,
|
|
Warn),
|
|
(
|
|
Warn = yes,
|
|
Pieces = [words("Warning:"),
|
|
words("module name does not match file name: "), nl,
|
|
fixed(FileName), words("contains module"),
|
|
qual_sym_name(ModuleName), suffix("."), nl,
|
|
words("Smart recompilation will not work unless"),
|
|
words("a module name to file name mapping is created"),
|
|
words("using"), quote("mmc -f *.m"), suffix("."), nl],
|
|
write_error_pieces_plain(Globals, Pieces, !IO),
|
|
record_warning(Globals, !IO)
|
|
;
|
|
Warn = no
|
|
),
|
|
io_set_disable_smart_recompilation(yes, !IO)
|
|
else
|
|
true
|
|
)
|
|
),
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Stats),
|
|
maybe_report_stats(Stats, !IO),
|
|
SourceFileName = FileName ++ ".m"
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% For the MLDS->C and LLDS->C back-ends, we currently compile
|
|
% each submodule to its own C file.
|
|
% XXX Maybe it would be better to compile nested modules
|
|
% to a single C file, with code like this:
|
|
%
|
|
% list.map2_foldl(compile_to_llds, SubModuleList,
|
|
% LLDS_FragmentList),
|
|
% merge_llds_fragments(LLDS_FragmentList, LLDS),
|
|
% output_pass(LLDS_FragmentList)
|
|
%
|
|
:- pred augment_and_process_all_submodules(globals::in,
|
|
op_mode_augment::in, string::in, module_name::in,
|
|
maybe(timestamp)::in, set(module_name)::in, have_read_module_maps::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
list(raw_compilation_unit)::in, list(error_spec)::in,
|
|
list(string)::out, list(string)::out, io::di, io::uo) is det.
|
|
|
|
augment_and_process_all_submodules(Globals, OpModeAugment,
|
|
FileName, SourceFileModuleName, MaybeTimestamp, NestedSubModules,
|
|
HaveReadModuleMaps, FindTimestampFiles, RawCompUnits, !.Specs,
|
|
ModulesToLink, ExtraObjFiles, !IO) :-
|
|
list.map_foldl2(
|
|
augment_and_process_module(Globals, OpModeAugment,
|
|
FileName, SourceFileModuleName, MaybeTimestamp, NestedSubModules,
|
|
HaveReadModuleMaps, FindTimestampFiles),
|
|
RawCompUnits, ExtraObjFileLists, !Specs, !IO),
|
|
% XXX _NumErrors
|
|
write_error_specs(!.Specs, Globals, 0, _NumWarnings, 0, _NumErrors, !IO),
|
|
list.map(module_to_link, RawCompUnits, ModulesToLink),
|
|
list.condense(ExtraObjFileLists, ExtraObjFiles).
|
|
|
|
:- pred module_to_link(raw_compilation_unit::in, string::out) is det.
|
|
|
|
module_to_link(raw_compilation_unit(ModuleName, _, _), ModuleToLink) :-
|
|
module_name_to_file_name_stem(ModuleName, ModuleToLink).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Given the parse tree of a module, read in the interface and optimization
|
|
% files it needs, and compile it.
|
|
%
|
|
% Stage number assignments:
|
|
%
|
|
% 1 to 99 front end pass
|
|
% 100 to 299 middle pass
|
|
% 300 to 399 LLDS back end pass
|
|
% 400 to 499 MLDS back end pass
|
|
% 500 to 599 bytecode back end pass
|
|
%
|
|
% The initial arrangement had the stage numbers increasing by five
|
|
% so that new stages can be slotted in without too much trouble.
|
|
%
|
|
:- pred augment_and_process_module(globals::in,
|
|
op_mode_augment::in, file_name::in, module_name::in,
|
|
maybe(timestamp)::in, set(module_name)::in, have_read_module_maps::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
raw_compilation_unit::in, list(string)::out,
|
|
list(error_spec)::in, list(error_spec)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
augment_and_process_module(Globals, OpModeAugment,
|
|
SourceFileName, SourceFileModuleName, MaybeTimestamp,
|
|
NestedSubModules0, HaveReadModuleMaps, FindTimestampFiles,
|
|
RawCompUnit, ExtraObjFiles, !Specs, !IO) :-
|
|
maybe_check_for_no_exports(Globals, RawCompUnit, !Specs),
|
|
RawCompUnit = raw_compilation_unit(ModuleName, _, _),
|
|
( if ModuleName = SourceFileModuleName then
|
|
NestedSubModules = NestedSubModules0
|
|
else
|
|
set.init(NestedSubModules)
|
|
),
|
|
grab_imported_modules(Globals, SourceFileName, SourceFileModuleName,
|
|
MaybeTimestamp, NestedSubModules, RawCompUnit, HaveReadModuleMaps,
|
|
ModuleAndImports, !IO),
|
|
module_and_imports_get_aug_comp_unit(ModuleAndImports, _AugCompUnit,
|
|
ImportedSpecs, Errors),
|
|
!:Specs = ImportedSpecs ++ !.Specs,
|
|
set.intersect(Errors, fatal_read_module_errors, FatalErrors),
|
|
( if set.is_empty(FatalErrors) then
|
|
process_augmented_module(Globals, OpModeAugment, ModuleAndImports,
|
|
NestedSubModules, FindTimestampFiles, ExtraObjFiles,
|
|
no_prev_dump, _, !Specs, !IO)
|
|
else
|
|
ExtraObjFiles = []
|
|
).
|
|
|
|
:- pred process_augmented_module(globals::in, op_mode_augment::in,
|
|
module_and_imports::in, set(module_name)::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
list(string)::out, dump_info::in, dump_info::out,
|
|
list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.
|
|
|
|
process_augmented_module(Globals, OpModeAugment, ModuleAndImports,
|
|
NestedSubModules, FindTimestampFiles, ExtraObjFiles,
|
|
!DumpInfo, !Specs, !IO) :-
|
|
(
|
|
( OpModeAugment = opmau_typecheck_only
|
|
; OpModeAugment = opmau_errorcheck_only
|
|
),
|
|
% If we are only typechecking or error checking, then we should not
|
|
% modify any files; this includes writing to .d files.
|
|
WriteDFile = do_not_write_d_file
|
|
;
|
|
( OpModeAugment = opmau_make_trans_opt_int
|
|
; OpModeAugment = opmau_generate_code(_)
|
|
),
|
|
WriteDFile = write_d_file
|
|
;
|
|
OpModeAugment = opmau_make_opt_int,
|
|
% Don't write the `.d' file when making the `.opt' file because
|
|
% we can't work out the full transitive implementation dependencies.
|
|
WriteDFile = do_not_write_d_file
|
|
;
|
|
( OpModeAugment = opmau_make_analysis_registry
|
|
; OpModeAugment = opmau_make_xml_documentation
|
|
),
|
|
% XXX I (zs) think we should assign do_not_write_d_file for these.
|
|
WriteDFile = write_d_file
|
|
),
|
|
pre_hlds_pass(Globals, OpModeAugment, WriteDFile, ModuleAndImports, HLDS1,
|
|
QualInfo, MaybeTimestampMap, UndefTypes, UndefModes, PreHLDSErrors,
|
|
!DumpInfo, !Specs, !IO),
|
|
frontend_pass(OpModeAugment, QualInfo, UndefTypes, UndefModes,
|
|
PreHLDSErrors, FrontEndErrors, HLDS1, HLDS20, !DumpInfo, !Specs, !IO),
|
|
( if
|
|
PreHLDSErrors = no,
|
|
FrontEndErrors = no,
|
|
contains_errors(Globals, !.Specs) = no
|
|
then
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
maybe_write_dependency_graph(Verbose, Stats, HLDS20, HLDS21, !IO),
|
|
(
|
|
OpModeAugment = opmau_typecheck_only,
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeAugment = opmau_errorcheck_only,
|
|
% We may still want to run `unused_args' so that we get
|
|
% the appropriate warnings.
|
|
globals.lookup_bool_option(Globals, warn_unused_args, UnusedArgs),
|
|
(
|
|
UnusedArgs = yes,
|
|
globals.set_option(optimize_unused_args, bool(no),
|
|
Globals, NoOptUnusedArgsGlobals),
|
|
module_info_set_globals(NoOptUnusedArgsGlobals,
|
|
HLDS21, HLDS21a),
|
|
maybe_unused_args(Verbose, Stats, _UnusedArgsInfos,
|
|
HLDS21a, _HLDS22, !IO)
|
|
;
|
|
UnusedArgs = no
|
|
),
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeAugment = opmau_make_opt_int,
|
|
% Only run up to typechecking when making the .opt file.
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeAugment = opmau_make_trans_opt_int,
|
|
output_trans_opt_file(HLDS21, !DumpInfo, !IO),
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeAugment = opmau_make_analysis_registry,
|
|
prepare_for_intermodule_analysis(Globals, Verbose, Stats,
|
|
HLDS21, HLDS22, !IO),
|
|
output_analysis_file(HLDS22, !DumpInfo, !IO),
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeAugment = opmau_make_xml_documentation,
|
|
xml_documentation(HLDS21, !IO),
|
|
ExtraObjFiles = []
|
|
;
|
|
OpModeAugment = opmau_generate_code(OpModeCodeGen),
|
|
maybe_prepare_for_intermodule_analysis(Globals, Verbose, Stats,
|
|
HLDS21, HLDS22, !IO),
|
|
after_front_end_passes(Globals, OpModeCodeGen, NestedSubModules,
|
|
FindTimestampFiles, MaybeTimestampMap, HLDS22,
|
|
ExtraObjFiles, !Specs, !DumpInfo, !IO)
|
|
)
|
|
else
|
|
% If the number of errors is > 0, make sure that the compiler
|
|
% exits with a non-zero exit status.
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
( if ExitStatus = 0 then
|
|
io.set_exit_status(1, !IO)
|
|
else
|
|
true
|
|
),
|
|
ExtraObjFiles = []
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type maybe_write_d_file
|
|
---> do_not_write_d_file
|
|
; write_d_file.
|
|
|
|
:- pred pre_hlds_pass(globals::in, op_mode_augment::in, maybe_write_d_file::in,
|
|
module_and_imports::in, module_info::out, make_hlds_qual_info::out,
|
|
maybe(module_timestamp_map)::out, bool::out, bool::out, bool::out,
|
|
dump_info::in, dump_info::out, list(error_spec)::in, list(error_spec)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
pre_hlds_pass(Globals, OpModeAugment, WriteDFile0, ModuleAndImports0, HLDS1,
|
|
QualInfo, MaybeTimestampMap, UndefTypes, UndefModes,
|
|
FoundSemanticError, !DumpInfo, !Specs, !IO) :-
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
|
|
globals.lookup_bool_option(Globals, invoked_by_mmc_make, MMCMake),
|
|
( if
|
|
% Don't write the `.d' file when making the `.opt' file because
|
|
% we can't work out the full transitive implementation dependencies.
|
|
( MMCMake = yes
|
|
; OpModeAugment = opmau_make_opt_int
|
|
)
|
|
then
|
|
WriteDFile = do_not_write_d_file
|
|
else
|
|
WriteDFile = WriteDFile0
|
|
),
|
|
|
|
module_and_imports_get_module_name(ModuleAndImports0, ModuleName),
|
|
(
|
|
WriteDFile = do_not_write_d_file,
|
|
MaybeTransOptDeps = no
|
|
;
|
|
WriteDFile = write_d_file,
|
|
% We need the MaybeTransOptDeps when creating the .trans_opt file.
|
|
% However, we *also* need the MaybeTransOptDeps when writing out
|
|
% .d files. In the absence of MaybeTransOptDeps, we will write out
|
|
% a .d file that does not include the trans_opt_deps mmake rule,
|
|
% which will require an "mmake depend" before the next rebuild.
|
|
maybe_read_dependency_file(Globals, ModuleName, MaybeTransOptDeps, !IO)
|
|
),
|
|
|
|
% Errors in .opt and .trans_opt files result in software errors.
|
|
maybe_grab_optfiles(Globals, OpModeAugment, Verbose, MaybeTransOptDeps,
|
|
ModuleAndImports0, ModuleAndImports1, IntermodError, !IO),
|
|
|
|
% We pay attention to IntermodError instead of _Error. XXX Is this right?
|
|
module_and_imports_get_aug_comp_unit(ModuleAndImports1, AugCompUnit1,
|
|
ItemSpecs, _Error),
|
|
!:Specs = ItemSpecs ++ !.Specs,
|
|
MaybeTimestampMap = ModuleAndImports1 ^ mai_maybe_timestamp_map,
|
|
|
|
globals.lookup_string_option(Globals, event_set_file_name,
|
|
EventSetFileName),
|
|
( if EventSetFileName = "" then
|
|
EventSetName = "",
|
|
EventSpecMap1 = map.init,
|
|
EventSetErrors = no
|
|
else
|
|
read_event_set(EventSetFileName, EventSetName0, EventSpecMap0,
|
|
EventSetSpecs, !IO),
|
|
!:Specs = EventSetSpecs ++ !.Specs,
|
|
EventSetErrors = contains_errors(Globals, EventSetSpecs),
|
|
(
|
|
EventSetErrors = no,
|
|
EventSetName = EventSetName0,
|
|
EventSpecMap1 = EventSpecMap0
|
|
;
|
|
EventSetErrors = yes,
|
|
EventSetName = "",
|
|
EventSpecMap1 = map.init
|
|
)
|
|
),
|
|
|
|
maybe_write_out_errors_no_module(Verbose, Globals, !Specs, !IO),
|
|
maybe_write_string(Verbose, "% Module qualifying items...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
module_qualify_aug_comp_unit(Globals, AugCompUnit1, AugCompUnit2,
|
|
EventSpecMap1, EventSpecMap2, EventSetFileName, MQInfo0,
|
|
MQUndefTypes, MQUndefInsts, MQUndefModes, MQUndefTypeClasses,
|
|
[], QualifySpecs),
|
|
!:Specs = QualifySpecs ++ !.Specs,
|
|
maybe_write_out_errors_no_module(Verbose, Globals, !Specs, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO),
|
|
|
|
mq_info_get_recompilation_info(MQInfo0, RecompInfo0),
|
|
maybe_write_out_errors_no_module(Verbose, Globals, !Specs, !IO),
|
|
maybe_write_string(Verbose,
|
|
"% Expanding equivalence types and insts...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
expand_eqv_types_insts(AugCompUnit2, AugCompUnit,
|
|
EventSpecMap2, EventSpecMap, TypeEqvMap, UsedModules,
|
|
RecompInfo0, RecompInfo, ExpandSpecs),
|
|
ExpandErrors = contains_errors(Globals, ExpandSpecs),
|
|
!:Specs = ExpandSpecs ++ !.Specs,
|
|
maybe_write_out_errors_no_module(Verbose, Globals, !Specs, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO),
|
|
mq_info_set_recompilation_info(RecompInfo, MQInfo0, MQInfo),
|
|
|
|
EventSet = event_set(EventSetName, EventSpecMap),
|
|
make_hlds(Globals, AugCompUnit, EventSet, MQInfo, TypeEqvMap, UsedModules,
|
|
Verbose, Stats, HLDS0, QualInfo,
|
|
MakeHLDSFoundInvalidType, MakeHLDSFoundInvalidInstOrMode,
|
|
FoundSemanticError, !Specs, !IO),
|
|
maybe_write_definitions(Verbose, Stats, HLDS0, !IO),
|
|
maybe_write_definition_line_counts(Verbose, Stats, HLDS0, !IO),
|
|
|
|
( if
|
|
MQUndefTypes = did_not_find_undef_type,
|
|
MQUndefTypeClasses = did_not_find_undef_typeclass,
|
|
EventSetErrors = no,
|
|
ExpandErrors = no,
|
|
MakeHLDSFoundInvalidType = did_not_find_invalid_type
|
|
then
|
|
UndefTypes = no
|
|
else
|
|
UndefTypes = yes
|
|
),
|
|
( if
|
|
MQUndefInsts = did_not_find_undef_inst,
|
|
MQUndefModes = did_not_find_undef_mode,
|
|
MakeHLDSFoundInvalidInstOrMode = did_not_find_invalid_inst_or_mode
|
|
then
|
|
UndefModes = no
|
|
else
|
|
UndefModes = yes
|
|
),
|
|
|
|
maybe_dump_hlds(HLDS0, 1, "initial", !DumpInfo, !IO),
|
|
|
|
(
|
|
WriteDFile = do_not_write_d_file
|
|
;
|
|
WriteDFile = write_d_file,
|
|
module_info_get_all_deps(HLDS0, AllDeps),
|
|
write_dependency_file(Globals, ModuleAndImports0, AllDeps,
|
|
MaybeTransOptDeps, !IO),
|
|
globals.lookup_bool_option(Globals,
|
|
generate_mmc_make_module_dependencies, OutputMMCMakeDeps),
|
|
(
|
|
OutputMMCMakeDeps = yes,
|
|
make_write_module_dep_file(Globals, ModuleAndImports0, !IO)
|
|
;
|
|
OutputMMCMakeDeps = no
|
|
)
|
|
),
|
|
|
|
% Only stop on syntax errors in .opt files.
|
|
( if
|
|
( FoundSemanticError = yes
|
|
; IntermodError = yes
|
|
)
|
|
then
|
|
module_info_incr_errors(HLDS0, HLDS1)
|
|
else
|
|
HLDS1 = HLDS0
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% maybe_read_dependency_file(Globals, ModuleName, MaybeTransOptDeps, !IO):
|
|
%
|
|
% If transitive intermodule optimization has been enabled, then read
|
|
% <ModuleName>.d to find the modules which <ModuleName>.trans_opt may
|
|
% depend on. Otherwise return `no'.
|
|
%
|
|
:- pred maybe_read_dependency_file(globals::in, module_name::in,
|
|
maybe(list(module_name))::out, io::di, io::uo) is det.
|
|
|
|
maybe_read_dependency_file(Globals, ModuleName, MaybeTransOptDeps, !IO) :-
|
|
globals.lookup_bool_option(Globals, transitive_optimization, TransOpt),
|
|
(
|
|
TransOpt = yes,
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
module_name_to_file_name(Globals, do_not_create_dirs, ".d",
|
|
ModuleName, DependencyFileName, !IO),
|
|
maybe_write_string(Verbose, "% Reading auto-dependency file `", !IO),
|
|
maybe_write_string(Verbose, DependencyFileName, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.open_input(DependencyFileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(Stream),
|
|
io.set_input_stream(Stream, OldStream, !IO),
|
|
module_name_to_file_name(Globals, do_not_create_dirs,
|
|
".trans_opt_date", ModuleName, TransOptDateFileName, !IO),
|
|
SearchPattern = TransOptDateFileName ++ " :",
|
|
read_dependency_file_find_start(SearchPattern, FindResult, !IO),
|
|
(
|
|
FindResult = yes,
|
|
read_dependency_file_get_modules(TransOptDeps, !IO),
|
|
MaybeTransOptDeps = yes(TransOptDeps)
|
|
;
|
|
FindResult = no,
|
|
% error reading .d file
|
|
MaybeTransOptDeps = no
|
|
),
|
|
io.set_input_stream(OldStream, _, !IO),
|
|
io.close_input(Stream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
OpenResult = error(IOError),
|
|
maybe_write_string(Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.error_message(IOError, IOErrorMessage),
|
|
string.append_list(["error opening file `", DependencyFileName,
|
|
"' for input: ", IOErrorMessage], Message),
|
|
report_error(Message, !IO),
|
|
MaybeTransOptDeps = no
|
|
)
|
|
;
|
|
TransOpt = no,
|
|
MaybeTransOptDeps = no
|
|
).
|
|
|
|
% Read lines from the dependency file (module.d) until one is found
|
|
% which begins with SearchPattern.
|
|
%
|
|
:- pred read_dependency_file_find_start(string::in, bool::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_dependency_file_find_start(SearchPattern, Success, !IO) :-
|
|
io.read_line_as_string(Result, !IO),
|
|
(
|
|
Result = ok(Line),
|
|
( if string.prefix(Line, SearchPattern) then
|
|
% Have found the start.
|
|
Success = yes
|
|
else
|
|
read_dependency_file_find_start(SearchPattern, Success, !IO)
|
|
)
|
|
;
|
|
( Result = error(_)
|
|
; Result = eof
|
|
),
|
|
Success = no
|
|
).
|
|
|
|
% Read lines until one is found which does not contain whitespace
|
|
% followed by a word which ends in .trans_opt. Remove the .trans_opt
|
|
% ending from all the words which are read in and return the resulting
|
|
% list of modules.
|
|
%
|
|
:- pred read_dependency_file_get_modules(list(module_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_dependency_file_get_modules(TransOptDeps, !IO) :-
|
|
io.read_line(Result, !IO),
|
|
( if
|
|
Result = ok(CharList0),
|
|
% Remove any whitespace from the beginning of the line,
|
|
% then take all characters until another whitespace occurs.
|
|
list.drop_while(char.is_whitespace, CharList0, CharList1),
|
|
NotIsWhitespace = (pred(Char::in) is semidet :-
|
|
not char.is_whitespace(Char)
|
|
),
|
|
list.take_while(NotIsWhitespace, CharList1, CharList),
|
|
string.from_char_list(CharList, FileName0),
|
|
string.remove_suffix(FileName0, ".trans_opt", FileName)
|
|
then
|
|
( if string.append("Mercury/trans_opts/", BaseFileName, FileName) then
|
|
ModuleFileName = BaseFileName
|
|
else
|
|
ModuleFileName = FileName
|
|
),
|
|
file_name_to_module_name(ModuleFileName, Module),
|
|
read_dependency_file_get_modules(TransOptDeps0, !IO),
|
|
TransOptDeps = [Module | TransOptDeps0]
|
|
else
|
|
TransOptDeps = []
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred maybe_grab_optfiles(globals::in, op_mode_augment::in, bool::in,
|
|
maybe(list(module_name))::in,
|
|
module_and_imports::in, module_and_imports::out, bool::out,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_grab_optfiles(Globals, OpModeAugment, Verbose, MaybeTransOptDeps,
|
|
Imports0, Imports, Error, !IO) :-
|
|
globals.lookup_bool_option(Globals, intermodule_optimization, IntermodOpt),
|
|
globals.lookup_bool_option(Globals, use_opt_files, UseOptInt),
|
|
globals.lookup_bool_option(Globals, transitive_optimization, TransOpt),
|
|
globals.lookup_bool_option(Globals, intermodule_analysis,
|
|
IntermodAnalysis),
|
|
( if
|
|
( UseOptInt = yes
|
|
; IntermodOpt = yes
|
|
; IntermodAnalysis = yes
|
|
),
|
|
OpModeAugment \= opmau_make_opt_int
|
|
then
|
|
maybe_write_string(Verbose, "% Reading .opt files...\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
grab_opt_files(Globals, Imports0, Imports1, Error1, !IO),
|
|
maybe_write_string(Verbose, "% Done.\n", !IO)
|
|
else
|
|
Imports1 = Imports0,
|
|
Error1 = no
|
|
),
|
|
(
|
|
OpModeAugment = opmau_make_trans_opt_int,
|
|
(
|
|
MaybeTransOptDeps = yes(TransOptDeps),
|
|
% When creating the trans_opt file, only import the
|
|
% trans_opt files which are lower in the ordering.
|
|
grab_trans_opt_files(Globals, TransOptDeps, Imports1, Imports,
|
|
Error2, !IO)
|
|
;
|
|
MaybeTransOptDeps = no,
|
|
Imports = Imports1,
|
|
Error2 = no,
|
|
module_and_imports_get_module_name(Imports, ModuleName),
|
|
globals.lookup_bool_option(Globals, warn_missing_trans_opt_deps,
|
|
WarnNoTransOptDeps),
|
|
(
|
|
WarnNoTransOptDeps = yes,
|
|
Pieces = [words("Warning: cannot read trans-opt dependencies"),
|
|
words("for module"), qual_sym_name(ModuleName),
|
|
suffix("."), nl,
|
|
words("You need to remake the dependencies."), nl],
|
|
Msg = error_msg(no, do_not_treat_as_first, 0,
|
|
[always(Pieces)]),
|
|
Spec = error_spec(severity_warning, phase_read_files, [Msg]),
|
|
% XXX _NumErrors
|
|
write_error_spec(Spec, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO)
|
|
;
|
|
WarnNoTransOptDeps = no
|
|
)
|
|
)
|
|
;
|
|
OpModeAugment = opmau_make_opt_int,
|
|
% If we are making the `.opt' file, then we cannot read any
|
|
% `.trans_opt' files, since `.opt' files aren't allowed to depend on
|
|
% `.trans_opt' files.
|
|
Imports = Imports1,
|
|
Error2 = no
|
|
;
|
|
( OpModeAugment = opmau_make_analysis_registry
|
|
; OpModeAugment = opmau_make_xml_documentation
|
|
; OpModeAugment = opmau_typecheck_only
|
|
; OpModeAugment = opmau_errorcheck_only
|
|
; OpModeAugment = opmau_generate_code(_)
|
|
),
|
|
(
|
|
TransOpt = yes,
|
|
% If transitive optimization is enabled, but we are not creating
|
|
% the .opt or .trans opt file, then import the trans_opt files
|
|
% for all the modules that are imported (or used), and for all
|
|
% ancestor modules.
|
|
TransOptFiles = set.union_list([
|
|
Imports0 ^ mai_parent_deps,
|
|
set.sorted_list_to_set(
|
|
multi_map.keys(Imports0 ^ mai_int_deps)),
|
|
set.sorted_list_to_set(
|
|
multi_map.keys(Imports0 ^ mai_imp_deps))
|
|
]),
|
|
set.to_sorted_list(TransOptFiles, TransOptFilesList),
|
|
grab_trans_opt_files(Globals, TransOptFilesList, Imports1, Imports,
|
|
Error2, !IO)
|
|
;
|
|
TransOpt = no,
|
|
Imports = Imports1,
|
|
Error2 = no
|
|
)
|
|
),
|
|
bool.or(Error1, Error2, Error).
|
|
|
|
%---------------------%
|
|
|
|
:- pred make_hlds(globals::in, aug_compilation_unit::in,
|
|
event_set::in, mq_info::in, type_eqv_map::in, used_modules::in,
|
|
bool::in, bool::in, module_info::out, make_hlds_qual_info::out,
|
|
found_invalid_type::out, found_invalid_inst_or_mode::out, bool::out,
|
|
list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.
|
|
|
|
make_hlds(Globals, AugCompUnit, EventSet, MQInfo, TypeEqvMap, UsedModules,
|
|
Verbose, Stats, !:HLDS, QualInfo,
|
|
FoundInvalidType, FoundInvalidInstOrMode,
|
|
FoundSemanticError, !Specs, !IO) :-
|
|
maybe_write_out_errors_no_module(Verbose, Globals, !Specs, !IO),
|
|
maybe_write_string(Verbose, "% Converting parse tree to hlds...\n", !IO),
|
|
ModuleName = aug_compilation_unit_project_name(AugCompUnit),
|
|
module_name_to_file_name(Globals, do_create_dirs, ".hlds_dump",
|
|
ModuleName, DumpBaseFileName, !IO),
|
|
parse_tree_to_hlds(AugCompUnit, Globals, DumpBaseFileName, MQInfo,
|
|
TypeEqvMap, UsedModules, QualInfo,
|
|
FoundInvalidType, FoundInvalidInstOrMode, !:HLDS, MakeSpecs),
|
|
!:Specs = MakeSpecs ++ !.Specs,
|
|
module_info_set_event_set(EventSet, !HLDS),
|
|
io.get_exit_status(Status, !IO),
|
|
SpecsErrors = contains_errors(Globals, !.Specs),
|
|
( if
|
|
( Status \= 0
|
|
; SpecsErrors = yes
|
|
)
|
|
then
|
|
FoundSemanticError = yes,
|
|
io.set_exit_status(1, !IO)
|
|
else
|
|
FoundSemanticError = no
|
|
),
|
|
maybe_write_out_errors_no_module(Verbose, Globals, !Specs, !IO),
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%---------------------%
|
|
|
|
:- pred maybe_write_definitions(bool::in, bool::in,
|
|
module_info::in, io::di, io::uo) is det.
|
|
|
|
maybe_write_definitions(Verbose, Stats, HLDS, !IO) :-
|
|
module_info_get_globals(HLDS, Globals),
|
|
globals.lookup_bool_option(Globals, show_definitions, ShowDefns),
|
|
(
|
|
ShowDefns = yes,
|
|
maybe_write_string(Verbose, "% Writing definitions...", !IO),
|
|
module_info_get_name(HLDS, ModuleName),
|
|
module_name_to_file_name(Globals, do_create_dirs, ".defns",
|
|
ModuleName, FileName, !IO),
|
|
io.open_output(FileName, Res, !IO),
|
|
(
|
|
Res = ok(FileStream),
|
|
hlds.hlds_defns.write_hlds_defns(FileStream, HLDS, !IO),
|
|
io.close_output(FileStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
Res = error(IOError),
|
|
ErrorMsg = "unable to write definitions: " ++
|
|
io.error_message(IOError),
|
|
report_error(ErrorMsg, !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ShowDefns = no
|
|
).
|
|
|
|
:- pred maybe_write_definition_line_counts(bool::in, bool::in,
|
|
module_info::in, io::di, io::uo) is det.
|
|
|
|
maybe_write_definition_line_counts(Verbose, Stats, HLDS, !IO) :-
|
|
module_info_get_globals(HLDS, Globals),
|
|
globals.lookup_bool_option(Globals, show_definition_line_counts,
|
|
LineCounts),
|
|
(
|
|
LineCounts = yes,
|
|
maybe_write_string(Verbose,
|
|
"% Writing definition line coints...", !IO),
|
|
module_info_get_name(HLDS, ModuleName),
|
|
module_name_to_file_name(Globals, do_create_dirs, ".defn_line_counts",
|
|
ModuleName, FileName, !IO),
|
|
io.open_output(FileName, Res, !IO),
|
|
(
|
|
Res = ok(FileStream),
|
|
hlds.hlds_defns.write_hlds_defn_line_counts(FileStream, HLDS, !IO),
|
|
io.close_output(FileStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
Res = error(IOError),
|
|
ErrorMsg = "unable to write definition line counts: " ++
|
|
io.error_message(IOError),
|
|
report_error(ErrorMsg, !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
LineCounts = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_write_dependency_graph(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_write_dependency_graph(Verbose, Stats, !HLDS, !IO) :-
|
|
module_info_get_globals(!.HLDS, Globals),
|
|
globals.lookup_bool_option(Globals, show_dependency_graph, ShowDepGraph),
|
|
(
|
|
ShowDepGraph = yes,
|
|
maybe_write_string(Verbose, "% Writing dependency graph...", !IO),
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_file_name(Globals, do_create_dirs, ".dependency_graph",
|
|
ModuleName, FileName, !IO),
|
|
io.open_output(FileName, Res, !IO),
|
|
(
|
|
Res = ok(FileStream),
|
|
io.set_output_stream(FileStream, OutputStream, !IO),
|
|
write_dependency_graph(!HLDS, !IO),
|
|
io.set_output_stream(OutputStream, _, !IO),
|
|
io.close_output(FileStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
Res = error(IOError),
|
|
ErrorMsg = "unable to write dependency graph: " ++
|
|
io.error_message(IOError),
|
|
report_error(ErrorMsg, !IO)
|
|
),
|
|
maybe_report_stats(Stats, !IO)
|
|
;
|
|
ShowDepGraph = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_prepare_for_intermodule_analysis(globals::in, bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_prepare_for_intermodule_analysis(Globals, Verbose, Stats, !HLDS, !IO) :-
|
|
globals.lookup_bool_option(Globals, intermodule_analysis,
|
|
IntermodAnalysis),
|
|
(
|
|
IntermodAnalysis = yes,
|
|
prepare_for_intermodule_analysis(Globals, Verbose, Stats, !HLDS, !IO)
|
|
;
|
|
IntermodAnalysis = no
|
|
).
|
|
|
|
:- pred prepare_for_intermodule_analysis(globals::in, bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
prepare_for_intermodule_analysis(Globals, Verbose, Stats, !HLDS, !IO) :-
|
|
maybe_write_string(Verbose, "% Preparing for intermodule analysis...\n",
|
|
!IO),
|
|
|
|
module_info_get_all_deps(!.HLDS, ModuleNames),
|
|
|
|
globals.lookup_accumulating_option(Globals, local_module_id,
|
|
LocalModulesList),
|
|
SymNames = list.map(string_to_sym_name, LocalModulesList),
|
|
LocalModuleNames = set.from_list(SymNames),
|
|
|
|
module_info_get_analysis_info(!.HLDS, AnalysisInfo0),
|
|
prepare_intermodule_analysis(Globals, ModuleNames, LocalModuleNames,
|
|
AnalysisInfo0, AnalysisInfo, !IO),
|
|
module_info_set_analysis_info(AnalysisInfo, !HLDS),
|
|
|
|
maybe_write_string(Verbose, "% done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred after_front_end_passes(globals::in, op_mode_codegen::in,
|
|
set(module_name)::in,
|
|
find_timestamp_file_names::in(find_timestamp_file_names),
|
|
maybe(module_timestamp_map)::in, module_info::in,
|
|
list(string)::out, list(error_spec)::in, list(error_spec)::out,
|
|
dump_info::in, dump_info::out, io::di, io::uo) is det.
|
|
|
|
after_front_end_passes(Globals, OpModeCodeGen, NestedSubModules,
|
|
FindTimestampFiles, MaybeTimestampMap, !.HLDS,
|
|
ExtraObjFiles, !Specs, !DumpInfo, !IO) :-
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
globals.lookup_bool_option(Globals, statistics, Stats),
|
|
maybe_output_prof_call_graph(Verbose, Stats, !HLDS, !IO),
|
|
middle_pass(!HLDS, !DumpInfo, !IO),
|
|
globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
|
|
globals.get_target(Globals, Target),
|
|
|
|
% Remove any existing `.used' file before writing the output file.
|
|
% This avoids leaving the old `used' file lying around if compilation
|
|
% is interrupted after the new output file is written but before the new
|
|
% `.used' file is written.
|
|
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_file_name(Globals, do_not_create_dirs, ".used",
|
|
ModuleName, UsageFileName, !IO),
|
|
io.remove_file(UsageFileName, _, !IO),
|
|
|
|
globals.lookup_bool_option(Globals, halt_at_warn, HaltAtWarn),
|
|
(
|
|
HaltAtWarn = no,
|
|
FrontEndErrors = contains_errors(Globals, !.Specs)
|
|
;
|
|
HaltAtWarn = yes,
|
|
FrontEndErrors = contains_errors_and_or_warnings(Globals, !.Specs)
|
|
),
|
|
module_info_get_num_errors(!.HLDS, NumErrors),
|
|
( if
|
|
FrontEndErrors = no,
|
|
NumErrors = 0
|
|
then
|
|
(
|
|
Target = target_csharp,
|
|
mlds_backend(!.HLDS, _, MLDS, NewSpecs, !DumpInfo, !IO),
|
|
!:Specs = NewSpecs ++ !.Specs,
|
|
% mlds_to_csharp never goes beyond generating C# code.
|
|
mlds_to_csharp(!.HLDS, MLDS, Succeeded, !IO),
|
|
ExtraObjFiles = []
|
|
;
|
|
Target = target_java,
|
|
mlds_backend(!.HLDS, _, MLDS, NewSpecs, !DumpInfo, !IO),
|
|
!:Specs = NewSpecs ++ !.Specs,
|
|
mlds_to_java(!.HLDS, MLDS, TargetCodeSucceeded, !IO),
|
|
(
|
|
OpModeCodeGen = opmcg_target_code_only,
|
|
Succeeded = TargetCodeSucceeded
|
|
;
|
|
( OpModeCodeGen = opmcg_target_and_object_code_only
|
|
; OpModeCodeGen = opmcg_target_object_and_executable
|
|
),
|
|
(
|
|
TargetCodeSucceeded = no,
|
|
Succeeded = no
|
|
;
|
|
TargetCodeSucceeded = yes,
|
|
io.output_stream(OutputStream, !IO),
|
|
module_name_to_file_name(Globals, do_not_create_dirs,
|
|
".java", ModuleName, JavaFile, !IO),
|
|
compile_java_files(Globals, OutputStream, [JavaFile],
|
|
Succeeded, !IO),
|
|
maybe_set_exit_status(Succeeded, !IO)
|
|
)
|
|
),
|
|
ExtraObjFiles = []
|
|
;
|
|
Target = target_c,
|
|
% Produce the grade independent header file <module>.mh
|
|
% containing function prototypes for the procedures referred to
|
|
% by foreign_export pragmas.
|
|
export.get_foreign_export_decls(!.HLDS, ExportDecls),
|
|
export.produce_header_file(!.HLDS, ExportDecls, ModuleName, !IO),
|
|
(
|
|
HighLevelCode = yes,
|
|
mlds_backend(!.HLDS, _, MLDS, NewSpecs, !DumpInfo, !IO),
|
|
!:Specs = NewSpecs ++ !.Specs,
|
|
mlds_to_high_level_c(Globals, MLDS, TargetCodeSucceeded, !IO),
|
|
(
|
|
OpModeCodeGen = opmcg_target_code_only,
|
|
Succeeded = TargetCodeSucceeded
|
|
;
|
|
( OpModeCodeGen = opmcg_target_and_object_code_only
|
|
; OpModeCodeGen = opmcg_target_object_and_executable
|
|
),
|
|
(
|
|
TargetCodeSucceeded = no,
|
|
Succeeded = no
|
|
;
|
|
TargetCodeSucceeded = yes,
|
|
module_name_to_file_name(Globals, do_not_create_dirs,
|
|
".c", ModuleName, C_File, !IO),
|
|
get_linked_target_type(Globals, TargetType),
|
|
get_object_code_type(Globals, TargetType, PIC),
|
|
maybe_pic_object_file_extension(Globals, PIC, Obj),
|
|
module_name_to_file_name(Globals, do_create_dirs,
|
|
Obj, ModuleName, O_File, !IO),
|
|
io.output_stream(OutputStream, !IO),
|
|
do_compile_c_file(Globals, OutputStream, PIC,
|
|
C_File, O_File, Succeeded, !IO),
|
|
maybe_set_exit_status(Succeeded, !IO)
|
|
)
|
|
),
|
|
ExtraObjFiles = []
|
|
;
|
|
HighLevelCode = no,
|
|
llds_backend_pass(!HLDS, GlobalData, LLDS, !DumpInfo, !IO),
|
|
% llds_output_pass looks up the target_code_only option
|
|
% to see whether it should generate object code, using the
|
|
% same logic as the HighLevelCode = yes case above.
|
|
% XXX Move that logic here, for symmetry.
|
|
llds_output_pass(OpModeCodeGen, !.HLDS, GlobalData, LLDS,
|
|
ModuleName, Succeeded, ExtraObjFiles, !IO)
|
|
)
|
|
;
|
|
Target = target_erlang,
|
|
erlang_backend(!.HLDS, ELDS, !DumpInfo, !IO),
|
|
% elds_to_erlang never goes beyond generating Erlang code.
|
|
elds_to_erlang(!.HLDS, ELDS, Succeeded, !IO),
|
|
ExtraObjFiles = []
|
|
),
|
|
(
|
|
Succeeded = yes,
|
|
recompilation.usage.write_usage_file(!.HLDS, NestedSubModules,
|
|
MaybeTimestampMap, !IO),
|
|
FindTimestampFiles(ModuleName, TimestampFiles, !IO),
|
|
list.foldl(touch_datestamp(Globals), TimestampFiles, !IO)
|
|
;
|
|
Succeeded = no
|
|
% An error should have been reported earlier.
|
|
)
|
|
else
|
|
% If the number of errors is > 0, make sure that the compiler
|
|
% exits with a non-zero exit status.
|
|
io.get_exit_status(ExitStatus, !IO),
|
|
( if ExitStatus = 0 then
|
|
io.set_exit_status(1, !IO)
|
|
else
|
|
true
|
|
),
|
|
ExtraObjFiles = []
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Outputs the file <module_name>.prof, which contains the static
|
|
% call graph in terms of label names, if the profiling flag is enabled.
|
|
%
|
|
:- pred maybe_output_prof_call_graph(bool::in, bool::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
maybe_output_prof_call_graph(Verbose, Stats, !HLDS, !IO) :-
|
|
module_info_get_globals(!.HLDS, Globals),
|
|
globals.lookup_bool_option(Globals, profile_calls, ProfileCalls),
|
|
globals.lookup_bool_option(Globals, profile_time, ProfileTime),
|
|
( if
|
|
( ProfileCalls = yes
|
|
; ProfileTime = yes
|
|
)
|
|
then
|
|
maybe_write_string(Verbose,
|
|
"% Outputting profiling call graph...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
module_info_get_name(!.HLDS, ModuleName),
|
|
module_name_to_file_name(Globals, do_create_dirs, ".prof",
|
|
ModuleName, ProfFileName, !IO),
|
|
io.open_output(ProfFileName, Res, !IO),
|
|
(
|
|
Res = ok(FileStream),
|
|
io.set_output_stream(FileStream, OutputStream, !IO),
|
|
write_prof_dependency_graph(!HLDS, !IO),
|
|
io.set_output_stream(OutputStream, _, !IO),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
Res = error(IOError),
|
|
ErrorMsg = "unable to write profiling static call graph: " ++
|
|
io.error_message(IOError),
|
|
report_error(ErrorMsg, !IO)
|
|
),
|
|
maybe_write_string(Verbose, " done.\n", !IO),
|
|
maybe_report_stats(Stats, !IO)
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred halt_at_module_error(globals::in, read_module_errors::in) is semidet.
|
|
|
|
halt_at_module_error(Globals, Errors) :-
|
|
set.is_non_empty(Errors),
|
|
(
|
|
globals.lookup_bool_option(Globals, halt_at_syntax_errors, HaltSyntax),
|
|
HaltSyntax = yes
|
|
;
|
|
set.intersect(Errors, fatal_read_module_errors, FatalErrors),
|
|
set.is_non_empty(FatalErrors)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred gc_init(io::di, io::uo) is det.
|
|
|
|
% This version is only used if there is no matching foreign_proc version.
|
|
gc_init(!IO).
|
|
|
|
:- pragma foreign_proc("C",
|
|
gc_init(_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, tabled_for_io],
|
|
"
|
|
#ifdef MR_BOEHM_GC
|
|
/*
|
|
** Explicitly force the initial heap size to be at least 4 Mb.
|
|
**
|
|
** This works around a bug in the Boehm collector (for versions up
|
|
** to at least 6.2) where the collector would sometimes abort with
|
|
** the message `unexpected mark stack overflow' (e.g. in grade hlc.gc
|
|
** on dec-alpha-osf3.2).
|
|
**
|
|
** Doing this should also improve performance slightly by avoiding
|
|
** frequent garbage collection during start-up.
|
|
*/
|
|
GC_expand_hp(4 * 1024 * 1024);
|
|
#endif
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module top_level.mercury_compile_main.
|
|
%---------------------------------------------------------------------------%
|