mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 09:53:36 +00:00
... to account for the fact that they now contain constructed parse trees,
as well as read-in parse trees.
compiler/read_modules.m:
Make the rename described above.
Make it possible to differentiate between constructed and read-in
parse trees, in case this becomes necessary in the future.
compiler/deps_map.m:
compiler/generate_dep_d_files.m:
compiler/grab_modules.m:
compiler/make.get_module_dep_info.m:
compiler/make.make_info.m:
compiler/mercury_compile_main.m:
compiler/mercury_compile_make_hlds.m:
compiler/recompilation.check.m:
compiler/write_module_interface_files.m:
Conform to the changes above.
In make.get_module_dep_info.m, add an XXX.
1232 lines
52 KiB
Mathematica
1232 lines
52 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1996-2009, 2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: read_modules.m.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.read_modules.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.timestamp.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.parse_error.
|
|
:- import_module parse_tree.prog_item.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% After we have read in Mercury code from a source file, interface file or
|
|
% optimization file, we record the parse tree we get from it, so we can
|
|
% avoid having to read it again.
|
|
% XXX ITEM_LIST We seem to sometimes re-read it anyway. Fix this.
|
|
%
|
|
% Since we use different types to represent the parse trees of source,
|
|
% interface and optimization files, we use three maps, one for each
|
|
% parse tree type. Each map maps a key, which consists of a module name
|
|
% and the kind of a file (e.g. .int0 vs .int2 for interface files)
|
|
% to the parse tree we got for that file.
|
|
%
|
|
% XXX ITEM_LIST The code that reads in optimization files does not
|
|
% record its results in hptm_opt. I (zs) don't know whether that is
|
|
% a bug (leading to duplicate reads of optimization files) or a feature
|
|
% (keeping files that are by construction read exactly once out of a map
|
|
% where they won't be needed again).
|
|
%
|
|
|
|
:- type have_parse_tree_maps
|
|
---> have_parse_tree_maps(
|
|
hptm_src :: have_parse_tree_src_map,
|
|
|
|
hptm_module_src :: have_parse_tree_msrc_map,
|
|
|
|
hptm_int0 :: have_parse_tree_int0_map,
|
|
hptm_int1 :: have_parse_tree_int1_map,
|
|
hptm_int2 :: have_parse_tree_int2_map,
|
|
hptm_int3 :: have_parse_tree_int3_map,
|
|
|
|
hptm_plain_opt :: have_parse_tree_plain_opt_map,
|
|
hptm_trans_opt :: have_parse_tree_trans_opt_map
|
|
).
|
|
|
|
:- func init_have_parse_tree_maps = have_parse_tree_maps.
|
|
|
|
:- type have_parse_tree_src_map ==
|
|
have_parse_tree_map(parse_tree_src).
|
|
|
|
:- type have_parse_tree_key(Kind)
|
|
---> have_parse_tree_key(module_name, Kind).
|
|
|
|
:- type have_parse_tree_msrc_map ==
|
|
map(module_name, parse_tree_module_src).
|
|
|
|
:- type have_parse_tree_int0_map ==
|
|
have_parse_tree_map(parse_tree_int0).
|
|
:- type have_parse_tree_int1_map ==
|
|
have_parse_tree_map(parse_tree_int1).
|
|
:- type have_parse_tree_int2_map ==
|
|
have_parse_tree_map(parse_tree_int2).
|
|
:- type have_parse_tree_int3_map ==
|
|
have_parse_tree_map(parse_tree_int3).
|
|
|
|
:- type have_parse_tree_plain_opt_map ==
|
|
have_parse_tree_map(parse_tree_plain_opt).
|
|
:- type have_parse_tree_trans_opt_map ==
|
|
have_parse_tree_map(parse_tree_trans_opt).
|
|
|
|
:- type have_parse_tree_map(PT) == map(module_name, have_module(PT)).
|
|
|
|
% This type records the result of one of the read_module_<filekind>
|
|
% predicates below. There are three possible outcomes:
|
|
%
|
|
% - We have tried to read the module and succeeded.
|
|
% - We have tried to read the module and failed.
|
|
% - We have not tried to read the module.
|
|
%
|
|
% The first will return have_module(...), while the second and
|
|
% third will return have_not_read_module(...).
|
|
%
|
|
% The reason for not having a separate function symbol for the third
|
|
% outcome is that it can happen only for the small minority of calls
|
|
% that ask that the file not be read in the event of a timestamp match,
|
|
% but a third function symbol would force *all* calls to handle a situation
|
|
% that cannot arise for them.
|
|
:- type have_module(PT)
|
|
---> have_module(
|
|
% We have read, or constructed, the module.
|
|
|
|
% The name of the file that either
|
|
% - we read the parse tree from, or
|
|
% - we have written the parse tree to.
|
|
file_name,
|
|
|
|
% The parse tree of the file.
|
|
PT,
|
|
|
|
% Where did we get the parse tree from? And what other info,
|
|
% if any, did the "getting" process give us?
|
|
have_parse_tree_source
|
|
|
|
)
|
|
; have_not_read_module(
|
|
% We have tried to read the module, but did not do so.
|
|
|
|
% The name of the file that we have either tried to read,
|
|
% or whose timestamp indicated we did not have to read it.
|
|
file_name,
|
|
|
|
% The errors we got in the attempt to read the file.
|
|
% If we did not attempt to read the file, this will contain
|
|
% no errors at all. (This is how you can tell these two cases
|
|
% apart.)
|
|
read_module_errors
|
|
).
|
|
|
|
:- type have_read_module(PT) =< have_module(PT)
|
|
---> have_module(file_name, PT, have_parse_tree_read)
|
|
; have_not_read_module(file_name, read_module_errors).
|
|
|
|
% How did we get access to this parse tree: did we read it in
|
|
% from the file system, or did we construct it? If we read it in,
|
|
% then record any errors we found during parsing. The read_module_errors
|
|
% field should contain neither frme_could_not_open_file nor
|
|
% frme_could_not_read_file, but may contain any other errors.
|
|
% In addition, we record the timestamp of the file we read in,
|
|
% *if* the compiler component that decided to read the file
|
|
% requested that the timestamp be returned. Not all do.
|
|
:- type have_parse_tree_source
|
|
---> was_read(maybe(timestamp), read_module_errors)
|
|
; was_constructed.
|
|
|
|
:- type have_parse_tree_read =< have_parse_tree_source
|
|
---> was_read(maybe(timestamp), read_module_errors).
|
|
|
|
:- pred have_parse_tree_source_get_maybe_timestamp_errors(
|
|
have_parse_tree_source::in,
|
|
maybe(timestamp)::out, read_module_errors::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type maybe_ignore_errors
|
|
---> ignore_errors
|
|
; do_not_ignore_errors.
|
|
|
|
% Why are we attempting to read and parse a file?
|
|
:- type read_reason_msg
|
|
---> rrm_file
|
|
% Because the compiler has been asked to compile the named file
|
|
% in some fashion. (This could be generating target code for it,
|
|
% making its interface files, making its optimization files, etc.)
|
|
; rrm_std
|
|
% Because the named file belongs to this module (as a source file,
|
|
% interface file, or optimization file), and the compiler has been
|
|
% asked to compile that module in some fashion.
|
|
; rrm_old
|
|
% Because the named file belongs to this module, and we need
|
|
% the old contents of the file, so that we can write out
|
|
% what we *now* think its contents should be, but *only*
|
|
% if the "new" contents differ from the old. The difference
|
|
% between whether (a) we keep the old contents as is, or
|
|
% (b) we overwrite them with identical contents, is the
|
|
% timestamp on the file. Keeping the old timestamp will
|
|
% prevent unnecessary rebuilds of other files that depend
|
|
% on that one.
|
|
; rrm_get_deps.
|
|
% Because the named file belongs to this module, and we need
|
|
% to get its dependencies.
|
|
|
|
% read_module_src(ProgressStream, Globals, ReadReasonMsg,
|
|
% IgnoreErrors, Search, ModuleName, FileName, ReadModuleAndTimestamps,
|
|
% HaveModule, !IO):
|
|
%
|
|
% Given a module name, read in and parse the source code of that file,
|
|
% printing progress messages along the way if the verbosity level
|
|
% calls for that.
|
|
%
|
|
% If Search is do_search, search all directories given by the option
|
|
% search_directories for the module;
|
|
% otherwise, search for those filenames only in the current directory.
|
|
% Return in the FileName part of HaveModule the actual source file
|
|
% name found (excluding the directory part). If the actual module name
|
|
% (as determined by the `:- module' declaration) does not match
|
|
% the specified module name, then report an error message,
|
|
% but record the *expected* module name in the parse tree,
|
|
% not the one we actually found. This is because most parts
|
|
% of the compiler (including deps_map.m and make.module_dep_file.m)
|
|
% rely on the invariant which says that if Errors does not contain
|
|
% any fatal errors, then the returned ParseTreeSrc contains the
|
|
% module with the expected name. Invocations of the compiler
|
|
% that don't specify --make or request dependency map don't really
|
|
% care which module name we return here; they will work either way,
|
|
% the only difference being whether the names of the files they generate
|
|
% are based on the expected or the actual module name.
|
|
%
|
|
% N.B. This reads a module given the MODULE name. If you want to read
|
|
% a module given the FILE name, use `read_module_src_from_file'.
|
|
%
|
|
% If ReadModuleAndTimestamps is always_read_module(dont_return_timestamp),
|
|
% return `no' in the MaybeTimestamp field of HaveModule.
|
|
%
|
|
% If ReadModuleAndTimestamps is always_read_module(do_return_timestamp),
|
|
% attempt to return the modification time of the file in the MaybeTimestamp
|
|
% field of HaveModule.
|
|
%
|
|
% If ReadModuleAndTimestamps is dont_read_module_if_match(OldTimeStamp),
|
|
% then
|
|
%
|
|
% - if the timestamp of that file is exactly OldTimestamp, then
|
|
% don't read the file, and return have_not_read_module with no errors,
|
|
% while
|
|
% - if the timestamp of that file differs from OldTimestamp (virtually
|
|
% always because it is newer), then read the module from the file
|
|
% as usual, parse and return its contents as usual, and also return
|
|
% its actual timestamp.
|
|
%
|
|
:- pred read_module_src(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, list(term.context)::in, read_module_and_timestamps::in,
|
|
have_read_module(parse_tree_src)::out, io::di, io::uo) is det.
|
|
|
|
% read_module_src_from_file(ProgressStream, Globals,
|
|
% FileName, FileNameDotM, ReadReasonMsg, Search, ReadModuleAndTimestamps,
|
|
% HaveModule, !IO):
|
|
%
|
|
% Does pretty much the same job as read_module_src, but its job is
|
|
% to read the module stored in a specified file (FileNameDotM),
|
|
% discovering the name of the module stored there by reading the file,
|
|
% as opposed to looking for the file containing a module with a specified
|
|
% name. It does not search for the right filename (that is FileNameDotM),
|
|
% but if Search is do_search, it does search for that filename in the
|
|
% specified directories.
|
|
%
|
|
% The rest of the argument list has the same meaning as in read_module_src.
|
|
%
|
|
:- pred read_module_src_from_file(io.text_output_stream::in, globals::in,
|
|
file_name::in, file_name::in, read_reason_msg::in, maybe_search::in,
|
|
read_module_and_timestamps::in, have_read_module(parse_tree_src)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% maybe_read_module_intN(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
% ModuleName, IntFileKind, ReturnTimestamp, HaveModule,
|
|
% !HaveParseTreeMaps, !IO):
|
|
%
|
|
% If !.HaveParseTreeMaps contains the already-read contents of the
|
|
% relevant kind of interface file for ModuleName, then return
|
|
% the information stored in !.HaveParseTreeMaps for that file.
|
|
% If it is not there, read that interface file using read_module_intN,
|
|
% regardless of its timestamp.
|
|
%
|
|
:- pred maybe_read_module_int0(io.text_output_stream::in, globals::in,
|
|
maybe_search::in, module_name::in, maybe_return_timestamp::in,
|
|
have_module(parse_tree_int0)::out,
|
|
have_parse_tree_maps::in, have_parse_tree_maps::out,
|
|
io::di, io::uo) is det.
|
|
:- pred maybe_read_module_int1(io.text_output_stream::in, globals::in,
|
|
maybe_search::in, module_name::in, maybe_return_timestamp::in,
|
|
have_module(parse_tree_int1)::out,
|
|
have_parse_tree_maps::in, have_parse_tree_maps::out,
|
|
io::di, io::uo) is det.
|
|
:- pred maybe_read_module_int2(io.text_output_stream::in, globals::in,
|
|
maybe_search::in, module_name::in, maybe_return_timestamp::in,
|
|
have_module(parse_tree_int2)::out,
|
|
have_parse_tree_maps::in, have_parse_tree_maps::out,
|
|
io::di, io::uo) is det.
|
|
:- pred maybe_read_module_int3(io.text_output_stream::in, globals::in,
|
|
maybe_search::in, module_name::in, maybe_return_timestamp::in,
|
|
have_module(parse_tree_int3)::out,
|
|
have_parse_tree_maps::in, have_parse_tree_maps::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% read_module_intN(ProgressStream, Globals, ReadReasonMsg,
|
|
% IgnoreErrors, Search, ModuleName, FileName, ReadModuleAndTimestamps,
|
|
% HaveModule, !IO):
|
|
%
|
|
% Given a module name, read in and parse the specified kind of interface
|
|
% file for that module, printing progress messages along the way
|
|
% if the verbosity level calls for that.
|
|
%
|
|
% The meanings of the arguments are pretty much the same as for
|
|
% read_module_src, but while the names of the files that contain source
|
|
% files may not be fully module qualified, the names of interface files
|
|
% are always fully module qualified, so read_module_int does not search
|
|
% for the right filename. It knows what filename it looks for; the only
|
|
% search it does, if Search is do_search, is to decide which directory
|
|
% among the search directories contains the file with that filename.
|
|
%
|
|
:- pred read_module_int0(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_module(parse_tree_int0)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int1(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_module(parse_tree_int1)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int2(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_module(parse_tree_int2)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int3(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_module(parse_tree_int3)::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% read_module_{plain,trans}_opt(ProgressStream, Globals, ModuleName,
|
|
% FileName, HaveModule, !IO):
|
|
%
|
|
% Given a module name, read in and parse the specified kind of optimization
|
|
% file for that module, printing progress messages along the way
|
|
% if the verbosity level calls for that.
|
|
%
|
|
% These predicates do a very similar job to read_module_intN,
|
|
% but there are several differences.
|
|
%
|
|
% - We do not do anything with errors in .opt and .trans_opt files;
|
|
% we leave that up to the caller. The reason for this is that
|
|
% a missing .opt file is not an error, unlike a missing .intN file.
|
|
%
|
|
% - We do always do search for .opt and .trans_opt files, but
|
|
% in the directories named by the intermod_directories option, *not*
|
|
% in the directories named by the search_directories option.
|
|
%
|
|
% - None of our callers are ever interested in timestamps,
|
|
% so these predicates never return them.
|
|
%
|
|
:- pred read_module_plain_opt(io.text_output_stream::in, globals::in,
|
|
module_name::in, have_module(parse_tree_plain_opt)::out,
|
|
io::di, io::uo) is det.
|
|
:- pred read_module_trans_opt(io.text_output_stream::in, globals::in,
|
|
module_name::in, have_module(parse_tree_trans_opt)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% get_default_module_name_for_file(FileName, FileNameDotM,
|
|
% DefaultModuleName, !IO):
|
|
%
|
|
% Return the default module name for a file, based either on the
|
|
% entry for FileNameDotM in the source file map (Mercury.modules)
|
|
% if that is available, and otherwise based just on the file name FileName.
|
|
% XXX Having two separate filenames here is old behavior. I (zs)
|
|
% don't know the reason for it. It can't be a distinction between
|
|
% expected and actual filename, since the caller is calling us
|
|
% because it does not know the actual module name, and thus has
|
|
% no basis on which to decide what filename to expect.
|
|
%
|
|
:- pred get_default_module_name_for_file(file_name::in, file_name::in,
|
|
module_name::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred return_timestamp_if_needed(maybe_return_timestamp::in,
|
|
maybe(timestamp)::in, maybe(timestamp)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.file_kind.
|
|
:- import_module parse_tree.find_module.
|
|
:- import_module parse_tree.parse_module.
|
|
:- import_module parse_tree.source_file_map.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module dir.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_have_parse_tree_maps =
|
|
have_parse_tree_maps(map.init, map.init, map.init,
|
|
map.init, map.init, map.init, map.init, map.init).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
have_parse_tree_source_get_maybe_timestamp_errors(Source,
|
|
MaybeTimestamp, Errors) :-
|
|
(
|
|
Source = was_read(MaybeTimestamp, Errors)
|
|
;
|
|
Source = was_constructed,
|
|
MaybeTimestamp = no,
|
|
Errors = read_module_errors(set.init, [], set.init, [], [])
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
read_module_src(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ExpectationContexts, ReadModuleAndTimestamps,
|
|
HaveModule, !IO) :-
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_src, FileName0, ReadDoneMsg, SearchDirs, !IO),
|
|
% For `.m' files, we need to deal with the case where the module name
|
|
% does not match the file name.
|
|
search_for_module_source_and_stream(SearchDirs, ModuleName,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = ok(FileNameAndStream),
|
|
actually_read_module_src(Globals, FileNameAndStream, ModuleName,
|
|
ExpectationContexts, ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
MaybeParseTreeSrc0, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals,
|
|
mfas_ok(FileNameAndStream), IgnoreErrors, fk_src, ReadDoneMsg,
|
|
FileName0, FileName, MaybeTimestampRes, MaybeTimestamp,
|
|
Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeSrc0 = yes(ParseTreeSrc0),
|
|
% If ModuleName = ParseTreeSrc0 ^ pts_module_name, this obviously
|
|
% does the right thing.
|
|
% If ModuleName != ParseTreeSrc0 ^ pts_module_name, then
|
|
% we must include ModuleName in ParseTreeSrc (see the comment
|
|
% above), and including recording its context as
|
|
% ParseTreeSrc0 ^ pts_module_name_context shouldn't mislead anyone
|
|
% who reads the error spec (which should be in Errors0) about
|
|
% the unexpected module name.
|
|
ParseTreeSrc = ParseTreeSrc0 ^ pts_module_name := ModuleName,
|
|
HaveModule = have_module(FileName, ParseTreeSrc,
|
|
was_read(MaybeTimestamp, Errors1))
|
|
;
|
|
MaybeParseTreeSrc0 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = error(ErrorMsg),
|
|
io_error_to_read_module_errors(frme_could_not_open_file,
|
|
phase_find_files(FileName0), ErrorMsg, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals, mfas_error(Errors0),
|
|
IgnoreErrors, fk_src, ReadDoneMsg, FileName0, FileName,
|
|
no, _MaybeTimestamp, Errors0, Errors1, !IO),
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_src_from_file(ProgressStream, Globals, FileName, FileNameDotM,
|
|
ReadReasonMsg, Search, ReadModuleAndTimestamps, HaveModule, !IO) :-
|
|
read_module_begin_from_file(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
FileName, FileNameDotM, DefaultModuleName,
|
|
ReadDoneMsg, SearchDirs, !IO),
|
|
search_for_file_and_stream(SearchDirs, FileNameDotM,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = ok(FileNameAndStream),
|
|
actually_read_module_src(Globals, FileNameAndStream,
|
|
DefaultModuleName, [], ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
MaybeParseTreeSrc, Errors0, !IO),
|
|
read_module_end_file(Globals, fk_src, ReadDoneMsg, FileNameDotM,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeSrc = yes(ParseTreeSrc),
|
|
HaveModule = have_module(FileNameDotM, ParseTreeSrc,
|
|
was_read(MaybeTimestamp, Errors1))
|
|
;
|
|
MaybeParseTreeSrc = no,
|
|
HaveModule = have_not_read_module(FileNameDotM, Errors1)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = error(ErrorMsg),
|
|
io_error_to_read_module_errors(frme_could_not_open_file,
|
|
phase_find_files(FileNameDotM), ErrorMsg, Errors0, !IO),
|
|
read_module_end_file(Globals, fk_src, ReadDoneMsg, FileNameDotM,
|
|
no, _MaybeTimestamp, Errors0, Errors, !IO),
|
|
HaveModule = have_not_read_module(FileNameDotM, Errors)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
maybe_read_module_int0(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveModule, !HaveParseTreeMaps, !IO) :-
|
|
OrigHPTM = !.HaveParseTreeMaps ^ hptm_int0,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHPTM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveModule = HaveReadModulePrime
|
|
else
|
|
read_module_int0(ProgressStream, Globals, rrm_std,
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveModule, !IO),
|
|
map.set(ModuleName, HaveModule, OrigHPTM, HPTM),
|
|
!HaveParseTreeMaps ^ hptm_int0 := HPTM
|
|
).
|
|
|
|
maybe_read_module_int1(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveModule, !HaveParseTreeMaps, !IO) :-
|
|
OrigHPTM = !.HaveParseTreeMaps ^ hptm_int1,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHPTM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveModule = HaveReadModulePrime
|
|
else
|
|
read_module_int1(ProgressStream, Globals, rrm_std,
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveModule, !IO),
|
|
map.set(ModuleName, HaveModule, OrigHPTM, HPTM),
|
|
!HaveParseTreeMaps ^ hptm_int1 := HPTM
|
|
).
|
|
|
|
maybe_read_module_int2(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveModule, !HaveParseTreeMaps, !IO) :-
|
|
OrigHPTM = !.HaveParseTreeMaps ^ hptm_int2,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHPTM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveModule = HaveReadModulePrime
|
|
else
|
|
read_module_int2(ProgressStream, Globals, rrm_std,
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveModule, !IO),
|
|
map.set(ModuleName, HaveModule, OrigHPTM, HPTM),
|
|
!HaveParseTreeMaps ^ hptm_int2 := HPTM
|
|
).
|
|
|
|
maybe_read_module_int3(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveModule, !HaveParseTreeMaps, !IO) :-
|
|
OrigHPTM = !.HaveParseTreeMaps ^ hptm_int3,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHPTM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveModule = HaveReadModulePrime
|
|
else
|
|
read_module_int3(ProgressStream, Globals, rrm_std,
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveModule, !IO),
|
|
map.set(ModuleName, HaveModule, OrigHPTM, HPTM),
|
|
!HaveParseTreeMaps ^ hptm_int3 := HPTM
|
|
).
|
|
|
|
:- pred search_module_name_timestamp_if_needed(have_parse_tree_map(PT)::in,
|
|
module_name::in, maybe_return_timestamp::in, have_module(PT)::out)
|
|
is semidet.
|
|
|
|
search_module_name_timestamp_if_needed(HPTM, ModuleName, ReturnTimestamp,
|
|
HaveModule) :-
|
|
map.search(HPTM, ModuleName, HaveReadModule0),
|
|
HaveReadModule0 = have_module(FN, PT, Source0),
|
|
(
|
|
Source0 = was_read(MaybeTimeStamp0, E),
|
|
return_timestamp_if_needed(ReturnTimestamp,
|
|
MaybeTimeStamp0, MaybeTimeStamp),
|
|
Source = was_read(MaybeTimeStamp, E)
|
|
;
|
|
Source0 = was_constructed,
|
|
(
|
|
ReturnTimestamp = dont_return_timestamp,
|
|
Source = Source0
|
|
;
|
|
ReturnTimestamp = do_return_timestamp,
|
|
unexpected($pred, "was_constructed")
|
|
)
|
|
),
|
|
HaveModule = have_module(FN, PT, Source).
|
|
|
|
%---------------------%
|
|
|
|
read_module_int0(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveModule, !IO) :-
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_int(ifk_int0), FileName0, ReadDoneMsg, SearchDirs, !IO),
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream),
|
|
actually_read_module_int0(Globals, FileNameAndStream, ModuleName, [],
|
|
ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
MaybeParseTreeInt0, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int0), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt0 = yes(ParseTreeInt0),
|
|
HaveModule = have_module(FileName, ParseTreeInt0,
|
|
was_read(MaybeTimestamp, Errors1))
|
|
;
|
|
MaybeParseTreeInt0 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int0), ReadDoneMsg, FileName0, FileName,
|
|
no, _MaybeTimestamp, Errors0, Errors1, !IO),
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_int1(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveModule, !IO) :-
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_int(ifk_int1), FileName0, ReadDoneMsg, SearchDirs, !IO),
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream),
|
|
actually_read_module_int1(Globals, FileNameAndStream, ModuleName, [],
|
|
ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
MaybeParseTreeInt1, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int1), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt1 = yes(ParseTreeInt1),
|
|
HaveModule = have_module(FileName, ParseTreeInt1,
|
|
was_read(MaybeTimestamp, Errors1))
|
|
;
|
|
MaybeParseTreeInt1 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int1), ReadDoneMsg, FileName0, FileName,
|
|
no, _MaybeTimestamp, Errors0, Errors1, !IO),
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_int2(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveModule, !IO) :-
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_int(ifk_int2), FileName0, ReadDoneMsg, SearchDirs, !IO),
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream),
|
|
actually_read_module_int2(Globals, FileNameAndStream, ModuleName, [],
|
|
ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
MaybeParseTreeInt2, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int2), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt2 = yes(ParseTreeInt2),
|
|
HaveModule = have_module(FileName, ParseTreeInt2,
|
|
was_read(MaybeTimestamp, Errors1))
|
|
;
|
|
MaybeParseTreeInt2 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int2), ReadDoneMsg, FileName0, FileName,
|
|
no, _MaybeTimestamp, Errors0, Errors1, !IO),
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_int3(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveModule, !IO) :-
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_int(ifk_int3), FileName0, ReadDoneMsg, SearchDirs, !IO),
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream),
|
|
actually_read_module_int3(Globals, FileNameAndStream, ModuleName, [],
|
|
ReadModuleAndTimestamps, MaybeTimestampRes,
|
|
MaybeParseTreeInt3, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int3), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt3 = yes(ParseTreeInt3),
|
|
HaveModule = have_module(FileName, ParseTreeInt3,
|
|
was_read(MaybeTimestamp, Errors1))
|
|
;
|
|
MaybeParseTreeInt3 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, fk_int(ifk_int3), ReadDoneMsg, FileName0, FileName,
|
|
no, _MaybeTimestamp, Errors0, Errors1, !IO),
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
read_module_plain_opt(ProgressStream, Globals, ModuleName,
|
|
HaveModule, !IO) :-
|
|
ReadReasonMsg = rrm_std,
|
|
Search = do_search,
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
MaybeTimestampRes = no,
|
|
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_opt(ofk_opt), FileName0, ReadDoneMsg, SearchDirs, !IO),
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream),
|
|
actually_read_module_plain_opt(Globals, FileNameAndStream, ModuleName,
|
|
MaybeParseTreePlainOpt, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_opt), ReadDoneMsg,
|
|
FileName0, FileName, MaybeTimestampRes, _MaybeTimestamp,
|
|
Errors0, Errors, !IO),
|
|
(
|
|
MaybeParseTreePlainOpt = yes(ParseTreePlainOpt),
|
|
HaveModule = have_module(FileName, ParseTreePlainOpt,
|
|
was_read(no, Errors))
|
|
;
|
|
MaybeParseTreePlainOpt = no,
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(ProgressStream, Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_opt), ReadDoneMsg,
|
|
FileName0, FileName, MaybeTimestampRes, _MaybeTimestamp,
|
|
Errors0, Errors, !IO),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_trans_opt(ProgressStream, Globals, ModuleName,
|
|
HaveModule, !IO) :-
|
|
ReadReasonMsg = rrm_std,
|
|
Search = do_search,
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
MaybeTimestampRes = no,
|
|
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, fk_opt(ofk_trans_opt), FileName0, ReadDoneMsg,
|
|
SearchDirs, !IO),
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO),
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream),
|
|
actually_read_module_trans_opt(Globals, FileNameAndStream, ModuleName,
|
|
MaybeParseTreeTransOpt, Errors0, !IO),
|
|
read_module_end_module(ProgressStream, Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_trans_opt),
|
|
ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, _MaybeTimestamp, Errors0, Errors, !IO),
|
|
(
|
|
MaybeParseTreeTransOpt = yes(ParseTreeTransOpt),
|
|
HaveModule = have_module(FileName, ParseTreeTransOpt,
|
|
was_read(no, Errors))
|
|
;
|
|
MaybeParseTreeTransOpt = no,
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(ProgressStream, Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_trans_opt),
|
|
ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, _MaybeTimestamp, Errors0, Errors, !IO),
|
|
HaveModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type maybe_file_and_stream
|
|
---> mfas_ok(path_name_and_stream)
|
|
; mfas_error(read_module_errors).
|
|
|
|
% search_for_file_and_stream_or_error(Dirs, FileName,
|
|
% MaybeFilePathNameAndStream, !IO):
|
|
%
|
|
% Search Dirs for FileName. If found, return the path name of the file
|
|
% and an open input stream reading from that file. Closing that stream
|
|
% is the caller's responsibility.
|
|
%
|
|
:- pred search_for_file_and_stream_or_error(list(dir_name)::in, file_name::in,
|
|
maybe_file_and_stream::out, io::di, io::uo) is det.
|
|
|
|
search_for_file_and_stream_or_error(SearchDirs, FileName0,
|
|
MaybeFileNameAndStream, !IO) :-
|
|
% NB. Consider using search_for_file_returning_dir_and_stream,
|
|
% which does not canonicalise the path, and is therefore more efficient.
|
|
search_for_file_and_stream(SearchDirs, FileName0,
|
|
RawMaybeFileNameAndStream, !IO),
|
|
(
|
|
RawMaybeFileNameAndStream = ok(FileNameAndStream),
|
|
MaybeFileNameAndStream = mfas_ok(FileNameAndStream)
|
|
;
|
|
RawMaybeFileNameAndStream = error(ErrorMsg),
|
|
io_error_to_read_module_errors(frme_could_not_find_file,
|
|
phase_find_files(FileName0), ErrorMsg, Errors, !IO),
|
|
MaybeFileNameAndStream = mfas_error(Errors)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_default_module_name_for_file(FileName, FileNameDotM,
|
|
DefaultModuleName, !IO) :-
|
|
( if dir.basename(FileName, BaseFileNamePrime) then
|
|
BaseFileName = BaseFileNamePrime
|
|
else
|
|
BaseFileName = ""
|
|
),
|
|
have_source_file_map(HaveMap, !IO),
|
|
(
|
|
HaveMap = no,
|
|
file_name_to_module_name(BaseFileName, DefaultModuleName)
|
|
;
|
|
HaveMap = yes,
|
|
lookup_source_file_module(FileNameDotM, MaybeModuleName, !IO),
|
|
(
|
|
MaybeModuleName = yes(DefaultModuleName)
|
|
;
|
|
MaybeModuleName = no,
|
|
file_name_to_module_name(BaseFileName, DefaultModuleName)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type read_done_msg
|
|
---> rdm_none
|
|
% Do not print a "done" message.
|
|
; rdm_progress(io.text_output_stream).
|
|
% Print a "done" message to the specified progress output stream.
|
|
|
|
%---------------------%
|
|
|
|
:- pred read_module_begin_from_file(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_search::in, file_name::in, file_name::in,
|
|
module_name::out, read_done_msg::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module_begin_from_file(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
FileName, FileNameDotM, DefaultModuleName, ReadDoneMsg,
|
|
SearchDirs, !IO) :-
|
|
% XXX Do not assume that the name of FileNameDotM guarantees
|
|
% that the string it holds ends in ".m".
|
|
get_default_module_name_for_file(FileName, FileNameDotM,
|
|
DefaultModuleName, !IO),
|
|
% The rest of this predicate should be kept in sync
|
|
% with read_module_begin.
|
|
(
|
|
Search = do_search,
|
|
globals.lookup_accumulating_option(Globals, search_directories,
|
|
SearchDirs)
|
|
;
|
|
Search = do_not_search,
|
|
SearchDirs = [dir.this_directory]
|
|
),
|
|
output_read_reason_msg(ProgressStream, Globals, ReadReasonMsg,
|
|
FileNameDotM, ReadDoneMsg, !IO).
|
|
|
|
:- pred read_module_begin(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, maybe_search::in, module_name::in, file_kind::in,
|
|
file_name::out, read_done_msg::out, list(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module_begin(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
ModuleName, FileKind, FileName, ReadDoneMsg, SearchDirs, !IO) :-
|
|
(
|
|
FileKind = fk_src,
|
|
% For the call to module_name_to_source_file_name, the value of Search
|
|
% does not matter.
|
|
module_name_to_source_file_name(ModuleName, FileName, !IO),
|
|
(
|
|
Search = do_search,
|
|
globals.lookup_accumulating_option(Globals, search_directories,
|
|
SearchDirs)
|
|
;
|
|
Search = do_not_search,
|
|
SearchDirs = [dir.this_directory]
|
|
)
|
|
;
|
|
(
|
|
FileKind = fk_int(IntFileKind),
|
|
int_file_kind_to_extension(IntFileKind, _ExtStr, Ext)
|
|
;
|
|
FileKind = fk_opt(OptFileKind),
|
|
opt_file_kind_to_extension(OptFileKind, _ExtStr, Ext)
|
|
),
|
|
% The rest of this predicate should be kept in sync
|
|
% with read_module_begin_from_file.
|
|
(
|
|
Search = do_search,
|
|
% XXX CLEANUP We should either pass SearchDirs to
|
|
% module_name_to_search_file_name, or get it to give SearchDirs
|
|
% to us.
|
|
module_name_to_search_file_name(Globals, $pred, Ext,
|
|
ModuleName, FileName),
|
|
(
|
|
FileKind = fk_int(_),
|
|
globals.lookup_accumulating_option(Globals,
|
|
search_directories, SearchDirs)
|
|
;
|
|
FileKind = fk_opt(_),
|
|
globals.lookup_accumulating_option(Globals,
|
|
intermod_directories, SearchDirs)
|
|
)
|
|
;
|
|
Search = do_not_search,
|
|
module_name_to_file_name(Globals, $pred, Ext,
|
|
ModuleName, FileName),
|
|
SearchDirs = [dir.this_directory]
|
|
)
|
|
),
|
|
output_read_reason_msg(ProgressStream, Globals, ReadReasonMsg,
|
|
FileName, ReadDoneMsg, !IO).
|
|
|
|
:- pred read_module_end_module(io.text_output_stream::in, globals::in,
|
|
maybe_file_and_stream::in, maybe_ignore_errors::in,
|
|
file_kind::in, read_done_msg::in, file_name::in, file_name::out,
|
|
maybe(io.res(timestamp))::in, maybe(timestamp)::out,
|
|
read_module_errors::in, read_module_errors::out, io::di, io::uo) is det.
|
|
|
|
read_module_end_module(ProgressStream, Globals, MaybeFileNameAndStream,
|
|
IgnoreErrors, FileKind, ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors, !IO) :-
|
|
% The code of read_module_end_module and read_module_end_file
|
|
% should be kept in sync.
|
|
(
|
|
MaybeFileNameAndStream = mfas_ok(path_name_and_stream(FileName, _))
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(_),
|
|
FileName = FileName0
|
|
),
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName0,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
handle_any_read_module_errors(FileKind, ReadDoneMsg,
|
|
IgnoreErrors, Errors1, Errors, !IO),
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
|
|
maybe_report_stats(ProgressStream, Statistics, !IO).
|
|
|
|
:- pred read_module_end_file(globals::in, file_kind::in, read_done_msg::in,
|
|
file_name::in, maybe(io.res(timestamp))::in, maybe(timestamp)::out,
|
|
read_module_errors::in, read_module_errors::out, io::di, io::uo) is det.
|
|
|
|
read_module_end_file(Globals, FileKind, ReadDoneMsg, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors, !IO) :-
|
|
% The code of read_module_end_module and read_module_end_file
|
|
% should be kept in sync.
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
% Unlike read_module_end_module, we assume do_not_ignore_errors.
|
|
handle_any_read_module_errors(FileKind, ReadDoneMsg,
|
|
do_not_ignore_errors, Errors1, Errors, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func no_file_errors(maybe_ignore_errors, read_module_errors)
|
|
= read_module_errors.
|
|
|
|
no_file_errors(IgnoreErrors, Errors0) = Errors :-
|
|
(
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
Errors = Errors0
|
|
;
|
|
IgnoreErrors = ignore_errors,
|
|
FatalErrors0 = Errors0 ^ rm_fatal_errors,
|
|
( if set.contains(FatalErrors0, frme_could_not_find_file) then
|
|
FatalError = frme_could_not_find_file
|
|
else
|
|
FatalError = frme_could_not_open_file
|
|
),
|
|
Errors = no_file_errors_ignored(FatalError)
|
|
).
|
|
|
|
% According to IgnoreErrors = ignore_errors, being unable to
|
|
% read the file is not an error, so delete all the error_specs,
|
|
% but keep the fatal error to prevent this module from being put
|
|
% into deps_maps. This is because modules in deps_maps get .d files
|
|
% created for them, which is not appropriate for a file
|
|
% that does not exist, at least in the current directory.
|
|
%
|
|
% XXX Why not add a field to read_module_errors that specifically
|
|
% says whether the module should be put into deps_maps?
|
|
%
|
|
:- func no_file_errors_ignored(fatal_read_module_error) = read_module_errors.
|
|
|
|
no_file_errors_ignored(FatalError) = Errors :-
|
|
Errors = read_module_errors(
|
|
set.make_singleton_set(FatalError), [],
|
|
set.init, [], []).
|
|
|
|
%---------------------%
|
|
|
|
:- pred handle_any_read_module_errors(file_kind::in,
|
|
read_done_msg::in, maybe_ignore_errors::in,
|
|
read_module_errors::in, read_module_errors::out, io::di, io::uo) is det.
|
|
|
|
handle_any_read_module_errors(FileKind, ReadDoneMsg, IgnoreErrors,
|
|
Errors0, Errors, !IO) :-
|
|
(
|
|
IgnoreErrors = ignore_errors,
|
|
FatalErrors0 = Errors0 ^ rm_fatal_errors,
|
|
( if set.contains(FatalErrors0, frme_could_not_open_file) then
|
|
output_read_done_msg(ReadDoneMsg, "not found.\n", !IO),
|
|
Errors = no_file_errors_ignored(frme_could_not_open_file)
|
|
else
|
|
% XXX CLEANUP we should not print "done" if Errors is nonempty.
|
|
output_read_done_msg(ReadDoneMsg, "done.\n", !IO),
|
|
Errors = Errors0
|
|
)
|
|
;
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
FatalErrors0 = Errors0 ^ rm_fatal_errors,
|
|
NonFatalErrors0 = Errors0 ^ rm_nonfatal_errors,
|
|
( if
|
|
( set.is_non_empty(FatalErrors0)
|
|
; set.is_non_empty(NonFatalErrors0)
|
|
)
|
|
then
|
|
( if set.is_non_empty(FatalErrors0) then
|
|
( if set.contains(FatalErrors0, frme_could_not_open_file) then
|
|
Msg = "not found.\n"
|
|
else
|
|
Msg = "fatal error(s).\n"
|
|
)
|
|
else
|
|
% Any errors must be nonfatal. Are they syntax errors?
|
|
( if
|
|
( set.contains(NonFatalErrors0,
|
|
rme_no_module_decl_at_start)
|
|
; set.contains(NonFatalErrors0,
|
|
rme_no_section_decl_at_start)
|
|
; set.contains(NonFatalErrors0,
|
|
rme_end_module_not_at_end_of_src)
|
|
; set.contains(NonFatalErrors0,
|
|
rme_unexpected_term_in_int_or_opt)
|
|
; set.contains(NonFatalErrors0, rme_could_not_read_term)
|
|
; set.contains(NonFatalErrors0, rme_could_not_parse_item)
|
|
)
|
|
then
|
|
Msg = "parse error(s).\n"
|
|
else
|
|
Msg = "nonfatal error(s).\n"
|
|
)
|
|
),
|
|
output_read_done_msg(ReadDoneMsg, Msg, !IO),
|
|
(
|
|
FileKind = fk_opt(_),
|
|
Errors = Errors0
|
|
;
|
|
( FileKind = fk_src
|
|
; FileKind = fk_int(_)
|
|
),
|
|
Errors = Errors0
|
|
)
|
|
else
|
|
Errors = Errors0,
|
|
% XXX CLEANUP This should just print "done".
|
|
output_read_done_msg(ReadDoneMsg, "successful parse.\n", !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred output_read_reason_msg(io.text_output_stream::in, globals::in,
|
|
read_reason_msg::in, string::in, read_done_msg::out,
|
|
io::di, io::uo) is det.
|
|
|
|
output_read_reason_msg(ProgressStream, Globals, ReadReasonMsg, FileName,
|
|
ReadDoneMsg, !IO) :-
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
(
|
|
VeryVerbose = no,
|
|
ReadDoneMsg = rdm_none
|
|
;
|
|
VeryVerbose = yes,
|
|
(
|
|
ReadReasonMsg = rrm_file,
|
|
string.format("%% Reading `%s'... ", [s(FileName)], Msg)
|
|
;
|
|
ReadReasonMsg = rrm_std,
|
|
string.format("%% Reading `%s'... ", [s(FileName)], Msg)
|
|
;
|
|
ReadReasonMsg = rrm_old,
|
|
string.format("%% Reading old version of `%s'... ",
|
|
[s(FileName)], Msg)
|
|
;
|
|
ReadReasonMsg = rrm_get_deps,
|
|
string.format("%% Getting dependencies for `%s'... ",
|
|
[s(FileName)], Msg)
|
|
),
|
|
ReadDoneMsg = rdm_progress(ProgressStream),
|
|
io.write_string(ProgressStream, Msg, !IO),
|
|
io.flush_output(ProgressStream, !IO)
|
|
).
|
|
|
|
:- pred output_read_done_msg(read_done_msg::in, string::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_read_done_msg(ReadDoneMsg, Msg, !IO) :-
|
|
(
|
|
ReadDoneMsg = rdm_none
|
|
;
|
|
ReadDoneMsg = rdm_progress(ProgressStream),
|
|
io.write_string(ProgressStream, Msg, !IO),
|
|
io.flush_output(ProgressStream, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0, MaybeTimestamp) :-
|
|
(
|
|
ReturnTimestamp = do_return_timestamp,
|
|
(
|
|
MaybeTimestamp0 = no,
|
|
% This can happen if
|
|
%
|
|
% - code that does not need a timestamp enters a parse tree
|
|
% into the have_module_map, and then later
|
|
%
|
|
% - code that does need a timestamp finds the parse tree there.
|
|
%
|
|
% We abort because I (zs) don't think this should happen:
|
|
% the use of have_parse_tree_maps in module_imports.m never
|
|
% needs timestamps, the smart recompilation modules always
|
|
% need timestamps, but the have_parse_tree_maps they use
|
|
% are completely separate (for now). If it turns out I am wrong,
|
|
% or we *do* want these two subsystems to use the same
|
|
% have_parse_tree_maps, then there are two obvious possibilities:
|
|
% either *always* store the timestamp of a file we read in, or
|
|
% get the timestamp from the OS the first time it is needed
|
|
% (have_parse_tree_maps entries include the filename, so this
|
|
% is possible). The first solution is simpler, the second can
|
|
% execute fewer system calls.
|
|
unexpected($pred, "do_return_timestamp but no timestamp")
|
|
;
|
|
MaybeTimestamp0 = yes(_),
|
|
MaybeTimestamp = MaybeTimestamp0
|
|
)
|
|
;
|
|
ReturnTimestamp = dont_return_timestamp,
|
|
MaybeTimestamp = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred check_timestamp_report_if_needed_and_missing(globals::in,
|
|
file_name::in, maybe(io.res(timestamp))::in, maybe(timestamp)::out,
|
|
read_module_errors::in, read_module_errors::out, io::di, io::uo) is det.
|
|
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, !Errors, !IO) :-
|
|
(
|
|
MaybeTimestampRes = yes(TimestampRes),
|
|
(
|
|
TimestampRes = ok(Timestamp),
|
|
MaybeTimestamp = yes(Timestamp)
|
|
;
|
|
TimestampRes = error(IOError),
|
|
MaybeTimestamp = no,
|
|
globals.lookup_bool_option(Globals, smart_recompilation,
|
|
SmartRecompilation),
|
|
% Should we print the warning if smart recompilation has
|
|
% already been disabled by an earlier error? At the moment, we do.
|
|
(
|
|
SmartRecompilation = yes,
|
|
record_and_report_missing_timestamp(Globals, FileName,
|
|
IOError, !Errors, !IO)
|
|
;
|
|
SmartRecompilation = no
|
|
)
|
|
)
|
|
;
|
|
MaybeTimestampRes = no,
|
|
MaybeTimestamp = no
|
|
).
|
|
|
|
:- pred record_and_report_missing_timestamp(globals::in, file_name::in,
|
|
io.error::in, read_module_errors::in, read_module_errors::out,
|
|
io::di, io::uo) is det.
|
|
|
|
record_and_report_missing_timestamp(Globals, FileName, IOError,
|
|
!Errors, !IO) :-
|
|
io_set_disable_smart_recompilation(disable_smart_recompilation, !IO),
|
|
io_set_disable_generate_item_version_numbers(disable_item_version_numbers,
|
|
!IO),
|
|
globals.lookup_bool_option(Globals, warn_smart_recompilation, Warn),
|
|
(
|
|
Warn = yes,
|
|
io.error_message(IOError, IOErrorMsg),
|
|
Pieces = [words("Warning: cannot find modification time for"),
|
|
quote(FileName), suffix(":"), nl_indent_delta(1),
|
|
words(IOErrorMsg), suffix("."), nl_indent_delta(-1),
|
|
words("Smart recompilation will not work."), nl],
|
|
Spec = simplest_no_context_spec($pred, severity_warning,
|
|
phase_read_files, Pieces),
|
|
add_nonfatal_error(rme_cannot_find_modify_time, [Spec], !Errors)
|
|
;
|
|
Warn = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module parse_tree.read_modules.
|
|
%---------------------------------------------------------------------------%
|