mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +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.
449 lines
18 KiB
Mathematica
449 lines
18 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.timestamp.m.
|
|
% Authors: stayl, wangp.
|
|
%
|
|
% Timestamp handling predicates used to implement `mmc --make'.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module make.timestamp.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.timestamp.
|
|
:- import_module make.make_info.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.find_module.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func init_target_file_timestamp_map = target_file_timestamp_map.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Find the timestamp for the given target.
|
|
%
|
|
:- pred get_target_id_timestamp(io.text_output_stream::in, globals::in,
|
|
target_id::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% get_target_timestamp(ProgressStream, Globals, TargetFile,
|
|
% MaybeTimestamp, !Info, !IO):
|
|
%
|
|
% Return the timestamp for TargetFile, if it exists.
|
|
%
|
|
:- pred get_target_timestamp(io.text_output_stream::in, globals::in,
|
|
target_file::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% get_target_timestamp_search(ProgressStream, Globals, TargetFile,
|
|
% MaybeTimestamp, !Info, !IO):
|
|
%
|
|
% Search for TargetFile in the places where its target type indicates
|
|
% it should be searched for, and return its timestamp if it exists.
|
|
%
|
|
:- pred get_target_timestamp_search(io.text_output_stream::in, globals::in,
|
|
target_file::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% get_file_timestamp(SearchAuthDirs, FileName,
|
|
% SearchDirs, MaybeTimestamp, !Info, !IO):
|
|
%
|
|
% Find the timestamp of the first file matching the given
|
|
% file name in one of the search directories. We return the list of
|
|
% directories we search in SearchDirs, or, if we got MaybeTimestamp
|
|
% from the cache, we return the list of directories we searched
|
|
% when the cache entry was created.
|
|
%
|
|
:- pred get_file_timestamp(search_auth_dirs::in, file_name::in,
|
|
list(dir_name)::out, maybe_error(timestamp)::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 make.file_names.
|
|
:- import_module make.get_module_dep_info.
|
|
:- import_module make.hash.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.module_dep_info.
|
|
:- import_module transform_hlds.
|
|
:- import_module transform_hlds.mmc_analysis.
|
|
|
|
:- import_module dir.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module version_hash_table.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_target_file_timestamp_map =
|
|
version_hash_table.unsafe_init_default(target_file_hash).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_target_id_timestamp(ProgressStream, Globals, TargetId,
|
|
MaybeTimestamp, !Info, !IO) :-
|
|
(
|
|
TargetId = non_merc_target(FileName),
|
|
get_file_timestamp(search_auth_cur_dir, FileName,
|
|
_SearchDirs, MaybeTimestamp, !Info, !IO)
|
|
;
|
|
TargetId = merc_target(Target),
|
|
get_target_timestamp_search(ProgressStream, Globals, Target,
|
|
MaybeTimestamp0, !Info, !IO),
|
|
( if
|
|
Target = target_file(_, module_target_c_header(header_mih)),
|
|
MaybeTimestamp0 = ok(_)
|
|
then
|
|
% Don't rebuild the `.o' file if an irrelevant part of a
|
|
% `.mih' file has changed. If a relevant part of a `.mih'
|
|
% file changed, the interface files of the imported module
|
|
% must have changed in a way that would force the `.c' and
|
|
% `.o' files of the current module to be rebuilt.
|
|
MaybeTimestamp = ok(oldest_timestamp)
|
|
else
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_target_timestamp(ProgressStream, Globals, TargetFile, MaybeTimestamp,
|
|
!Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
( if TargetType = module_target_analysis_registry then
|
|
% XXX LEGACY
|
|
module_target_to_file_name(Globals, $pred,
|
|
TargetType, ModuleName, FileName, _FileNameProposed, !IO),
|
|
get_target_timestamp_analysis_registry(ProgressStream, Globals,
|
|
TargetFile, FileName, MaybeTimestamp, !Info, !IO)
|
|
else
|
|
( if is_timestamp_in_cache(!.Info, TargetFile, Timestamp) then
|
|
trace [compile_time(flag("target_timestamp_cache")), io(!TIO)] (
|
|
verify_cached_target_file_timestamp(ProgressStream,
|
|
Globals, TargetFile, Timestamp, !.Info, _Info, !TIO)
|
|
),
|
|
MaybeTimestamp = ok(Timestamp)
|
|
else
|
|
% XXX LEGACY
|
|
module_maybe_nested_target_file_to_file_name(ProgressStream,
|
|
Globals, $pred, TargetFile,
|
|
FileName, _FileNameProposed, !Info, !IO),
|
|
get_target_timestamp_uncached(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, MaybeTimestamp, !Info, !IO),
|
|
record_timestamp_if_ok(TargetFile, MaybeTimestamp, !Info)
|
|
)
|
|
).
|
|
|
|
get_target_timestamp_search(ProgressStream, Globals, TargetFile,
|
|
MaybeTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
( if TargetType = module_target_analysis_registry then
|
|
% XXX LEGACY
|
|
module_target_to_search_file_name(Globals, $pred,
|
|
TargetType, ModuleName, SearchAuthDirs,
|
|
FileName, _FileNameProposed, !IO),
|
|
get_target_timestamp_analysis_registry_search(ProgressStream, Globals,
|
|
SearchAuthDirs, TargetFile, FileName, MaybeTimestamp, !Info, !IO)
|
|
else
|
|
( if is_timestamp_in_cache(!.Info, TargetFile, Timestamp) then
|
|
trace [compile_time(flag("target_timestamp_cache")), io(!TIO)] (
|
|
verify_cached_target_file_timestamp_search(ProgressStream,
|
|
Globals, TargetFile, Timestamp, !.Info, _Info, !TIO)
|
|
),
|
|
MaybeTimestamp = ok(Timestamp)
|
|
else
|
|
% XXX LEGACY
|
|
module_maybe_nested_target_file_to_search_file_name(ProgressStream,
|
|
Globals, $pred, TargetFile, SearchAuthDirs,
|
|
FileName, _FileNameProposed, !Info, !IO),
|
|
get_target_timestamp_uncached_search(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchAuthDirs,
|
|
MaybeTimestamp, !Info, !IO),
|
|
record_timestamp_if_ok(TargetFile, MaybeTimestamp, !Info)
|
|
)
|
|
).
|
|
|
|
%---------------%
|
|
|
|
% The code points we are called from are hit very frequently, so it is
|
|
% worth caching timestamps by target_file. It avoids having to compute
|
|
% a file name for a target_file first, before looking up its timestamp.
|
|
% XXX Wouldn't the search be even faster if, instead of the module's
|
|
% name, our caller gave us its module_index?
|
|
%
|
|
:- pred is_timestamp_in_cache(make_info::in, target_file::in,
|
|
timestamp::out) is semidet.
|
|
|
|
is_timestamp_in_cache(Info0, TargetFile, Timestamp) :-
|
|
Cache0 = make_info_get_target_file_timestamp_map(Info0),
|
|
version_hash_table.search(Cache0, TargetFile, Timestamp).
|
|
|
|
:- pred record_timestamp_if_ok(target_file::in, maybe_error(timestamp)::in,
|
|
make_info::in, make_info::out) is det.
|
|
|
|
record_timestamp_if_ok(TargetFile, MaybeTimestamp, !Info) :-
|
|
(
|
|
MaybeTimestamp = ok(Timestamp),
|
|
TargetFileTimestampMap0 =
|
|
make_info_get_target_file_timestamp_map(!.Info),
|
|
version_hash_table.det_insert(TargetFile, Timestamp,
|
|
TargetFileTimestampMap0, TargetFileTimestampMap),
|
|
make_info_set_target_file_timestamp_map(TargetFileTimestampMap, !Info)
|
|
;
|
|
MaybeTimestamp = error(_)
|
|
% Do not record errors. These would usually be due to files
|
|
% not yet made, and the result would have to be updated
|
|
% once the file *is* made.
|
|
).
|
|
|
|
%---------------%
|
|
|
|
:- pred verify_cached_target_file_timestamp(io.text_output_stream::in,
|
|
globals::in, target_file::in, timestamp::in,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
verify_cached_target_file_timestamp(ProgressStream, Globals,
|
|
TargetFile, CachedTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
% XXX LEGACY
|
|
module_maybe_nested_target_file_to_file_name(ProgressStream,
|
|
Globals, $pred, TargetFile, FileName, _FileNameProposed, !Info, !IO),
|
|
get_target_timestamp_uncached(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, MaybeFileTimestamp, !Info, !IO),
|
|
abort_for_any_verification_failure(CachedTimestamp,
|
|
MaybeFileTimestamp, !Info).
|
|
|
|
:- pred verify_cached_target_file_timestamp_search(io.text_output_stream::in,
|
|
globals::in, target_file::in, timestamp::in,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
verify_cached_target_file_timestamp_search(ProgressStream, Globals,
|
|
TargetFile, CachedTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
% XXX LEGACY
|
|
module_maybe_nested_target_file_to_search_file_name(ProgressStream,
|
|
Globals, $pred, TargetFile, SearchAuthDirs,
|
|
FileName, _FileNameProposed, !Info, !IO),
|
|
get_target_timestamp_uncached_search(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchAuthDirs,
|
|
MaybeFileTimestamp, !Info, !IO),
|
|
abort_for_any_verification_failure(CachedTimestamp,
|
|
MaybeFileTimestamp, !Info).
|
|
|
|
%---------------%
|
|
|
|
% We take a in,out pair of make_info args because we don't want calls
|
|
% to this predicate to be deleted as det code that computes nothing.
|
|
%
|
|
:- pred abort_for_any_verification_failure(timestamp::in,
|
|
maybe_error(timestamp)::in, make_info::in, make_info::out) is det.
|
|
:- pragma no_inline(pred(abort_for_any_verification_failure/4)).
|
|
|
|
abort_for_any_verification_failure(CachedTimestamp, MaybeFileTimestamp,
|
|
Info0, Info) :-
|
|
(
|
|
MaybeFileTimestamp = ok(FileTimestamp),
|
|
( if CachedTimestamp = FileTimestamp then
|
|
Info = Info0
|
|
else
|
|
string.format(
|
|
"target file timestamp differs: %s (cached) vs %s (actual)",
|
|
[s(timestamp_to_string(CachedTimestamp)),
|
|
s(timestamp_to_string(FileTimestamp))], Msg),
|
|
unexpected($pred, Msg)
|
|
)
|
|
;
|
|
MaybeFileTimestamp = error(Error),
|
|
string.format(
|
|
"target file timestamp differs: %s (cached) vs %s (actual)",
|
|
[s(timestamp_to_string(CachedTimestamp)), s(Error)], Msg),
|
|
unexpected($pred, Msg)
|
|
).
|
|
|
|
%---------------%
|
|
|
|
% Special treatment for `.analysis' files. If the corresponding
|
|
% `.analysis_status' file says the `.analysis' file is invalid,
|
|
% then we treat it as out of date.
|
|
%
|
|
:- pred get_target_timestamp_analysis_registry(io.text_output_stream::in,
|
|
globals::in, target_file::in, file_name::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
get_target_timestamp_analysis_registry(ProgressStream, Globals,
|
|
TargetFile, FileName, MaybeTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
|
|
( if map.search(FileTimestampMap0, FileName, MapValue) then
|
|
MapValue = {_SearchDirs, MaybeTimestamp}
|
|
else
|
|
do_read_module_overall_status(mmc, Globals, ModuleName, Status, !IO),
|
|
(
|
|
( Status = optimal
|
|
; Status = suboptimal
|
|
),
|
|
get_target_timestamp_uncached(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, MaybeTimestamp, !Info, !IO)
|
|
;
|
|
Status = invalid,
|
|
MaybeTimestamp = error("invalid module"),
|
|
map.det_insert(FileName, {[], MaybeTimestamp},
|
|
FileTimestampMap0, FileTimestampMap),
|
|
make_info_set_file_timestamp_map(FileTimestampMap, !Info)
|
|
)
|
|
).
|
|
|
|
:- pred get_target_timestamp_analysis_registry_search(
|
|
io.text_output_stream::in, globals::in, search_auth_dirs::in,
|
|
target_file::in, file_name::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
get_target_timestamp_analysis_registry_search(ProgressStream, Globals,
|
|
SearchAuthDirs, TargetFile, FileName, MaybeTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
|
|
( if map.search(FileTimestampMap0, FileName, MapValue) then
|
|
MapValue = {_SearchDirs, MaybeTimestamp}
|
|
else
|
|
do_read_module_overall_status(mmc, Globals, ModuleName, Status, !IO),
|
|
(
|
|
( Status = optimal
|
|
; Status = suboptimal
|
|
),
|
|
get_target_timestamp_uncached_search(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchAuthDirs,
|
|
MaybeTimestamp, !Info, !IO)
|
|
;
|
|
Status = invalid,
|
|
MaybeTimestamp = error("invalid module"),
|
|
map.det_insert(FileName, {[], MaybeTimestamp},
|
|
FileTimestampMap0, FileTimestampMap),
|
|
make_info_set_file_timestamp_map(FileTimestampMap, !Info)
|
|
)
|
|
).
|
|
|
|
%---------------%
|
|
|
|
:- pred get_target_timestamp_uncached(io.text_output_stream::in,
|
|
globals::in, module_name::in, module_target_type::in, file_name::in,
|
|
maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
get_target_timestamp_uncached(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, MaybeTimestamp, !Info, !IO) :-
|
|
SearchAuthDirs = search_auth_cur_dir,
|
|
get_file_timestamp(SearchAuthDirs, FileName,
|
|
SearchDirs, MaybeTimestamp0, !Info, !IO),
|
|
get_target_timestamp_handle_any_errors(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchDirs,
|
|
MaybeTimestamp0, MaybeTimestamp, !Info, !IO).
|
|
|
|
:- pred get_target_timestamp_uncached_search(io.text_output_stream::in,
|
|
globals::in, module_name::in, module_target_type::in, file_name::in,
|
|
search_auth_dirs::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
get_target_timestamp_uncached_search(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchAuthDirs, MaybeTimestamp,
|
|
!Info, !IO) :-
|
|
get_file_timestamp(SearchAuthDirs, FileName,
|
|
SearchDirs, MaybeTimestamp0, !Info, !IO),
|
|
get_target_timestamp_handle_any_errors(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchDirs,
|
|
MaybeTimestamp0, MaybeTimestamp, !Info, !IO).
|
|
|
|
% If a `.opt' file in another directory doesn't exist, it just means
|
|
% that a library wasn't compiled with `--intermodule-optimization'.
|
|
% Similarly for `.analysis' files.
|
|
%
|
|
:- pred get_target_timestamp_handle_any_errors(io.text_output_stream::in,
|
|
globals::in, module_name::in, module_target_type::in, file_name::in,
|
|
list(dir_name)::in,
|
|
maybe_error(timestamp)::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
get_target_timestamp_handle_any_errors(ProgressStream, Globals,
|
|
ModuleName, TargetType, FileName, SearchDirs,
|
|
MaybeTimestamp0, MaybeTimestamp, !Info, !IO) :-
|
|
( if
|
|
MaybeTimestamp0 = error(_),
|
|
( TargetType = module_target_opt
|
|
; TargetType = module_target_analysis_registry
|
|
)
|
|
then
|
|
get_maybe_module_dep_info(ProgressStream, Globals,
|
|
ModuleName, MaybeModuleDepInfo, !Info, !IO),
|
|
( if
|
|
MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo),
|
|
module_dep_info_get_source_file_dir(ModuleDepInfo, ModuleDir),
|
|
% NOTE This test can't succeed for the non-search caller.
|
|
ModuleDir \= dir.this_directory
|
|
then
|
|
MaybeTimestamp = ok(oldest_timestamp),
|
|
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
|
|
map.set(FileName, {SearchDirs, MaybeTimestamp},
|
|
FileTimestampMap0, FileTimestampMap),
|
|
make_info_set_file_timestamp_map(FileTimestampMap, !Info)
|
|
else
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
)
|
|
else
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_file_timestamp(SearchAuthDirs, FileName,
|
|
SearchDirs, MaybeTimestamp, !Info, !IO) :-
|
|
FileTimestampMap0 = make_info_get_file_timestamp_map(!.Info),
|
|
( if map.search(FileTimestampMap0, FileName, MapValue) then
|
|
MapValue = {SearchDirs, MaybeTimestamp}
|
|
else
|
|
search_for_file_mod_time(SearchAuthDirs, FileName,
|
|
SearchDirs, SearchResult, !IO),
|
|
(
|
|
SearchResult = ok(TimeT),
|
|
Timestamp = time_t_to_timestamp(TimeT),
|
|
MaybeTimestamp = ok(Timestamp),
|
|
map.det_insert(FileName, {SearchDirs, MaybeTimestamp},
|
|
FileTimestampMap0, FileTimestampMap),
|
|
make_info_set_file_timestamp_map(FileTimestampMap, !Info)
|
|
;
|
|
SearchResult = error(_SearchError),
|
|
% XXX MAKE We should not ignore _SearchError.
|
|
% XXX SEARCH_ERROR SearchDirs
|
|
string.format("file `%s' not found", [s(FileName)], NotFoundMsg),
|
|
MaybeTimestamp = error(NotFoundMsg)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module make.timestamp.
|
|
%---------------------------------------------------------------------------%
|