mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
Carve make.get_module_dep_info.m out of make.module_dep_file.m.
compiler/make.get_module_dep_info.m:
compiler/make.module_dep_file.m:
As above. make.module_dep_file now deals only with reading and writing
.module_dep files, as its name suggests, while make.get_module_dep_info.m,
which is significantly larger, manages the process of getting
module_dep_infos, which it can do either by finding .module_dep files,
or by building them if necessary.
compiler/make.m:
Include the new module.
compiler/notes/compiler_design.html:
Document the new module.
compiler/make.check_up_to_date.m:
compiler/make.dependencies.m:
compiler/make.file_names.m:
compiler/make.find_local_modules.m:
compiler/make.module_target.m:
compiler/make.program_target.m:
compiler/make.timestamp.m:
Conform to the changes above.
This commit is contained in:
@@ -19,70 +19,55 @@
|
||||
:- interface.
|
||||
|
||||
:- import_module libs.
|
||||
:- import_module libs.file_util.
|
||||
:- import_module libs.globals.
|
||||
:- import_module make.make_info.
|
||||
:- import_module mdbcomp.
|
||||
:- import_module mdbcomp.sym_name.
|
||||
:- import_module parse_tree.
|
||||
:- import_module parse_tree.module_baggage.
|
||||
:- import_module parse_tree.module_dep_info.
|
||||
|
||||
:- import_module io.
|
||||
:- import_module term.
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
% Get the dependencies for a given module.
|
||||
% Dependencies are generated on demand, not by a `mmc --make depend'
|
||||
% command, so this predicate may need to read the source for the module.
|
||||
% Exported to mercury_compile_make_hlds.m.
|
||||
%
|
||||
:- pred get_maybe_module_dep_info(io.text_output_stream::in, globals::in,
|
||||
module_name::in, maybe_module_dep_info::out,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
:- pred write_module_dep_file(io.text_output_stream::in, globals::in,
|
||||
burdened_module::in, io::di, io::uo) is det.
|
||||
|
||||
% Exported to make.get_module_dep_info.m.
|
||||
%
|
||||
:- pred do_write_module_dep_file(io.text_output_stream::in, globals::in,
|
||||
burdened_module::in, io::di, io::uo) is det.
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- pred parse_module_dep_file_term(module_name::in, dir_name::in, term::in,
|
||||
module_dep_summary::out) is semidet.
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- implementation.
|
||||
|
||||
:- import_module libs.file_util.
|
||||
:- import_module libs.maybe_util.
|
||||
:- import_module libs.options.
|
||||
:- import_module libs.process_util.
|
||||
:- import_module libs.timestamp.
|
||||
:- import_module make.build.
|
||||
:- import_module make.file_names.
|
||||
:- import_module make.module_target.
|
||||
:- import_module make.timestamp.
|
||||
:- import_module make.util.
|
||||
:- import_module parse_tree.file_names.
|
||||
:- import_module parse_tree.find_module.
|
||||
:- import_module parse_tree.get_dependencies.
|
||||
:- import_module parse_tree.item_util.
|
||||
:- import_module parse_tree.module_dep_info.
|
||||
:- import_module parse_tree.parse_error.
|
||||
:- import_module parse_tree.parse_sym_name.
|
||||
:- import_module parse_tree.parse_tree_out_misc.
|
||||
:- import_module parse_tree.parse_tree_out_sym_name.
|
||||
:- import_module parse_tree.prog_data_foreign.
|
||||
:- import_module parse_tree.prog_item.
|
||||
:- import_module parse_tree.read_modules.
|
||||
:- import_module parse_tree.write_error_spec.
|
||||
:- import_module parse_tree.write_module_interface_files.
|
||||
|
||||
:- import_module bool.
|
||||
:- import_module dir.
|
||||
:- import_module getopt.
|
||||
:- import_module io.file.
|
||||
:- import_module list.
|
||||
:- import_module map.
|
||||
:- import_module maybe.
|
||||
:- import_module mercury_term_parser.
|
||||
:- import_module require.
|
||||
:- import_module set.
|
||||
:- import_module string.
|
||||
:- import_module term.
|
||||
:- import_module term_int.
|
||||
:- import_module term_io.
|
||||
|
||||
@@ -122,225 +107,6 @@ version_number(module_dep_file_v2, 2).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
get_maybe_module_dep_info(ProgressStream, Globals, ModuleName,
|
||||
MaybeModuleDepInfo, !Info, !IO) :-
|
||||
RebuildModuleDeps = make_info_get_rebuild_module_deps(!.Info),
|
||||
(
|
||||
ModuleName = unqualified(_),
|
||||
maybe_get_maybe_module_dep_info(ProgressStream, Globals,
|
||||
RebuildModuleDeps, ModuleName, MaybeModuleDepInfo, !Info, !IO)
|
||||
;
|
||||
ModuleName = qualified(_, _),
|
||||
% For submodules, we need to generate the dependencies for the
|
||||
% parent modules first (try_to_write_module_dep_files_for_top_module
|
||||
% expects to be given the top-level module in a source file).
|
||||
% If the module is a nested module, its dependencies will be generated
|
||||
% as a side effect of generating the parent's dependencies.
|
||||
AncestorsAndSelf = get_ancestors(ModuleName) ++ [ModuleName],
|
||||
Error0 = no,
|
||||
maybe_record_modules_maybe_module_dep_infos(ProgressStream, Globals,
|
||||
RebuildModuleDeps, AncestorsAndSelf, Error0, !Info, !IO),
|
||||
|
||||
ModuleDepMap = make_info_get_module_dependencies(!.Info),
|
||||
map.lookup(ModuleDepMap, ModuleName, MaybeModuleDepInfo)
|
||||
).
|
||||
|
||||
:- pred maybe_record_modules_maybe_module_dep_infos(io.text_output_stream::in,
|
||||
globals::in, maybe_rebuild_module_deps::in, list(module_name)::in,
|
||||
bool::in, make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
maybe_record_modules_maybe_module_dep_infos(_, _, _, [], _, !Info, !IO).
|
||||
maybe_record_modules_maybe_module_dep_infos(ProgressStream, Globals,
|
||||
RebuildModuleDeps, [ModuleName | ModuleNames], !.Error, !Info, !IO) :-
|
||||
(
|
||||
!.Error = no,
|
||||
maybe_get_maybe_module_dep_info(ProgressStream, Globals,
|
||||
RebuildModuleDeps, ModuleName, MaybeModuleDepInfo, !Info, !IO),
|
||||
(
|
||||
MaybeModuleDepInfo = some_module_dep_info(_)
|
||||
;
|
||||
MaybeModuleDepInfo = no_module_dep_info,
|
||||
!:Error = yes
|
||||
)
|
||||
;
|
||||
!.Error = yes,
|
||||
% If we found a problem when processing an ancestor, don't even try
|
||||
% to process the later modules.
|
||||
ModuleDepMap0 = make_info_get_module_dependencies(!.Info),
|
||||
% XXX Could this be map.det_update or map.det_insert?
|
||||
map.set(ModuleName, no_module_dep_info, ModuleDepMap0, ModuleDepMap),
|
||||
make_info_set_module_dependencies(ModuleDepMap, !Info)
|
||||
),
|
||||
maybe_record_modules_maybe_module_dep_infos(ProgressStream, Globals,
|
||||
RebuildModuleDeps, ModuleNames, !.Error, !Info, !IO).
|
||||
|
||||
:- pred maybe_get_maybe_module_dep_info(io.text_output_stream::in, globals::in,
|
||||
maybe_rebuild_module_deps::in, module_name::in, maybe_module_dep_info::out,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
maybe_get_maybe_module_dep_info(ProgressStream, Globals, RebuildModuleDeps,
|
||||
ModuleName, MaybeModuleDepInfo, !Info, !IO) :-
|
||||
ModuleDepMap0 = make_info_get_module_dependencies(!.Info),
|
||||
( if map.search(ModuleDepMap0, ModuleName, MaybeModuleDepInfo0) then
|
||||
MaybeModuleDepInfo = MaybeModuleDepInfo0
|
||||
else
|
||||
do_get_maybe_module_dep_info(ProgressStream, Globals,
|
||||
RebuildModuleDeps, ModuleName, MaybeModuleDepInfo, !Info, !IO)
|
||||
).
|
||||
|
||||
:- pred do_get_maybe_module_dep_info(io.text_output_stream::in, globals::in,
|
||||
maybe_rebuild_module_deps::in, module_name::in, maybe_module_dep_info::out,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
do_get_maybe_module_dep_info(ProgressStream, Globals, RebuildModuleDeps,
|
||||
ModuleName, !:MaybeModuleDepInfo, !Info, !IO) :-
|
||||
globals.lookup_accumulating_option(Globals, search_directories,
|
||||
SearchDirs),
|
||||
ModuleDepExt = ext_cur_ngs(ext_cur_ngs_misc_module_dep),
|
||||
module_name_to_file_name(Globals, $pred, ModuleDepExt,
|
||||
ModuleName, DepFileName),
|
||||
get_file_timestamp(SearchDirs, DepFileName, MaybeDepFileTimestamp,
|
||||
!Info, !IO),
|
||||
(
|
||||
MaybeDepFileTimestamp = ok(DepFileTimestamp),
|
||||
% We can't just use
|
||||
% `get_target_timestamp(ModuleName - source, ..)'
|
||||
% because that could recursively call get_maybe_module_dep_info,
|
||||
% leading to an infinite loop. Just using module_name_to_file_name
|
||||
% will fail if the module name doesn't match the file name, but
|
||||
% that case is handled below.
|
||||
module_name_to_source_file_name(ModuleName, SourceFileName, !IO),
|
||||
get_file_timestamp([dir.this_directory], SourceFileName,
|
||||
MaybeSourceFileTimestamp, !Info, !IO),
|
||||
(
|
||||
MaybeSourceFileTimestamp = ok(SourceFileTimestamp),
|
||||
( if
|
||||
( RebuildModuleDeps = do_not_rebuild_module_deps
|
||||
; compare((>), DepFileTimestamp, SourceFileTimestamp)
|
||||
)
|
||||
then
|
||||
% Since the source file was found in this directory,
|
||||
% do not use module_dep files which might be for
|
||||
% installed copies of the module.
|
||||
%
|
||||
% XXX SourceFileName may not actually be the correct source
|
||||
% file for the required module. Usually the purported source
|
||||
% file would have a later timestamp than the .module_dep file,
|
||||
% though, so the other branch would be taken.
|
||||
read_module_dep_file(ProgressStream, Globals,
|
||||
RebuildModuleDeps, [dir.this_directory], ModuleName,
|
||||
!Info, !IO)
|
||||
else
|
||||
try_to_write_module_dep_files_for_top_module(ProgressStream,
|
||||
Globals, ModuleName, !Info, !IO)
|
||||
)
|
||||
;
|
||||
MaybeSourceFileTimestamp = error(_),
|
||||
read_module_dep_file(ProgressStream, Globals,
|
||||
RebuildModuleDeps, SearchDirs, ModuleName, !Info, !IO),
|
||||
|
||||
% Check for the case where the module name doesn't match the
|
||||
% source file name (e.g. parse.m contains module mdb.parse).
|
||||
% Get the correct source file name from the module dependency file,
|
||||
% then check whether the module dependency file is up to date.
|
||||
map.lookup(make_info_get_module_dependencies(!.Info), ModuleName,
|
||||
!:MaybeModuleDepInfo),
|
||||
( if
|
||||
!.MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo0),
|
||||
module_dep_info_get_source_file_dir(ModuleDepInfo0,
|
||||
DepFileDir),
|
||||
DepFileDir = dir.this_directory
|
||||
then
|
||||
module_dep_info_get_source_file_name(ModuleDepInfo0,
|
||||
SourceFileName1),
|
||||
get_file_timestamp([dir.this_directory], SourceFileName1,
|
||||
MaybeSourceFileTimestamp1, !Info, !IO),
|
||||
(
|
||||
MaybeSourceFileTimestamp1 = ok(SourceFileTimestamp1),
|
||||
( if
|
||||
( RebuildModuleDeps = do_not_rebuild_module_deps
|
||||
; compare((>), DepFileTimestamp, SourceFileTimestamp1)
|
||||
)
|
||||
then
|
||||
true
|
||||
else
|
||||
% XXX The existence of a .module_dep file reflects
|
||||
% a previous state of the workspace, which may not
|
||||
% match the current workspace.
|
||||
%
|
||||
% Here is a (contrived) case where we run into
|
||||
% an issue:
|
||||
% 1. create prog.m which imports the standard library
|
||||
% mercury_term_lexer module
|
||||
% 2. copy the standard library mercury_term_lexer.m
|
||||
% file to the current directory for editing
|
||||
% 3. run mmc --make; it creates
|
||||
% mercury_term_lexer.module_dep
|
||||
% 4. change mercury_term_lexer.m into a submodule
|
||||
% of prog
|
||||
% 5. run mmc --make again, it no longer works
|
||||
%
|
||||
% The local mercury_term_lexer.module_dep prevents
|
||||
% mmc --make finding the mercury_term_lexer.module_dep
|
||||
% from the standard library, even though there is
|
||||
% no longer any local source file for the
|
||||
% `mercury_term_lexer' module.
|
||||
try_to_write_module_dep_files_for_top_module(
|
||||
ProgressStream, Globals, ModuleName, !Info, !IO)
|
||||
)
|
||||
;
|
||||
MaybeSourceFileTimestamp1 = error(Message),
|
||||
io.format(ProgressStream,
|
||||
"** Error reading file `%s' " ++
|
||||
"to generate dependencies: %s.\n",
|
||||
[s(SourceFileName1), s(Message)], !IO),
|
||||
maybe_write_importing_module(ProgressStream, ModuleName,
|
||||
make_info_get_importing_module(!.Info), !IO)
|
||||
)
|
||||
else
|
||||
true
|
||||
)
|
||||
)
|
||||
;
|
||||
MaybeDepFileTimestamp = error(_),
|
||||
SearchDirsString = join_list(", ",
|
||||
map((func(Dir) = "`" ++ Dir ++ "'"), SearchDirs)),
|
||||
debug_make_msg(Globals,
|
||||
string.format(
|
||||
"Module dependencies file '%s' not found in directories %s.\n",
|
||||
[s(DepFileName), s(SearchDirsString)]),
|
||||
DebugMsg),
|
||||
maybe_write_msg(ProgressStream, DebugMsg, !IO),
|
||||
|
||||
% Try to make the dependencies. This will succeed when the module name
|
||||
% doesn't match the file name and the dependencies for this module
|
||||
% haven't been built before. It will fail if the source file
|
||||
% is in another directory.
|
||||
(
|
||||
RebuildModuleDeps = do_rebuild_module_deps,
|
||||
try_to_write_module_dep_files_for_top_module(ProgressStream,
|
||||
Globals, ModuleName, !Info, !IO)
|
||||
;
|
||||
RebuildModuleDeps = do_not_rebuild_module_deps,
|
||||
ModuleDepMap0 = make_info_get_module_dependencies(!.Info),
|
||||
% XXX Could this be map.det_update or map.det_insert?
|
||||
map.set(ModuleName, no_module_dep_info,
|
||||
ModuleDepMap0, ModuleDepMap1),
|
||||
make_info_set_module_dependencies(ModuleDepMap1, !Info)
|
||||
)
|
||||
),
|
||||
ModuleDepMap2 = make_info_get_module_dependencies(!.Info),
|
||||
( if map.search(ModuleDepMap2, ModuleName, MaybeModuleDepInfo0) then
|
||||
!:MaybeModuleDepInfo = MaybeModuleDepInfo0
|
||||
else
|
||||
!:MaybeModuleDepInfo = no_module_dep_info,
|
||||
map.det_insert(ModuleName, no_module_dep_info,
|
||||
ModuleDepMap2, ModuleDepMap),
|
||||
make_info_set_module_dependencies(ModuleDepMap, !Info)
|
||||
).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
write_module_dep_file(ProgressStream, Globals, BurdenedModule0, !IO) :-
|
||||
BurdenedModule0 = burdened_module(Baggage0, ParseTreeModuleSrc),
|
||||
Baggage0 = module_baggage(SourceFileName, _SourceFileDir,
|
||||
@@ -358,9 +124,6 @@ write_module_dep_file(ProgressStream, Globals, BurdenedModule0, !IO) :-
|
||||
BurdenedModule = burdened_module(Baggage, ParseTreeModuleSrc),
|
||||
do_write_module_dep_file(ProgressStream, Globals, BurdenedModule, !IO).
|
||||
|
||||
:- pred do_write_module_dep_file(io.text_output_stream::in, globals::in,
|
||||
burdened_module::in, io::di, io::uo) is det.
|
||||
|
||||
do_write_module_dep_file(ProgressStream, Globals, BurdenedModule, !IO) :-
|
||||
BurdenedModule = burdened_module(Baggage, ParseTreeModuleSrc),
|
||||
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
|
||||
@@ -497,142 +260,6 @@ contains_foreign_export_to_string(ContainsForeignExport,
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- pred read_module_dep_file(io.text_output_stream::in, globals::in,
|
||||
maybe_rebuild_module_deps::in, list(dir_name)::in, module_name::in,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
read_module_dep_file(ProgressStream, Globals, RebuildModuleDeps,
|
||||
SearchDirs, ModuleName, !Info, !IO) :-
|
||||
module_name_to_search_file_name(Globals, $pred,
|
||||
ext_cur_ngs(ext_cur_ngs_misc_module_dep), ModuleName, DepFileName),
|
||||
search_for_file_returning_dir_and_contents(SearchDirs, DepFileName,
|
||||
MaybeDirAndContents, !IO),
|
||||
(
|
||||
MaybeDirAndContents = ok(DirAndContents),
|
||||
DirAndContents = path_name_and_contents(DepFileDir, DepFileContents),
|
||||
% Since .module_dep files are automatically generated, errors in them
|
||||
% should be vanishingly rare. However, this also means that if and when
|
||||
% they do occur, any errors are likely to be quite obscure. In such
|
||||
% cases, knowing *for sure* exactly *which* .module_dep file has
|
||||
% the error would be one of the first pieces of information that
|
||||
% any person looking to track down the problem would wan.
|
||||
% This is why we include DepFileDir in the pathname we pass
|
||||
% to read_term_from_string.
|
||||
DepFilePathName = DepFileDir / DepFileName,
|
||||
mercury_term_parser.read_term_from_string(DepFilePathName,
|
||||
DepFileContents, _EndPos, TermResult),
|
||||
(
|
||||
TermResult = term(_, Term),
|
||||
( if
|
||||
parse_module_dep_file_term(ModuleName, DepFileDir, Term,
|
||||
ModuleSummary)
|
||||
then
|
||||
handle_parsed_module_dep_file(ProgressStream, Globals,
|
||||
SearchDirs, ModuleName, DepFileDir, DepFileName,
|
||||
ModuleSummary, Result, !Info, !IO)
|
||||
else
|
||||
Result = error("failed to parse term")
|
||||
)
|
||||
;
|
||||
TermResult = eof,
|
||||
Result = error("unexpected eof")
|
||||
;
|
||||
TermResult = error(ParseError, _),
|
||||
Result = error("parse error: " ++ ParseError)
|
||||
),
|
||||
(
|
||||
Result = ok
|
||||
;
|
||||
Result = error(ErrorMsg),
|
||||
error_and_maybe_rebuilding_msg(RebuildModuleDeps,
|
||||
DepFileDir ++ "/" ++ DepFileName, ErrorMsg, Msg),
|
||||
% XXX MAKE_STREAM
|
||||
io.write_string(ProgressStream, Msg, !IO),
|
||||
(
|
||||
RebuildModuleDeps = do_rebuild_module_deps,
|
||||
try_to_write_module_dep_files_for_top_module(ProgressStream,
|
||||
Globals, ModuleName, !Info, !IO)
|
||||
;
|
||||
RebuildModuleDeps = do_not_rebuild_module_deps
|
||||
)
|
||||
)
|
||||
;
|
||||
MaybeDirAndContents = error(ErrorMsg),
|
||||
% XXX Why is this invocation of error_and_maybe_rebuilding_msg
|
||||
% more conditional than the one above?
|
||||
debug_make_msg(Globals,
|
||||
error_and_maybe_rebuilding_msg(RebuildModuleDeps,
|
||||
DepFileName, ErrorMsg),
|
||||
DebugMsg),
|
||||
maybe_write_msg(ProgressStream, DebugMsg, !IO),
|
||||
(
|
||||
RebuildModuleDeps = do_rebuild_module_deps,
|
||||
try_to_write_module_dep_files_for_top_module(ProgressStream,
|
||||
Globals, ModuleName, !Info, !IO)
|
||||
;
|
||||
RebuildModuleDeps = do_not_rebuild_module_deps
|
||||
)
|
||||
).
|
||||
|
||||
:- pred handle_parsed_module_dep_file(io.text_output_stream::in, globals::in,
|
||||
list(dir_name)::in, module_name::in, dir_name::in, file_name::in,
|
||||
module_dep_summary::in, maybe_error::out,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
handle_parsed_module_dep_file(ProgressStream, Globals, SearchDirs, ModuleName,
|
||||
DepFileDir, DepFileName, ModuleSummary, Result, !Info, !IO) :-
|
||||
ModuleDepInfo = module_dep_info_summary(ModuleSummary),
|
||||
MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo),
|
||||
|
||||
% Discard the module dependencies if the module is a local module
|
||||
% but the source file no longer exists.
|
||||
( if DepFileDir = dir.this_directory then
|
||||
SourceFileName = ModuleSummary ^ mds_source_file_name,
|
||||
check_regular_file_exists(SourceFileName, SourceFileExists, !IO),
|
||||
(
|
||||
SourceFileExists = ok
|
||||
;
|
||||
SourceFileExists = error(_),
|
||||
io.file.remove_file(DepFileName, _, !IO)
|
||||
)
|
||||
else
|
||||
SourceFileExists = ok
|
||||
),
|
||||
(
|
||||
SourceFileExists = ok,
|
||||
ModuleDepMap0 = make_info_get_module_dependencies(!.Info),
|
||||
% XXX Could this be map.det_insert?
|
||||
map.set(ModuleName, MaybeModuleDepInfo,
|
||||
ModuleDepMap0, ModuleDepMap),
|
||||
make_info_set_module_dependencies(ModuleDepMap, !Info),
|
||||
|
||||
% Read the dependencies for any nested children. If something
|
||||
% goes wrong (for example one of the files was removed), the
|
||||
% dependencies for all modules in the source file will be remade
|
||||
% (try_to_write_module_dep_files_for_top_module expects
|
||||
% to be given the top-level module in the source file).
|
||||
MaybeTopModule = ModuleSummary ^ mds_maybe_top_module,
|
||||
NestedSubModules =
|
||||
get_nested_children_list_of_top_module(MaybeTopModule),
|
||||
list.foldl2(
|
||||
read_module_dep_file(ProgressStream, Globals,
|
||||
do_not_rebuild_module_deps, SearchDirs),
|
||||
NestedSubModules, !Info, !IO),
|
||||
( if some_bad_module_dependency(!.Info, NestedSubModules) then
|
||||
Result = error("error in nested submodules")
|
||||
else
|
||||
Result = ok
|
||||
)
|
||||
;
|
||||
SourceFileExists = error(Error),
|
||||
Result = error(Error)
|
||||
).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- pred parse_module_dep_file_term(module_name::in, dir_name::in, term::in,
|
||||
module_dep_summary::out) is semidet.
|
||||
|
||||
parse_module_dep_file_term(ModuleName, DepFileDir, Term, ModuleSummary) :-
|
||||
atom_term(Term, "module", ModuleArgs),
|
||||
ModuleArgs = [
|
||||
@@ -766,337 +393,6 @@ contains_foreign_export_term(Term, ContainsForeignExport) :-
|
||||
atom_term(Term, Atom, []),
|
||||
contains_foreign_export_to_string(ContainsForeignExport, Atom).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- pred some_bad_module_dependency(make_info::in, list(module_name)::in)
|
||||
is semidet.
|
||||
|
||||
some_bad_module_dependency(Info, ModuleNames) :-
|
||||
list.member(ModuleName, ModuleNames),
|
||||
map.search(make_info_get_module_dependencies(Info), ModuleName,
|
||||
no_module_dep_info).
|
||||
|
||||
:- pred check_regular_file_exists(file_name::in, maybe_error::out,
|
||||
io::di, io::uo) is det.
|
||||
|
||||
check_regular_file_exists(FileName, FileExists, !IO) :-
|
||||
FollowSymLinks = yes,
|
||||
io.file.file_type(FollowSymLinks, FileName, ResFileType, !IO),
|
||||
(
|
||||
ResFileType = ok(FileType),
|
||||
(
|
||||
( FileType = regular_file
|
||||
; FileType = unknown
|
||||
),
|
||||
FileExists = ok
|
||||
;
|
||||
( FileType = directory
|
||||
; FileType = symbolic_link
|
||||
; FileType = named_pipe
|
||||
; FileType = socket
|
||||
; FileType = character_device
|
||||
; FileType = block_device
|
||||
; FileType = message_queue
|
||||
; FileType = semaphore
|
||||
; FileType = shared_memory
|
||||
),
|
||||
FileExists = error(FileName ++ ": not a regular file")
|
||||
)
|
||||
;
|
||||
ResFileType = error(Error),
|
||||
FileExists = error(FileName ++ ": " ++ io.error_message(Error))
|
||||
).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- pred error_and_maybe_rebuilding_msg(maybe_rebuild_module_deps::in,
|
||||
string::in, string::in, string::out) is det.
|
||||
|
||||
error_and_maybe_rebuilding_msg(RebuildModuleDeps, ModuleDepsFile,
|
||||
ErrorMsg, Msg) :-
|
||||
(
|
||||
RebuildModuleDeps = do_rebuild_module_deps,
|
||||
RebuildSuffix = " ...rebuilding"
|
||||
;
|
||||
RebuildModuleDeps = do_not_rebuild_module_deps,
|
||||
RebuildSuffix = ""
|
||||
),
|
||||
string.format("** Error reading file `%s': %s%s\n",
|
||||
[s(ModuleDepsFile), s(ErrorMsg), s(RebuildSuffix)], Msg).
|
||||
|
||||
% The module_name given must be the top level module in the source file.
|
||||
% get_module_dependencies ensures this by making the dependencies
|
||||
% for all parent modules of the requested module first.
|
||||
%
|
||||
:- pred try_to_write_module_dep_files_for_top_module(io.text_output_stream::in,
|
||||
globals::in, module_name::in,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
try_to_write_module_dep_files_for_top_module(ProgressStream, Globals,
|
||||
ModuleName, !Info, !IO) :-
|
||||
open_module_error_stream(Globals, ModuleName, ProgressStream,
|
||||
MaybeErrorStream, !Info, !IO),
|
||||
(
|
||||
MaybeErrorStream = es_ok(MESI, ErrorStream),
|
||||
% Both cannot_write_module_dep_files and
|
||||
% write_module_dep_files_for_source_file call
|
||||
% close_module_error_stream_handle_errors,
|
||||
% but factoring that call out of the latter would be non-trivial,
|
||||
% and is better left for when we replace the whole redirect/unredirect
|
||||
% machinery with the use of explicit streams.
|
||||
% XXX Why ask for the timestamp if we then ignore it?
|
||||
% NOTE: Asking for a timestamp and then ignoring it *could* make sense
|
||||
% if we recorded HaveReadSrc in a have_read_module_map, because
|
||||
% it would make the timestamp available for a later lookup,
|
||||
% However, we do not record HaveReadSrc in a have_read_module_map.
|
||||
read_module_src(ProgressStream, Globals, rrm_get_deps,
|
||||
do_not_ignore_errors, do_not_search, ModuleName, [],
|
||||
always_read_module(do_return_timestamp), HaveReadSrc, !IO),
|
||||
% XXX If execution will follow a path on which we call
|
||||
% cannot_write_module_dep_files, then as its almost-last act,
|
||||
% cannot_write_module_dep_files will delete Module.err.
|
||||
% Since that is a possibility, why write to ModuleName.err
|
||||
% *unconditionally*?
|
||||
(
|
||||
HaveReadSrc = have_read_module(SourceFileName, _MaybeTimestamp,
|
||||
ParseTreeSrc, ReadModuleErrors),
|
||||
FatalErrorSpecs0 = ReadModuleErrors ^ rm_fatal_error_specs,
|
||||
NonFatalErrorSpecs0 = ReadModuleErrors ^ rm_nonfatal_error_specs,
|
||||
write_error_specs(ErrorStream, Globals,
|
||||
FatalErrorSpecs0 ++ NonFatalErrorSpecs0, !IO),
|
||||
|
||||
Fatal = ReadModuleErrors ^ rm_fatal_errors,
|
||||
NonFatal = ReadModuleErrors ^ rm_nonfatal_errors,
|
||||
( if set.is_non_empty(Fatal) then
|
||||
DisplayErrorReadingFile = yes,
|
||||
cannot_write_module_dep_files(Globals,
|
||||
ProgressStream, MESI, ErrorStream, SourceFileName,
|
||||
ModuleName, ReadModuleErrors, DisplayErrorReadingFile,
|
||||
!Info, !IO)
|
||||
else if set.contains(NonFatal, rme_unexpected_module_name) then
|
||||
% If the source file does not contain the expected module,
|
||||
% then do not make the .module_dep file; it would leave
|
||||
% a .module_dep file for the wrong module lying around,
|
||||
% which the user needs to delete manually.
|
||||
DisplayErrorReadingFile = no,
|
||||
cannot_write_module_dep_files(Globals,
|
||||
ProgressStream, MESI, ErrorStream, SourceFileName,
|
||||
ModuleName, ReadModuleErrors, DisplayErrorReadingFile,
|
||||
!Info, !IO)
|
||||
else
|
||||
write_module_dep_files_for_source_file(Globals,
|
||||
ProgressStream, MESI, ErrorStream, SourceFileName,
|
||||
ModuleName, ParseTreeSrc, ReadModuleErrors, !Info, !IO)
|
||||
)
|
||||
;
|
||||
HaveReadSrc = have_not_read_module(SourceFileName,
|
||||
ReadModuleErrors),
|
||||
FatalErrorSpecs0 = ReadModuleErrors ^ rm_fatal_error_specs,
|
||||
NonFatalErrorSpecs0 = ReadModuleErrors ^ rm_nonfatal_error_specs,
|
||||
write_error_specs(ErrorStream, Globals,
|
||||
FatalErrorSpecs0 ++ NonFatalErrorSpecs0, !IO),
|
||||
|
||||
DisplayErrorReadingFile = yes,
|
||||
cannot_write_module_dep_files(Globals,
|
||||
ProgressStream, MESI, ErrorStream, SourceFileName, ModuleName,
|
||||
ReadModuleErrors, DisplayErrorReadingFile, !Info, !IO)
|
||||
)
|
||||
;
|
||||
MaybeErrorStream = es_error_already_reported
|
||||
).
|
||||
|
||||
:- pred cannot_write_module_dep_files(globals::in,
|
||||
io.text_output_stream::in,
|
||||
module_error_stream_info::in, io.text_output_stream::in,
|
||||
file_name::in, module_name::in, read_module_errors::in, bool::in,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
cannot_write_module_dep_files(Globals, ProgressStream, MESI, ErrorStream,
|
||||
SourceFileName, ModuleName, ReadModuleErrors,
|
||||
DisplayErrorReadingFile, !Info, !IO) :-
|
||||
Specs0 = get_read_module_specs(ReadModuleErrors),
|
||||
write_error_specs(ErrorStream, Globals, Specs0, !IO),
|
||||
(
|
||||
DisplayErrorReadingFile = yes,
|
||||
io.format(ProgressStream,
|
||||
"** Error reading file `%s' to generate dependencies.\n",
|
||||
[s(SourceFileName)], !IO),
|
||||
maybe_write_importing_module(ProgressStream, ModuleName,
|
||||
make_info_get_importing_module(!.Info), !IO)
|
||||
;
|
||||
DisplayErrorReadingFile = no
|
||||
),
|
||||
|
||||
% Display the contents of the `.err' file, then remove it
|
||||
% so we don't leave `.err' files lying around for nonexistent modules.
|
||||
globals.set_option(output_compile_error_lines, maybe_int(no),
|
||||
Globals, UnredirectGlobals),
|
||||
close_module_error_stream_handle_errors(UnredirectGlobals, ModuleName,
|
||||
ProgressStream, MESI, ErrorStream, !Info, !IO),
|
||||
module_name_to_file_name(Globals, $pred, ext_cur(ext_cur_user_err),
|
||||
ModuleName, ErrFileName),
|
||||
io.file.remove_file(ErrFileName, _, !IO),
|
||||
|
||||
ModuleDepMap0 = make_info_get_module_dependencies(!.Info),
|
||||
% XXX Could this be map.det_update?
|
||||
map.set(ModuleName, no_module_dep_info, ModuleDepMap0, ModuleDepMap),
|
||||
make_info_set_module_dependencies(ModuleDepMap, !Info).
|
||||
|
||||
:- pred write_module_dep_files_for_source_file(globals::in,
|
||||
io.text_output_stream::in,
|
||||
module_error_stream_info::in, io.text_output_stream::in,
|
||||
file_name::in, module_name::in, parse_tree_src::in,
|
||||
read_module_errors::in,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
write_module_dep_files_for_source_file(Globals, ProgressStream,
|
||||
MESI, ErrorStream, SourceFileName, ModuleName, ParseTreeSrc,
|
||||
ReadModuleErrors, !Info, !IO) :-
|
||||
parse_tree_src_to_burdened_module_list(Globals, SourceFileName,
|
||||
ParseTreeSrc, ReadModuleErrors, Specs, BurdenedModules),
|
||||
ParseTreeModuleSrcs = list.map((func(burdened_module(_, PTMS)) = PTMS),
|
||||
BurdenedModules),
|
||||
SubModuleNames = list.map(parse_tree_module_src_project_name,
|
||||
ParseTreeModuleSrcs),
|
||||
|
||||
% XXX Why are we ignoring all previously reported errors?
|
||||
io.set_exit_status(0, !IO),
|
||||
write_error_specs(ErrorStream, Globals, Specs, !IO),
|
||||
|
||||
list.foldl(make_info_add_burdened_module_as_dep, BurdenedModules, !Info),
|
||||
|
||||
% If there were no errors, write out the `.int3' file
|
||||
% while we have the contents of the module. The `int3' file
|
||||
% does not depend on anything else.
|
||||
MadeTarget = target_file(ModuleName, module_target_int3),
|
||||
get_make_target_file_name(Globals, $pred,
|
||||
MadeTarget, MadeTargetFileName, !IO),
|
||||
|
||||
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
||||
% We already know FatalReadError is empty.
|
||||
NonFatalErrors = ReadModuleErrors ^ rm_nonfatal_errors,
|
||||
( if set.is_empty(NonFatalErrors) then
|
||||
maybe_making_filename_msg(Globals, MadeTargetFileName, MakingMsg),
|
||||
maybe_write_msg(ProgressStream, MakingMsg, !IO),
|
||||
setup_checking_for_interrupt(CookieMSI, !IO),
|
||||
|
||||
globals.get_default_options(Globals, DefaultOptionTable),
|
||||
DetectedGradeFlags = make_info_get_detected_grade_flags(!.Info),
|
||||
OptionVariables = make_info_get_options_variables(!.Info),
|
||||
OptionArgs = make_info_get_option_args(!.Info),
|
||||
ExtraOptions = ["--make-short-interface"],
|
||||
setup_for_build_with_module_options(ProgressStream, DefaultOptionTable,
|
||||
invoked_by_mmc_make, ModuleName, DetectedGradeFlags,
|
||||
OptionVariables, OptionArgs, ExtraOptions, MayBuild, !IO),
|
||||
(
|
||||
MayBuild = may_not_build(MSISpecs),
|
||||
% XXX MAKE_STREAM
|
||||
write_error_specs(ErrorStream, Globals, MSISpecs, !IO),
|
||||
Succeeded0 = did_not_succeed
|
||||
;
|
||||
MayBuild = may_build(_AllOptions, BuildGlobals),
|
||||
make_int3_files(ProgressStream, ErrorStream, BuildGlobals,
|
||||
ParseTreeModuleSrcs, Succeeded0, !Info, !IO)
|
||||
),
|
||||
|
||||
CleanupMSI =
|
||||
cleanup_int3_files(ProgressStream, Globals, SubModuleNames),
|
||||
teardown_checking_for_interrupt(VeryVerbose, CookieMSI,
|
||||
CleanupMSI, Succeeded0, Succeeded, !Info, !IO)
|
||||
else
|
||||
Succeeded = did_not_succeed
|
||||
),
|
||||
|
||||
setup_checking_for_interrupt(CookieWMDF, !IO),
|
||||
list.foldl(do_write_module_dep_file(ProgressStream, Globals),
|
||||
BurdenedModules, !IO),
|
||||
CleanupWMDF =
|
||||
cleanup_module_dep_files(ProgressStream, Globals, SubModuleNames),
|
||||
teardown_checking_for_interrupt(VeryVerbose, CookieWMDF,
|
||||
CleanupWMDF, succeeded, _Succeeded, !Info, !IO),
|
||||
|
||||
record_made_target(ProgressStream, Globals, MadeTarget, MadeTargetFileName,
|
||||
process_module(task_make_int3), Succeeded, !Info, !IO),
|
||||
close_module_error_stream_handle_errors(Globals, ModuleName,
|
||||
ProgressStream, MESI, ErrorStream, !Info, !IO).
|
||||
|
||||
:- pred make_info_add_burdened_module_as_dep(burdened_module::in,
|
||||
make_info::in, make_info::out) is det.
|
||||
|
||||
make_info_add_burdened_module_as_dep(BurdenedModule, !Info) :-
|
||||
ParseTreeModuleSrc = BurdenedModule ^ bm_module,
|
||||
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
|
||||
ModuleDepInfo = module_dep_info_full(BurdenedModule),
|
||||
MaybeModuleDepInfo = some_module_dep_info(ModuleDepInfo),
|
||||
ModuleDepMap0 = make_info_get_module_dependencies(!.Info),
|
||||
% XXX Could this be map.det_insert?
|
||||
map.set(ModuleName, MaybeModuleDepInfo, ModuleDepMap0, ModuleDepMap),
|
||||
make_info_set_module_dependencies(ModuleDepMap, !Info).
|
||||
|
||||
:- pred make_int3_files(io.text_output_stream::in, io.text_output_stream::in,
|
||||
globals::in, list(parse_tree_module_src)::in, maybe_succeeded::out,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
make_int3_files(ProgressStream, ErrorStream, Globals,
|
||||
ParseTreeModuleSrcs, Succeeded, !Info, !IO) :-
|
||||
list.map_foldl(
|
||||
write_short_interface_file_int3(ProgressStream, ErrorStream, Globals),
|
||||
ParseTreeModuleSrcs, Succeededs, !IO),
|
||||
Succeeded = and_list(Succeededs).
|
||||
|
||||
:- pred cleanup_int3_files(io.text_output_stream::in, globals::in,
|
||||
list(module_name)::in,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
cleanup_int3_files(ProgressStream, Globals, ModuleNames, !Info, !IO) :-
|
||||
list.foldl2(cleanup_int3_file(ProgressStream, Globals), ModuleNames,
|
||||
!Info, !IO).
|
||||
|
||||
:- pred cleanup_int3_file(io.text_output_stream::in, globals::in,
|
||||
module_name::in, make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
cleanup_int3_file(ProgressStream, Globals, ModuleName, !Info, !IO) :-
|
||||
remove_make_target_file_by_name(ProgressStream, Globals, $pred,
|
||||
very_verbose, ModuleName, module_target_int3, !Info, !IO).
|
||||
|
||||
:- pred cleanup_module_dep_files(io.text_output_stream::in, globals::in,
|
||||
list(module_name)::in,
|
||||
make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
cleanup_module_dep_files(ProgressStream, Globals, ModuleNames, !Info, !IO) :-
|
||||
list.foldl2(cleanup_module_dep_file(ProgressStream, Globals),
|
||||
ModuleNames, !Info, !IO).
|
||||
|
||||
:- pred cleanup_module_dep_file(io.text_output_stream::in, globals::in,
|
||||
module_name::in, make_info::in, make_info::out, io::di, io::uo) is det.
|
||||
|
||||
cleanup_module_dep_file(ProgressStream, Globals, ModuleName, !Info, !IO) :-
|
||||
remove_module_file_for_make(ProgressStream, Globals, verbose_make,
|
||||
ModuleName, ext_cur_ngs(ext_cur_ngs_misc_module_dep), !Info, !IO).
|
||||
|
||||
:- pred maybe_write_importing_module(io.text_output_stream::in,
|
||||
module_name::in, maybe(import_or_include)::in, io::di, io::uo) is det.
|
||||
|
||||
maybe_write_importing_module(ProgressStream, ModuleName, MaybeIoI, !IO) :-
|
||||
(
|
||||
MaybeIoI = no
|
||||
;
|
||||
MaybeIoI = yes(ImportOrInclude),
|
||||
(
|
||||
ImportOrInclude = ioi_import(ImportingModuleName),
|
||||
io.format(ProgressStream,
|
||||
"** Module `%s' is imported by module `%s'.\n",
|
||||
[s(escaped_sym_name_to_string(ModuleName)),
|
||||
s(escaped_sym_name_to_string(ImportingModuleName))], !IO)
|
||||
;
|
||||
ImportOrInclude = ioi_include(IncludingModuleName),
|
||||
io.format(ProgressStream,
|
||||
"** Module `%s' is included by module `%s'.\n",
|
||||
[s(escaped_sym_name_to_string(ModuleName)),
|
||||
s(escaped_sym_name_to_string(IncludingModuleName))], !IO)
|
||||
)
|
||||
).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
:- end_module make.module_dep_file.
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
Reference in New Issue
Block a user