mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 09:53:36 +00:00
compiler/module_cmds.m:
Require all callers to specify output streams explicitly.
compiler/compile_target_code.m:
Require callers to specify output streams explicitly in most cases.
In the remaining cases, add "XXX STREAM" comments to request that
the explicit streams be passed later.
Add XXXs where preserving old behavior results in wrong-looking code.
Modify the signature of compile_java_files to encode the invariant that
the list of Java files given to it may not be empty.
compiler/error_util.m:
Provide a version of an error predicate that takes explicit streams.
compiler/make.build.m:
Mark predicates that help redirect implicit streams as predicates
that should not be used.
compiler/make.module_target.m:
compiler/make.program_target.m:
Add "XXX STREAM" comments to request that the explicit streams
be passed later.
compiler/module_qual.m:
Provide a means to construct the debug output stream.
compiler/module_qual.collect_mq_info.m:
Use the debug output stream where relevant.
compiler/analysis.m:
compiler/export.m:
compiler/recompilation.check.m:
compiler/source_file_map.m:
compiler/write_module_interface_files.m:
compiler/xml_documentation.m:
Conform to the changes above, mostly by passing explicit streams.
compiler/Mercury.options:
Stop specifying --no-warn-implicit-stream-calls for the above modules
(those that had it specified in the first place).
Delete a long-unneeded workaround.
compiler/mercury_compile_front_end.m:
compiler/mercury_compile_llds_back_end.m:
compiler/mercury_compile_main.m:
compiler/mercury_compile_middle_passes.m:
Specify progress and output streams explicitly when calling the modules
above (not in other places, just yet).
288 lines
11 KiB
Mathematica
288 lines
11 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2002-2009, 2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2019-2020 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: source_file_map.m.
|
|
% Author: stayl.
|
|
%
|
|
% Maintain a mapping from module name to source file name.
|
|
%
|
|
% The reason why this module is in the parse_tree package is that discovering
|
|
% what module is stored in a file requires reading the ":- module" declaration
|
|
% in that file.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.source_file_map.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% lookup_module_source_file(ModuleName, MaybeFileName, !IO)
|
|
% Return `yes(FileName)' if FileName is the source file for ModuleName,
|
|
% either through the source file map, or by default. Return `no' if no
|
|
% source file is available for ModuleName because the default file name
|
|
% is mapped to another module.
|
|
%
|
|
:- pred lookup_module_source_file(module_name::in, maybe(file_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pred lookup_source_file_module(file_name::in, maybe(module_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% Return `yes' if there is a valid Mercury.modules file.
|
|
%
|
|
:- pred have_source_file_map(bool::out, io::di, io::uo) is det.
|
|
|
|
% Return the default fully qualified source file name.
|
|
%
|
|
:- func default_source_file_name(module_name) = file_name.
|
|
|
|
% Given a list of file names, produce the Mercury.modules file.
|
|
%
|
|
:- pred write_source_file_map(globals::in, list(string)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.find_module.
|
|
:- import_module parse_tree.prog_out.
|
|
|
|
:- import_module bimap.
|
|
:- import_module dir.
|
|
:- import_module int.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lookup_module_source_file(ModuleName, MaybeFileName, !IO) :-
|
|
get_source_file_map(SourceFileMap, !IO),
|
|
( if bimap.search(SourceFileMap, ModuleName, FileName) then
|
|
MaybeFileName = yes(FileName)
|
|
else
|
|
DefaultFileName = default_source_file_name(ModuleName),
|
|
( if bimap.reverse_search(SourceFileMap, _, DefaultFileName) then
|
|
MaybeFileName = no
|
|
else
|
|
MaybeFileName = yes(DefaultFileName)
|
|
)
|
|
).
|
|
|
|
lookup_source_file_module(FileName, MaybeModuleName, !IO) :-
|
|
get_source_file_map(SourceFileMap, !IO),
|
|
( if bimap.reverse_search(SourceFileMap, ModuleName, FileName) then
|
|
MaybeModuleName = yes(ModuleName)
|
|
else
|
|
( if default_module_name_for_file(FileName, DefaultModuleName) then
|
|
( if bimap.search(SourceFileMap, DefaultModuleName, _) then
|
|
MaybeModuleName = no
|
|
else
|
|
MaybeModuleName = yes(DefaultModuleName)
|
|
)
|
|
else
|
|
MaybeModuleName = no
|
|
)
|
|
).
|
|
|
|
have_source_file_map(HaveMap, !IO) :-
|
|
get_source_file_map(SourceFileMap, !IO),
|
|
( if bimap.is_empty(SourceFileMap) then
|
|
HaveMap = no
|
|
else
|
|
HaveMap = yes
|
|
).
|
|
|
|
default_source_file_name(ModuleName) = sym_name_to_string(ModuleName) ++ ".m".
|
|
|
|
% If the file name ends in ".m", return the module name whose
|
|
% default file name that would be.
|
|
%
|
|
:- pred default_module_name_for_file(file_name::in, module_name::out)
|
|
is semidet.
|
|
|
|
default_module_name_for_file(FileName, DefaultModuleName) :-
|
|
string.remove_suffix(FileName, ".m", FileNameBeforeDotM),
|
|
file_name_to_module_name(FileNameBeforeDotM, DefaultModuleName).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Read the Mercury.modules file (if it exists) to find the mapping
|
|
% from module name to file name.
|
|
%
|
|
:- pred get_source_file_map(source_file_map::out, io::di, io::uo) is det.
|
|
|
|
get_source_file_map(SourceFileMap, !IO) :-
|
|
globals.io_get_maybe_source_file_map(MaybeSourceFileMap0, !IO),
|
|
(
|
|
MaybeSourceFileMap0 = yes(SourceFileMap0),
|
|
SourceFileMap = SourceFileMap0
|
|
;
|
|
MaybeSourceFileMap0 = no,
|
|
ModulesFileName = modules_file_name,
|
|
io.read_named_file_as_lines(ModulesFileName, ReadResult, !IO),
|
|
(
|
|
ReadResult = ok(FileLines),
|
|
bimap.init(SourceFileMap0),
|
|
parse_source_file_map(FileLines, ModulesFileName, 1, ErrorMsg,
|
|
SourceFileMap0, SourceFileMap1),
|
|
( if ErrorMsg = "" then
|
|
SourceFileMap = SourceFileMap1
|
|
else
|
|
% If the file does exist but is malformed, then
|
|
% we *should* generate an error, but pretending that
|
|
% the file exists and is empty preserves old behavior.
|
|
bimap.init(SourceFileMap)
|
|
)
|
|
;
|
|
ReadResult = error(_),
|
|
% If the file doesn't exist, then the mapping is empty.
|
|
% XXX ReadResult can be error/1 even when the file *does* exist.
|
|
% For example, the open could fail due to a permission problem.
|
|
SourceFileMap = bimap.init
|
|
),
|
|
globals.io_set_maybe_source_file_map(yes(SourceFileMap), !IO)
|
|
).
|
|
|
|
:- pred parse_source_file_map(list(string)::in, string::in, int::in,
|
|
string::out, source_file_map::in, source_file_map::out) is det.
|
|
|
|
parse_source_file_map(Lines, ModulesFileName, CurLineNumber, ErrorMsg,
|
|
!SourceFileMap) :-
|
|
(
|
|
Lines = [HeadLine | TailLines],
|
|
( if string.sub_string_search(HeadLine, "\t", TabIndex) then
|
|
string.length(HeadLine, LineLength),
|
|
string.unsafe_between(HeadLine, 0, TabIndex, ModuleNameStr),
|
|
string.unsafe_between(HeadLine, TabIndex + 1, LineLength,
|
|
FileName),
|
|
ModuleName = string_to_sym_name(ModuleNameStr),
|
|
% XXX A module cannot be contained in two files, which means that
|
|
% ModuleName should be a unique key in the forward map.
|
|
% However, with nested modules, a single file may contain
|
|
% more than one module, so FileName may *not* be a unique key
|
|
% in the backward map.
|
|
% XXX However, if Mercury.modules contains more than one line
|
|
% with the same filename, then this code has a bug, because
|
|
% the call sequence
|
|
%
|
|
% bimap.set("module_a", "filename", !SourceFileMap)
|
|
% bimap.set("module_a.sub1", "filename", !SourceFileMap)
|
|
% bimap.set("module_a.sub2", "filename", !SourceFileMap)
|
|
%
|
|
% will leave only one key that maps to the value "filename",
|
|
% which will be the last one added ("module_a.sub2" in this case).
|
|
%
|
|
% XXX We should call bimap.det_insert here to abort in such
|
|
% situations, but I (zs) am not sure that output generated by
|
|
% write_source_file_map is guaranteed to be a bijection.
|
|
bimap.set(ModuleName, FileName, !SourceFileMap),
|
|
parse_source_file_map(TailLines,
|
|
ModulesFileName, CurLineNumber + 1, ErrorMsg, !SourceFileMap)
|
|
else
|
|
string.format("line %d of %s is missing a tab character",
|
|
[i(CurLineNumber), s(ModulesFileName)], ErrorMsg)
|
|
)
|
|
;
|
|
Lines = [],
|
|
ErrorMsg = ""
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
write_source_file_map(Globals, FileNames, !IO) :-
|
|
% XXX We print error messages to stderr, because there is no appropriate
|
|
% module name we can give to globals.get_error_output_stream.
|
|
ModulesFileName = modules_file_name,
|
|
io.open_output(ModulesFileName, FileResult, !IO),
|
|
(
|
|
FileResult = ok(FileStream),
|
|
list.foldl2(write_source_file_map_2(Globals, FileStream), FileNames,
|
|
bimap.init, _, !IO),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
FileResult = error(Error),
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.format(StdErr,
|
|
"mercury_compile: error opening `%s' for output: %s",
|
|
[s(ModulesFileName), s(io.error_message(Error))], !IO),
|
|
io.set_exit_status(1, !IO)
|
|
).
|
|
|
|
:- pred write_source_file_map_2(globals::in, io.text_output_stream::in,
|
|
file_name::in,
|
|
bimap(module_name, file_name)::in, bimap(module_name, file_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
write_source_file_map_2(Globals, MapStream, FileName,
|
|
SeenModules0, SeenModules, !IO) :-
|
|
find_module_name(Globals, FileName, MaybeModuleName, !IO),
|
|
(
|
|
MaybeModuleName = yes(ModuleName),
|
|
( if
|
|
bimap.search(SeenModules0, ModuleName, PrevFileName),
|
|
PrevFileName \= FileName
|
|
then
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.format(StdErr,
|
|
"mercury_compile: " ++
|
|
"module `%s' defined in multiple files: %s, %s\n.",
|
|
[s(sym_name_to_string(ModuleName)),
|
|
s(PrevFileName), s(FileName)], !IO),
|
|
io.set_exit_status(1, !IO),
|
|
SeenModules = SeenModules0
|
|
else
|
|
bimap.set(ModuleName, FileName, SeenModules0, SeenModules)
|
|
),
|
|
( if string.remove_suffix(FileName, ".m", PartialFileName0) then
|
|
PartialFileName = PartialFileName0
|
|
else
|
|
PartialFileName = FileName
|
|
),
|
|
file_name_to_module_name(dir.det_basename(PartialFileName),
|
|
DefaultModuleName),
|
|
( if
|
|
% Only include a module in the mapping if the name doesn't match
|
|
% the default.
|
|
dir.dirname(PartialFileName) = dir.this_directory : string,
|
|
ModuleName = DefaultModuleName
|
|
then
|
|
true
|
|
else
|
|
io.format(MapStream, "%s\t%s\n",
|
|
[s(sym_name_to_escaped_string(ModuleName)), s(FileName)], !IO)
|
|
)
|
|
;
|
|
MaybeModuleName = no,
|
|
SeenModules = SeenModules0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func modules_file_name = string.
|
|
|
|
modules_file_name = "Mercury.modules".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module parse_tree.source_file_map.
|
|
%-----------------------------------------------------------------------------%
|