mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
compiler/make.util.m:
All callers of the remove_file_for_make predicate pass it
the names of *files*, not directories, to remove. So stop invoking
a library predicate intended to remove complete directort hierarchies.
library/io.file.m:
Document the lack of a predicate that removes *only* regular files
as a problem. (Even io.file.remove_file can, and usually will, remove
the named "file" if it is in fact a directory, provided it is empty.)
Make the descriptions of the exported predicates of this module
easier to read.
Make a foreign_proc more readable as well.
533 lines
18 KiB
Mathematica
533 lines
18 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2002-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: make.util.m.
|
|
% Authors: stayl, wangp.
|
|
%
|
|
% Assorted predicates used to implement `mmc --make'.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module make.util.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.maybe_util.
|
|
:- import_module libs.options.
|
|
:- import_module make.make_info.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.file_names.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Remove file a file, deleting the cached timestamp.
|
|
% The removal is reported to the user if the given boolean option is set.
|
|
% In general the option given should be `--very-verbose' when making a
|
|
% `.clean' or `.realclean target', and `--verbose-make' when cleaning
|
|
% after an interrupted build.
|
|
%
|
|
|
|
% Remove the target file and the corresponding timestamp file.
|
|
%
|
|
:- pred remove_make_target_file(io.text_output_stream::in, globals::in,
|
|
string::in, option::in, target_file::in,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% Remove the target file and the corresponding timestamp file.
|
|
%
|
|
:- pred remove_make_target_file_by_name(io.text_output_stream::in, globals::in,
|
|
string::in, option::in, module_name::in, module_target_type::in,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% remove_module_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
% ModuleName, Extension, !Info, !IO).
|
|
%
|
|
:- pred remove_module_file_for_make(io.text_output_stream::in, globals::in,
|
|
option::in, module_name::in, ext::in, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pred remove_file_for_make(io.text_output_stream::in, globals::in,
|
|
option::in, file_name::in, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func make_target_file_list(list(module_name), module_target_type) =
|
|
list(target_file).
|
|
|
|
:- func make_target_id_list(list(module_name), module_target_type)
|
|
= list(target_id).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type maybe_grade_dependent
|
|
---> not_grade_dependent
|
|
; grade_dependent.
|
|
|
|
% Test whether targets of a given type are grade-dependent or not.
|
|
% NOTE: some grade-dependent target types are also dependent on the
|
|
% ISA (instruction set architecture) of the target machine, while
|
|
% some are not. However, grade-independent target types are never
|
|
% architecture-dependent.
|
|
%
|
|
:- func is_target_grade_dependent(module_target_type) = maybe_grade_dependent.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type stop_or_continue
|
|
---> soc_stop
|
|
; soc_continue.
|
|
|
|
:- pred should_we_stop_or_continue(maybe_keep_going::in, maybe_succeeded::in,
|
|
stop_or_continue::out, maybe_succeeded::in, maybe_succeeded::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Debugging, progress, and error messages.
|
|
%
|
|
|
|
:- pred debug_make_msg(globals::in, pred(string)::in(pred(out) is det),
|
|
string::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% verbose_make_N_part_msg(Globals, Part1, ..., Msg):
|
|
%
|
|
% If `--verbose-make' is set, return a message consisting of the
|
|
% given input strings (with spaces between them) and a newline.
|
|
% Otherwise, return the empty string.
|
|
%
|
|
:- pred verbose_make_one_part_msg(globals::in,
|
|
string::in, string::out) is det.
|
|
:- pred verbose_make_two_part_msg(globals::in,
|
|
string::in, string::in, string::out) is det.
|
|
:- pred verbose_make_three_part_msg(globals::in,
|
|
string::in, string::in, string::in, string::out) is det.
|
|
:- pred verbose_make_four_part_msg(globals::in,
|
|
string::in, string::in, string::in, string::in, string::out) is det.
|
|
|
|
% option_set_N_part_msg(Globals, Option, Part1, ..., Msg):
|
|
%
|
|
% If the given option is set, return a message consisting of the
|
|
% given input strings (with spaces between them) and a newline.
|
|
% Otherwise, return the empty string.
|
|
%
|
|
:- pred option_set_one_part_msg(globals::in, option::in,
|
|
string::in, string::out) is det.
|
|
:- pred option_set_two_part_msg(globals::in, option::in,
|
|
string::in, string::in, string::out) is det.
|
|
:- pred option_set_three_part_msg(globals::in, option::in,
|
|
string::in, string::in, string::in, string::out) is det.
|
|
:- pred option_set_four_part_msg(globals::in, option::in,
|
|
string::in, string::in, string::in, string::in, string::out) is det.
|
|
|
|
% If `--verbose-make' is set, return a progress message saying that
|
|
% the compiler is "Making <filename>". Otherwise, return the empty string.
|
|
%
|
|
:- pred maybe_making_filename_msg(globals::in, file_name::in,
|
|
string::out) is det.
|
|
|
|
% If `--verbose-make' is set, return a progress message saying that
|
|
% the compiler is reanalysing invalid/suboptimal modules.
|
|
% Otherwise, return the empty string.
|
|
%
|
|
:- pred maybe_reanalyse_modules_msg(globals::in, string::out) is det.
|
|
|
|
% Return an error nessage saying there was an error making the given
|
|
% target, which has the given filename.
|
|
%
|
|
:- pred file_error_msg(file_name::in, string::out) is det.
|
|
|
|
% If
|
|
% - the warn_up_to_date option is set, and
|
|
% - the given target was specified on the command line, and
|
|
% - the given targe has not yet been deleted from that list of
|
|
% command line targets,
|
|
% then return a warning about it already being up to date.
|
|
% Otherwise, return the empty string. In both cases, this predicate
|
|
% will delete ths given target from the list of command line targets,
|
|
% which prevents duplicate "up to date" messages.
|
|
%
|
|
% XXX The list of command line targets is never used for any purpose
|
|
% other than this last test. I (zs) am not sure that it is actually useful
|
|
% for this purpose, because
|
|
%
|
|
% - the two places other than maybe_warn_up_to_date_target_msg that can
|
|
% delete targets from the list of command line targets, in
|
|
% make.module_target.m and make.program_target.m respectively,
|
|
% both do so only if the target is NOT up to date, and
|
|
%
|
|
% - it should not require this make_info field to prevent duplicate
|
|
% calls to this predicate if the target IS up to date.
|
|
%
|
|
:- pred maybe_warn_up_to_date_target_msg(globals::in, top_target_file::in,
|
|
file_name::in, make_info::in, make_info::out, string::out) is det.
|
|
|
|
% Write a message "Made symlink/copy of <filename>"
|
|
% if `--verbose-make' is set.
|
|
%
|
|
:- pred maybe_symlink_or_copy_linked_target_msg(globals::in, file_name::in,
|
|
string::out) is det.
|
|
|
|
%---------------------%
|
|
%
|
|
% All messages from the make-like functionality of "mmc --make" should be
|
|
% written out using a single call to io.write_string, and then a flush
|
|
% of the stream. This should allow messages from different processes
|
|
% during parallel builds to be interleaved *only* on message boundaries.
|
|
%
|
|
% XXX This is only really true for the "locked" variants, and even then,
|
|
% see the other XXX just below.
|
|
%
|
|
|
|
% Write out a message from mmc --make, if the message is not empty.
|
|
%
|
|
:- pred maybe_write_msg(io.text_output_stream::in, string::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% Write out a message from mmc --make, if the message is not empty,
|
|
% while locking stdout.
|
|
%
|
|
% XXX The current stream, or the specified stream, may be a stream
|
|
% OTHER THAN stdout.
|
|
%
|
|
:- pred maybe_write_msg_locked(io.text_output_stream::in, make_info::in,
|
|
string::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Timing.
|
|
%
|
|
|
|
:- pred get_real_milliseconds(int::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.compile_target_code.
|
|
:- import_module make.build.
|
|
:- import_module make.file_names.
|
|
:- import_module make.timestamp.
|
|
|
|
:- import_module bool.
|
|
:- import_module io.file.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module version_hash_table.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
remove_make_target_file(ProgressStream, Globals, From, VerboseOption, Target,
|
|
!Info, !IO) :-
|
|
Target = target_file(ModuleName, TargetType),
|
|
remove_make_target_file_by_name(ProgressStream, Globals, From,
|
|
VerboseOption, ModuleName, TargetType, !Info, !IO).
|
|
|
|
%---------------------%
|
|
|
|
remove_make_target_file_by_name(ProgressStream, Globals, From, VerboseOption,
|
|
ModuleName, TargetType, !Info, !IO) :-
|
|
% XXX LEGACY
|
|
module_target_to_file_name(Globals, From, TargetType,
|
|
ModuleName, FileNameLegacy, FileNameProposed, !IO),
|
|
remove_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
FileNameLegacy, !Info, !IO),
|
|
remove_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
FileNameProposed, !Info, !IO),
|
|
( if date_file_extension(TargetType, TimestampExt) then
|
|
remove_module_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
ModuleName, TimestampExt, !Info, !IO)
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
remove_module_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
ModuleName, Ext, !Info, !IO) :-
|
|
% XXX LEGACY
|
|
module_name_to_file_name(Globals, $pred, Ext, ModuleName,
|
|
FileNameLegacy, FileNameProposed),
|
|
remove_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
FileNameLegacy, !Info, !IO),
|
|
remove_file_for_make(ProgressStream, Globals, VerboseOption,
|
|
FileNameProposed, !Info, !IO).
|
|
|
|
%---------------------%
|
|
|
|
remove_file_for_make(ProgressStream, Globals, VerboseOption, FileName,
|
|
!Info, !IO) :-
|
|
option_set_two_part_msg(Globals, VerboseOption,
|
|
"Removing", FileName, RemovingMsg),
|
|
maybe_write_msg(ProgressStream, RemovingMsg, !IO),
|
|
io.file.remove_file(FileName, _, !IO),
|
|
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
|
|
map.delete(FileName, FileTimestampMap0, FileTimestampMap),
|
|
make_info_set_file_timestamp_map(FileTimestampMap, !Info),
|
|
% For simplicity, clear out all target file timestamps.
|
|
make_info_set_target_file_timestamp_map(init_target_file_timestamp_map,
|
|
!Info).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
make_target_file_list(ModuleNames, TargetType) =
|
|
list.map((func(ModuleName) = target_file(ModuleName, TargetType)),
|
|
ModuleNames).
|
|
|
|
make_target_id_list(ModuleNames, TargetType) =
|
|
list.map((func(Module) = merc_target(target_file(Module, TargetType))),
|
|
ModuleNames).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
is_target_grade_dependent(Target) = IsDependent :-
|
|
(
|
|
( Target = module_target_source
|
|
; Target = module_target_errors
|
|
; Target = module_target_int0
|
|
; Target = module_target_int1
|
|
; Target = module_target_int2
|
|
; Target = module_target_int3
|
|
; Target = module_target_c_header(header_mh)
|
|
; Target = module_target_xml_doc
|
|
),
|
|
IsDependent = not_grade_dependent
|
|
;
|
|
( Target = module_target_opt
|
|
; Target = module_target_analysis_registry
|
|
; Target = module_target_track_flags
|
|
; Target = module_target_c_header(header_mih)
|
|
; Target = module_target_c_code
|
|
; Target = module_target_csharp_code
|
|
; Target = module_target_java_code
|
|
; Target = module_target_java_class_code
|
|
; Target = module_target_object_code(_)
|
|
; Target = module_target_fact_table_object(_, _)
|
|
),
|
|
IsDependent = grade_dependent
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
should_we_stop_or_continue(KeepGoing, JobSucceeded, StopOrContinue,
|
|
!Succeeded) :-
|
|
(
|
|
JobSucceeded = succeeded,
|
|
StopOrContinue = soc_continue
|
|
;
|
|
JobSucceeded = did_not_succeed,
|
|
!:Succeeded = did_not_succeed,
|
|
(
|
|
KeepGoing = do_not_keep_going,
|
|
StopOrContinue = soc_stop
|
|
;
|
|
KeepGoing = do_keep_going,
|
|
StopOrContinue = soc_continue
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Debugging, progress, and error messages.
|
|
%
|
|
|
|
debug_make_msg(Globals, MakeMsgPred, Msg) :-
|
|
globals.lookup_bool_option(Globals, debug_make, DebugMake),
|
|
(
|
|
DebugMake = no,
|
|
Msg = ""
|
|
;
|
|
DebugMake = yes,
|
|
MakeMsgPred(Msg)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
verbose_make_one_part_msg(Globals, Part1, Msg) :-
|
|
option_set_one_part_msg(Globals, verbose_make, Part1, Msg).
|
|
|
|
verbose_make_two_part_msg(Globals, Part1, Part2, Msg) :-
|
|
option_set_two_part_msg(Globals, verbose_make, Part1, Part2, Msg).
|
|
|
|
verbose_make_three_part_msg(Globals, Part1, Part2, Part3, Msg) :-
|
|
option_set_three_part_msg(Globals, verbose_make, Part1, Part2, Part3, Msg).
|
|
|
|
verbose_make_four_part_msg(Globals, Part1, Part2, Part3, Part4, Msg) :-
|
|
option_set_four_part_msg(Globals, verbose_make, Part1, Part2, Part3,
|
|
Part4, Msg).
|
|
|
|
option_set_one_part_msg(Globals, Option, Part1, Msg) :-
|
|
globals.lookup_bool_option(Globals, Option, OptionValue),
|
|
(
|
|
OptionValue = no,
|
|
Msg = ""
|
|
;
|
|
OptionValue = yes,
|
|
string.format("%s\n", [s(Part1)], Msg)
|
|
).
|
|
|
|
option_set_two_part_msg(Globals, Option, Part1, Part2, Msg) :-
|
|
globals.lookup_bool_option(Globals, Option, OptionValue),
|
|
(
|
|
OptionValue = no,
|
|
Msg = ""
|
|
;
|
|
OptionValue = yes,
|
|
string.format("%s %s\n", [s(Part1), s(Part2)], Msg)
|
|
).
|
|
|
|
option_set_three_part_msg(Globals, Option, Part1, Part2, Part3, Msg) :-
|
|
globals.lookup_bool_option(Globals, Option, OptionValue),
|
|
(
|
|
OptionValue = no,
|
|
Msg = ""
|
|
;
|
|
OptionValue = yes,
|
|
string.format("%s %s %s\n", [s(Part1), s(Part2), s(Part3)], Msg)
|
|
).
|
|
|
|
option_set_four_part_msg(Globals, Option, Part1, Part2, Part3, Part4, Msg) :-
|
|
globals.lookup_bool_option(Globals, Option, OptionValue),
|
|
(
|
|
OptionValue = no,
|
|
Msg = ""
|
|
;
|
|
OptionValue = yes,
|
|
string.format("%s %s %s %s\n",
|
|
[s(Part1), s(Part2), s(Part3), s(Part4)], Msg)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
maybe_making_filename_msg(Globals, FileName, Msg) :-
|
|
globals.lookup_bool_option(Globals, verbose_make, VerboseMake),
|
|
(
|
|
VerboseMake = no,
|
|
Msg = ""
|
|
;
|
|
VerboseMake = yes,
|
|
string.format("Making %s\n", [s(FileName)], Msg)
|
|
).
|
|
|
|
maybe_reanalyse_modules_msg(Globals, Msg) :-
|
|
globals.lookup_bool_option(Globals, verbose_make, VerboseMake),
|
|
(
|
|
VerboseMake = no,
|
|
Msg = ""
|
|
;
|
|
VerboseMake = yes,
|
|
Msg = "Reanalysing invalid/suboptimal modules\n"
|
|
).
|
|
|
|
file_error_msg(FileName, Msg) :-
|
|
string.format("** Error making `%s'.\n", [s(FileName)], Msg).
|
|
% with_locked_stdout(Info, io.write_string(Msg), !IO).
|
|
|
|
maybe_warn_up_to_date_target_msg(Globals, Target, FileName, !Info, Msg) :-
|
|
globals.lookup_bool_option(Globals, warn_up_to_date, Warn),
|
|
CmdLineTargets0 = make_info_get_command_line_targets(!.Info),
|
|
(
|
|
Warn = yes,
|
|
( if set.member(Target, CmdLineTargets0) then
|
|
string.format("** Nothing to be done for `%s'.\n", [s(FileName)],
|
|
Msg)
|
|
else
|
|
Msg = ""
|
|
)
|
|
;
|
|
Warn = no,
|
|
Msg = ""
|
|
),
|
|
set.delete(Target, CmdLineTargets0, CmdLineTargets),
|
|
make_info_set_command_line_targets(CmdLineTargets, !Info).
|
|
|
|
maybe_symlink_or_copy_linked_target_msg(Globals, FileName, Msg) :-
|
|
globals.lookup_bool_option(Globals, verbose_make, VerboseMake),
|
|
(
|
|
VerboseMake = no,
|
|
Msg = ""
|
|
;
|
|
VerboseMake = yes,
|
|
string.format("Made symlink/copy of %s\n", [s(FileName)], Msg)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
maybe_write_msg(OutStream, Msg, !IO) :-
|
|
( if Msg = "" then
|
|
true
|
|
else
|
|
io.write_string(OutStream, Msg, !IO),
|
|
io.flush_output(OutStream, !IO)
|
|
).
|
|
|
|
maybe_write_msg_locked(OutStream, Info, Msg, !IO) :-
|
|
( if Msg = "" then
|
|
true
|
|
else
|
|
MaybeLock = make_info_get_maybe_stdout_lock(Info),
|
|
(
|
|
MaybeLock = yes(Lock),
|
|
lock_stdout(Lock, !IO),
|
|
io.write_string(OutStream, Msg, !IO),
|
|
io.flush_output(OutStream, !IO),
|
|
unlock_stdout(Lock, !IO)
|
|
;
|
|
MaybeLock = no,
|
|
io.write_string(OutStream, Msg, !IO),
|
|
io.flush_output(OutStream, !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Timing.
|
|
%
|
|
|
|
:- pragma foreign_proc("C",
|
|
get_real_milliseconds(Time::out, _IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
|
|
"
|
|
Time = MR_get_real_milliseconds();
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
get_real_milliseconds(Time::out, _IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
|
|
"
|
|
Time = System.Environment.TickCount;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
get_real_milliseconds(Time::out, _IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
|
|
"
|
|
// The loss of precision is acceptable for mmc --make.
|
|
Time = (int) System.currentTimeMillis();
|
|
").
|
|
|
|
get_real_milliseconds(_, _, _) :-
|
|
sorry($file, $pred).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module make.util.
|
|
%---------------------------------------------------------------------------%
|