mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
compiler/make.build.m:
compiler/make.check_up_to_date.m:
compiler/make.file_names.m:
compiler/make.find_local_modules.m:
compiler/make.hash.m:
compiler/make.index_set.m:
compiler/make.library_install.m:
compiler/make.make_info.m:
compiler/make.module_target.m:
compiler/make.prereqs.m:
compiler/make.prereqs_cache.m:
compiler/make.program_target.m:
compiler/make.timestamp.m:
compiler/make.top_level.m:
compiler/make.util.m:
Perform the above rename, and similar ones as well.
Replace the dep_target/dep_file distinction, which was misleading,
with the merc_target/non_merc_target distinction. (Both name files
that can be prerequisites, but the Mercury-related files can be built
by mmc --make.)
In one case, make unnecessarily-exported type definitions private.
compiler/mercury_compile_make_hlds.m:
compiler/mmakefiles.m:
Perform similar renames.
800 lines
33 KiB
Mathematica
800 lines
33 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2023-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.check_up_to_date.m.
|
|
%
|
|
% Code to check whether the files that another file depends on are up-to-date.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module make.check_up_to_date.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.maybe_util.
|
|
:- import_module libs.timestamp.
|
|
:- import_module make.make_info.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Values of this type represent the set of files that building a make
|
|
% target can generate, either from scratch, or by overwriting a
|
|
% previously-generated file. Basically, this is the set of files
|
|
% that would be on left hand side of a make rule:
|
|
%
|
|
% lhs_files: rhs_files
|
|
% action
|
|
%
|
|
:- type make_lhs_files
|
|
---> make_lhs_files(
|
|
% The first two fields contain the set of files on the
|
|
% left hand side of the rule. The two fields partition
|
|
% those files based on the answer to the question:
|
|
% does this file have a corresponding date file? More
|
|
% precisely, does date_file_extension succeed for the
|
|
% target file's target type? If it does not, it is part of
|
|
% tf_dateless_target_files; if it does, it is part of
|
|
% tf_dated_target_files.
|
|
mlf_dateless_target_files :: list(target_file),
|
|
mlf_dated_target_files :: list(target_file),
|
|
|
|
% The names of the date files of the files in the
|
|
% tf_dated_target_files field.
|
|
mlf_date_files :: list(file_name),
|
|
|
|
% The names of any target code files that the make action
|
|
% whose effects we are describing may create or update.
|
|
% Currently, this will be the set of files named in fact_table
|
|
% declarations.
|
|
mlf_foreign_code_files :: list(file_name)
|
|
).
|
|
|
|
:- type maybe_oldest_lhs_file
|
|
---> some_lhs_file_is_missing
|
|
; all_lhs_files_exist_oldest_timestamp(timestamp).
|
|
|
|
:- type should_rebuild_lhs
|
|
---> all_lhs_files_up_to_date
|
|
; some_lhs_file_needs_rebuilding.
|
|
|
|
:- type lhs_result
|
|
---> can_rebuild_lhs(should_rebuild_lhs)
|
|
; rhs_error.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% must_or_should_we_rebuild_lhs(ProgressStream, Globals,
|
|
% TargetFile, TargetFileName, MakeLhsFiles, RhsTargetIds, LhsResult,
|
|
% !Info, !IO):
|
|
%
|
|
% The TargetFile and TargetFileName arguments specify the principal
|
|
% target of a make rule in structured and string form respectively,
|
|
% while MakeLhsFiles and RhsTargetIds specify the full set of the lhs and
|
|
% rhs files respectively.
|
|
%
|
|
% Decide whether we either
|
|
%
|
|
% - *must* execute the action of a mmc --make rule, because one or more
|
|
% of the lhs files do not exist; or
|
|
%
|
|
% - if all the lhs files, whether we *should* execute the action, because
|
|
% one or more of the lhs files are out-of-date with respect to the rhs
|
|
% files.
|
|
%
|
|
% As part of the above process, check whether we have all the info we need
|
|
% about the rhs files to make that decision. If not, then we cannot execute
|
|
% the action, even if we otherwise would like to do so.
|
|
%
|
|
% Return the decision in LhsResult.
|
|
%
|
|
:- pred must_or_should_we_rebuild_lhs(io.text_output_stream::in, globals::in,
|
|
target_file::in, file_name::in, make_lhs_files::in,
|
|
list(target_id)::in, lhs_result::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% should_we_rebuild_lhs(ProgressStream, Globals, TargetFileName,
|
|
% MaybeOldestLhsFile, BuildRhsSucceeded, RhsTargetIds, LhsResult,
|
|
% !Info, !IO):
|
|
%
|
|
% Decide whether we should execute the make action to (re)build
|
|
% the lhs of a mmc --make rule, returning that decision in LhsResult.
|
|
%
|
|
% MaybeOldestLhsFile specifies whether all the lhs files already exist,
|
|
% and if they do, what the timestamp of the oldest of these is.
|
|
% TargetFileName is the name of the main target on the lhs of the rule.
|
|
%
|
|
% RhsTargetIds lists the files on the rhs of the rule.
|
|
%
|
|
% BuildRhsSucceeded says whether the building of RhsTargetIds has
|
|
% succeeded.
|
|
% XXX This argument should not be needed; if it is did_not_succeed, then
|
|
% this predicate should not be called. The only call site of this predicate
|
|
% in must_or_should_we_rebuild_lhs below *does* always pass succeeded,
|
|
% but this is not necessarily true for the calls in make.program_target.m
|
|
% to this predicate, and to should_we_rebuild_lhs_given_timestamps.
|
|
%
|
|
% XXX This predicate double-checks whether the building of RhsTargetIds
|
|
% succeeded by looking up their dependency statuses. This should NOT be
|
|
% necessary; BuildRhsSucceeded = succeeded *should* imply that all these
|
|
% files have target_status_up_to_date. This is ensured by code before
|
|
% the one call to should_we_rebuild_lhs_given_timestamps in
|
|
% make.program_target.m, but not (as far as I, zs, can see) in the two
|
|
% calls to should_we_rebuild_lhs, in must_or_should_we_rebuild_lhs below,
|
|
% and in make.program_target.m.
|
|
%
|
|
% XXX The best way to fix this would probably be to replace the calls
|
|
% in make.program_target.m to should_we_rebuild_lhs and to
|
|
% should_we_rebuild_lhs_given_timestamps by a SINGLE call to this
|
|
% predicate.
|
|
%
|
|
:- pred should_we_rebuild_lhs(io.text_output_stream::in,
|
|
globals::in, file_name::in, maybe_oldest_lhs_file::in, maybe_succeeded::in,
|
|
list(target_id)::in, lhs_result::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% should_we_rebuild_lhs_given_timestamps(ProgressStream, Globals,
|
|
% TargetFileName, MaybeOldestLhsFile, BuildRhsSucceeded,
|
|
% RhsTargetStatusTuples, RhsMaybeTimestamps, LhsResult, !IO):
|
|
%
|
|
% A version of the predicate above that
|
|
%
|
|
% - assumes that the failure of the building the rhs files has already
|
|
% been handled, and which
|
|
%
|
|
% - requires its callers to supply it with the timestamps (if any)
|
|
% of the rhs files.
|
|
%
|
|
% Exported for make.program_target.m.
|
|
%
|
|
:- pred should_we_rebuild_lhs_given_timestamps(io.text_output_stream::in,
|
|
globals::in, file_name::in, maybe_oldest_lhs_file::in, maybe_succeeded::in,
|
|
list(target_status_result)::in, list(maybe_error(timestamp))::in,
|
|
lhs_result::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type target_status_result
|
|
---> target_status_result(
|
|
target_id,
|
|
maybe(file_name),
|
|
target_status
|
|
).
|
|
|
|
:- pred get_target_id_status(io.text_output_stream::in, globals::in,
|
|
target_id::in, target_status_result::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module analysis.
|
|
:- import_module analysis.framework.
|
|
:- import_module analysis.operations.
|
|
:- import_module libs.options.
|
|
:- import_module make.file_names.
|
|
:- import_module make.get_module_dep_info.
|
|
:- import_module make.timestamp.
|
|
:- import_module make.util.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.find_module.
|
|
:- import_module parse_tree.module_dep_info.
|
|
:- import_module transform_hlds.
|
|
:- import_module transform_hlds.mmc_analysis.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module dir.
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module version_hash_table.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
must_or_should_we_rebuild_lhs(ProgressStream, Globals,
|
|
TargetFile, TargetFileName, MakeLhsFiles, RhsTargetIds, LhsResult,
|
|
!Info, !IO) :-
|
|
% Check whether all the lhs files exist, because if some are missing,
|
|
% then we need to execute the action.
|
|
MakeLhsFiles = make_lhs_files(DatelessLhsTargetFiles,
|
|
DatedLhsTargetFiles, LhsDateFileNames, LhsForeignCodeFileNames),
|
|
list.map_foldl2(
|
|
get_target_timestamp(ProgressStream, Globals),
|
|
DatelessLhsTargetFiles, DatelessLhsFileTimestamps, !Info, !IO),
|
|
list.map_foldl2(
|
|
get_target_timestamp(ProgressStream, Globals),
|
|
DatedLhsTargetFiles, DatedLhsFileTimestamps, !Info, !IO),
|
|
( if
|
|
( list.member(error(_), DatelessLhsFileTimestamps)
|
|
; list.member(error(_), DatedLhsFileTimestamps)
|
|
)
|
|
then
|
|
% Some lhs file does not exist.
|
|
% XXX MAKE The one that does not exist may not be TargetFileName.
|
|
debug_make_msg(Globals,
|
|
string.format("%s: target file does not exist\n",
|
|
[s(TargetFileName)]),
|
|
DebugMsg),
|
|
maybe_write_msg(ProgressStream, DebugMsg, !IO),
|
|
LhsResult = can_rebuild_lhs(some_lhs_file_needs_rebuilding)
|
|
else
|
|
% All the lhs files exist, so check whether they are all up-to-date.
|
|
( if
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
TargetType = module_target_analysis_registry
|
|
then
|
|
should_we_force_reanalysis_of_suboptimal_module(Globals,
|
|
ModuleName, ForceReanalysis, !.Info, !IO)
|
|
else
|
|
ForceReanalysis = no
|
|
),
|
|
(
|
|
ForceReanalysis = yes,
|
|
LhsResult = can_rebuild_lhs(some_lhs_file_needs_rebuilding)
|
|
;
|
|
ForceReanalysis = no,
|
|
% Compare the oldest of the timestamps of the lhs files
|
|
% with the timestamps of the rhs.
|
|
GetLocalTimestamps = get_file_timestamp(search_auth_cur_dir),
|
|
list.map2_foldl2(GetLocalTimestamps,
|
|
LhsDateFileNames, _, LhsDateFileTimestamps, !Info, !IO),
|
|
list.map2_foldl2(GetLocalTimestamps,
|
|
LhsForeignCodeFileNames, _, LhsForeignCodeFileTimestamps,
|
|
!Info, !IO),
|
|
AllLhsTimestamps = DatelessLhsFileTimestamps ++
|
|
LhsDateFileTimestamps ++ LhsForeignCodeFileTimestamps,
|
|
find_oldest_lhs_file(AllLhsTimestamps, MaybeOldestLhsTimestamp),
|
|
% This predicate gets called only if the (re)building
|
|
% of the rhs files succeeded.
|
|
BuildRhsSucceeded = succeeded,
|
|
should_we_rebuild_lhs(ProgressStream, Globals, TargetFileName,
|
|
MaybeOldestLhsTimestamp, BuildRhsSucceeded, RhsTargetIds,
|
|
LhsResult, !Info, !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred should_we_force_reanalysis_of_suboptimal_module(globals::in,
|
|
module_name::in, bool::out, make_info::in, io::di, io::uo) is det.
|
|
|
|
should_we_force_reanalysis_of_suboptimal_module(Globals, ModuleName,
|
|
ForceReanalysis, Info, !IO) :-
|
|
( if make_info_get_reanalysis_passes(Info) > 0 then
|
|
do_read_module_overall_status(mmc, Globals, ModuleName, AnalysisStatus,
|
|
!IO),
|
|
(
|
|
( AnalysisStatus = suboptimal
|
|
; AnalysisStatus = invalid
|
|
),
|
|
ForceReanalysis = yes
|
|
;
|
|
AnalysisStatus = optimal,
|
|
ForceReanalysis = no
|
|
)
|
|
else
|
|
ForceReanalysis = no
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred find_oldest_lhs_file(list(maybe_error(timestamp))::in,
|
|
maybe_oldest_lhs_file::out) is det.
|
|
|
|
find_oldest_lhs_file(LhsMaybeTimestamps, MaybeOldestLhsTimestamp) :-
|
|
(
|
|
LhsMaybeTimestamps = [],
|
|
unexpected($pred, "LhsMaybeTimestamps = []")
|
|
;
|
|
LhsMaybeTimestamps = [HeadLhsMaybeTimestamp | TailLhsMaybeTimestamps],
|
|
(
|
|
HeadLhsMaybeTimestamp = error(_),
|
|
MaybeOldestLhsTimestamp = some_lhs_file_is_missing
|
|
;
|
|
HeadLhsMaybeTimestamp = ok(HeadLhsTimestamp),
|
|
find_oldest_lhs_file_loop(TailLhsMaybeTimestamps, HeadLhsTimestamp,
|
|
MaybeOldestLhsTimestamp)
|
|
)
|
|
).
|
|
|
|
:- pred find_oldest_lhs_file_loop(list(maybe_error(timestamp))::in,
|
|
timestamp::in, maybe_oldest_lhs_file::out) is det.
|
|
|
|
find_oldest_lhs_file_loop(LhsMaybeTimestamps, !.OldestLhsTimestamp,
|
|
MaybeOldestLhsTimestamp) :-
|
|
(
|
|
LhsMaybeTimestamps = [HeadLhsMaybeTimestamp | TailLhsMaybeTimestamps],
|
|
(
|
|
HeadLhsMaybeTimestamp = error(_),
|
|
MaybeOldestLhsTimestamp = some_lhs_file_is_missing
|
|
;
|
|
HeadLhsMaybeTimestamp = ok(HeadLhsTimestamp),
|
|
( if compare((<), HeadLhsTimestamp, !.OldestLhsTimestamp) then
|
|
!:OldestLhsTimestamp = HeadLhsTimestamp
|
|
else
|
|
true
|
|
),
|
|
find_oldest_lhs_file_loop(TailLhsMaybeTimestamps,
|
|
!.OldestLhsTimestamp, MaybeOldestLhsTimestamp)
|
|
)
|
|
;
|
|
LhsMaybeTimestamps = [],
|
|
MaybeOldestLhsTimestamp =
|
|
all_lhs_files_exist_oldest_timestamp(!.OldestLhsTimestamp)
|
|
).
|
|
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
should_we_rebuild_lhs(ProgressStream, Globals, TargetFileName,
|
|
MaybeOldestLhsFile, BuildRhsSucceeded, RhsTargetIds, LhsResult,
|
|
!Info, !IO) :-
|
|
list.map_foldl2(get_target_id_status(ProgressStream, Globals),
|
|
RhsTargetIds, RhsTargetStatusTuples, !Info, !IO),
|
|
list.filter(
|
|
( pred(target_status_result(_, _, TargetStatus)::in) is semidet :-
|
|
TargetStatus \= target_status_up_to_date
|
|
), RhsTargetStatusTuples, UnbuiltRhsTargetStatusTuples0),
|
|
(
|
|
UnbuiltRhsTargetStatusTuples0 = [_ | _],
|
|
get_target_file_names(Globals,
|
|
UnbuiltRhsTargetStatusTuples0, UnbuiltRhsTargetStatusTuples, !IO),
|
|
debug_make_msg(Globals,
|
|
describe_unbuilt_dependencies(TargetFileName,
|
|
UnbuiltRhsTargetStatusTuples),
|
|
DebugMsg),
|
|
maybe_write_msg(ProgressStream, DebugMsg, !IO),
|
|
LhsResult = rhs_error
|
|
;
|
|
UnbuiltRhsTargetStatusTuples0 = [],
|
|
debug_make_msg(Globals,
|
|
string.format("%s: finished dependencies\n", [s(TargetFileName)]),
|
|
DebugMsg),
|
|
maybe_write_msg(ProgressStream, DebugMsg, !IO),
|
|
list.map_foldl2(get_target_id_timestamp(ProgressStream, Globals),
|
|
RhsTargetIds, RhsMaybeTimestamps, !Info, !IO),
|
|
|
|
should_we_rebuild_lhs_given_timestamps(ProgressStream, Globals,
|
|
TargetFileName, MaybeOldestLhsFile, BuildRhsSucceeded,
|
|
RhsTargetStatusTuples, RhsMaybeTimestamps, LhsResult, !IO)
|
|
).
|
|
|
|
should_we_rebuild_lhs_given_timestamps(ProgressStream, Globals, TargetFileName,
|
|
MaybeOldestLhsFile, BuildRhsSucceeded,
|
|
RhsTargetStatusTuples, RhsMaybeTimestamps, LhsResult, !IO) :-
|
|
(
|
|
MaybeOldestLhsFile = some_lhs_file_is_missing,
|
|
% The missing file must be rebuilt, even if all other LHS files
|
|
% are up-to-date.
|
|
%
|
|
% XXX However, while TargetFileName will be one of the files
|
|
% on the lhs of the implicit mmc --make rule, it may be a file
|
|
% *other than* TargetFileName that does not exist, so this message
|
|
% *may* be misleading. (Of course, in the common case, the lhs
|
|
% file list will contain just one file, in which case the missing
|
|
% file *has* to be TargetFileName.)
|
|
LhsResult = can_rebuild_lhs(some_lhs_file_needs_rebuilding),
|
|
debug_make_msg(Globals,
|
|
string.format("%s does not exist.\n", [s(TargetFileName)]),
|
|
DebugMsg),
|
|
maybe_write_msg(ProgressStream, DebugMsg, !IO)
|
|
;
|
|
MaybeOldestLhsFile =
|
|
all_lhs_files_exist_oldest_timestamp(OldestLhsFileTimestamp),
|
|
find_timestamps_and_errors(RhsMaybeTimestamps,
|
|
[], RhsTimestamps, not_found_error, FoundError),
|
|
(
|
|
FoundError = found_error,
|
|
LhsResult = rhs_error,
|
|
get_target_file_names(Globals, RhsTargetStatusTuples,
|
|
FilledInRhsTargetStatusTuples, !IO),
|
|
(
|
|
BuildRhsSucceeded = succeeded,
|
|
% Something has gone wrong -- building the target has
|
|
% succeeded, but there are some files missing.
|
|
% Report an error.
|
|
rhs_timestamps_missing_msg(TargetFileName, BuildRhsSucceeded,
|
|
FilledInRhsTargetStatusTuples,
|
|
RhsMaybeTimestamps, MissingDepsMsg),
|
|
io.write_string(ProgressStream, MissingDepsMsg, !IO)
|
|
;
|
|
BuildRhsSucceeded = did_not_succeed,
|
|
debug_make_msg(Globals,
|
|
rhs_timestamps_missing_msg(TargetFileName,
|
|
BuildRhsSucceeded, FilledInRhsTargetStatusTuples,
|
|
RhsMaybeTimestamps),
|
|
MaybeMissingDepsMsg),
|
|
maybe_write_msg(ProgressStream, MaybeMissingDepsMsg, !IO)
|
|
)
|
|
;
|
|
FoundError = not_found_error,
|
|
globals.lookup_bool_option(Globals, part_opmode_rebuild, Rebuild),
|
|
(
|
|
Rebuild = yes,
|
|
% With `--rebuild', we always consider the lhs files to be
|
|
% out-of-date, regardless of their timestamps, or the
|
|
% timestamps of the rhs files.
|
|
ShouldRebuildLhs = some_lhs_file_needs_rebuilding
|
|
;
|
|
Rebuild = no,
|
|
is_any_rhs_file_newer_than_oldest_lhs(RhsTimestamps,
|
|
OldestLhsFileTimestamp, ShouldRebuildLhs),
|
|
(
|
|
ShouldRebuildLhs = some_lhs_file_needs_rebuilding,
|
|
get_target_file_names(Globals, RhsTargetStatusTuples,
|
|
FilledInRhsTargetStatusTuples, !IO),
|
|
debug_make_msg(Globals,
|
|
describe_newer_dependencies(TargetFileName,
|
|
OldestLhsFileTimestamp,
|
|
FilledInRhsTargetStatusTuples, RhsTimestamps),
|
|
DebugMsg),
|
|
maybe_write_msg(ProgressStream, DebugMsg, !IO)
|
|
;
|
|
ShouldRebuildLhs = all_lhs_files_up_to_date
|
|
)
|
|
),
|
|
LhsResult = can_rebuild_lhs(ShouldRebuildLhs)
|
|
)
|
|
).
|
|
|
|
:- type maybe_found_error
|
|
---> not_found_error
|
|
; found_error.
|
|
|
|
:- pred find_timestamps_and_errors(list(maybe_error(timestamp))::in,
|
|
list(timestamp)::in, list(timestamp)::out,
|
|
maybe_found_error::in, maybe_found_error::out) is det.
|
|
|
|
find_timestamps_and_errors([], !RhsTimestamps, !FoundError).
|
|
find_timestamps_and_errors([RhsMaybeTimestamp | RhsMaybeTimestamps],
|
|
!RhsTimestamps, !FoundError) :-
|
|
(
|
|
RhsMaybeTimestamp = error(_),
|
|
!:FoundError = found_error
|
|
;
|
|
RhsMaybeTimestamp = ok(RhsTimestamp),
|
|
!:RhsTimestamps = [RhsTimestamp | !.RhsTimestamps]
|
|
),
|
|
find_timestamps_and_errors(RhsMaybeTimestamps,
|
|
!RhsTimestamps, !FoundError).
|
|
|
|
:- pred is_any_rhs_file_newer_than_oldest_lhs(list(timestamp)::in,
|
|
timestamp::in, should_rebuild_lhs::out) is det.
|
|
|
|
is_any_rhs_file_newer_than_oldest_lhs(RhsTimestamps, OldestLhsTimestamp,
|
|
Result) :-
|
|
(
|
|
RhsTimestamps = [HeadRhsTimestamp | TailRhsTimestamps],
|
|
( if compare((>), HeadRhsTimestamp, OldestLhsTimestamp) then
|
|
% The rhs file that HeadRhsTimestamp belongs to is newer than
|
|
% the oldest file on the lhs.
|
|
Result = some_lhs_file_needs_rebuilding
|
|
else
|
|
% The rhs file that HeadRhsTimestamp belongs to is NOT newer than
|
|
% the oldest file on the lhs; check the other rhs timestamps.
|
|
is_any_rhs_file_newer_than_oldest_lhs(TailRhsTimestamps,
|
|
OldestLhsTimestamp, Result)
|
|
)
|
|
;
|
|
RhsTimestamps = [],
|
|
% We have checked all of the rhs files' timestamps, and none are
|
|
% newer than the oldest file on the lhs.
|
|
Result = all_lhs_files_up_to_date
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_target_id_status(ProgressStream, Globals, TargetId, Result, !Info, !IO) :-
|
|
(
|
|
TargetId = non_merc_target(TargetFileName),
|
|
TargetStatusMap0 = make_info_get_target_status_map(!.Info),
|
|
( if
|
|
version_hash_table.search(TargetStatusMap0, TargetId, StatusPrime)
|
|
then
|
|
Status = StatusPrime
|
|
else
|
|
get_target_id_timestamp(ProgressStream, Globals,
|
|
TargetId, MaybeTimestamp, !Info, !IO),
|
|
(
|
|
MaybeTimestamp = ok(_),
|
|
Status = target_status_up_to_date
|
|
;
|
|
MaybeTimestamp = error(Error),
|
|
Status = target_status_error,
|
|
io.format(ProgressStream, "** Error: %s\n", [s(Error)], !IO)
|
|
),
|
|
version_hash_table.det_insert(TargetId, Status,
|
|
TargetStatusMap0, TargetStatusMap),
|
|
make_info_set_target_status_map(TargetStatusMap, !Info)
|
|
),
|
|
Result = target_status_result(TargetId, yes(TargetFileName), Status)
|
|
;
|
|
TargetId = merc_target(Target),
|
|
Target = target_file(ModuleName, FileType),
|
|
(
|
|
( FileType = module_target_source
|
|
; FileType = module_target_track_flags
|
|
),
|
|
% Source files are always up-to-date.
|
|
% .track_flags should already have been made, if required,
|
|
% so are also up-to-date.
|
|
% XXX LEGACY
|
|
module_target_file_to_file_name(Globals, $pred, Target,
|
|
TargetFileName, _TargetFileNameProposed, !IO),
|
|
ModuleTarget = module_target(module_target_source),
|
|
TopTargetFile = top_target_file(ModuleName, ModuleTarget),
|
|
maybe_warn_up_to_date_target_msg(Globals, TopTargetFile,
|
|
TargetFileName, !Info, UpToDateMsg),
|
|
maybe_write_msg(ProgressStream, UpToDateMsg, !IO),
|
|
MaybeTargetFileName = yes(TargetFileName),
|
|
Status = target_status_up_to_date,
|
|
Result =
|
|
target_status_result(TargetId, MaybeTargetFileName, Status)
|
|
;
|
|
( FileType = module_target_errors
|
|
; FileType = module_target_int0
|
|
; FileType = module_target_int1
|
|
; FileType = module_target_int2
|
|
; FileType = module_target_int3
|
|
; FileType = module_target_opt
|
|
; FileType = module_target_analysis_registry
|
|
; FileType = module_target_c_header(_)
|
|
; FileType = module_target_c_code
|
|
; FileType = module_target_csharp_code
|
|
; FileType = module_target_java_code
|
|
; FileType = module_target_java_class_code
|
|
; FileType = module_target_object_code(_)
|
|
; FileType = module_target_fact_table_object(_, _)
|
|
; FileType = module_target_xml_doc
|
|
),
|
|
% We pass TargetId, which contains Target, which contains
|
|
% ModuleName, because get_target_id_status_main_path needs
|
|
% all of them, and this way, it does not have to rebuild
|
|
% Target or TargetId.
|
|
get_target_id_status_main_path(ProgressStream, Globals,
|
|
TargetId, Target, ModuleName, Result, !Info, !IO)
|
|
)
|
|
).
|
|
|
|
:- pred get_target_id_status_main_path(io.text_output_stream::in,
|
|
globals::in, target_id::in, target_file::in, module_name::in,
|
|
target_status_result::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
:- pragma inline(pred(get_target_id_status_main_path/10)).
|
|
|
|
get_target_id_status_main_path(ProgressStream, Globals,
|
|
TargetId, Target, ModuleName, Result, !Info, !IO) :-
|
|
TargetStatusMap0 = make_info_get_target_status_map(!.Info),
|
|
% XXX The management of dependency file status map here is incorrect.
|
|
%
|
|
% The code here checks whether TargetId is in TargetStatusMap0, and
|
|
% if it is not there, it computes its Status, and then inserts that Status
|
|
% into TargetStatusMap. So far so good. The problem is that
|
|
%
|
|
% - the code of the else-part calls get_maybe_module_dep_info
|
|
% - which calls maybe_get_maybe_module_dep_info
|
|
% - which calls do_get_maybe_module_dep_info
|
|
% - which calls write_module_dep_files_for_source_file
|
|
% - which calls record_made_target
|
|
% - which calls record_made_target_given_maybe_touched_files
|
|
% - which calls update_target_status
|
|
%
|
|
% which adds an entry to the dependency file status map.
|
|
% This entry CAN be for TargetId, and if it is, then the call to
|
|
% version_hash_table.det_insert at the end of the else-part
|
|
% will throw an exception that leads to a compiler abort.
|
|
% The command "mmc --make --options-file xyz after_end_module.int3"
|
|
% in tests/invalid_nodepend exhibits this behavior as of 2023 oct 23,
|
|
% provided the given options file sets things up properly for mmc --make.
|
|
%
|
|
% Unfortunately, the right way to fix this is not clear. For example,
|
|
% replacing the version_hash_table.det_insert below with
|
|
% version_hash_table.set would fix this symptom, but I (zs) think
|
|
% that is unlikely to fix the underlying problem.
|
|
( if
|
|
version_hash_table.search(TargetStatusMap0, TargetId, StatusPrime)
|
|
then
|
|
Status = StatusPrime,
|
|
% In this common case, our caller does not need the target file name.
|
|
% Calling get_make_target_file_name to construct the target file name
|
|
% would therefore be an unnecessary cost, and as it happens,
|
|
% it would be an unnecessary LARGE cost in execution time.
|
|
MaybeTargetFileName = no
|
|
else
|
|
% XXX LEGACY
|
|
module_target_file_to_file_name(Globals, $pred,
|
|
Target, TargetFileName, _TargetFileNameProposed, !IO),
|
|
MaybeTargetFileName = yes(TargetFileName),
|
|
get_maybe_module_dep_info(ProgressStream, Globals, ModuleName,
|
|
MaybeModuleDepInfo, !Info, !IO),
|
|
(
|
|
MaybeModuleDepInfo = no_module_dep_info,
|
|
Status = target_status_error
|
|
;
|
|
MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo),
|
|
module_dep_info_get_source_file_dir(ModuleDepInfo, ModuleDir),
|
|
( if ModuleDir = dir.this_directory then
|
|
% XXX What is the reason for returning this value here?
|
|
Status = target_status_not_considered
|
|
else
|
|
% Targets from libraries are always considered to be
|
|
% up-to-date if they exist.
|
|
% XXX Presumably this code treats any code in another directory
|
|
% as if it were in a library.
|
|
get_target_timestamp_search(ProgressStream, Globals,
|
|
Target, MaybeTimestamp, !Info, !IO),
|
|
(
|
|
MaybeTimestamp = ok(_),
|
|
Status = target_status_up_to_date
|
|
;
|
|
MaybeTimestamp = error(Error),
|
|
Status = target_status_error,
|
|
string.format("** Error: file `%s' not found: %s\n",
|
|
[s(TargetFileName), s(Error)], ErrorMsg),
|
|
% Try to write this with one call to avoid
|
|
% interleaved output when doing parallel builds.
|
|
io.write_string(ProgressStream, ErrorMsg, !IO)
|
|
)
|
|
)
|
|
),
|
|
TargetStatusMap1 = make_info_get_target_status_map(!.Info),
|
|
version_hash_table.det_insert(TargetId, Status,
|
|
TargetStatusMap1, TargetStatusMap),
|
|
make_info_set_target_status_map(TargetStatusMap, !Info)
|
|
),
|
|
Result = target_status_result(TargetId, MaybeTargetFileName, Status).
|
|
|
|
%---------------------%
|
|
|
|
% This type is similar to target_status_result, but its second argument
|
|
% has type file_name, not maybe(file_name).
|
|
:- type target_status_known_file
|
|
---> target_status_known_file(
|
|
target_id,
|
|
file_name,
|
|
target_status
|
|
).
|
|
|
|
:- pred get_target_file_names(globals::in,
|
|
list(target_status_result)::in,
|
|
list(target_status_known_file)::out, io::di, io::uo) is det.
|
|
|
|
get_target_file_names(Globals, Tuples0, Tuples, !IO) :-
|
|
list.map_foldl(get_target_file_name(Globals), Tuples0, Tuples, !IO).
|
|
|
|
:- pred get_target_file_name(globals::in, target_status_result::in,
|
|
target_status_known_file::out, io::di, io::uo) is det.
|
|
|
|
get_target_file_name(Globals, Tuple0, Tuple, !IO) :-
|
|
Tuple0 = target_status_result(TargetId, MaybeTargetFileName, Status),
|
|
(
|
|
MaybeTargetFileName = yes(TargetFileName)
|
|
;
|
|
MaybeTargetFileName = no,
|
|
% XXX LEGACY
|
|
target_id_to_file_name(Globals, TargetId,
|
|
TargetFileName, _TargetFileNameProposed, !IO)
|
|
),
|
|
Tuple = target_status_known_file(TargetId, TargetFileName, Status).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to construct messages for all users of mmc --make.
|
|
%
|
|
|
|
:- pred rhs_timestamps_missing_msg(file_name::in,
|
|
maybe_succeeded::in, list(target_status_known_file)::in,
|
|
list(maybe_error(timestamp))::in, string::out) is det.
|
|
|
|
rhs_timestamps_missing_msg(TargetFileName, BuildRhsSucceeded,
|
|
RhsTargetStatusTuples, RhsTimestamps, Msg) :-
|
|
assoc_list.from_corresponding_lists(RhsTargetStatusTuples, RhsTimestamps,
|
|
RhsTimestampAL),
|
|
list.filter_map(
|
|
( pred(Pair::in, Tuple::out) is semidet :-
|
|
Pair = Tuple - error(_)
|
|
), RhsTimestampAL, ErrorRhsStatusTuples),
|
|
GetFileName = (func(target_status_known_file(_, FN, _)) = FN),
|
|
ErrorFileNames = list.map(GetFileName, ErrorRhsStatusTuples),
|
|
list.sort(ErrorFileNames, SortedErrorFileNames),
|
|
SortedErrorFileNamesStr = string.join_list(", ", SortedErrorFileNames),
|
|
% This line can get very long.
|
|
string.format("** dependencies for `%s' do not exist: %s\n",
|
|
[s(TargetFileName), s(SortedErrorFileNamesStr)], DoNotExistMsg),
|
|
(
|
|
BuildRhsSucceeded = succeeded,
|
|
Msg = DoNotExistMsg ++
|
|
"** This indicates a bug in `mmc --make'.\n"
|
|
;
|
|
BuildRhsSucceeded = did_not_succeed,
|
|
Msg = DoNotExistMsg
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to construct messages that can help debug mmc --make.
|
|
%
|
|
|
|
:- pred describe_unbuilt_dependencies(file_name::in,
|
|
list(target_status_known_file)::in, string::out) is det.
|
|
|
|
describe_unbuilt_dependencies(TargetFileName, UnbuiltDependencies,
|
|
UnbuiltDependenciesDesc) :-
|
|
string.format("%s: dependencies could not be built.\n\t",
|
|
[s(TargetFileName)], Header),
|
|
list.map(describe_target_dependency_status, UnbuiltDependencies,
|
|
UnbuiltDependencyDescs),
|
|
string.append_list([Header | UnbuiltDependencyDescs],
|
|
UnbuiltDependenciesDesc).
|
|
|
|
:- pred describe_target_dependency_status(target_status_known_file::in,
|
|
string::out) is det.
|
|
|
|
describe_target_dependency_status(TargetTuple, Desc) :-
|
|
TargetTuple =
|
|
target_status_known_file(_, TargetFileName, TargetStatus),
|
|
(
|
|
TargetStatus = target_status_not_considered,
|
|
TargetStatusStr = "target_status_not_considered"
|
|
;
|
|
TargetStatus = target_status_being_built,
|
|
TargetStatusStr = "target_status_being_built"
|
|
;
|
|
TargetStatus = target_status_up_to_date,
|
|
TargetStatusStr = "target_status_up_to_date"
|
|
;
|
|
TargetStatus = target_status_error,
|
|
TargetStatusStr = "target_status_error"
|
|
),
|
|
string.format("\t%s - %s\n", [s(TargetFileName), s(TargetStatusStr)],
|
|
Desc).
|
|
|
|
%---------------------%
|
|
|
|
:- pred describe_newer_dependencies(string::in, timestamp::in,
|
|
list(target_status_known_file)::in, list(timestamp)::in,
|
|
string::out) is det.
|
|
|
|
describe_newer_dependencies(TargetFileName, OldestLhsFileTimestamp,
|
|
RhsTargetStatusTuples, RhsTimestamps, Desc) :-
|
|
string.format("%s [%s]: newer dependencies:\n",
|
|
[s(TargetFileName), s(string(OldestLhsFileTimestamp))], Header),
|
|
assoc_list.from_corresponding_lists(RhsTargetStatusTuples, RhsTimestamps,
|
|
RhsTimestampAL),
|
|
list.filter(
|
|
( pred((_TargetStatusTuple - RhsTimestamp)::in) is semidet :-
|
|
compare((>), RhsTimestamp, OldestLhsFileTimestamp)
|
|
), RhsTimestampAL, NewerRhsTimestampAL),
|
|
list.sort(NewerRhsTimestampAL, SortedNewerRhsTimestampAL),
|
|
list.map(describe_target_file_and_timestamp, SortedNewerRhsTimestampAL,
|
|
NewerDescs),
|
|
string.append_list([Header | NewerDescs], Desc).
|
|
|
|
:- pred describe_target_file_and_timestamp(
|
|
pair(target_status_known_file, timestamp)::in, string::out) is det.
|
|
|
|
describe_target_file_and_timestamp(TargetStatusTuple - Timestamp, Desc) :-
|
|
TargetStatusTuple = target_status_known_file(TargetId, TargetFileName, _),
|
|
string.format("\t%s %s %s\n",
|
|
[s(string(TargetId)), s(TargetFileName), s(string(Timestamp))], Desc).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module make.check_up_to_date.
|
|
%---------------------------------------------------------------------------%
|