mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 10:23:46 +00:00
compiler/mercury_compile_main.m:
compiler/mercury_compile_front_end.m:
compiler/mercury_compile_llds_back_end.m:
compiler/mercury_compile_make_hlds.m:
compiler/mercury_compile_middle_passes.m:
compiler/mercury_compile_mlds_back_end.m:
Pass progress and error streams explicitly in these top modules
of the compiler. Use "XXX STREAM" to mark places where we could switch
from using stderr for both the progress and error streams to using
module-specific files as the progress and/or error streams.
compiler/passes_aux.m:
Add a "maybe_" prefix to the names of the predicates that print progress
messages at the appropriate verbosity levels, as their printing of those
messages is conditional.
Provide versions of those predicates that take explicitly specified
streams to write to, and mark the versions that write to the current
output stream as obsolete.
The predicate that wrote progress messages for procedures
used to have two versions, one taking a pred_proc_id, and one taking
a pred_id/proc_id pair. Delete the latter, because the arity difference
that differentiated the two versions is now needed for the difference
between supplying and not supplying an explicit stream.
compiler/file_util.m:
compiler/hlds_error_util.m:
compiler/write_error_spec.m:
Delete several predicates that wrote to the current output stream,
since all their callers now use the versions that specify an explicit
output stream.
compiler/check_promise.m:
compiler/check_typeclass.m:
compiler/closure_analysis.m:
compiler/complexity.m:
compiler/cse_detection.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/deps_map.m:
compiler/direct_arg_in_out.m:
compiler/grab_modules.m:
compiler/handle_options.m:
compiler/hhf.m:
compiler/inlining.m:
compiler/make.module_dep_file.m:
compiler/ml_proc_gen.m:
compiler/ml_top_gen.m:
compiler/mode_constraints.m:
compiler/modes.m:
compiler/polymorphism.m:
compiler/purity.m:
compiler/read_modules.m:
compiler/recompilation.check.m:
compiler/saved_vars.m:
compiler/simplify_proc.m:
compiler/size_prof.m:
compiler/stack_opt.m:
compiler/switch_detection.m:
compiler/typecheck.m:
compiler/unique_modes.m:
compiler/unneeded_code.m:
compiler/write_module_interface_files.m:
Get these modules to take an explicitly specified stream to which
to write progress messages when they are invoked from mercury_compile_*.m.
For predicates in these modules that can be invoked both directly
by mercury_compile_*.m *and* by other modules, the latter effectively
as a subcontractor, make them take a maybe(stream), with the intention
being that all the other modules that use the predicate as a subcontractor
would pass a "no". This avoids the need to pass progress streams
down to the internals of other passes, and also avoids overwhelming
the user invoking the compiler with unnecessary details.
As above, and also delete a progress message that shouldn't be needed
anymore.
Move a test of option value compatibility from
mercury_compile_middle_passes.m to handle_options.m, where it belongs.
compiler/float_regs.m:
Write a debug message to the debug stream.
compiler/pd_info.m:
Include the progress stream in the pd_info structure, because this is
the simplest way to ensure that all parts of the partial deduction pass
have access to it.
compiler/make.build.m:
compiler/make.program_target.m:
compiler/make.track_flags.m:
Make the minimal changes needed to conform to the changes above.
The rest can be done when the make package is converted to consistently
use explicit streams.
compiler/bytecode_gen.m:
compiler/structure_reuse.direct.m:
compiler/structure_reuse.versions.m:
compiler/structure_sharing.analysis.m:
Make the minimal changes needed to conform to the changes above.
The rest can be done when these modules start being maintained again.
compiler/Mercury.options:
Stop specifying --no-warn-implicit-stream-calls for mercury_compile_*.m,
since this diff makes that unnecessary.
Start specifying --no-warn-implicit-stream-calls for some modules that
are not currently being actively maintained, because the addition of
progress-reporting predicates that take explicitly specified streams
would otherwise cause the generation of such warnings for them.
1196 lines
52 KiB
Mathematica
1196 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 hrmm_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_read_module_maps
|
|
---> have_read_module_maps(
|
|
hrmm_src :: have_read_module_src_map,
|
|
|
|
hrmm_module_src :: have_read_module_msrc_map,
|
|
|
|
hrmm_int0 :: have_read_module_int0_map,
|
|
hrmm_int1 :: have_read_module_int1_map,
|
|
hrmm_int2 :: have_read_module_int2_map,
|
|
hrmm_int3 :: have_read_module_int3_map,
|
|
|
|
hrmm_plain_opt :: have_read_module_plain_opt_map,
|
|
hrmm_trans_opt :: have_read_module_trans_opt_map
|
|
).
|
|
|
|
:- func init_have_read_module_maps = have_read_module_maps.
|
|
|
|
:- type have_read_module_src_map ==
|
|
have_read_module_map(parse_tree_src).
|
|
|
|
:- type have_read_module_key(Kind)
|
|
---> have_read_module_key(module_name, Kind).
|
|
|
|
:- type have_read_module_msrc_map ==
|
|
map(module_name, parse_tree_module_src).
|
|
|
|
:- type have_read_module_int0_map ==
|
|
have_read_module_map(parse_tree_int0).
|
|
:- type have_read_module_int1_map ==
|
|
have_read_module_map(parse_tree_int1).
|
|
:- type have_read_module_int2_map ==
|
|
have_read_module_map(parse_tree_int2).
|
|
:- type have_read_module_int3_map ==
|
|
have_read_module_map(parse_tree_int3).
|
|
|
|
:- type have_read_module_plain_opt_map ==
|
|
have_read_module_map(parse_tree_plain_opt).
|
|
:- type have_read_module_trans_opt_map ==
|
|
have_read_module_map(parse_tree_trans_opt).
|
|
|
|
:- type have_read_module_map(PT) == map(module_name, have_read_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_read_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_read_module(PT)
|
|
---> have_read_module(
|
|
% We have read the module.
|
|
|
|
% The name of the file that we have read.
|
|
file_name,
|
|
|
|
% The timestamp of the file, if the caller requested
|
|
% that it be returned. Not all do.
|
|
maybe(timestamp),
|
|
|
|
% The parse tree of the file we have read.
|
|
PT,
|
|
|
|
% Any errors we found during parsing. It should contain
|
|
% neither frme_could_not_open_file or frme_could_not_read_file,
|
|
% but may contain any other errors.
|
|
read_module_errors
|
|
)
|
|
; have_not_read_module(
|
|
% We have not read the module.
|
|
|
|
% 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 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(module_name)
|
|
% 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(module_name)
|
|
% 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(module_name).
|
|
% Because the named file belongs to this module, and we need
|
|
% to get its dependencies.
|
|
|
|
% read_module_src(MaybeProgressStream, Globals, ReadReasonMsg,
|
|
% IgnoreErrors, Search, ModuleName, FileName, ReadModuleAndTimestamps,
|
|
% HaveReadModule, !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 HaveReadModule 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 HaveReadModule.
|
|
%
|
|
% If ReadModuleAndTimestamps is always_read_module(do_return_timestamp),
|
|
% attempt to return the modification time of the file in the MaybeTimestamp
|
|
% field of HaveReadModule.
|
|
%
|
|
% 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(maybe(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(Globals, FileName, FileNameDotM,
|
|
% ReadReasonMsg, Search, ReadModuleAndTimestamps, HaveReadModule, !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(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.
|
|
|
|
% read_module_intN(MaybeProgressStream, Globals, ReadReasonMsg,
|
|
% IgnoreErrors, Search, ModuleName, FileName, ReadModuleAndTimestamps,
|
|
% HaveReadModule, !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(maybe(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_read_module(parse_tree_int0)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int1(maybe(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_read_module(parse_tree_int1)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int2(maybe(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_read_module(parse_tree_int2)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int3(maybe(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_read_module(parse_tree_int3)::out, io::di, io::uo) is det.
|
|
|
|
:- pred read_module_int0_no_stream(globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_read_module(parse_tree_int0)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int1_no_stream(globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_read_module(parse_tree_int1)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int2_no_stream(globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_read_module(parse_tree_int2)::out, io::di, io::uo) is det.
|
|
:- pred read_module_int3_no_stream(globals::in,
|
|
read_reason_msg::in, maybe_ignore_errors::in, maybe_search::in,
|
|
module_name::in, read_module_and_timestamps::in,
|
|
have_read_module(parse_tree_int3)::out, io::di, io::uo) is det.
|
|
|
|
% read_module_{plain,trans}_opt(ProgressStream, Globals, ModuleName,
|
|
% FileName, HaveReadModule, !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_read_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_read_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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% maybe_read_module_intN(ProgressStream, Globals, ReadReasonMsg, Search,
|
|
% ModuleName, IntFileKind, ReturnTimestamp, HaveReadModule,
|
|
% !HaveReadModuleMaps, !IO):
|
|
%
|
|
% If HaveReadModuleMap contains the already-read contents of the
|
|
% relevant kind of interface file for ModuleName, then return
|
|
% the information stored in HaveReadModuleMap 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_read_module(parse_tree_int0)::out,
|
|
have_read_module_maps::in, have_read_module_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_read_module(parse_tree_int1)::out,
|
|
have_read_module_maps::in, have_read_module_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_read_module(parse_tree_int2)::out,
|
|
have_read_module_maps::in, have_read_module_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_read_module(parse_tree_int3)::out,
|
|
have_read_module_maps::in, have_read_module_maps::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 parse_tree.write_error_spec.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module dir.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_have_read_module_maps =
|
|
have_read_module_maps(map.init, map.init, map.init,
|
|
map.init, map.init, map.init, map.init, map.init).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
read_module_src(MaybeProgressStream, Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ExpectationContexts, ReadModuleAndTimestamps,
|
|
HaveReadModule, !IO) :-
|
|
read_module_begin(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(MaybeProgressStream, 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,
|
|
HaveReadModule = have_read_module(FileName, MaybeTimestamp,
|
|
ParseTreeSrc, Errors1)
|
|
;
|
|
MaybeParseTreeSrc0 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = error(ErrorMsg),
|
|
io_error_to_read_module_errors(ErrorMsg, frme_could_not_open_file,
|
|
Errors0, !IO),
|
|
read_module_end_module(MaybeProgressStream, Globals,
|
|
mfas_error(Errors0), IgnoreErrors, fk_src, ReadDoneMsg,
|
|
FileName0, FileName, no, _MaybeTimestamp, Errors0, Errors1, !IO),
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_src_from_file(Globals, FileName, FileNameDotM, ReadReasonMsg,
|
|
Search, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_begin_from_file(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),
|
|
HaveReadModule = have_read_module(FileNameDotM, MaybeTimestamp,
|
|
ParseTreeSrc, Errors1)
|
|
;
|
|
MaybeParseTreeSrc = no,
|
|
HaveReadModule = have_not_read_module(FileNameDotM, Errors1)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = error(ErrorMsg),
|
|
io_error_to_read_module_errors(ErrorMsg, frme_could_not_open_file,
|
|
Errors0, !IO),
|
|
read_module_end_file(Globals, fk_src, ReadDoneMsg, FileNameDotM,
|
|
no, _MaybeTimestamp, Errors0, Errors, !IO),
|
|
HaveReadModule = have_not_read_module(FileNameDotM, Errors)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
read_module_int0(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_begin(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_int3), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt0 = yes(ParseTreeInt0),
|
|
HaveReadModule = have_read_module(FileName, MaybeTimestamp,
|
|
ParseTreeInt0, Errors1)
|
|
;
|
|
MaybeParseTreeInt0 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveReadModule = 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),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_int1(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_begin(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_int3), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt1 = yes(ParseTreeInt1),
|
|
HaveReadModule = have_read_module(FileName, MaybeTimestamp,
|
|
ParseTreeInt1, Errors1)
|
|
;
|
|
MaybeParseTreeInt1 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveReadModule = 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),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_int2(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_begin(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_int3), ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, Errors0, Errors1, !IO),
|
|
(
|
|
MaybeParseTreeInt2 = yes(ParseTreeInt2),
|
|
HaveReadModule = have_read_module(FileName, MaybeTimestamp,
|
|
ParseTreeInt2, Errors1)
|
|
;
|
|
MaybeParseTreeInt2 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveReadModule = 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),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_int3(ProgressStream, Globals, ReadReasonMsg, IgnoreErrors, Search,
|
|
ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_begin(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),
|
|
HaveReadModule = have_read_module(FileName, MaybeTimestamp,
|
|
ParseTreeInt3, Errors1)
|
|
;
|
|
MaybeParseTreeInt3 = no,
|
|
Errors = no_file_errors(IgnoreErrors, Errors1),
|
|
HaveReadModule = 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),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
read_module_int0_no_stream(Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_int0(no, Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO).
|
|
|
|
read_module_int1_no_stream(Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_int1(no, Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO).
|
|
|
|
read_module_int2_no_stream(Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_int2(no, Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO).
|
|
|
|
read_module_int3_no_stream(Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO) :-
|
|
read_module_int3(no, Globals, ReadReasonMsg, IgnoreErrors,
|
|
Search, ModuleName, ReadModuleAndTimestamps, HaveReadModule, !IO).
|
|
|
|
%---------------------%
|
|
|
|
read_module_plain_opt(ProgressStream, Globals, ModuleName,
|
|
HaveReadModule, !IO) :-
|
|
ReadReasonMsg = rrm_std(ModuleName),
|
|
Search = do_search,
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
MaybeTimestampRes = no,
|
|
|
|
read_module_begin(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(yes(ProgressStream), Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_opt), ReadDoneMsg,
|
|
FileName0, FileName, MaybeTimestampRes, _MaybeTimestamp,
|
|
Errors0, Errors, !IO),
|
|
(
|
|
MaybeParseTreePlainOpt = yes(ParseTreePlainOpt),
|
|
HaveReadModule = have_read_module(FileName, no,
|
|
ParseTreePlainOpt, Errors)
|
|
;
|
|
MaybeParseTreePlainOpt = no,
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(yes(ProgressStream), Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_opt), ReadDoneMsg,
|
|
FileName0, FileName, MaybeTimestampRes, _MaybeTimestamp,
|
|
Errors0, Errors, !IO),
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
).
|
|
|
|
read_module_trans_opt(ProgressStream, Globals, ModuleName,
|
|
HaveReadModule, !IO) :-
|
|
ReadReasonMsg = rrm_std(ModuleName),
|
|
Search = do_search,
|
|
IgnoreErrors = do_not_ignore_errors,
|
|
MaybeTimestampRes = no,
|
|
|
|
read_module_begin(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(yes(ProgressStream), Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_trans_opt),
|
|
ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, _MaybeTimestamp, Errors0, Errors, !IO),
|
|
(
|
|
MaybeParseTreeTransOpt = yes(ParseTreeTransOpt),
|
|
HaveReadModule = have_read_module(FileName, no,
|
|
ParseTreeTransOpt, Errors)
|
|
;
|
|
MaybeParseTreeTransOpt = no,
|
|
HaveReadModule = have_not_read_module(FileName, Errors)
|
|
)
|
|
;
|
|
MaybeFileNameAndStream = mfas_error(Errors0),
|
|
read_module_end_module(yes(ProgressStream), Globals,
|
|
MaybeFileNameAndStream, IgnoreErrors, fk_opt(ofk_trans_opt),
|
|
ReadDoneMsg, FileName0, FileName,
|
|
MaybeTimestampRes, _MaybeTimestamp, Errors0, Errors, !IO),
|
|
HaveReadModule = 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(ErrorMsg,
|
|
frme_could_not_open_file, 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)
|
|
)
|
|
).
|
|
|
|
:- pred read_module_begin_from_file(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(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(Globals, ReadReasonMsg, FileNameDotM,
|
|
ReadDoneMsg, !IO).
|
|
|
|
:- pred read_module_begin(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(Globals, ReadReasonMsg, Search, ModuleName, FileKind,
|
|
FileName, ReadDoneMsg, SearchDirs, !IO) :-
|
|
file_kind_to_extension(FileKind, _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, !IO),
|
|
(
|
|
( FileKind = fk_src
|
|
; 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, do_not_create_dirs, Ext,
|
|
ModuleName, FileName, !IO),
|
|
SearchDirs = [dir.this_directory]
|
|
),
|
|
output_read_reason_msg(Globals, ReadReasonMsg, FileName, ReadDoneMsg, !IO).
|
|
|
|
:- pred read_module_end_module(maybe(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(MaybeProgressStream, 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, !IO),
|
|
handle_any_read_module_errors(Globals, FileKind, ReadDoneMsg,
|
|
IgnoreErrors, Errors0, Errors, !IO),
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
|
|
(
|
|
MaybeProgressStream = no
|
|
;
|
|
MaybeProgressStream = yes(ProgressStream),
|
|
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, !IO),
|
|
% Unlike read_module_end_module, we assume do_not_ignore_errors.
|
|
handle_any_read_module_errors(Globals, FileKind, ReadDoneMsg,
|
|
do_not_ignore_errors, Errors0, 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,
|
|
Errors = no_file_errors_ignored
|
|
).
|
|
|
|
% 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.
|
|
%
|
|
:- func no_file_errors_ignored = read_module_errors.
|
|
|
|
no_file_errors_ignored = Errors :-
|
|
Errors = read_module_errors(
|
|
set.make_singleton_set(frme_could_not_open_file), [],
|
|
set.init, [], []).
|
|
|
|
:- pred handle_any_read_module_errors(globals::in, 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(Globals, 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
|
|
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,
|
|
( if there_are_no_errors(Errors0) then
|
|
Errors = Errors0,
|
|
% XXX CLEANUP This should just print "done".
|
|
output_read_done_msg(ReadDoneMsg, "successful parse.\n", !IO)
|
|
else
|
|
FatalErrors0 = Errors0 ^ rm_fatal_errors,
|
|
( if set.contains(FatalErrors0, frme_could_not_open_file) then
|
|
output_read_done_msg(ReadDoneMsg, "not found.\n", !IO)
|
|
else if set.is_non_empty(FatalErrors0) then
|
|
output_read_done_msg(ReadDoneMsg, "fatal error(s).\n", !IO)
|
|
else
|
|
% XXX CLEANUP Some of the errors are not PARSE errors.
|
|
output_read_done_msg(ReadDoneMsg, "parse error(s).\n", !IO)
|
|
),
|
|
(
|
|
FileKind = fk_opt(_),
|
|
Errors = Errors0
|
|
;
|
|
( FileKind = fk_src
|
|
; FileKind = fk_int(_)
|
|
),
|
|
(
|
|
ReadDoneMsg = rdm_none,
|
|
Errors = Errors0
|
|
;
|
|
(
|
|
ReadDoneMsg = rdm_current,
|
|
io.output_stream(ErrorStream, !IO)
|
|
;
|
|
ReadDoneMsg = rdm_progress(ErrorStream)
|
|
),
|
|
% XXX STREAM It should be possible to print error messages
|
|
% to the relevant module's .err file, not just to the
|
|
% progress stream.
|
|
FatalErrorSpecs0 = Errors0 ^ rm_fatal_error_specs,
|
|
NonFatalErrorSpecs0 = Errors0 ^ rm_nonfatal_error_specs,
|
|
write_error_specs(ErrorStream, Globals,
|
|
FatalErrorSpecs0 ++ NonFatalErrorSpecs0, !IO),
|
|
Errors = ((Errors0
|
|
^ rm_fatal_error_specs := [])
|
|
^ rm_nonfatal_error_specs := [])
|
|
),
|
|
io.set_exit_status(1, !IO)
|
|
)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type read_done_msg
|
|
---> rdm_none
|
|
% Do not print a "done" message.
|
|
; rdm_current
|
|
% Print a "done" message to the current output stream.
|
|
; rdm_progress(io.text_output_stream).
|
|
% Print a "done" message to the specified progress output stream.
|
|
|
|
:- pred output_read_reason_msg(globals::in, read_reason_msg::in, string::in,
|
|
read_done_msg::out, io::di, io::uo) is det.
|
|
|
|
output_read_reason_msg(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),
|
|
% We should be directing the output to the progress stream
|
|
% of the module we are reading, but we don't know its name yet.
|
|
% XXX Now that we require every module to be stored in a file
|
|
% whose name *either* directly corresponds to the name of the
|
|
% module, *or* is stored in Mercury.modules, this is not true.
|
|
io.output_stream(Stream, !IO),
|
|
ReadDoneMsg = rdm_current
|
|
;
|
|
(
|
|
ReadReasonMsg = rrm_std(ModuleName),
|
|
string.format("%% Reading `%s'... ",
|
|
[s(FileName)], Msg)
|
|
;
|
|
ReadReasonMsg = rrm_old(ModuleName),
|
|
string.format("%% Reading old version of `%s'... ",
|
|
[s(FileName)], Msg)
|
|
;
|
|
ReadReasonMsg = rrm_get_deps(ModuleName),
|
|
string.format("%% Getting dependencies for `%s'... ",
|
|
[s(FileName)], Msg)
|
|
),
|
|
get_progress_output_stream(Globals, ModuleName, Stream, !IO),
|
|
ReadDoneMsg = rdm_progress(Stream)
|
|
),
|
|
io.write_string(Stream, Msg, !IO),
|
|
io.flush_output(Stream, !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_current,
|
|
io.output_stream(Stream, !IO)
|
|
;
|
|
ReadDoneMsg = rdm_progress(Stream)
|
|
),
|
|
io.write_string(Stream, Msg, !IO),
|
|
io.flush_output(Stream, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
maybe_read_module_int0(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveReadModule, !HaveReadModuleMaps, !IO) :-
|
|
OrigHRMM = !.HaveReadModuleMaps ^ hrmm_int0,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHRMM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveReadModule = HaveReadModulePrime
|
|
else
|
|
read_module_int0(yes(ProgressStream), Globals, rrm_std(ModuleName),
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveReadModule, !IO),
|
|
map.set(ModuleName, HaveReadModule, OrigHRMM, HRMM),
|
|
!HaveReadModuleMaps ^ hrmm_int0 := HRMM
|
|
).
|
|
|
|
maybe_read_module_int1(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveReadModule, !HaveReadModuleMaps, !IO) :-
|
|
OrigHRMM = !.HaveReadModuleMaps ^ hrmm_int1,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHRMM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveReadModule = HaveReadModulePrime
|
|
else
|
|
read_module_int1(yes(ProgressStream), Globals, rrm_std(ModuleName),
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveReadModule, !IO),
|
|
map.set(ModuleName, HaveReadModule, OrigHRMM, HRMM),
|
|
!HaveReadModuleMaps ^ hrmm_int1 := HRMM
|
|
).
|
|
|
|
maybe_read_module_int2(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveReadModule, !HaveReadModuleMaps, !IO) :-
|
|
OrigHRMM = !.HaveReadModuleMaps ^ hrmm_int2,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHRMM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveReadModule = HaveReadModulePrime
|
|
else
|
|
read_module_int2(yes(ProgressStream), Globals, rrm_std(ModuleName),
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveReadModule, !IO),
|
|
map.set(ModuleName, HaveReadModule, OrigHRMM, HRMM),
|
|
!HaveReadModuleMaps ^ hrmm_int2 := HRMM
|
|
).
|
|
|
|
maybe_read_module_int3(ProgressStream, Globals, Search, ModuleName,
|
|
ReturnTimestamp, HaveReadModule, !HaveReadModuleMaps, !IO) :-
|
|
OrigHRMM = !.HaveReadModuleMaps ^ hrmm_int3,
|
|
( if
|
|
search_module_name_timestamp_if_needed(OrigHRMM, ModuleName,
|
|
ReturnTimestamp, HaveReadModulePrime)
|
|
then
|
|
HaveReadModule = HaveReadModulePrime
|
|
else
|
|
read_module_int3(yes(ProgressStream), Globals, rrm_std(ModuleName),
|
|
do_not_ignore_errors, Search, ModuleName,
|
|
always_read_module(ReturnTimestamp), HaveReadModule, !IO),
|
|
map.set(ModuleName, HaveReadModule, OrigHRMM, HRMM),
|
|
!HaveReadModuleMaps ^ hrmm_int3 := HRMM
|
|
).
|
|
|
|
:- pred search_module_name_timestamp_if_needed(have_read_module_map(PT)::in,
|
|
module_name::in, maybe_return_timestamp::in, have_read_module(PT)::out)
|
|
is semidet.
|
|
|
|
search_module_name_timestamp_if_needed(HRMM, ModuleName, ReturnTimestamp,
|
|
HaveReadModule) :-
|
|
map.search(HRMM, ModuleName, HaveReadModule0),
|
|
HaveReadModule0 = have_read_module(FN, MaybeTimeStamp0, PT, E),
|
|
return_timestamp_if_needed(ReturnTimestamp,
|
|
MaybeTimeStamp0, MaybeTimeStamp),
|
|
HaveReadModule = have_read_module(FN, MaybeTimeStamp, PT, E).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
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_read_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_read_module_maps in module_imports.m never
|
|
% needs timestamps, the smart recompilation modules always
|
|
% need timestamps, but the have_read_module_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_read_module_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_read_module_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,
|
|
io::di, io::uo) is det.
|
|
|
|
check_timestamp_report_if_needed_and_missing(Globals, FileName,
|
|
MaybeTimestampRes, MaybeTimestamp, !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, !IO)
|
|
;
|
|
SmartRecompilation = no
|
|
)
|
|
)
|
|
;
|
|
MaybeTimestampRes = no,
|
|
MaybeTimestamp = no
|
|
).
|
|
|
|
:- pred record_and_report_missing_timestamp(globals::in, file_name::in,
|
|
io.error::in, io::di, io::uo) is det.
|
|
|
|
record_and_report_missing_timestamp(Globals, FileName, Error, !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(Error, ErrorMsg),
|
|
Pieces = [words("Warning: cannot find modification time for"),
|
|
quote(FileName), suffix(":"), nl,
|
|
words(ErrorMsg), suffix("."), nl,
|
|
words("Smart recompilation will not work."), nl],
|
|
Spec = simplest_no_context_spec($pred, severity_warning,
|
|
phase_read_files, Pieces),
|
|
write_error_spec(Globals, Spec, !IO)
|
|
;
|
|
Warn = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module parse_tree.read_modules.
|
|
%---------------------------------------------------------------------------%
|