mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
... 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.
439 lines
17 KiB
Mathematica
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.
|
|
%---------------------------------------------------------------------------%
|