mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-30 16:54:41 +00:00
compiler/Mercury.options:
Don't disable --warn-unused-imports for any packages.
compiler/make.m:
compiler/recompilation.m:
Delete the imports in these packages that aren't used in the module itself.
(These are the imports that --warn-unused-imports warns about.)
The other packages in the compiler no unused imports.
compiler/make.dependencies.m:
compiler/make.module_dep_file.m:
compiler/make.module_target.m:
compiler/make.program_target.m:
compiler/make.util.m:
compiler/recompilation.check.m:
compiler/recompilation.usage.m:
compiler/recompilation.version.m:
Conform to the above. In most cases, this means adding the imports
that previously these modules got from their parent package.
In some cases, it means *deleting* unused imports that previously
weren't warned about because the import also came from the parent
module. Some imports deleted from the packages weren't needed
in *any* of their modules.
1495 lines
57 KiB
Mathematica
1495 lines
57 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2002-2011 The University of Melbourne.
|
|
% 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.dependencies.m.
|
|
% Author: stayl.
|
|
%
|
|
% Code to find the dependencies for a particular target,
|
|
% e.g. module.c depends on module.m, import.int, etc.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module make.dependencies.
|
|
:- interface.
|
|
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module make.util.
|
|
|
|
:- import_module bool.
|
|
:- import_module enum.
|
|
:- import_module io.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
:- import_module sparse_bitset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Dependency computation does a lot of unions so we use a set
|
|
% representation suited to that purpose, namely bitsets. We can't store
|
|
% module_names and dependency_files in those sets, so we keep a mapping
|
|
% between module_name <-> module_index and dependency_file <->
|
|
% dependency_file_index in the make_info structure, and work with sets of
|
|
% indices instead.
|
|
%
|
|
% sparse_bitset is faster than tree_bitset by my tests.
|
|
%
|
|
:- type deps_set(T) == sparse_bitset(T).
|
|
% :- type deps_set(T) == tree_bitset(T).
|
|
|
|
:- type module_index.
|
|
:- instance enum(module_index).
|
|
|
|
:- type dependency_file_index.
|
|
:- instance enum(dependency_file_index).
|
|
|
|
% find_module_deps(ModuleName, Succeeded, Deps, !Info, !IO).
|
|
%
|
|
% The reason we don't return maybe(Deps) is that with `--keep-going'
|
|
% we want to do as much work as possible.
|
|
%
|
|
:- type find_module_deps(T) ==
|
|
pred(globals, module_index, bool, deps_set(T), make_info, make_info,
|
|
io, io).
|
|
:- inst find_module_deps ==
|
|
(pred(in, in, out, out, in, out, di, uo) is det).
|
|
|
|
:- type find_module_deps_plain_set(T) ==
|
|
pred(globals, module_index, bool, set(T), make_info, make_info, io, io).
|
|
:- inst find_module_deps_plain_set ==
|
|
(pred(in, in, out, out, in, out, di, uo) is det).
|
|
|
|
:- type dependency_file
|
|
---> dep_target(target_file)
|
|
% A target which could be made.
|
|
|
|
; dep_file(file_name, maybe(option)).
|
|
% An ordinary file which `mmc --make' does not know how to rebuild.
|
|
% The option gives a list of directories in which to search.
|
|
|
|
% Return a closure which will find the dependencies for a target type
|
|
% given a module name.
|
|
%
|
|
:- func target_dependencies(globals::in, module_target_type::in) =
|
|
(find_module_deps(dependency_file_index)::out(find_module_deps)) is det.
|
|
|
|
% Union the output set of dependencies for a given module
|
|
% with the accumulated set. This is used with
|
|
% foldl3_maybe_stop_at_error to iterate over a list of
|
|
% module_names to find all target files for those modules.
|
|
%
|
|
:- pred union_deps(find_module_deps(T)::in(find_module_deps), globals::in,
|
|
module_index::in, bool::out, deps_set(T)::in, deps_set(T)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
:- pred union_deps_plain_set(
|
|
find_module_deps_plain_set(T)::in(find_module_deps_plain_set),
|
|
globals::in, module_index::in, bool::out, set(T)::in, set(T)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
:- pred deps_set_foldl3_maybe_stop_at_error(bool::in,
|
|
foldl3_pred_with_status(T, Acc, Info, IO)::in(foldl3_pred_with_status),
|
|
globals::in, deps_set(T)::in, bool::out, Acc::in, Acc::out,
|
|
Info::in, Info::out, IO::di, IO::uo) is det <= enum(T).
|
|
|
|
% Convert a list of module_names to a module_index set.
|
|
%
|
|
:- pred module_names_to_index_set(list(module_name)::in,
|
|
deps_set(module_index)::out, make_info::in, make_info::out) is det.
|
|
|
|
% Convert a module_index set to a module_name set.
|
|
%
|
|
:- pred module_index_set_to_plain_set(make_info::in,
|
|
deps_set(module_index)::in, set(module_name)::out) is det.
|
|
|
|
% Convert a dependency_file_index set to a dependency_file set.
|
|
%
|
|
:- pred dependency_file_index_set_to_plain_set(make_info::in,
|
|
deps_set(dependency_file_index)::in, set(dependency_file)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find all modules in the current directory which are reachable
|
|
% (by import or include) from the given module.
|
|
%
|
|
:- pred find_reachable_local_modules(globals::in, module_name::in, bool::out,
|
|
set(module_name)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% Remove all nested modules from a list of modules.
|
|
%
|
|
:- pred remove_nested_modules(globals::in, list(module_name)::in,
|
|
list(module_name)::out, make_info::in, make_info::out, io::di, io::uo)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find all modules in the current directory which are reachable (by import)
|
|
% from the given module. Return a list of `--local-module-id' options
|
|
% suitable for the command line.
|
|
%
|
|
:- pred make_local_module_id_options(globals::in, module_name::in, bool::out,
|
|
list(string)::out, make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred dependency_status(globals::in, dependency_file::in,
|
|
dependency_status::out, make_info::in, make_info::out, io::di, io::uo)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type dependencies_result
|
|
---> deps_up_to_date
|
|
; deps_out_of_date
|
|
; deps_error.
|
|
|
|
% check_dependencies(Globals, TargetFileName, TargetFileTimestamp,
|
|
% BuildDepsSucceeded, Dependencies, Result, !IO)
|
|
%
|
|
% Check that all the dependency targets are up-to-date.
|
|
%
|
|
:- pred check_dependencies(globals::in, file_name::in,
|
|
maybe_error(timestamp)::in, bool::in, list(dependency_file)::in,
|
|
dependencies_result::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% check_dependencies(Globals, TargetFileName, TargetFileTimestamp,
|
|
% BuildDepsSucceeded, Dependencies, Result, !IO)
|
|
%
|
|
% Check that all the dependency files are up-to-date.
|
|
%
|
|
:- pred check_dependency_timestamps(globals::in, file_name::in,
|
|
maybe_error(timestamp)::in, bool::in, list(File)::in,
|
|
pred(File, io, io)::(pred(in, di, uo) is det),
|
|
list(maybe_error(timestamp))::in, dependencies_result::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type cached_direct_imports.
|
|
:- func init_cached_direct_imports = cached_direct_imports.
|
|
|
|
:- type cached_transitive_dependencies.
|
|
:- func init_cached_transitive_dependencies = cached_transitive_dependencies.
|
|
|
|
:- type cached_foreign_imports.
|
|
:- func init_cached_foreign_imports = cached_foreign_imports.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module dir.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Bitset indices.
|
|
%
|
|
|
|
:- type module_index
|
|
---> module_index(int).
|
|
|
|
:- type dependency_file_index
|
|
---> dependency_file_index(int).
|
|
|
|
:- instance enum(module_index) where [
|
|
to_int(module_index(I)) = I,
|
|
from_int(I) = module_index(I)
|
|
].
|
|
|
|
:- instance enum(dependency_file_index) where [
|
|
to_int(dependency_file_index(I)) = I,
|
|
from_int(I) = dependency_file_index(I)
|
|
].
|
|
|
|
:- pred module_name_to_index(module_name::in, module_index::out,
|
|
make_info::in, make_info::out) is det.
|
|
|
|
module_name_to_index(ModuleName, Index, !Info) :-
|
|
Map0 = !.Info ^ module_index_map,
|
|
( if
|
|
version_hash_table.search(Map0 ^ mim_forward_map, ModuleName, Index0)
|
|
then
|
|
Index = Index0
|
|
else
|
|
Map0 = module_index_map(Forward0, Reverse0, Size0),
|
|
Index = module_index(Size0),
|
|
Size = Size0 + 1,
|
|
version_hash_table.det_insert(ModuleName, Index, Forward0, Forward),
|
|
TrueSize = version_array.size(Reverse0),
|
|
( if Size > TrueSize then
|
|
NewSize = increase_array_size(TrueSize),
|
|
version_array.resize(NewSize, ModuleName, Reverse0, Reverse)
|
|
else
|
|
version_array.set(Size0, ModuleName, Reverse0, Reverse)
|
|
),
|
|
Map = module_index_map(Forward, Reverse, Size),
|
|
!Info ^ module_index_map := Map
|
|
).
|
|
|
|
:- func increase_array_size(int) = int.
|
|
|
|
increase_array_size(N) = (if N = 0 then 1 else N * 2).
|
|
|
|
:- pred module_index_to_name(make_info::in, module_index::in, module_name::out)
|
|
is det.
|
|
|
|
module_index_to_name(Info, Index, ModuleName) :-
|
|
Index = module_index(I),
|
|
ModuleName = Info ^ module_index_map ^ mim_reverse_map ^ elem(I).
|
|
|
|
module_names_to_index_set(ModuleNames, IndexSet, !Info) :-
|
|
module_names_to_index_set_2(ModuleNames, sparse_bitset.init,
|
|
IndexSet, !Info).
|
|
|
|
:- pred module_names_to_index_set_2(list(module_name)::in,
|
|
deps_set(module_index)::in, deps_set(module_index)::out,
|
|
make_info::in, make_info::out) is det.
|
|
|
|
module_names_to_index_set_2([], !IndexSet, !Info).
|
|
module_names_to_index_set_2([ModuleName | ModuleNames], !Set, !Info) :-
|
|
module_name_to_index(ModuleName, ModuleIndex, !Info),
|
|
sparse_bitset.insert(ModuleIndex, !Set),
|
|
module_names_to_index_set_2(ModuleNames, !Set, !Info).
|
|
|
|
module_index_set_to_plain_set(Info, ModuleIndices, Modules) :-
|
|
foldl(module_index_set_to_plain_set_2(Info), ModuleIndices,
|
|
set.init, Modules).
|
|
|
|
:- pred module_index_set_to_plain_set_2(make_info::in, module_index::in,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
module_index_set_to_plain_set_2(Info, ModuleIndex, !Set) :-
|
|
module_index_to_name(Info, ModuleIndex, ModuleName),
|
|
set.insert(ModuleName, !Set).
|
|
|
|
:- pred dependency_file_to_index(dependency_file::in,
|
|
dependency_file_index::out, make_info::in, make_info::out) is det.
|
|
|
|
dependency_file_to_index(DepFile, Index, !Info) :-
|
|
Map0 = !.Info ^ dep_file_index_map,
|
|
ForwardMap0 = Map0 ^ dfim_forward_map,
|
|
( if version_hash_table.search(ForwardMap0, DepFile, Index0) then
|
|
Index = Index0
|
|
else
|
|
Map0 = dependency_file_index_map(Forward0, Reverse0, Size0),
|
|
Index = dependency_file_index(Size0),
|
|
Size = Size0 + 1,
|
|
version_hash_table.det_insert(DepFile, Index, Forward0, Forward),
|
|
TrueSize = version_array.size(Reverse0),
|
|
( if Size > TrueSize then
|
|
NewSize = increase_array_size(TrueSize),
|
|
version_array.resize(NewSize, DepFile, Reverse0, Reverse)
|
|
else
|
|
version_array.set(Size0, DepFile, Reverse0, Reverse)
|
|
),
|
|
Map = dependency_file_index_map(Forward, Reverse, Size),
|
|
!Info ^ dep_file_index_map := Map
|
|
).
|
|
|
|
:- pred index_to_dependency_file(make_info::in, dependency_file_index::in,
|
|
dependency_file::out) is det.
|
|
|
|
index_to_dependency_file(Info, Index, DepFile) :-
|
|
Index = dependency_file_index(Int),
|
|
DepFile = Info ^ dep_file_index_map ^ dfim_reverse_map ^ elem(Int).
|
|
|
|
dependency_file_index_set_to_plain_set(Info, DepIndices, DepFiles) :-
|
|
foldl(dependency_file_index_set_to_plain_set_2(Info), DepIndices,
|
|
[], DepFilesList),
|
|
DepFiles = set.from_list(DepFilesList).
|
|
|
|
:- pred dependency_file_index_set_to_plain_set_2(make_info::in,
|
|
dependency_file_index::in,
|
|
list(dependency_file)::in, list(dependency_file)::out) is det.
|
|
|
|
dependency_file_index_set_to_plain_set_2(Info, DepIndex, List0, List) :-
|
|
index_to_dependency_file(Info, DepIndex, DepFile),
|
|
List = [DepFile | List0].
|
|
|
|
:- pred dependency_files_to_index_set(list(dependency_file)::in,
|
|
deps_set(dependency_file_index)::out, make_info::in, make_info::out)
|
|
is det.
|
|
|
|
dependency_files_to_index_set(DepFiles, DepIndexSet, !Info) :-
|
|
list.foldl2(dependency_files_to_index_set_2, DepFiles,
|
|
init, DepIndexSet, !Info).
|
|
|
|
:- pred dependency_files_to_index_set_2(dependency_file::in,
|
|
deps_set(dependency_file_index)::in, deps_set(dependency_file_index)::out,
|
|
make_info::in, make_info::out) is det.
|
|
|
|
dependency_files_to_index_set_2(DepFiles, !Set, !Info) :-
|
|
dependency_file_to_index(DepFiles, DepIndex, !Info),
|
|
insert(DepIndex, !Set).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type deps_result(T)
|
|
---> deps_result(
|
|
dr_success :: bool,
|
|
dr_set :: deps_set(T)
|
|
).
|
|
|
|
:- type module_deps_result == deps_result(module_index).
|
|
|
|
union_deps(FindDeps, Globals, ModuleIndex, Success, Deps0, Deps, !Info, !IO) :-
|
|
FindDeps(Globals, ModuleIndex, Success, Deps1, !Info, !IO),
|
|
Deps = union(Deps0, Deps1).
|
|
|
|
union_deps_plain_set(FindDeps, Globals, ModuleName, Success, Deps0, Deps,
|
|
!Info, !IO) :-
|
|
FindDeps(Globals, ModuleName, Success, Deps1, !Info, !IO),
|
|
Deps = set.union(Deps0, Deps1).
|
|
|
|
% Note that we go to some effort in this module to stop dependency
|
|
% calculation as soon as possible if there are errors.
|
|
% This is important, because the calls to get_module_dependencies from
|
|
% the dependency calculation predicates can result in every module in
|
|
% the program being read.
|
|
%
|
|
:- func combine_deps(find_module_deps(T)::in(find_module_deps),
|
|
find_module_deps(T)::in(find_module_deps)) =
|
|
(find_module_deps(T)::out(find_module_deps)) is det.
|
|
|
|
combine_deps(FindDeps1, FindDeps2) =
|
|
combine_deps_2(FindDeps1, FindDeps2).
|
|
|
|
:- pred combine_deps_2(
|
|
find_module_deps(T)::in(find_module_deps),
|
|
find_module_deps(T)::in(find_module_deps),
|
|
globals::in, module_index::in, bool::out, deps_set(T)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
combine_deps_2(FindDeps1, FindDeps2, Globals, ModuleIndex, Success, Deps,
|
|
!Info, !IO) :-
|
|
FindDeps1(Globals, ModuleIndex, Success1, Deps1, !Info, !IO),
|
|
( if
|
|
Success1 = no,
|
|
!.Info ^ keep_going = no
|
|
then
|
|
Success = no,
|
|
Deps = Deps1
|
|
else
|
|
FindDeps2(Globals, ModuleIndex, Success2, Deps2, !Info, !IO),
|
|
Success = Success1 `and` Success2,
|
|
Deps = union(Deps1, Deps2)
|
|
).
|
|
|
|
:- func combine_deps_list(list(
|
|
find_module_deps(T))::in(list_skel(find_module_deps))) =
|
|
(find_module_deps(T)::out(find_module_deps)) is det.
|
|
|
|
combine_deps_list([]) = no_deps.
|
|
combine_deps_list([FindDeps]) = FindDeps.
|
|
combine_deps_list([FindDeps1, FindDeps2 | FindDepsTail]) =
|
|
combine_deps(FindDeps1, combine_deps_list([FindDeps2 | FindDepsTail])).
|
|
|
|
deps_set_foldl3_maybe_stop_at_error(KeepGoing, P, Globals, Ts,
|
|
Success, !Acc, !Info, !IO) :-
|
|
foldl3_maybe_stop_at_error(KeepGoing, P, Globals, to_sorted_list(Ts),
|
|
Success, !Acc, !Info, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
target_dependencies(_, module_target_source) = no_deps.
|
|
target_dependencies(Globals, module_target_errors) =
|
|
compiled_code_dependencies(Globals).
|
|
target_dependencies(_, module_target_private_interface) =
|
|
interface_file_dependencies.
|
|
target_dependencies(_, module_target_long_interface) =
|
|
interface_file_dependencies.
|
|
target_dependencies(_, module_target_short_interface) =
|
|
interface_file_dependencies.
|
|
target_dependencies(_, module_target_unqualified_short_interface) =
|
|
module_target_source `of` self.
|
|
target_dependencies(_, module_target_track_flags) = no_deps.
|
|
target_dependencies(Globals, module_target_c_header(_)) =
|
|
target_dependencies(Globals, module_target_c_code).
|
|
target_dependencies(Globals, module_target_c_code) =
|
|
compiled_code_dependencies(Globals).
|
|
target_dependencies(Globals, module_target_csharp_code) =
|
|
compiled_code_dependencies(Globals).
|
|
target_dependencies(Globals, module_target_java_code) =
|
|
compiled_code_dependencies(Globals).
|
|
target_dependencies(_, module_target_java_class_code) =
|
|
module_target_java_code `of` self.
|
|
target_dependencies(Globals, module_target_erlang_header) =
|
|
target_dependencies(Globals, module_target_erlang_code).
|
|
target_dependencies(Globals, module_target_erlang_code) =
|
|
compiled_code_dependencies(Globals).
|
|
target_dependencies(_, module_target_erlang_beam_code) =
|
|
combine_deps_list([
|
|
module_target_erlang_code `of` self,
|
|
% The `.erl' file will -include the header files of imported modules.
|
|
module_target_erlang_header `of` direct_imports,
|
|
module_target_erlang_header `of` indirect_imports,
|
|
module_target_erlang_header `of` intermod_imports
|
|
]).
|
|
target_dependencies(Globals, module_target_object_code(PIC)) = Deps :-
|
|
globals.get_target(Globals, CompilationTarget),
|
|
TargetCode = target_to_module_target_code(CompilationTarget, PIC),
|
|
globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
|
|
|
|
% For --highlevel-code, the `.c' file will #include the header
|
|
% file for all imported modules.
|
|
( if
|
|
CompilationTarget = target_c,
|
|
HighLevelCode = yes
|
|
then
|
|
HeaderDeps = combine_deps_list([
|
|
module_target_c_header(header_mih) `of` direct_imports,
|
|
module_target_c_header(header_mih) `of` indirect_imports,
|
|
module_target_c_header(header_mih) `of` parents,
|
|
module_target_c_header(header_mih) `of` intermod_imports
|
|
])
|
|
else
|
|
HeaderDeps = no_deps
|
|
),
|
|
Deps = combine_deps_list([
|
|
TargetCode `of` self,
|
|
module_target_c_header(header_mh) `of` foreign_imports,
|
|
HeaderDeps
|
|
]).
|
|
target_dependencies(_, module_target_intermodule_interface) =
|
|
combine_deps_list([
|
|
module_target_source `of` self,
|
|
module_target_private_interface `of` parents,
|
|
module_target_long_interface `of` non_intermod_direct_imports,
|
|
module_target_short_interface `of` non_intermod_indirect_imports
|
|
]).
|
|
target_dependencies(_, module_target_analysis_registry) =
|
|
combine_deps_list([
|
|
module_target_source `of` self,
|
|
module_target_private_interface `of` parents,
|
|
module_target_long_interface `of` non_intermod_direct_imports,
|
|
module_target_short_interface `of` non_intermod_indirect_imports,
|
|
module_target_intermodule_interface `of` direct_imports,
|
|
module_target_intermodule_interface `of` indirect_imports,
|
|
module_target_intermodule_interface `of` intermod_imports
|
|
]).
|
|
target_dependencies(Globals, module_target_foreign_object(PIC, _)) =
|
|
get_foreign_deps(Globals, PIC).
|
|
target_dependencies(Globals, module_target_fact_table_object(PIC, _)) =
|
|
get_foreign_deps(Globals, PIC).
|
|
target_dependencies(_, module_target_xml_doc) =
|
|
combine_deps_list([
|
|
module_target_source `of` self,
|
|
module_target_private_interface `of` parents,
|
|
module_target_long_interface `of` non_intermod_direct_imports,
|
|
module_target_short_interface `of` non_intermod_indirect_imports
|
|
]).
|
|
|
|
:- func get_foreign_deps(globals::in, pic::in) =
|
|
(find_module_deps(dependency_file_index)::out(find_module_deps)) is det.
|
|
|
|
get_foreign_deps(Globals, PIC) = Deps :-
|
|
globals.get_target(Globals, CompilationTarget),
|
|
TargetCode = target_to_module_target_code(CompilationTarget, PIC),
|
|
Deps = combine_deps_list([
|
|
TargetCode `of` self
|
|
]).
|
|
|
|
:- func target_to_module_target_code(compilation_target, pic)
|
|
= module_target_type.
|
|
|
|
target_to_module_target_code(_CompilationTarget, _PIC) = TargetCode :-
|
|
% XXX it looks wrong to be returning module_target_c_code for
|
|
% all compilation targets.
|
|
TargetCode = module_target_c_code.
|
|
|
|
:- func interface_file_dependencies =
|
|
(find_module_deps(dependency_file_index)::out(find_module_deps)) is det.
|
|
|
|
interface_file_dependencies =
|
|
combine_deps_list([
|
|
module_target_source `of` self,
|
|
module_target_private_interface `of` parents,
|
|
module_target_unqualified_short_interface `of` direct_imports,
|
|
module_target_unqualified_short_interface `of` indirect_imports
|
|
]).
|
|
|
|
:- func compiled_code_dependencies(globals::in) =
|
|
(find_module_deps(dependency_file_index)::out(find_module_deps)) is det.
|
|
|
|
compiled_code_dependencies(Globals) = Deps :-
|
|
globals.lookup_bool_option(Globals, intermodule_optimization, IntermodOpt),
|
|
globals.lookup_bool_option(Globals, intermodule_analysis,
|
|
IntermodAnalysis),
|
|
globals.lookup_bool_option(Globals, track_flags, TrackFlags),
|
|
AnyIntermod = bool.or(IntermodOpt, IntermodAnalysis),
|
|
(
|
|
AnyIntermod = yes,
|
|
Deps0 = combine_deps_list([
|
|
module_target_intermodule_interface `of` self,
|
|
module_target_intermodule_interface `of` intermod_imports,
|
|
map_find_module_deps(imports,
|
|
map_find_module_deps(parents, intermod_imports)),
|
|
base_compiled_code_dependencies(TrackFlags)
|
|
])
|
|
;
|
|
AnyIntermod = no,
|
|
Deps0 = base_compiled_code_dependencies(TrackFlags)
|
|
),
|
|
(
|
|
IntermodAnalysis = yes,
|
|
Deps = combine_deps_list([
|
|
module_target_analysis_registry `of` self,
|
|
module_target_analysis_registry `of` direct_imports,
|
|
Deps0
|
|
])
|
|
;
|
|
IntermodAnalysis = no,
|
|
Deps = Deps0
|
|
).
|
|
|
|
:- func base_compiled_code_dependencies(bool::in) =
|
|
(find_module_deps(dependency_file_index)::out(find_module_deps)) is det.
|
|
|
|
base_compiled_code_dependencies(TrackFlags) = Deps :-
|
|
(
|
|
TrackFlags = yes,
|
|
Deps0 = module_target_track_flags `of` self
|
|
;
|
|
TrackFlags = no,
|
|
Deps0 = no_deps
|
|
),
|
|
Deps = combine_deps_list([
|
|
module_target_source `of` self,
|
|
fact_table_files `files_of` self,
|
|
foreign_include_files `files_of` self,
|
|
map_find_module_deps(imports, self),
|
|
Deps0
|
|
]).
|
|
|
|
:- func imports =
|
|
(find_module_deps(dependency_file_index)::out(find_module_deps)) is det.
|
|
|
|
imports = combine_deps_list([
|
|
module_target_private_interface `of` parents,
|
|
module_target_long_interface `of` direct_imports,
|
|
module_target_short_interface `of` indirect_imports
|
|
]).
|
|
|
|
:- func of(module_target_type, find_module_deps(module_index)) =
|
|
find_module_deps(dependency_file_index).
|
|
:- mode of(in, in(find_module_deps)) = out(find_module_deps) is det.
|
|
|
|
of(FileType, FindDeps) =
|
|
of_2(FileType, FindDeps).
|
|
|
|
:- pred of_2(module_target_type::in,
|
|
find_module_deps(module_index)::in(find_module_deps),
|
|
globals::in, module_index::in, bool::out,
|
|
deps_set(dependency_file_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
of_2(FileType, FindDeps, Globals, ModuleIndex, Success, TargetFiles,
|
|
!Info, !IO) :-
|
|
FindDeps(Globals, ModuleIndex, Success, ModuleIndexs, !Info, !IO),
|
|
foldl2(of_3(FileType), ModuleIndexs, init, TargetFiles, !Info).
|
|
|
|
:- pred of_3(module_target_type::in, module_index::in,
|
|
deps_set(dependency_file_index)::in, deps_set(dependency_file_index)::out,
|
|
make_info::in, make_info::out) is det.
|
|
|
|
of_3(FileType, ModuleIndex, !Set, !Info) :-
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
TargetFile = dep_target(target_file(ModuleName, FileType)),
|
|
dependency_file_to_index(TargetFile, TargetFileIndex, !Info),
|
|
insert(TargetFileIndex, !Set).
|
|
|
|
:- func files_of(find_module_deps_plain_set(dependency_file),
|
|
find_module_deps(module_index)) = find_module_deps(dependency_file_index).
|
|
:- mode files_of(in(find_module_deps_plain_set), in(find_module_deps))
|
|
= out(find_module_deps) is det.
|
|
|
|
files_of(FindFiles, FindDeps) =
|
|
files_of_2(FindFiles, FindDeps).
|
|
|
|
:- pred files_of_2(
|
|
find_module_deps_plain_set(dependency_file)::
|
|
in(find_module_deps_plain_set),
|
|
find_module_deps(module_index)::in(find_module_deps),
|
|
globals::in, module_index::in, bool::out,
|
|
deps_set(dependency_file_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
files_of_2(FindFiles, FindDeps, Globals, ModuleIndex, Success, DepIndices,
|
|
!Info, !IO) :-
|
|
KeepGoing = !.Info ^ keep_going,
|
|
FindDeps(Globals, ModuleIndex, Success0, ModuleIndices, !Info, !IO),
|
|
( if
|
|
Success0 = no,
|
|
KeepGoing = no
|
|
then
|
|
Success = no,
|
|
DepIndices = init
|
|
else
|
|
deps_set_foldl3_maybe_stop_at_error(KeepGoing,
|
|
union_deps_plain_set(FindFiles),
|
|
Globals, ModuleIndices, Success1, init, FileNames, !Info, !IO),
|
|
Success = Success0 `and` Success1,
|
|
dependency_files_to_index_set(set.to_sorted_list(FileNames),
|
|
DepIndices, !Info)
|
|
).
|
|
|
|
:- pred map_find_module_deps(
|
|
find_module_deps(T)::in(find_module_deps),
|
|
find_module_deps(module_index)::in(find_module_deps),
|
|
globals::in, module_index::in, bool::out, deps_set(T)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
map_find_module_deps(FindDeps2, FindDeps1, Globals, ModuleIndex, Success,
|
|
Result, !Info, !IO) :-
|
|
KeepGoing = !.Info ^ keep_going,
|
|
FindDeps1(Globals, ModuleIndex, Success0, Modules0, !Info, !IO),
|
|
( if
|
|
Success0 = no,
|
|
KeepGoing = no
|
|
then
|
|
Success = no,
|
|
Result = init
|
|
else
|
|
deps_set_foldl3_maybe_stop_at_error(KeepGoing, union_deps(FindDeps2),
|
|
Globals, Modules0, Success1, init, Result, !Info, !IO),
|
|
Success = Success0 `and` Success1
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred no_deps(globals::in, module_index::in, bool::out, deps_set(T)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
no_deps(_, _, yes, init, !Info, !IO).
|
|
|
|
:- pred self(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
self(_Globals, ModuleIndex, yes, make_singleton_set(ModuleIndex), !Info, !IO).
|
|
|
|
:- pred parents(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
parents(_Globals, ModuleIndex, yes, AncestorIndices, !Info, !IO) :-
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
Ancestors = get_ancestors(ModuleName),
|
|
module_names_to_index_set(Ancestors, AncestorIndices, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type cached_direct_imports == map(module_index, module_deps_result).
|
|
|
|
init_cached_direct_imports = map.init.
|
|
|
|
:- pred direct_imports(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
direct_imports(Globals, ModuleIndex, Success, Modules, !Info, !IO) :-
|
|
( if Result0 = !.Info ^ cached_direct_imports ^ elem(ModuleIndex) then
|
|
Result0 = deps_result(Success, Modules)
|
|
else
|
|
KeepGoing = !.Info ^ keep_going,
|
|
|
|
non_intermod_direct_imports(Globals, ModuleIndex, Success0, Modules0,
|
|
!Info, !IO),
|
|
( if
|
|
Success0 = no,
|
|
KeepGoing = no
|
|
then
|
|
Success = no,
|
|
Modules = init
|
|
else
|
|
% We also read `.int' files for the modules for which we read
|
|
% `.opt' files, and for the modules imported by those modules.
|
|
intermod_imports(Globals, ModuleIndex, Success1, IntermodModules,
|
|
!Info, !IO),
|
|
( if
|
|
Success1 = no,
|
|
KeepGoing = no
|
|
then
|
|
Success = no,
|
|
Modules = init
|
|
else
|
|
deps_set_foldl3_maybe_stop_at_error(!.Info ^ keep_going,
|
|
union_deps(non_intermod_direct_imports), Globals,
|
|
IntermodModules, Success2,
|
|
union(Modules0, IntermodModules), Modules1,
|
|
!Info, !IO),
|
|
Success = Success0 `and` Success1 `and` Success2,
|
|
Modules = delete(Modules1, ModuleIndex)
|
|
)
|
|
),
|
|
!Info ^ cached_direct_imports ^ elem(ModuleIndex)
|
|
:= deps_result(Success, Modules)
|
|
).
|
|
|
|
% Return the modules for which `.int' files are read in a compilation
|
|
% which does not use `--intermodule-optimization'.
|
|
%
|
|
:- pred non_intermod_direct_imports(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
non_intermod_direct_imports(Globals, ModuleIndex, Success, Modules,
|
|
!Info, !IO) :-
|
|
( if
|
|
!.Info ^ cached_non_intermod_direct_imports ^ elem(ModuleIndex)
|
|
= Result
|
|
then
|
|
Result = deps_result(Success, Modules)
|
|
else
|
|
non_intermod_direct_imports_2(Globals, ModuleIndex, Success, Modules,
|
|
!Info, !IO),
|
|
!Info ^ cached_non_intermod_direct_imports ^ elem(ModuleIndex)
|
|
:= deps_result(Success, Modules)
|
|
).
|
|
|
|
:- pred non_intermod_direct_imports_2(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
non_intermod_direct_imports_2(Globals, ModuleIndex, Success, Modules,
|
|
!Info, !IO) :-
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
get_module_dependencies(Globals, ModuleName, MaybeModuleAndImports,
|
|
!Info, !IO),
|
|
(
|
|
MaybeModuleAndImports = yes(ModuleAndImports),
|
|
|
|
% Find the direct imports of this module (modules for which we will
|
|
% read the `.int' files).
|
|
%
|
|
% Note that we need to do this both for the imports of this module
|
|
% and for the imports of its ancestors. This is because if this module
|
|
% is a submodule, then it may depend on things imported only by its
|
|
% ancestors.
|
|
%
|
|
set.to_sorted_list(ModuleAndImports ^ mai_int_deps, IntDeps),
|
|
set.to_sorted_list(ModuleAndImports ^ mai_imp_deps, ImpDeps),
|
|
module_names_to_index_set(IntDeps, DepsInt, !Info),
|
|
module_names_to_index_set(ImpDeps, DepsImp, !Info),
|
|
Modules0 = union(DepsInt, DepsImp),
|
|
(
|
|
ModuleName = qualified(ParentModule, _),
|
|
module_name_to_index(ParentModule, ParentIndex, !Info),
|
|
non_intermod_direct_imports(Globals, ParentIndex, Success,
|
|
ParentImports, !Info, !IO),
|
|
Modules = union(ParentImports, Modules0)
|
|
;
|
|
ModuleName = unqualified(_),
|
|
Success = yes,
|
|
Modules = Modules0
|
|
)
|
|
;
|
|
MaybeModuleAndImports = no,
|
|
Success = no,
|
|
Modules = init
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Return the list of modules for which we should read `.int2' files.
|
|
%
|
|
:- pred indirect_imports(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
indirect_imports(Globals, ModuleIndex, Success, Modules, !Info, !IO) :-
|
|
indirect_imports_2(Globals, direct_imports, ModuleIndex,
|
|
Success, Modules, !Info, !IO).
|
|
|
|
% Return the list of modules for which we should read `.int2' files,
|
|
% ignoring those which need to be read as a result of importing modules
|
|
% imported by a `.opt' file.
|
|
%
|
|
:- pred non_intermod_indirect_imports(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
non_intermod_indirect_imports(Globals, ModuleIndex, Success, Modules,
|
|
!Info, !IO) :-
|
|
indirect_imports_2(Globals, non_intermod_direct_imports, ModuleIndex,
|
|
Success, Modules, !Info, !IO).
|
|
|
|
:- pred indirect_imports_2(globals::in,
|
|
find_module_deps(module_index)::in(find_module_deps),
|
|
module_index::in, bool::out, deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
indirect_imports_2(Globals, FindDirectImports, ModuleIndex, Success,
|
|
IndirectImports, !Info, !IO) :-
|
|
FindDirectImports(Globals, ModuleIndex, DirectSuccess, DirectImports,
|
|
!Info, !IO),
|
|
% XXX The original version of this code by stayl had the line assigning
|
|
% to KeepGoing textually *before* the call to FindDirectImports, but
|
|
% looked up the keep_going in the version of !Info *after* that call.
|
|
KeepGoing = !.Info ^ keep_going,
|
|
( if
|
|
DirectSuccess = no,
|
|
KeepGoing = no
|
|
then
|
|
Success = no,
|
|
IndirectImports = init
|
|
else
|
|
deps_set_foldl3_maybe_stop_at_error(!.Info ^ keep_going,
|
|
union_deps(find_transitive_implementation_imports), Globals,
|
|
DirectImports, IndirectSuccess,
|
|
init, IndirectImports0, !Info, !IO),
|
|
IndirectImports = difference(
|
|
delete(IndirectImports0, ModuleIndex),
|
|
DirectImports),
|
|
Success = DirectSuccess `and` IndirectSuccess
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Return the list of modules for which we should read `.opt' files.
|
|
%
|
|
:- pred intermod_imports(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out, io::di, io::uo)
|
|
is det.
|
|
|
|
intermod_imports(Globals, ModuleIndex, Success, Modules, !Info, !IO) :-
|
|
globals.get_any_intermod(Globals, AnyIntermod),
|
|
(
|
|
AnyIntermod = yes,
|
|
globals.lookup_bool_option(Globals, read_opt_files_transitively,
|
|
Transitive),
|
|
(
|
|
Transitive = yes,
|
|
find_transitive_implementation_imports(Globals, ModuleIndex,
|
|
Success, Modules, !Info, !IO)
|
|
;
|
|
Transitive = no,
|
|
non_intermod_direct_imports(Globals, ModuleIndex, Success,
|
|
Modules, !Info, !IO)
|
|
)
|
|
;
|
|
AnyIntermod = no,
|
|
Success = yes,
|
|
Modules = init
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type cached_foreign_imports == map(module_index, module_deps_result).
|
|
|
|
init_cached_foreign_imports = map.init.
|
|
|
|
:- pred foreign_imports(globals::in, module_index::in, bool::out,
|
|
deps_set(module_index)::out, make_info::in, make_info::out, io::di, io::uo)
|
|
is det.
|
|
|
|
foreign_imports(Globals, ModuleIndex, Success, Modules, !Info, !IO) :-
|
|
% The object file depends on the header files for the modules
|
|
% mentioned in `:- pragma foreign_import_module' declarations
|
|
% in the current module and the `.opt' files it imports.
|
|
|
|
globals.get_backend_foreign_languages(Globals, Languages),
|
|
intermod_imports(Globals, ModuleIndex, IntermodSuccess, IntermodModules,
|
|
!Info, !IO),
|
|
deps_set_foldl3_maybe_stop_at_error(!.Info ^ keep_going,
|
|
union_deps(find_module_foreign_imports(set.list_to_set(Languages))),
|
|
Globals, insert(IntermodModules, ModuleIndex),
|
|
ForeignSuccess, init, Modules, !Info, !IO),
|
|
Success = IntermodSuccess `and` ForeignSuccess.
|
|
|
|
:- pred find_module_foreign_imports(set(foreign_language)::in,
|
|
globals::in, module_index::in, bool::out, deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
find_module_foreign_imports(Languages, Globals, ModuleIndex, Success,
|
|
ForeignModules, !Info, !IO) :-
|
|
find_transitive_implementation_imports(Globals, ModuleIndex, Success0,
|
|
ImportedModules, !Info, !IO),
|
|
(
|
|
Success0 = yes,
|
|
deps_set_foldl3_maybe_stop_at_error(!.Info ^ keep_going,
|
|
union_deps(find_module_foreign_imports_2(Languages)),
|
|
Globals, insert(ImportedModules, ModuleIndex),
|
|
Success, init, ForeignModules, !Info, !IO)
|
|
;
|
|
Success0 = no,
|
|
Success = no,
|
|
ForeignModules = init
|
|
).
|
|
|
|
:- pred find_module_foreign_imports_2(set(foreign_language)::in,
|
|
globals::in, module_index::in, bool::out, deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
find_module_foreign_imports_2(Languages, Globals, ModuleIndex, Success,
|
|
ForeignModules, !Info, !IO) :-
|
|
% Languages should be constant for the duration of the process.
|
|
( if Result0 = !.Info ^ cached_foreign_imports ^ elem(ModuleIndex) then
|
|
Result0 = deps_result(Success, ForeignModules)
|
|
else
|
|
find_module_foreign_imports_3(Languages, Globals, ModuleIndex,
|
|
Success, ForeignModules, !Info, !IO),
|
|
!Info ^ cached_foreign_imports ^ elem(ModuleIndex)
|
|
:= deps_result(Success, ForeignModules)
|
|
).
|
|
|
|
:- pred find_module_foreign_imports_3(set(foreign_language)::in,
|
|
globals::in, module_index::in, bool::out, deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
find_module_foreign_imports_3(Languages, Globals, ModuleIndex,
|
|
Success, ForeignModules, !Info, !IO) :-
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
|
|
(
|
|
MaybeImports = yes(Imports),
|
|
ForeignModuleInfos = Imports ^ mai_foreign_import_modules,
|
|
LangForeignModuleNameSets =
|
|
set.map(get_lang_foreign_import_modules(ForeignModuleInfos),
|
|
Languages),
|
|
set.power_union(LangForeignModuleNameSets, ForeignModuleNameSet),
|
|
module_names_to_index_set(set.to_sorted_list(ForeignModuleNameSet),
|
|
ForeignModules, !Info),
|
|
Success = yes
|
|
;
|
|
MaybeImports = no,
|
|
ForeignModules = init,
|
|
Success = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred fact_table_files(globals::in, module_index::in,
|
|
bool::out, set(dependency_file)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
fact_table_files(Globals, ModuleIndex, Success, Files, !Info, !IO) :-
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
|
|
(
|
|
MaybeImports = yes(Imports),
|
|
Success = yes,
|
|
FilesList = map((func(File) = dep_file(File, no)),
|
|
Imports ^ mai_fact_table_deps),
|
|
Files = set.list_to_set(FilesList)
|
|
;
|
|
MaybeImports = no,
|
|
Success = no,
|
|
Files = init
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred foreign_include_files(globals::in, module_index::in,
|
|
bool::out, set(dependency_file)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
foreign_include_files(Globals, ModuleIndex, Success, Files, !Info, !IO) :-
|
|
globals.get_backend_foreign_languages(Globals, Languages),
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
|
|
(
|
|
MaybeImports = yes(Imports),
|
|
Success = yes,
|
|
SourceFileName = Imports ^ mai_source_file_name,
|
|
ForeignIncludeFilesCord = Imports ^ mai_foreign_include_files,
|
|
FilesList = get_foreign_include_files(set.from_list(Languages),
|
|
SourceFileName, cord.list(ForeignIncludeFilesCord)),
|
|
Files = set.from_list(FilesList)
|
|
;
|
|
MaybeImports = no,
|
|
Success = no,
|
|
Files = set.init
|
|
).
|
|
|
|
:- func get_foreign_include_files(set(foreign_language), file_name,
|
|
list(foreign_include_file_info)) = list(dependency_file).
|
|
|
|
get_foreign_include_files(Languages, SourceFileName, ForeignIncludes)
|
|
= Files :-
|
|
list.filter_map(get_foreign_include_files_2(Languages, SourceFileName),
|
|
ForeignIncludes, Files).
|
|
|
|
:- pred get_foreign_include_files_2(set(foreign_language)::in, file_name::in,
|
|
foreign_include_file_info::in, dependency_file::out) is semidet.
|
|
|
|
get_foreign_include_files_2(Languages, SourceFileName, ForeignInclude, File) :-
|
|
ForeignInclude = foreign_include_file_info(Language, IncludeFileName),
|
|
set.member(Language, Languages),
|
|
make_include_file_path(SourceFileName, IncludeFileName, IncludePath),
|
|
File = dep_file(IncludePath, no).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type transitive_dependencies_root
|
|
---> transitive_dependencies_root(
|
|
module_index,
|
|
transitive_dependencies_type,
|
|
module_locn
|
|
).
|
|
|
|
:- type transitive_dependencies_type
|
|
---> interface_imports
|
|
; all_imports % every import_module and use_module
|
|
; all_dependencies. % all_imports plus every include_module
|
|
|
|
:- type module_locn
|
|
---> local_module % The source file for the module is in
|
|
% the current directory.
|
|
; any_module.
|
|
|
|
:- type cached_transitive_dependencies ==
|
|
map(transitive_dependencies_root, deps_result(module_index)).
|
|
|
|
init_cached_transitive_dependencies = map.init.
|
|
|
|
find_reachable_local_modules(Globals, ModuleName, Success, Modules,
|
|
!Info, !IO) :-
|
|
module_name_to_index(ModuleName, ModuleIndex, !Info),
|
|
find_transitive_module_dependencies(Globals, all_dependencies,
|
|
local_module, ModuleIndex, Success, Modules0, !Info, !IO),
|
|
module_index_set_to_plain_set(!.Info, Modules0, Modules).
|
|
|
|
:- pred find_transitive_implementation_imports(globals::in, module_index::in,
|
|
bool::out, deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
find_transitive_implementation_imports(Globals, ModuleIndex, Success, Modules,
|
|
!Info, !IO) :-
|
|
find_transitive_module_dependencies(Globals, all_imports, any_module,
|
|
ModuleIndex, Success, Modules0, !Info, !IO),
|
|
Modules = insert(Modules0, ModuleIndex).
|
|
|
|
:- pred find_transitive_module_dependencies(globals::in,
|
|
transitive_dependencies_type::in, module_locn::in, module_index::in,
|
|
bool::out, deps_set(module_index)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
find_transitive_module_dependencies(Globals, DependenciesType, ModuleLocn,
|
|
ModuleIndex, Success, Modules, !Info, !IO) :-
|
|
DepsRoot = transitive_dependencies_root(ModuleIndex, DependenciesType,
|
|
ModuleLocn),
|
|
CachedTransDeps = !.Info ^ cached_transitive_dependencies,
|
|
( if map.search(CachedTransDeps, DepsRoot, Result0) then
|
|
Result0 = deps_result(Success, Modules)
|
|
else
|
|
globals.lookup_bool_option(Globals, keep_going, KeepGoing),
|
|
find_transitive_module_dependencies_2(KeepGoing, DependenciesType,
|
|
ModuleLocn, Globals, ModuleIndex, Success, init, Modules,
|
|
!Info, !IO),
|
|
!Info ^ cached_transitive_dependencies ^ elem(DepsRoot)
|
|
:= deps_result(Success, Modules)
|
|
).
|
|
|
|
:- pred find_transitive_module_dependencies_2(bool::in,
|
|
transitive_dependencies_type::in, module_locn::in, globals::in,
|
|
module_index::in, bool::out,
|
|
deps_set(module_index)::in, deps_set(module_index)::out,
|
|
make_info::in, make_info::out, io::di, io::uo) is det.
|
|
|
|
find_transitive_module_dependencies_2(KeepGoing, DependenciesType, ModuleLocn,
|
|
Globals, ModuleIndex, Success, Modules0, Modules, !Info, !IO) :-
|
|
( if
|
|
member(ModuleIndex, Modules0)
|
|
then
|
|
Success = yes,
|
|
Modules = Modules0
|
|
else if
|
|
DepsRoot = transitive_dependencies_root(ModuleIndex,
|
|
DependenciesType, ModuleLocn),
|
|
Result0 = !.Info ^ cached_transitive_dependencies ^ elem(DepsRoot)
|
|
then
|
|
Result0 = deps_result(Success, Modules1),
|
|
Modules = union(Modules0, Modules1)
|
|
else
|
|
module_index_to_name(!.Info, ModuleIndex, ModuleName),
|
|
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
|
|
(
|
|
MaybeImports = yes(Imports),
|
|
( if
|
|
(
|
|
ModuleLocn = any_module
|
|
;
|
|
ModuleLocn = local_module,
|
|
Imports ^ mai_module_dir = dir.this_directory
|
|
)
|
|
then
|
|
(
|
|
% Parents don't need to be considered here.
|
|
% Anywhere the interface of the child module is needed,
|
|
% the parent must also have been imported.
|
|
DependenciesType = interface_imports,
|
|
ImportsToCheck = Imports ^ mai_int_deps
|
|
;
|
|
DependenciesType = all_dependencies,
|
|
ImportsToCheck = set.union_list([
|
|
Imports ^ mai_int_deps,
|
|
Imports ^ mai_imp_deps,
|
|
Imports ^ mai_parent_deps,
|
|
Imports ^ mai_children,
|
|
get_all_foreign_import_modules(
|
|
Imports ^ mai_foreign_import_modules)
|
|
])
|
|
;
|
|
DependenciesType = all_imports,
|
|
ImportsToCheck = set.union_list([
|
|
Imports ^ mai_int_deps,
|
|
Imports ^ mai_imp_deps,
|
|
Imports ^ mai_parent_deps,
|
|
get_all_foreign_import_modules(
|
|
Imports ^ mai_foreign_import_modules)
|
|
])
|
|
),
|
|
module_names_to_index_set(set.to_sorted_list(ImportsToCheck),
|
|
ImportsToCheckSet, !Info),
|
|
ImportingModule = !.Info ^ importing_module,
|
|
!Info ^ importing_module := yes(ModuleName),
|
|
Modules1 = insert(Modules0, ModuleIndex),
|
|
deps_set_foldl3_maybe_stop_at_error(KeepGoing,
|
|
find_transitive_module_dependencies_2(KeepGoing,
|
|
DependenciesType, ModuleLocn),
|
|
Globals, ImportsToCheckSet, Success, Modules1, Modules,
|
|
!Info, !IO),
|
|
!Info ^ importing_module := ImportingModule
|
|
else
|
|
Success = yes,
|
|
Modules = Modules0
|
|
)
|
|
;
|
|
MaybeImports = no,
|
|
Success = no,
|
|
Modules = Modules0
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
remove_nested_modules(Globals, Modules0, Modules, !Info, !IO) :-
|
|
list.foldl3(collect_nested_modules(Globals), Modules0,
|
|
set.init, NestedModules, !Info, !IO),
|
|
list.negated_filter(set.contains(NestedModules), Modules0, Modules).
|
|
|
|
:- pred collect_nested_modules(globals::in, module_name::in,
|
|
set(module_name)::in, set(module_name)::out, make_info::in, make_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
collect_nested_modules(Globals, ModuleName, !NestedModules, !Info, !IO) :-
|
|
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
|
|
(
|
|
MaybeImports = yes(Imports),
|
|
set.union(Imports ^ mai_nested_children, !NestedModules)
|
|
;
|
|
MaybeImports = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
make_local_module_id_options(Globals, ModuleName, Success, Options,
|
|
!Info, !IO) :-
|
|
find_reachable_local_modules(Globals, ModuleName, Success, LocalModules,
|
|
!Info, !IO),
|
|
set.fold(make_local_module_id_option, LocalModules, [], Options).
|
|
|
|
:- pred make_local_module_id_option(module_name::in, list(string)::in,
|
|
list(string)::out) is det.
|
|
|
|
make_local_module_id_option(ModuleName, Opts0, Opts) :-
|
|
ModuleNameStr = sym_name_to_string(ModuleName),
|
|
Opts = ["--local-module-id", ModuleNameStr | Opts0].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred check_dependencies_debug_unbuilt(globals::in, file_name::in,
|
|
assoc_list(dependency_file, dependency_status)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
check_dependencies_debug_unbuilt(Globals, TargetFileName, UnbuiltDependencies,
|
|
!IO) :-
|
|
io.write_string(TargetFileName, !IO),
|
|
io.write_string(": dependencies could not be built.\n\t", !IO),
|
|
io.write_list(UnbuiltDependencies, ",\n\t",
|
|
make_write_target_dependency_status(Globals), !IO),
|
|
io.nl(!IO).
|
|
|
|
:- pred make_write_target_dependency_status(globals::in,
|
|
pair(dependency_file, dependency_status)::in, io::di, io::uo) is det.
|
|
|
|
make_write_target_dependency_status(Globals, DepTarget - DepStatus, !IO) :-
|
|
(
|
|
DepStatus = deps_status_not_considered,
|
|
DepStatusStr = "deps_status_not_considered"
|
|
;
|
|
DepStatus = deps_status_being_built,
|
|
DepStatusStr = "deps_status_being_built"
|
|
;
|
|
DepStatus = deps_status_up_to_date,
|
|
DepStatusStr = "deps_status_up_to_date"
|
|
;
|
|
DepStatus = deps_status_error,
|
|
DepStatusStr = "deps_status_error"
|
|
),
|
|
make_write_dependency_file(Globals, DepTarget, !IO),
|
|
io.write_string(" - ", !IO),
|
|
io.write_string(DepStatusStr, !IO).
|
|
|
|
check_dependencies(Globals, TargetFileName, MaybeTimestamp, BuildDepsSucceeded,
|
|
DepFiles, DepsResult, !Info, !IO) :-
|
|
list.map_foldl2(dependency_status(Globals), DepFiles, DepStatusList,
|
|
!Info, !IO),
|
|
assoc_list.from_corresponding_lists(DepFiles, DepStatusList, DepStatusAL),
|
|
list.filter(
|
|
( pred((_ - DepStatus)::in) is semidet :-
|
|
DepStatus \= deps_status_up_to_date
|
|
), DepStatusAL, UnbuiltDependencies),
|
|
(
|
|
UnbuiltDependencies = [_ | _],
|
|
debug_make_msg(Globals,
|
|
check_dependencies_debug_unbuilt(Globals, TargetFileName,
|
|
UnbuiltDependencies),
|
|
!IO),
|
|
DepsResult = deps_error
|
|
;
|
|
UnbuiltDependencies = [],
|
|
debug_make_msg(Globals,
|
|
io.write_string(TargetFileName ++ ": finished dependencies\n"),
|
|
!IO),
|
|
list.map_foldl2(get_dependency_timestamp(Globals), DepFiles,
|
|
DepTimestamps, !Info, !IO),
|
|
|
|
check_dependency_timestamps(Globals, TargetFileName, MaybeTimestamp,
|
|
BuildDepsSucceeded, DepFiles, make_write_dependency_file(Globals),
|
|
DepTimestamps, DepsResult, !IO)
|
|
).
|
|
|
|
:- pred check_dependencies_timestamps_write_missing_deps(file_name::in,
|
|
bool::in, list(File)::in, pred(File, io, io)::(pred(in, di, uo) is det),
|
|
list(maybe_error(timestamp))::in, io::di, io::uo) is det.
|
|
|
|
check_dependencies_timestamps_write_missing_deps(TargetFileName,
|
|
BuildDepsSucceeded, DepFiles, WriteDepFile, DepTimestamps, !IO) :-
|
|
assoc_list.from_corresponding_lists(DepFiles, DepTimestamps,
|
|
DepTimestampAL),
|
|
list.filter_map(
|
|
( pred(Pair::in, DepFile::out) is semidet :-
|
|
Pair = DepFile - error(_)
|
|
), DepTimestampAL, ErrorDeps0),
|
|
list.sort(ErrorDeps0, ErrorDeps),
|
|
io.write_string("** dependencies for `", !IO),
|
|
io.write_string(TargetFileName, !IO),
|
|
io.write_string("' do not exist: ", !IO),
|
|
io.write_list(ErrorDeps, ", ", WriteDepFile, !IO),
|
|
io.nl(!IO),
|
|
(
|
|
BuildDepsSucceeded = yes,
|
|
io.write_string("** This indicates a bug in `mmc --make'.\n", !IO)
|
|
;
|
|
BuildDepsSucceeded = no
|
|
).
|
|
|
|
check_dependency_timestamps(Globals, TargetFileName, MaybeTimestamp,
|
|
BuildDepsSucceeded, DepFiles, WriteDepFile, DepTimestamps,
|
|
DepsResult, !IO) :-
|
|
(
|
|
MaybeTimestamp = error(_),
|
|
DepsResult = deps_out_of_date,
|
|
debug_make_msg(Globals,
|
|
io.write_string(TargetFileName ++ " does not exist.\n"), !IO)
|
|
;
|
|
MaybeTimestamp = ok(Timestamp),
|
|
( if error_in_timestamps(DepTimestamps) then
|
|
DepsResult = deps_error,
|
|
WriteMissingDeps =
|
|
check_dependencies_timestamps_write_missing_deps(
|
|
TargetFileName, BuildDepsSucceeded, DepFiles,
|
|
WriteDepFile, DepTimestamps),
|
|
(
|
|
BuildDepsSucceeded = yes,
|
|
|
|
% Something has gone wrong -- building the target has
|
|
% succeeded, but there are some files missing.
|
|
% Report an error.
|
|
|
|
WriteMissingDeps(!IO)
|
|
;
|
|
BuildDepsSucceeded = no,
|
|
debug_make_msg(Globals, WriteMissingDeps, !IO)
|
|
)
|
|
else
|
|
globals.lookup_bool_option(Globals, rebuild, Rebuild),
|
|
(
|
|
Rebuild = yes,
|
|
% With `--rebuild', we always consider the target to be
|
|
% out-of-date, regardless of the timestamps of its
|
|
% dependencies.
|
|
DepsResult = deps_out_of_date
|
|
;
|
|
Rebuild = no,
|
|
( if newer_timestamp(DepTimestamps, Timestamp) then
|
|
debug_newer_dependencies(Globals, TargetFileName,
|
|
MaybeTimestamp, DepFiles, DepTimestamps, !IO),
|
|
DepsResult = deps_out_of_date
|
|
else
|
|
DepsResult = deps_up_to_date
|
|
)
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred error_in_timestamps(list(maybe_error(timestamp))::in) is semidet.
|
|
|
|
error_in_timestamps([H | T]) :-
|
|
( H = error(_)
|
|
; error_in_timestamps(T)
|
|
).
|
|
|
|
:- pred newer_timestamp(list(maybe_error(timestamp))::in, timestamp::in)
|
|
is semidet.
|
|
|
|
newer_timestamp([H | T], Timestamp) :-
|
|
(
|
|
H = ok(DepTimestamp),
|
|
compare((>), DepTimestamp, Timestamp)
|
|
;
|
|
newer_timestamp(T, Timestamp)
|
|
).
|
|
|
|
:- pred debug_newer_dependencies(globals::in, string::in,
|
|
maybe_error(timestamp)::in, list(T)::in, list(maybe_error(timestamp))::in,
|
|
io::di, io::uo) is det.
|
|
|
|
debug_newer_dependencies(Globals, TargetFileName, MaybeTimestamp,
|
|
DepFiles, DepTimestamps, !IO) :-
|
|
debug_make_msg(Globals,
|
|
debug_newer_dependencies_2(TargetFileName, MaybeTimestamp,
|
|
DepFiles, DepTimestamps),
|
|
!IO).
|
|
|
|
:- pred debug_newer_dependencies_2(string::in, maybe_error(timestamp)::in,
|
|
list(T)::in, list(maybe_error(timestamp))::in, io::di, io::uo) is det.
|
|
|
|
debug_newer_dependencies_2(TargetFileName, MaybeTimestamp,
|
|
DepFiles, DepTimestamps, !IO) :-
|
|
io.write_string(TargetFileName, !IO),
|
|
io.write_string(" [", !IO),
|
|
io.write(MaybeTimestamp, !IO),
|
|
io.write_string("]: newer dependencies:\n", !IO),
|
|
assoc_list.from_corresponding_lists(DepFiles, DepTimestamps,
|
|
DepTimestampAL),
|
|
list.filter(
|
|
( pred((_DepFile - MaybeDepTimestamp)::in) is semidet :-
|
|
(
|
|
MaybeDepTimestamp = error(_)
|
|
;
|
|
MaybeDepTimestamp = ok(DepTimestamp),
|
|
MaybeTimestamp = ok(Timestamp),
|
|
compare((>), DepTimestamp, Timestamp)
|
|
)
|
|
), DepTimestampAL, NewerDepsAL0),
|
|
list.sort(NewerDepsAL0, NewerDepsAL),
|
|
make_write_dependency_file_and_timestamp_list(NewerDepsAL, !IO).
|
|
|
|
:- pred make_write_dependency_file_and_timestamp_list(
|
|
assoc_list(T, maybe_error(timestamp))::in, io::di, io::uo) is det.
|
|
|
|
make_write_dependency_file_and_timestamp_list([], !IO).
|
|
make_write_dependency_file_and_timestamp_list([Head | Tail], !IO) :-
|
|
Head = DepFile - MaybeTimestamp,
|
|
io.write_char('\t', !IO),
|
|
io.write(DepFile, !IO),
|
|
io.write_char(' ', !IO),
|
|
io.write(MaybeTimestamp, !IO),
|
|
io.nl(!IO),
|
|
make_write_dependency_file_and_timestamp_list(Tail, !IO).
|
|
|
|
dependency_status(Globals, Dep, Status, !Info, !IO) :-
|
|
(
|
|
Dep = dep_file(_FileName, _),
|
|
DepStatusMap = !.Info ^ dependency_status,
|
|
( if version_hash_table.search(DepStatusMap, Dep, StatusPrime) then
|
|
Status = StatusPrime
|
|
else
|
|
get_dependency_timestamp(Globals, Dep, MaybeTimestamp, !Info, !IO),
|
|
(
|
|
MaybeTimestamp = ok(_),
|
|
Status = deps_status_up_to_date
|
|
;
|
|
MaybeTimestamp = error(Error),
|
|
Status = deps_status_error,
|
|
io.write_string("** Error: ", !IO),
|
|
io.write_string(Error, !IO),
|
|
io.nl(!IO)
|
|
),
|
|
!Info ^ dependency_status ^ elem(Dep) := Status
|
|
)
|
|
;
|
|
Dep = dep_target(Target),
|
|
Target = target_file(ModuleName, FileType),
|
|
( if
|
|
( FileType = module_target_source
|
|
; FileType = module_target_track_flags
|
|
)
|
|
then
|
|
% Source files are always up-to-date.
|
|
% .track_flags should already have been made, if required,
|
|
% so are also up-to-date.
|
|
ModuleTarget = module_target(module_target_source),
|
|
maybe_warn_up_to_date_target(Globals, ModuleName - ModuleTarget,
|
|
!Info, !IO),
|
|
Status = deps_status_up_to_date
|
|
else if
|
|
DepStatusMap = !.Info ^ dependency_status,
|
|
version_hash_table.search(DepStatusMap, Dep, StatusPrime)
|
|
then
|
|
Status = StatusPrime
|
|
else
|
|
get_module_dependencies(Globals, ModuleName, MaybeImports,
|
|
!Info, !IO),
|
|
(
|
|
MaybeImports = no,
|
|
Status = deps_status_error
|
|
;
|
|
MaybeImports = yes(Imports),
|
|
( if Imports ^ mai_module_dir = dir.this_directory then
|
|
Status = deps_status_not_considered
|
|
else
|
|
% Targets from libraries are always considered to be
|
|
% up-to-date if they exist.
|
|
|
|
get_target_timestamp(Globals, do_search, Target,
|
|
MaybeTimestamp, !Info, !IO),
|
|
(
|
|
MaybeTimestamp = ok(_),
|
|
Status = deps_status_up_to_date
|
|
;
|
|
MaybeTimestamp = error(Error),
|
|
Status = deps_status_error,
|
|
io.write_string("** Error: file `", !IO),
|
|
make_write_target_file(Globals, Target, !IO),
|
|
io.write_string("' not found: ", !IO),
|
|
io.write_string(Error, !IO),
|
|
io.nl(!IO)
|
|
)
|
|
)
|
|
),
|
|
!Info ^ dependency_status ^ elem(Dep) := Status
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module make.dependencies.
|
|
%-----------------------------------------------------------------------------%
|