Files
mercury/compiler/make.check_up_to_date.m
Zoltan Somogyi 8e8d34f857 Rename dependency_files as target_ids.
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.
2025-09-24 15:06:40 +10:00

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.
%---------------------------------------------------------------------------%