mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
Specifically, make it warn about interface imports that are not used
in the initial HLDS, even if they *are* used after the expansion of
type-, inst- and mode-equivalences.
compiler/hlds_module.m:
Add a slot to the HLDS to store the set of modules that are
imported in the interface but are unused there when the HLDS
is first constructed.
compiler/module_qual.qualify_items.m:
Compute this set, and return it to mercury_compile_make_hlds.m.
Make the code module qualifying aug_compilation_units warn about
unused interface imports only if unused_imports.m won't do the same later.
compiler/mercury_compile_make_hlds.m:
Pass the set to make_hlds_passes.m.
compiler/make_hlds_passes.m:
Store the set in the initial HLDS.
compiler/prog_data_used_modules.m:
Replace set_ordlists with set_tree234s.
compiler/unused_imports.m:
Consider an interface-imported module unused in the interface
if module_qual.qualify_items.m considered it unused, even if
changes made by equiv_type.m has added uses of it later.
compiler/handle_options.m:
Stop making --warn-unused-imports imply --no-warn-unused-interface-imports,
since new logic in module_qual.qualify_items.m makes this unnecessary.
compiler/make_module_file_names.m:
compiler/type_inst_mode_map.m:
compiler/write_deps_file.m:
Move imports from the interface section to the implementation section,
in response to the new, more thorough warnings.
449 lines
20 KiB
Mathematica
449 lines
20 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2008-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2017, 2019-2025 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: write_deps_file.m.
|
|
%
|
|
% This module writes .dep, .dv and .d files, after getting its subcontractors
|
|
% d_file_deps.m and generate_mmakefile_fragments.m to figure out
|
|
% what should go into them.
|
|
%
|
|
% We generate one .dep and one .dv file for each program, with those files
|
|
% being named prog.dep and prog.dv (if the name of the program is "prog").
|
|
% We generate one .d file for each module in the program, with the file
|
|
% being named mod.d (if the name of the module is "mod").
|
|
%
|
|
% The .dv file contains the definitions of all the mmake variable definitions
|
|
% relating to the program, while the .dep file contains all the rules
|
|
% relating to the program. The reason for this split is that we want mmake
|
|
% to glue all these mmakefile fragments together in the following order:
|
|
%
|
|
% - the program's .dv file
|
|
% - the Mmakefile in the current directory
|
|
% - the .d files of the program's modules
|
|
% - the program's .dep file
|
|
% - the standard Mmake.rules file
|
|
%
|
|
% This arrangement gives the Mmakefile access to the values of the
|
|
% variables defined in the program's .dv file, such as prog.mods and
|
|
% prog.parent_mods, which list all the modules and all the parent modules
|
|
% in the program respectively. On the other hand, by including the
|
|
% automatically generated .dep file *after* the Mmakefile, we allow
|
|
% the rules in the .dep file to refer to variables defined in the Mmakefile.
|
|
% (Usually the rules allow, but do not require, the Mmakefile to define
|
|
% these variables.)
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.write_deps_file.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module parse_tree.d_file_deps.
|
|
:- import_module parse_tree.deps_map.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.generate_mmakefile_fragments.
|
|
:- import_module parse_tree.module_baggage.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% generate_and_write_dep_file_gendep(ProgressStream, Globals,
|
|
% FileOrModule, DepsMap, Specs, !IO):
|
|
%
|
|
% Generate the per-program makefile dependencies files
|
|
% (`.dep' and `.dv' files) for the program whose top-level module
|
|
% is specified by FileOrModule.
|
|
%
|
|
% This involves first transitively reading in all imported or ancestor
|
|
% modules. While we are at it, we also generate the per-module makefile
|
|
% dependency files (`.d' files) for all those modules. Return as Specs
|
|
% any errors and/or warnings we found along the way.
|
|
%
|
|
% This predicate implements "mmc --generate-dependencies <prog>",
|
|
% which is used very frequently.
|
|
%
|
|
:- pred generate_and_write_dep_file_gendep(io.text_output_stream::in,
|
|
globals::in, file_or_module::in, deps_map::out, list(error_spec)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% generate_and_write_d_file_gendep(ProgressStream, Globals, FIleOrModule,
|
|
% DepsMap, Specs, !IO):
|
|
%
|
|
% Generate the per-module makefile dependency file ('.d' file)
|
|
% for the given module.
|
|
%
|
|
% This predicate implements "mmc --generate-dependency <module>",
|
|
% which is almost never used.
|
|
%
|
|
:- pred generate_and_write_d_file_gendep(io.text_output_stream::in,
|
|
globals::in, file_or_module::in, deps_map::out, list(error_spec)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% generate_and_write_d_file_hlds(ProgressStream, Globals,
|
|
% BurdenedAugCompUnit, AvailModuleSets, MaybeInclTransOptRule, !IO):
|
|
%
|
|
% Write out the per-module makefile dependencies (`.d') file for the
|
|
% specified module. AvailModuleSets gives the set of all module names
|
|
% on which the generated code of this module might depend.
|
|
% MaybeInclTransOptRule controls whether to include a trans_opt_deps rule
|
|
% in the file, and if so, what the rule should say.
|
|
%
|
|
% This predicate is invoked as part of every mmc invocation
|
|
% that is intended to generate target language code.
|
|
%
|
|
:- pred generate_and_write_d_file_hlds(io.text_output_stream::in, globals::in,
|
|
burdened_aug_comp_unit::in, avail_module_sets::in,
|
|
maybe_include_trans_opt_rule::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.mmakefiles.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.make_module_file_names.
|
|
:- import_module parse_tree.maybe_error.
|
|
:- import_module parse_tree.module_cmds.
|
|
:- import_module parse_tree.parse_error.
|
|
:- import_module parse_tree.prog_parse_tree.
|
|
|
|
:- import_module bool.
|
|
:- import_module dir.
|
|
:- import_module io.file.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_and_write_dep_file_gendep(ProgressStream, Globals, FileOrModule,
|
|
DepsMap, !:Specs, !IO) :-
|
|
generate_deps_map(ProgressStream, Globals, do_not_search,
|
|
FileOrModule, ModuleName, DepsMap, !:Specs, !IO),
|
|
do_we_have_a_valid_module_dep(DepsMap, ModuleName, MaybeBurdenedModule),
|
|
(
|
|
MaybeBurdenedModule = error1(FatalErrorSpecs),
|
|
% The error_specs in FatalErrorSpecs may already be in !.Specs,
|
|
% but even if we add them again here, they will be printed just once.
|
|
!:Specs = FatalErrorSpecs ++ !.Specs
|
|
;
|
|
MaybeBurdenedModule = ok1(BurdenedModule),
|
|
BurdenedModule = burdened_module(Baggage, _ParseTreeModuleSrc),
|
|
generate_and_write_dep_dv_files_gendep(ProgressStream, Globals,
|
|
DepsMap, ModuleName, Baggage, !IO),
|
|
compute_dep_graphs_gendep(ProgressStream, Globals, ModuleName,
|
|
DepsMap, DepGraphs, BurdenedModules, !Specs, !IO),
|
|
generate_and_write_d_file_gendep_depgraphs(ProgressStream, Globals,
|
|
DepGraphs, BurdenedModules, !IO)
|
|
).
|
|
|
|
generate_and_write_d_file_gendep(ProgressStream, Globals, FileOrModule,
|
|
DepsMap, !:Specs, !IO) :-
|
|
generate_deps_map(ProgressStream, Globals, do_search,
|
|
FileOrModule, ModuleName, DepsMap, !:Specs, !IO),
|
|
do_we_have_a_valid_module_dep(DepsMap, ModuleName, MaybeBurdenedModule),
|
|
(
|
|
MaybeBurdenedModule = error1(FatalErrorSpecs),
|
|
% The error_specs in FatalErrorSpecs may already be in !.Specs,
|
|
% but even if we add them again here, they will be printed just once.
|
|
!:Specs = FatalErrorSpecs ++ !.Specs
|
|
;
|
|
MaybeBurdenedModule = ok1(BurdenedModule),
|
|
compute_dep_graphs_gendep(ProgressStream, Globals, ModuleName,
|
|
DepsMap, DepGraphs, _BurdenedModules, !Specs, !IO),
|
|
generate_and_write_d_file_gendep_depgraphs(ProgressStream, Globals,
|
|
DepGraphs, [BurdenedModule], !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_and_write_dep_dv_files_gendep(io.text_output_stream::in,
|
|
globals::in, deps_map::in, module_name::in, module_baggage::in,
|
|
io::di, io::uo) is det.
|
|
|
|
generate_and_write_dep_dv_files_gendep(ProgressStream, Globals, DepsMap,
|
|
ModuleName, Baggage, !IO) :-
|
|
% First, build up a map of the dependencies.
|
|
SourceFileName = Baggage ^ mb_source_file_name,
|
|
|
|
generate_dv_mmakefile(Globals, SourceFileName, ModuleName, DepsMap,
|
|
MmakeFileDv),
|
|
generate_dep_mmakefile(Globals, SourceFileName, ModuleName, DepsMap,
|
|
MmakeFileDep, !IO),
|
|
MmakeFileStrDv = mmakefile_to_string(MmakeFileDv),
|
|
MmakeFileStrDep = mmakefile_to_string(MmakeFileDep),
|
|
|
|
% XXX LEGACY
|
|
module_name_to_file_name_create_dirs(Globals, $pred,
|
|
ext_cur_ngs(ext_cur_ngs_mf_dv), ModuleName,
|
|
FileNameDv, _FileNameDvProposed, !IO),
|
|
module_name_to_file_name_create_dirs(Globals, $pred,
|
|
ext_cur_ngs(ext_cur_ngs_mf_dep), ModuleName,
|
|
FileNameDep, _FileNameDepProposed, !IO),
|
|
|
|
write_string_to_file(ProgressStream, Globals,
|
|
"Writing auto-dependency file", FileNameDv, MmakeFileStrDv,
|
|
_SucceededDv, !IO),
|
|
write_string_to_file(ProgressStream, Globals,
|
|
"Writing auto-dependency file", FileNameDep, MmakeFileStrDep,
|
|
_SucceededDep, !IO),
|
|
|
|
% For Java, the main target is actually a shell script
|
|
% which will set CLASSPATH appropriately, and then invoke java
|
|
% on the appropriate .class file. Rather than generating
|
|
% an Mmake rule to build this file when it is needed,
|
|
% we just generate this file at "mmake depend" time, since
|
|
% that is simpler and probably more efficient anyway.
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_java,
|
|
create_java_shell_script(ProgressStream, Globals, ModuleName,
|
|
_Succeeded, !IO)
|
|
;
|
|
( Target = target_c
|
|
; Target = target_csharp
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% generate_and_write_d_file_gendep_depgraphs(ProgressStream, Globals,
|
|
% DepGraphs, BurdenedModules, !IO):
|
|
%
|
|
% This predicate writes out the .d files for all the modules in the
|
|
% BurdenedModules list.
|
|
%
|
|
:- pred generate_and_write_d_file_gendep_depgraphs(io.text_output_stream::in,
|
|
globals::in, dep_graphs::in, list(burdened_module)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
generate_and_write_d_file_gendep_depgraphs(ProgressStream, Globals,
|
|
DepGraphs, BurdenedModules, !IO) :-
|
|
Cache0 = init_module_file_name_cache,
|
|
generate_and_write_d_file_gendep_depgraphs_loop(ProgressStream, Globals,
|
|
DepGraphs, BurdenedModules, Cache0, _Cache, !IO).
|
|
|
|
:- pred generate_and_write_d_file_gendep_depgraphs_loop(
|
|
io.text_output_stream::in, globals::in, dep_graphs::in,
|
|
list(burdened_module)::in,
|
|
module_file_name_cache::in, module_file_name_cache::out,
|
|
io::di, io::uo) is det.
|
|
|
|
generate_and_write_d_file_gendep_depgraphs_loop(_, _, _, [], !Cache, !IO).
|
|
generate_and_write_d_file_gendep_depgraphs_loop(ProgressStream, Globals,
|
|
DepGraphs, [BurdenedModule | BurdenedModules], !Cache, !IO) :-
|
|
generate_and_write_d_file_gendep_depgraphs(ProgressStream, Globals,
|
|
DepGraphs, BurdenedModule, !Cache, !IO),
|
|
generate_and_write_d_file_gendep_depgraphs_loop(ProgressStream, Globals,
|
|
DepGraphs, BurdenedModules, !Cache, !IO).
|
|
|
|
:- pred generate_and_write_d_file_gendep_depgraphs(io.text_output_stream::in,
|
|
globals::in, dep_graphs::in, burdened_module::in,
|
|
module_file_name_cache::in, module_file_name_cache::out,
|
|
io::di, io::uo) is det.
|
|
|
|
generate_and_write_d_file_gendep_depgraphs(ProgressStream, Globals, DepGraphs,
|
|
BurdenedModule, !Cache, !IO) :-
|
|
BurdenedModule = burdened_module(Baggage, ParseTreeModuleSrc),
|
|
% XXX DFILE Note that even if a fatal error occurred for one of the files
|
|
% that the current Module depends on, a .d file is still produced,
|
|
% even though it probably contains incorrect information.
|
|
ModuleErrors = Baggage ^ mb_errors,
|
|
FatalErrors = ModuleErrors ^ rm_fatal_errors,
|
|
( if set.is_empty(FatalErrors) then
|
|
construct_d_file_deps_gendep(Globals, DepGraphs,
|
|
Baggage, ParseTreeModuleSrc, DFileDeps),
|
|
generate_d_mmakefile_contents(Globals, Baggage, ParseTreeModuleSrc,
|
|
DFileDeps, FileNameD, FileContentsStrD, !Cache, !IO),
|
|
write_out_d_file(ProgressStream, Globals,
|
|
FileNameD, FileContentsStrD, !IO)
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_and_write_d_file_hlds(ProgressStream, Globals, BurdenedAugCompUnit,
|
|
AvailModuleSets, MaybeInclTransOptRule, !IO) :-
|
|
Cache0 = init_module_file_name_cache,
|
|
construct_d_file_deps_hlds(Globals, BurdenedAugCompUnit, AvailModuleSets,
|
|
MaybeInclTransOptRule, DFileDeps),
|
|
BurdenedAugCompUnit = burdened_aug_comp_unit(Baggage, AugCompUnit),
|
|
ParseTreeModuleSrc = AugCompUnit ^ acu_module_src,
|
|
generate_d_mmakefile_contents(Globals, Baggage, ParseTreeModuleSrc,
|
|
DFileDeps, FileNameD, FileContentsStrD, Cache0, _Cache, !IO),
|
|
write_out_d_file(ProgressStream, Globals,
|
|
FileNameD, FileContentsStrD, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% generate_d_mmakefile_contents(Globals, BurdenedAugCompUnit, DFileDeps,
|
|
% FileNameD, FileContentsStrD, !Cache, !IO):
|
|
%
|
|
% General FileContentsStrD as the new contents of FileNameD.
|
|
%
|
|
:- pred generate_d_mmakefile_contents(globals::in,
|
|
module_baggage::in, parse_tree_module_src::in,
|
|
d_file_deps::in, file_name::out, string::out,
|
|
module_file_name_cache::in, module_file_name_cache::out,
|
|
io::di, io::uo) is det.
|
|
|
|
generate_d_mmakefile_contents(Globals, Baggage, ParseTreeModuleSrc,
|
|
DFileDeps, FileNameD, FileContentsStrD, !Cache, !IO) :-
|
|
ModuleName = ParseTreeModuleSrc ^ ptms_module_name,
|
|
% XXX LEGACY
|
|
ExtD = ext_cur_ngs(ext_cur_ngs_mf_d),
|
|
module_name_to_file_name_create_dirs(Globals, $pred, ExtD, ModuleName,
|
|
FileNameD, _FileNameDProposed, !IO),
|
|
construct_intermod_deps(Globals, ParseTreeModuleSrc, DFileDeps,
|
|
IntermodDeps, !Cache, !IO),
|
|
generate_d_mmakefile(Globals, Baggage, ParseTreeModuleSrc,
|
|
DFileDeps, IntermodDeps, MmakeFileD, !Cache, !IO),
|
|
FileContentsStrD = mmakefile_to_string(MmakeFileD).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred write_out_d_file(io.text_output_stream::in, globals::in,
|
|
file_name::in, string::in, io::di, io::uo) is det.
|
|
|
|
write_out_d_file(ProgressStream, Globals, FileNameD, FileContentsStrD, !IO) :-
|
|
% To avoid problems with concurrent updates of `.d' files during
|
|
% parallel makes, we first create the file with a temporary name,
|
|
% and then rename it to the desired name when we have finished.
|
|
% XXX I (zs) think that if two mmake actions both update the same .d file,
|
|
% then having them executed in parallel is itself a bug, and that bug
|
|
% should be fixed, instead of being accommodated, as we do here.
|
|
% Therefore I think that this predicate should be deleted,
|
|
% and its uses replaced by calls to write_string_to_file.
|
|
io.file.make_temp_file(dir.dirname(FileNameD), "tmp_d", "",
|
|
TmpFileNameDResult, !IO),
|
|
(
|
|
TmpFileNameDResult = error(Error),
|
|
Message = "Could not create temporary file: " ++ error_message(Error),
|
|
report_arbitrary_error(ProgressStream, Message, !IO)
|
|
;
|
|
TmpFileNameDResult = ok(TmpFileNameD),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
(
|
|
Verbose = no
|
|
;
|
|
Verbose = yes,
|
|
io.format(ProgressStream,
|
|
"%% Writing auto-dependency file `%s'...",
|
|
[s(FileNameD)], !IO),
|
|
io.flush_output(ProgressStream, !IO)
|
|
),
|
|
io.open_output(TmpFileNameD, Result, !IO),
|
|
(
|
|
Result = error(IOError),
|
|
maybe_write_string(ProgressStream, Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(ProgressStream, Verbose, !IO),
|
|
io.error_message(IOError, IOErrorMessage),
|
|
string.format("error opening temporary file `%s' for output: %s",
|
|
[s(TmpFileNameD), s(IOErrorMessage)], Message),
|
|
report_arbitrary_error(ProgressStream, Message, !IO)
|
|
;
|
|
Result = ok(DepStream),
|
|
io.write_string(DepStream, FileContentsStrD, !IO),
|
|
io.close_output(DepStream, !IO),
|
|
|
|
io.file.rename_file(TmpFileNameD, FileNameD,
|
|
FirstRenameResult, !IO),
|
|
(
|
|
FirstRenameResult = error(_),
|
|
% On some systems, we need to remove the existing file first,
|
|
% if any. So try again that way.
|
|
io.file.remove_file(FileNameD, RemoveResult, !IO),
|
|
(
|
|
RemoveResult = error(RemoveError),
|
|
maybe_write_string(ProgressStream, Verbose,
|
|
" failed.\n", !IO),
|
|
maybe_flush_output(ProgressStream, Verbose, !IO),
|
|
io.error_message(RemoveError, RemoveErrorMsg),
|
|
string.format("can't remove file `%s': %s",
|
|
[s(FileNameD), s(RemoveErrorMsg)], Message),
|
|
report_arbitrary_error(ProgressStream, Message, !IO)
|
|
;
|
|
RemoveResult = ok,
|
|
io.file.rename_file(TmpFileNameD,
|
|
FileNameD, SecondRenameResult, !IO),
|
|
(
|
|
SecondRenameResult = error(RenameError),
|
|
maybe_write_string(ProgressStream, Verbose,
|
|
" failed.\n", !IO),
|
|
maybe_flush_output(ProgressStream, Verbose, !IO),
|
|
io.error_message(RenameError, RenameErrorMsg),
|
|
string.format("can't rename file `%s' as `%s': %s",
|
|
[s(TmpFileNameD), s(FileNameD),
|
|
s(RenameErrorMsg)], Message),
|
|
report_arbitrary_error(ProgressStream, Message, !IO)
|
|
;
|
|
SecondRenameResult = ok,
|
|
maybe_write_string(ProgressStream, Verbose,
|
|
" done.\n", !IO)
|
|
)
|
|
)
|
|
;
|
|
FirstRenameResult = ok,
|
|
maybe_write_string(ProgressStream, Verbose, " done.\n", !IO)
|
|
)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Check whether we could read the main `.m' file.
|
|
%
|
|
:- pred do_we_have_a_valid_module_dep(deps_map::in, module_name::in,
|
|
maybe1(burdened_module)::out) is det.
|
|
|
|
do_we_have_a_valid_module_dep(DepsMap, ModuleName, MaybeBurdenedModule) :-
|
|
( if map.search(DepsMap, ModuleName, ModuleDep) then
|
|
ModuleDep = deps(_, BurdenedModule),
|
|
BurdenedModule = burdened_module(Baggage, _ParseTreeModuleSrc),
|
|
Errors = Baggage ^ mb_errors,
|
|
FatalErrors = Errors ^ rm_fatal_errors,
|
|
( if set.is_non_empty(FatalErrors) then
|
|
FatalErrorSpecs = Errors ^ rm_fatal_error_specs,
|
|
(
|
|
FatalErrorSpecs = [],
|
|
string.format("FatalErrorSpecs = [], with FatalErrors = %s\n",
|
|
[s(string.string(FatalErrors))], UnexpectedMsg),
|
|
unexpected($pred, UnexpectedMsg)
|
|
;
|
|
FatalErrorSpecs = [_ | _],
|
|
MaybeBurdenedModule = error1(FatalErrorSpecs)
|
|
)
|
|
else
|
|
MaybeBurdenedModule = ok1(BurdenedModule)
|
|
)
|
|
else
|
|
% We don't have an error message to return here, but we also
|
|
% neither want or need to return one, because if execution got here,
|
|
% then the compiler should have already tried to read in ModuleName,
|
|
% and printed a diagnostic for the failure of that attempt.
|
|
MaybeBurdenedModule = error1([])
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module parse_tree.write_deps_file.
|
|
%---------------------------------------------------------------------------%
|