Files
mercury/compiler/make.module_dep_file.m
Zoltan Somogyi 47e7b8eedd Reduce the number of modules that set the exit status ...
... by removing four more modules from that set.

compiler/make.module_dep_file.m:
compiler/mercury_compile_front_end.m:
compiler/mercury_compile_middle_passes.m:
    Replace bespoke error reporting code with invocations of standardized
    error reporting predicates.

compiler/source_file_map.m:
    Delete an unused predicate.
2026-04-03 04:16:56 +11:00

439 lines
17 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 expandtab
%---------------------------------------------------------------------------%
% Copyright (C) 2002-2009, 2011 The University of Melbourne.
% Copyright (C) 2014-2017, 2019-2020, 2024-2026 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: make.module_dep_file.m.
% Author: stayl.
%
% Code to read and write the `<module>.module_dep' files, which contain
% information about inter-module dependencies.
%
%---------------------------------------------------------------------------%
:- module make.module_dep_file.
:- interface.
:- import_module libs.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.maybe_error.
:- import_module parse_tree.module_baggage.
:- import_module parse_tree.module_dep_info.
:- import_module io.
%---------------------------------------------------------------------------%
% Exported to mercury_compile_make_hlds.m.
%
:- 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 read_module_dep_file(dir_name::in, file_name::in, string::in,
module_name::in, maybe1(module_dep_summary, string)::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module parse_tree.file_names.
:- import_module parse_tree.get_dependencies.
:- 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.prog_parse_tree.
:- import_module dir.
:- 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.
%---------------------------------------------------------------------------%
% The version 1 module_dep file format is the same as version 2 except that
% it does not include a list of files included by `pragma foreign_decl' and
% `pragma foreign_code'. We stopped generating version 1 .module_dep files
% on 2021 Jul 31.
%
% XXX We should consider
%
% - adding a version 3 that differs from 2 in deleting the field
% that now *always* contains "no_main",
% - replacing the braces that wrap the contents of each structured field
% with a function symbol that explicitly specifies the meaning of
% that contents, and
% - switching to always generating version 3.
%
% XXX The precise on-disk representation of each (current) .module_dep
% file format version should be explicitly documented. This documentation
% should explain
%
% - what the meaning of each field is,
% - what purposes does it serve, and
% - what invariants (if any) apply to it.
%
% It should also have some examples to help readers understand it all.
%
:- type module_dep_file_version
---> module_dep_file_v1
; module_dep_file_v2.
:- pred version_number(module_dep_file_version, int).
:- mode version_number(in, out) is det.
:- mode version_number(out, in) is semidet.
version_number(module_dep_file_v1, 1).
version_number(module_dep_file_v2, 2).
%---------------------------------------------------------------------------%
write_module_dep_file(ProgressStream, Globals, BurdenedModule0, !IO) :-
BurdenedModule0 = burdened_module(Baggage0, ParseTreeModuleSrc),
Baggage0 = module_baggage(SourceFileName, _SourceFileDir,
SourceFileTopModuleName, MaybeTopModule,
_MaybeTimestamp, _MaybeTimestampMap, _GrabbedFileMap, _Errors),
MaybeTimestamp = maybe.no,
MaybeTimestampMap = maybe.no,
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
GrabbedFileMap = map.singleton(ModuleName, gf_src(ParseTreeModuleSrc)),
Errors = init_read_module_errors,
Baggage = module_baggage(SourceFileName, dir.this_directory,
SourceFileTopModuleName, MaybeTopModule,
MaybeTimestamp, MaybeTimestampMap, GrabbedFileMap, Errors),
BurdenedModule = burdened_module(Baggage, ParseTreeModuleSrc),
do_write_module_dep_file(ProgressStream, Globals, BurdenedModule, !IO).
do_write_module_dep_file(ProgressStream, Globals, BurdenedModule, !IO) :-
BurdenedModule = burdened_module(Baggage, ParseTreeModuleSrc),
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
% XXX LEGACY
module_name_to_file_name_create_dirs(Globals, $pred,
ext_cur_ngs(ext_cur_ngs_misc_module_dep),
ModuleName, ProgDepFile, _ProgDepFileProposed, !IO),
io.open_output(ProgDepFile, ProgDepResult, !IO),
(
ProgDepResult = ok(ProgDepStream),
do_write_module_dep_file_to_stream(ProgDepStream, Globals,
Baggage, ParseTreeModuleSrc, !IO),
io.close_output(ProgDepStream, !IO)
;
ProgDepResult = error(IOError),
report_cannot_open_file_for_output(ProgressStream, Globals,
ProgDepFile, IOError, !IO)
).
:- pred do_write_module_dep_file_to_stream(io.text_output_stream::in,
globals::in, module_baggage::in, parse_tree_module_src::in,
io::di, io::uo) is det.
do_write_module_dep_file_to_stream(Stream, Globals,
Baggage, ParseTreeModuleSrc, !IO) :-
Version = module_dep_file_v2,
version_number(Version, VersionNumber),
SourceFileName = term_io.escaped_string(Baggage ^ mb_source_file_name),
SourceFileTopModuleName = Baggage ^ mb_source_file_top_module_name,
SourceFileTopModuleNameStr =
mercury_bracketed_sym_name_to_string(SourceFileTopModuleName),
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
Ancestors = set.to_sorted_list(get_ancestors_set(ModuleName)),
IncludeMap = ParseTreeModuleSrc ^ ptms_include_map,
Children = map.keys(IncludeMap),
parse_tree_module_src_get_int_imp_deps(ParseTreeModuleSrc,
IntDepSet, ImpDepSet),
set.to_sorted_list(IntDepSet, IntDeps),
set.to_sorted_list(ImpDepSet, ImpDeps),
MaybeTopModule = Baggage ^ mb_maybe_top_module,
NestedSubModules = get_nested_children_list_of_top_module(MaybeTopModule),
get_fact_tables(ParseTreeModuleSrc, FactTableFilesSet),
FactTableFilesStrs = list.map(term_io.quoted_string,
set.to_sorted_list(FactTableFilesSet)),
globals.get_backend_foreign_languages(Globals, BackendLangsList),
BackendLangs = set.list_to_set(BackendLangsList),
get_foreign_code_langs(ParseTreeModuleSrc, CodeLangs),
get_foreign_export_langs(ParseTreeModuleSrc, ExportLangs),
set.intersect(BackendLangs, CodeLangs, BackendCodeLangs),
set.intersect(BackendLangs, ExportLangs, BackendExportLangs),
CodeLangStrs = list.map(mercury_foreign_language_to_string,
set.to_sorted_list(BackendCodeLangs)),
( if set.is_empty(BackendExportLangs) then
ContainsForeignExport = contains_no_foreign_export
else
ContainsForeignExport = contains_foreign_export
),
get_fim_specs(ParseTreeModuleSrc, FIMSpecs),
get_foreign_include_file_infos(ParseTreeModuleSrc, ForeignIncludeFiles),
FIMSpecStrs = list.map(fim_spec_to_string, set.to_sorted_list(FIMSpecs)),
FIFOStrs = list.map(foreign_include_file_info_to_string,
set.to_sorted_list(ForeignIncludeFiles)),
contains_foreign_export_to_string(ContainsForeignExport,
ContainsForeignExportStr),
io.format(Stream,
"module(%d, ""%s"",\n" ++
"\t%s,\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t{%s},\n" ++
"\t%s,\n" ++
% The has_main/no_main slot is not needed anymore,
% so we just put no_main in there always.
"\tno_main,\n" ++
"\t{%s}\n" ++
").\n",
[i(VersionNumber), s(SourceFileName),
s(SourceFileTopModuleNameStr),
s(bracketed_sym_names_to_comma_list_string(Ancestors)),
s(bracketed_sym_names_to_comma_list_string(IntDeps)),
s(bracketed_sym_names_to_comma_list_string(ImpDeps)),
s(bracketed_sym_names_to_comma_list_string(Children)),
s(bracketed_sym_names_to_comma_list_string(NestedSubModules)),
s(string.join_list(", ", FactTableFilesStrs)),
s(string.join_list(", ", CodeLangStrs)),
s(string.join_list(", ", FIMSpecStrs)),
s(ContainsForeignExportStr),
s(string.join_list(", ", FIFOStrs))],
!IO).
:- func bracketed_sym_names_to_comma_list_string(list(sym_name)) = string.
bracketed_sym_names_to_comma_list_string(SymNames) = Str :-
Strs = list.map(mercury_bracketed_sym_name_to_string, SymNames),
Str = string.join_list(", ", Strs).
:- func fim_spec_to_string(fim_spec) = string.
fim_spec_to_string(FIMSpec) = Str :-
FIMSpec = fim_spec(Lang, ForeignImport),
LangStr = mercury_foreign_language_to_string(Lang),
ForeignImportStr = mercury_bracketed_sym_name_to_string(ForeignImport),
Str = LangStr ++ " - " ++ ForeignImportStr.
:- func foreign_include_file_info_to_string(foreign_include_file_info)
= string.
foreign_include_file_info_to_string(ForeignInclude) = Str :-
ForeignInclude = foreign_include_file_info(Lang, FileName),
LangStr = mercury_foreign_language_to_string(Lang),
Str = LangStr ++ " - " ++ term_io.quoted_string(FileName).
:- pred contains_foreign_export_to_string(contains_foreign_export, string).
:- mode contains_foreign_export_to_string(in, out) is det.
:- mode contains_foreign_export_to_string(out, in) is semidet.
contains_foreign_export_to_string(ContainsForeignExport,
ContainsForeignExportStr) :-
(
ContainsForeignExport = contains_foreign_export,
ContainsForeignExportStr = "contains_foreign_export"
;
ContainsForeignExport = contains_no_foreign_export,
% Yes, without the "contains_" prefix. Don't change it unless you mean
% to break compatibility with older .module_dep files.
ContainsForeignExportStr = "no_foreign_export"
).
%---------------------------------------------------------------------------%
read_module_dep_file(DepFileDir, DepFileName, DepFileContents, ModuleName,
Result) :-
% 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
Result = ok1(ModuleSummary)
else
Result = error1("failed to parse term")
)
;
TermResult = eof,
Result = error1("unexpected eof")
;
TermResult = error(Error, _),
Result = error1("parse 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 = [
VersionNumberTerm,
SourceFileTerm,
SourceFileTopModuleNameTerm,
ParentsTerm, % XXX Redundant term
IntDepsTerm,
ImpDepsTerm,
ChildrenTerm,
NestedSubModulesTerm,
FactDepsTerm,
ForeignLanguagesTerm,
ForeignImportsTerm,
ContainsForeignExportTerm,
_HasMainTerm % XXX Redundant term
| ModuleArgsTail
],
version_number_term(VersionNumberTerm, Version),
string_term(SourceFileTerm, SourceFileName),
try_parse_sym_name_and_no_args(SourceFileTopModuleNameTerm,
SourceFileTopModuleName),
sym_names_term(ParentsTerm, Parents),
sym_names_term(IntDepsTerm, IntDeps),
sym_names_term(ImpDepsTerm, ImpDeps),
sym_names_term(ChildrenTerm, Children),
sym_names_term(NestedSubModulesTerm, NestedSubModules0),
braces_term(fact_dep_term, FactDepsTerm, FactDeps),
braces_term(foreign_language_term, ForeignLanguagesTerm, ForeignLanguages),
braces_term(foreign_import_term, ForeignImportsTerm, ForeignImports),
contains_foreign_export_term(ContainsForeignExportTerm,
ContainsForeignExport),
(
Version = module_dep_file_v1,
ModuleArgsTail = [],
ForeignIncludes = []
;
Version = module_dep_file_v2,
ModuleArgsTail = [ForeignIncludesTerm],
braces_term(foreign_include_term, ForeignIncludesTerm, ForeignIncludes)
),
require_det (
( if ModuleName = SourceFileTopModuleName then
MaybeTopModule = top_module(set.list_to_set(NestedSubModules0))
else
MaybeTopModule = not_top_module,
expect(unify(NestedSubModules0, []), $pred,
"NestedSubModules0 != []")
),
set.list_to_set(Parents, ParentsSet),
AncestorsSet = get_ancestors_set(ModuleName),
expect(set.equal(ParentsSet, AncestorsSet), $pred,
"ParentsSet != AncestorsSet"),
ContainsForeignCode =
foreign_code_langs_known(set.list_to_set(ForeignLanguages)),
ModuleSummary = module_dep_summary(SourceFileName, DepFileDir,
SourceFileTopModuleName, ModuleName, set.list_to_set(Children),
MaybeTopModule,
set.list_to_set(IntDeps), set.list_to_set(ImpDeps),
set.list_to_set(FactDeps), set.list_to_set(ForeignImports),
set.list_to_set(ForeignIncludes),
ContainsForeignCode, ContainsForeignExport)
).
:- pred atom_term(term::in, string::out, list(term)::out) is semidet.
atom_term(Term, Atom, Args) :-
Term = term.functor(term.atom(Atom), Args, _).
:- pred version_number_term(term::in, module_dep_file_version::out) is semidet.
version_number_term(Term, Version) :-
term_int.decimal_term_to_int(Term, Int),
version_number(Version, Int).
:- pred string_term(term::in, string::out) is semidet.
string_term(Term, String) :-
Term = term.functor(term.string(String), [], _).
:- pred braces_term(pred(term, U), term, list(U)).
:- mode braces_term(in(pred(in, out) is semidet), in, out) is semidet.
braces_term(P, Term, Args) :-
atom_term(Term, "{}", ArgTerms),
list.map(P, ArgTerms, Args).
:- pred sym_names_term(term::in, list(sym_name)::out) is semidet.
sym_names_term(Term, SymNames) :-
braces_term(try_parse_sym_name_and_no_args, Term, SymNames).
:- pred fact_dep_term(term::in, string::out) is semidet.
fact_dep_term(Term, FactDep) :-
string_term(Term, FactDep).
:- pred foreign_language_term(term::in, foreign_language::out) is semidet.
foreign_language_term(Term, Lang) :-
string_term(Term, String),
globals.convert_foreign_language(String, Lang).
:- pred foreign_import_term(term::in, fim_spec::out) is semidet.
foreign_import_term(Term, FIMSpec) :-
atom_term(Term, "-", [LanguageTerm, ImportedModuleTerm]),
foreign_language_term(LanguageTerm, Language),
try_parse_sym_name_and_no_args(ImportedModuleTerm, ImportedModuleName),
FIMSpec = fim_spec(Language, ImportedModuleName).
:- pred foreign_include_term(term::in, foreign_include_file_info::out)
is semidet.
foreign_include_term(Term, ForeignInclude) :-
atom_term(Term, "-", [LanguageTerm, FileNameTerm]),
foreign_language_term(LanguageTerm, Language),
string_term(FileNameTerm, FileName),
ForeignInclude = foreign_include_file_info(Language, FileName).
:- pred contains_foreign_export_term(term::in, contains_foreign_export::out)
is semidet.
contains_foreign_export_term(Term, ContainsForeignExport) :-
atom_term(Term, Atom, []),
contains_foreign_export_to_string(ContainsForeignExport, Atom).
%---------------------------------------------------------------------------%
:- end_module make.module_dep_file.
%---------------------------------------------------------------------------%