mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
375 lines
15 KiB
Mathematica
375 lines
15 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2023 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.file_names.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func init_target_file_timestamps = target_file_timestamps.
|
|
|
|
% Find the timestamp updated when a target is produced.
|
|
%
|
|
:- pred get_timestamp_file_timestamp(globals::in, target_file::in,
|
|
maybe_error(timestamp)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% Find the timestamp for the given dependency file.
|
|
%
|
|
:- pred get_dependency_timestamp(io.text_output_stream::in, globals::in,
|
|
dependency_file::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% get_target_timestamp(Globals, Search, TargetFile, Timestamp)
|
|
%
|
|
% Find the timestamp for the given target file.
|
|
% `Search' should be `do_search' if the file could be part of an
|
|
% installed library.
|
|
%
|
|
:- pred get_target_timestamp(io.text_output_stream::in, globals::in,
|
|
maybe_search::in, target_file::in, maybe_error(timestamp)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
% Find the timestamp of the first file matching the given
|
|
% file name in one of the given directories.
|
|
%
|
|
:- pred get_file_timestamp(list(dir_name)::in, file_name::in,
|
|
maybe_error(timestamp)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% If any of the inputs contain an error, return the first error.
|
|
% XXX We should return all errors, not just the first.
|
|
% If none of the inputs contain an error, return the oldest timestamp.
|
|
%
|
|
:- pred find_error_or_older_ok_timestamp(maybe_error(timestamp)::in,
|
|
maybe_error(timestamp)::in, maybe_error(timestamp)::out) is det.
|
|
:- pred find_error_or_oldest_ok_timestamp(list(maybe_error(timestamp))::in,
|
|
maybe_error(timestamp)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module analysis.
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.compile_target_code.
|
|
:- import_module libs.options.
|
|
:- 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.find_module.
|
|
:- 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 string.
|
|
:- import_module version_hash_table.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_target_file_timestamps =
|
|
version_hash_table.unsafe_init_default(target_file_hash).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_timestamp_file_timestamp(Globals, target_file(ModuleName, TargetType),
|
|
MaybeTimestamp, !Info, !IO) :-
|
|
( if timestamp_extension(TargetType, TimestampExt) then
|
|
module_name_to_file_name(Globals, $pred,
|
|
TimestampExt, ModuleName, FileName)
|
|
else
|
|
module_target_to_file_name(Globals, $pred, TargetType,
|
|
ModuleName, FileName, !IO)
|
|
),
|
|
% We should only ever look for timestamp files in the current directory.
|
|
% Timestamp files are only used when processing a module, and only modules
|
|
% in the current directory are processed.
|
|
SearchDirs = [dir.this_directory],
|
|
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_dependency_timestamp(ProgressStream, Globals, DependencyFile,
|
|
MaybeTimestamp, !Info, !IO) :-
|
|
(
|
|
DependencyFile = dep_file(FileName),
|
|
SearchDirs = [dir.this_directory],
|
|
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO)
|
|
;
|
|
DependencyFile = dep_target(Target),
|
|
get_target_timestamp(ProgressStream, Globals, do_search, 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, Search, TargetFile,
|
|
MaybeTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(_ModuleName, TargetType),
|
|
( if TargetType = module_target_analysis_registry then
|
|
ForSearch = maybe_search_to_maybe_for_search(Search),
|
|
get_file_name_for_target_file(ProgressStream, Globals, $pred,
|
|
ForSearch, TargetFile, FileName, !Info, !IO),
|
|
get_target_timestamp_analysis_registry(ProgressStream, Globals,
|
|
Search, TargetFile, FileName, MaybeTimestamp, !Info, !IO)
|
|
else
|
|
% This path is 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 the timestamp for that file.
|
|
TargetFileTimestamps0 = make_info_get_target_file_timestamps(!.Info),
|
|
( if
|
|
version_hash_table.search(TargetFileTimestamps0, TargetFile,
|
|
Timestamp)
|
|
then
|
|
MaybeTimestamp = ok(Timestamp)
|
|
else
|
|
ForSearch = maybe_search_to_maybe_for_search(Search),
|
|
get_file_name_for_target_file(ProgressStream, Globals, $pred,
|
|
ForSearch, TargetFile, FileName, !Info, !IO),
|
|
get_target_timestamp_2(ProgressStream, Globals,
|
|
Search, TargetFile, FileName, MaybeTimestamp, !Info, !IO),
|
|
(
|
|
MaybeTimestamp = ok(Timestamp),
|
|
TargetFileTimestamps1 =
|
|
make_info_get_target_file_timestamps(!.Info),
|
|
version_hash_table.det_insert(TargetFile, Timestamp,
|
|
TargetFileTimestamps1, TargetFileTimestamps),
|
|
make_info_set_target_file_timestamps(TargetFileTimestamps,
|
|
!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.
|
|
)
|
|
)
|
|
).
|
|
|
|
% 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, maybe_search::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, Search,
|
|
TargetFile, FileName, MaybeTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, _TargetType),
|
|
FileTimestamps0 = make_info_get_file_timestamps(!.Info),
|
|
( if map.search(FileTimestamps0, FileName, MaybeTimestamp0) then
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
else
|
|
do_read_module_overall_status(mmc, Globals, ModuleName, Status, !IO),
|
|
(
|
|
( Status = optimal
|
|
; Status = suboptimal
|
|
),
|
|
get_target_timestamp_2(ProgressStream, Globals, Search, TargetFile,
|
|
FileName, MaybeTimestamp, !Info, !IO)
|
|
;
|
|
Status = invalid,
|
|
MaybeTimestamp = error("invalid module"),
|
|
map.det_insert(FileName, MaybeTimestamp,
|
|
FileTimestamps0, FileTimestamps),
|
|
make_info_set_file_timestamps(FileTimestamps, !Info)
|
|
)
|
|
).
|
|
|
|
:- pred get_target_timestamp_2(io.text_output_stream::in, globals::in,
|
|
maybe_search::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_2(ProgressStream, Globals, Search, TargetFile, FileName,
|
|
MaybeTimestamp, !Info, !IO) :-
|
|
TargetFile = target_file(ModuleName, TargetType),
|
|
(
|
|
Search = do_search,
|
|
get_search_directories(Globals, TargetType, SearchDirs)
|
|
;
|
|
Search = do_not_search,
|
|
SearchDirs = [dir.this_directory]
|
|
),
|
|
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp0, !Info, !IO),
|
|
( if
|
|
MaybeTimestamp0 = error(_),
|
|
( TargetType = module_target_opt
|
|
; TargetType = module_target_analysis_registry
|
|
)
|
|
then
|
|
% 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.
|
|
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),
|
|
ModuleDir \= dir.this_directory
|
|
then
|
|
MaybeTimestamp = ok(oldest_timestamp),
|
|
FileTimestamps0 = make_info_get_file_timestamps(!.Info),
|
|
map.set(FileName, MaybeTimestamp, FileTimestamps0, FileTimestamps),
|
|
make_info_set_file_timestamps(FileTimestamps, !Info)
|
|
else
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
)
|
|
else
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
).
|
|
|
|
:- pred get_search_directories(globals::in, module_target_type::in,
|
|
list(dir_name)::out) is det.
|
|
|
|
get_search_directories(Globals, TargetType, SearchDirs) :-
|
|
MaybeOpt = get_search_option_for_file_type(TargetType),
|
|
(
|
|
MaybeOpt = yes(SearchDirOpt),
|
|
globals.lookup_accumulating_option(Globals, SearchDirOpt, SearchDirs0),
|
|
% Make sure the current directory is searched for C headers
|
|
% and libraries.
|
|
( if list.member(dir.this_directory, SearchDirs0) then
|
|
SearchDirs = SearchDirs0
|
|
else
|
|
SearchDirs = [dir.this_directory | SearchDirs0]
|
|
)
|
|
;
|
|
MaybeOpt = no,
|
|
SearchDirs = [dir.this_directory]
|
|
).
|
|
|
|
:- func get_search_option_for_file_type(module_target_type) = maybe(option).
|
|
|
|
get_search_option_for_file_type(ModuleTargetType) = MaybeSearchOption :-
|
|
(
|
|
( ModuleTargetType = module_target_source
|
|
; ModuleTargetType = module_target_errors
|
|
; ModuleTargetType = module_target_track_flags
|
|
; ModuleTargetType = module_target_c_code
|
|
; ModuleTargetType = module_target_csharp_code
|
|
; ModuleTargetType = module_target_java_code
|
|
; ModuleTargetType = module_target_java_class_code
|
|
; ModuleTargetType = module_target_object_code(_)
|
|
; ModuleTargetType = module_target_foreign_object(_, _)
|
|
; ModuleTargetType = module_target_fact_table_object(_, _)
|
|
; ModuleTargetType = module_target_xml_doc
|
|
),
|
|
MaybeSearchOption = no
|
|
;
|
|
( ModuleTargetType = module_target_int0
|
|
; ModuleTargetType = module_target_int1
|
|
; ModuleTargetType = module_target_int2
|
|
; ModuleTargetType = module_target_int3
|
|
),
|
|
MaybeSearchOption = yes(search_directories)
|
|
;
|
|
( ModuleTargetType = module_target_opt
|
|
; ModuleTargetType = module_target_analysis_registry
|
|
),
|
|
MaybeSearchOption = yes(intermod_directories)
|
|
;
|
|
ModuleTargetType = module_target_c_header(_),
|
|
MaybeSearchOption = yes(c_include_directory)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO) :-
|
|
FileTimestamps0 = make_info_get_file_timestamps(!.Info),
|
|
( if map.search(FileTimestamps0, FileName, MaybeTimestamp0) then
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
else
|
|
search_for_file_mod_time(SearchDirs, FileName, SearchResult, !IO),
|
|
(
|
|
SearchResult = ok(TimeT),
|
|
Timestamp = time_t_to_timestamp(TimeT),
|
|
MaybeTimestamp = ok(Timestamp),
|
|
map.det_insert(FileName, MaybeTimestamp,
|
|
FileTimestamps0, FileTimestamps),
|
|
make_info_set_file_timestamps(FileTimestamps, !Info)
|
|
;
|
|
SearchResult = error(_SearchError),
|
|
% XXX MAKE We should not ignore _SearchError.
|
|
string.format("file `%s' not found", [s(FileName)], NotFoundMsg),
|
|
MaybeTimestamp = error(NotFoundMsg)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
find_error_or_older_ok_timestamp(MaybeTimestampA, MaybeTimestampB,
|
|
MaybeTimestamp) :-
|
|
(
|
|
MaybeTimestampA = error(_),
|
|
MaybeTimestamp = MaybeTimestampA
|
|
;
|
|
MaybeTimestampA = ok(TimestampA),
|
|
(
|
|
MaybeTimestampB = error(_),
|
|
MaybeTimestamp = MaybeTimestampB
|
|
;
|
|
MaybeTimestampB = ok(TimestampB),
|
|
( if compare((<), TimestampA, TimestampB) then
|
|
Timestamp = TimestampA
|
|
else
|
|
Timestamp = TimestampB
|
|
),
|
|
MaybeTimestamp = ok(Timestamp)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
find_error_or_oldest_ok_timestamp(MaybeTimestamps, MaybeTimestamp) :-
|
|
list.foldl(find_error_or_older_ok_timestamp, MaybeTimestamps,
|
|
ok(newest_timestamp), MaybeTimestamp).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module make.timestamp.
|
|
%---------------------------------------------------------------------------%
|