Files
mercury/compiler/make.dependencies.m
Simon Taylor c14749473b If a module exports foreign types, the interface files need
Estimated hours taken: 15
Branches: main

If a module exports foreign types, the interface files need
to contain a `:- pragma foreign_import_module' declaration
for the module to avoid target code compilation errors in
importing modules.

compiler/modules.m:
	Add a foreign_import_module declaration to item
	lists files where needed.

compiler/modules.m:
compiler/make.dependencies.m:
	Handle extra dependencies.

compiler/intermod.m:
compiler/mercury_compile.m:
	Remove code to add foreign_import_module declarations;
	this is now handled in modules.m.

compiler/foreign.m:
	Add a function to work out the language from a
	foreign type declaration.

tests/hard_coded/export_test2.m:
	Test case.
2004-01-01 05:57:13 +00:00

1054 lines
35 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 2002-2004 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.
% find_module_deps(ModuleName, Succeeded, Deps, Info0, Info).
%
% 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(module_name, bool, set(T),
make_info, make_info, io__state, io__state).
:- inst find_module_deps ==
(pred(in, out, out, in, out, di, uo) is det).
:- type dependency_file
---> target(target_file) % A target which could be made.
; 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, module_target_type) =
find_module_deps(dependency_file).
:- mode target_dependencies(in, in) = 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),
module_name::in, bool::out, set(T)::in, set(T)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
%-----------------------------------------------------------------------------%
% Find all modules in the current directory which are
% reachable (by import) from the given module.
:- pred find_reachable_local_modules(module_name::in, bool::out,
set(module_name)::out, make_info::in, make_info::out,
io__state::di, io__state::uo) is det.
%-----------------------------------------------------------------------------%
:- pred dependency_status(dependency_file::in, dependency_status::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
%-----------------------------------------------------------------------------%
:- type dependencies_result
---> up_to_date
; out_of_date
; error
.
% check_dependencies(TargetFileName, TargetFileTimestamp,
% BuildDepsSucceeded, Dependencies, Result)
%
% Check that all the dependency targets are up-to-date.
:- pred check_dependencies(file_name::in, maybe_error(timestamp)::in, bool::in,
list(dependency_file)::in, dependencies_result::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
% check_dependencies(TargetFileName, TargetFileTimestamp,
% BuildDepsSucceeded, Dependencies, Result)
%
% Check that all the dependency files are up-to-date.
:- pred check_dependency_timestamps(file_name::in, maybe_error(timestamp)::in,
bool::in, list(File)::in,
pred(File, io__state, io__state)::(pred(in, di, uo) is det),
list(maybe_error(timestamp))::in, dependencies_result::out,
io__state::di, io__state::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.
%-----------------------------------------------------------------------------%
:- implementation.
:- type deps_result(T) == pair(bool, set(T)).
:- type module_deps_result == deps_result(module_name).
union_deps(FindDeps, ModuleName, Success, Deps0,
set__union(Deps0, Deps), Info0, Info) -->
FindDeps(ModuleName, Success, Deps, Info0, Info).
% 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), find_module_deps(T)) =
find_module_deps(T).
:- mode combine_deps(in(find_module_deps), in(find_module_deps)) =
out(find_module_deps) is det.
combine_deps(FindDeps1, FindDeps2) =
(pred(ModuleName::in, Success::out, Deps::out,
Info0::in, Info::out, di, uo) is det -->
FindDeps1(ModuleName, Success1, Deps1, Info0, Info1),
( { Success1 = no, Info1 ^ keep_going = no } ->
{ Info = Info1 },
{ Success = no },
{ Deps = Deps1 }
;
FindDeps2(ModuleName, Success2, Deps2, Info1, Info),
{ Success = Success1 `and` Success2 },
{ Deps = set__union(Deps1, Deps2) }
)
).
:- func combine_deps_list(list(find_module_deps(T))) =
find_module_deps(T).
:- mode combine_deps_list(in(list_skel(find_module_deps))) =
out(find_module_deps) is det.
combine_deps_list([]) = no_deps.
combine_deps_list([FindDeps | FindDepsList]) =
( FindDepsList = [] ->
FindDeps
;
combine_deps(FindDeps, combine_deps_list(FindDepsList))
).
target_dependencies(_, source) = no_deps.
target_dependencies(Globals, errors) = compiled_code_dependencies(Globals).
target_dependencies(_, private_interface) = interface_file_dependencies.
target_dependencies(_, long_interface) = interface_file_dependencies.
target_dependencies(_, short_interface) = interface_file_dependencies.
target_dependencies(_, unqualified_short_interface) = source `of` self.
target_dependencies(Globals, aditi_code) = compiled_code_dependencies(Globals).
target_dependencies(Globals, c_header(_)) =
target_dependencies(Globals, c_code).
target_dependencies(Globals, c_code) = compiled_code_dependencies(Globals).
target_dependencies(Globals, il_code) = compiled_code_dependencies(Globals).
target_dependencies(_, il_asm) =
combine_deps_list([
il_code `of` self
]).
target_dependencies(Globals, java_code) = compiled_code_dependencies(Globals).
target_dependencies(Globals, asm_code(_)) =
compiled_code_dependencies(Globals).
target_dependencies(Globals, object_code(PIC)) = Deps :-
globals__get_target(Globals, CompilationTarget),
TargetCode = ( CompilationTarget = asm -> asm_code(PIC) ; c_code ),
globals__lookup_bool_option(Globals, highlevel_code, HighLevelCode),
%
% For --highlevel-code, the `.c' file will #include the header
% file for all imported modules.
%
HeaderDeps =
( CompilationTarget = c, HighLevelCode = yes ->
combine_deps_list([
c_header(mih) `of` direct_imports,
c_header(mih) `of` indirect_imports,
c_header(mih) `of` parents,
c_header(mih) `of` intermod_imports
])
;
no_deps
),
Deps = combine_deps_list([
TargetCode `of` self,
c_header(mh) `of` foreign_imports,
HeaderDeps
]).
target_dependencies(_, intermodule_interface) =
combine_deps_list([
source `of` self,
private_interface `of` parents,
long_interface `of` non_intermod_direct_imports,
short_interface `of` non_intermod_indirect_imports
]).
target_dependencies(_, foreign_il_asm(_)) =
combine_deps_list([
il_asm `of` self,
il_asm `of` filter(maybe_keep_std_lib_module, direct_imports),
il_asm `of` filter(maybe_keep_std_lib_module,
foreign_imports(il)),
foreign_il_asm(managed_cplusplus) `of`
filter(maybe_keep_std_lib_module,
foreign_imports(managed_cplusplus)),
foreign_il_asm(csharp) `of`
filter(maybe_keep_std_lib_module,
foreign_imports(csharp))
]).
target_dependencies(Globals, foreign_object(PIC, _)) =
get_foreign_deps(Globals, PIC).
target_dependencies(Globals, fact_table_object(PIC, _)) =
get_foreign_deps(Globals, PIC).
:- func get_foreign_deps(globals, pic) = find_module_deps(dependency_file).
:- mode get_foreign_deps(in, in) = out(find_module_deps) is det.
get_foreign_deps(Globals, PIC) = Deps :-
globals__get_target(Globals, CompilationTarget),
TargetCode = ( CompilationTarget = asm -> asm_code(PIC) ; c_code ),
Deps = combine_deps_list([
TargetCode `of` self
]).
:- func interface_file_dependencies =
(find_module_deps(dependency_file)::out(find_module_deps)) is det.
interface_file_dependencies =
combine_deps_list([
source `of` self,
private_interface `of` parents,
unqualified_short_interface `of` direct_imports,
unqualified_short_interface `of` indirect_imports
]).
:- func compiled_code_dependencies(globals::in) =
(find_module_deps(dependency_file)::out(find_module_deps)) is det.
compiled_code_dependencies(Globals) = Deps :-
globals__lookup_bool_option(Globals,
intermodule_optimization, Intermod),
( Intermod = yes ->
Deps = combine_deps_list([
intermodule_interface `of` self,
intermodule_interface `of` intermod_imports,
map_find_module_deps(imports,
map_find_module_deps(parents,
intermod_imports)),
compiled_code_dependencies
])
;
Deps = compiled_code_dependencies
).
:- func compiled_code_dependencies =
(find_module_deps(dependency_file)::out(find_module_deps)) is det.
compiled_code_dependencies =
combine_deps_list([
source `of` self,
fact_table `files_of` self,
map_find_module_deps(imports, self)
]).
:- func imports =
(find_module_deps(dependency_file)::out(find_module_deps)) is det.
imports = combine_deps_list([
private_interface `of` parents,
long_interface `of` direct_imports,
short_interface `of` indirect_imports
]).
:- func module_target_type `of` find_module_deps(module_name) =
find_module_deps(dependency_file).
:- mode in `of` in(find_module_deps) = out(find_module_deps) is det.
FileType `of` FindDeps =
(pred(ModuleName::in, Success::out, TargetFiles::out,
Info0::in, Info::out, di, uo) is det -->
FindDeps(ModuleName, Success, ModuleNames, Info0, Info),
{ TargetFiles = set__sorted_list_to_set(
make_dependency_list(set__to_sorted_list(ModuleNames),
FileType)) }
).
:- func find_module_deps(pair(file_name, maybe(option))) `files_of`
find_module_deps(module_name) =
find_module_deps(dependency_file).
:- mode in(find_module_deps) `files_of` in(find_module_deps)
= out(find_module_deps) is det.
FindFiles `files_of` FindDeps =
(pred(ModuleName::in, Success::out, DepFiles::out,
Info0::in, Info::out, di, uo) is det -->
{ KeepGoing = Info0 ^ keep_going },
FindDeps(ModuleName, Success0, ModuleNames, Info0, Info1),
( { Success0 = no, KeepGoing = no } ->
{ Success = no },
{ Info = Info1 },
{ DepFiles = set__init }
;
foldl3_maybe_stop_at_error(KeepGoing,
union_deps(FindFiles),
set__to_sorted_list(ModuleNames),
Success1, set__init, FileNames, Info1, Info),
{ Success = Success0 `and` Success1 },
{ DepFiles = set__sorted_list_to_set(
list__map(
(func(FileName - Option) = file(FileName, Option)),
set__to_sorted_list(FileNames))) }
)
).
:- pred map_find_module_deps(find_module_deps(T)::in(find_module_deps),
find_module_deps(module_name)::in(find_module_deps),
module_name::in, bool::out, set(T)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
map_find_module_deps(FindDeps2, FindDeps1, ModuleName,
Success, Result, Info0, Info) -->
{ KeepGoing = Info0 ^ keep_going },
FindDeps1(ModuleName, Success0, Modules0, Info0, Info1),
( { Success0 = no, KeepGoing = no } ->
{ Success = no },
{ Result = set__init },
{ Info = Info1 }
;
foldl3_maybe_stop_at_error(KeepGoing, union_deps(FindDeps2),
set__to_sorted_list(Modules0), Success1,
set__init, Result, Info1, Info),
{ Success = Success0 `and` Success1 }
).
%-----------------------------------------------------------------------------%
:- pred no_deps(module_name::in, bool::out, set(T)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
no_deps(_, yes, set__init, Info, Info) --> [].
:- pred self(module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
self(ModuleName, yes, set__make_singleton_set(ModuleName), Info, Info) --> [].
:- pred parents(module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
parents(ModuleName, yes, set__list_to_set(get_ancestors(ModuleName)),
Info, Info) --> [].
%-----------------------------------------------------------------------------%
:- type cached_direct_imports == map(module_name, module_deps_result).
init_cached_direct_imports = map__init.
:- pred direct_imports(module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
direct_imports(ModuleName, Success, Modules, Info0, Info) -->
( { Result0 = Info0 ^ cached_direct_imports ^ elem(ModuleName) } ->
{ Result0 = Success - Modules },
{ Info = Info0 }
;
{ KeepGoing = Info0 ^ keep_going },
non_intermod_direct_imports(ModuleName, Success0,
Modules0, Info0, Info1),
( { Success0 = no, KeepGoing = no } ->
{ Info3 = Info1 },
{ Success = no },
{ Modules = set__init }
;
%
% We also read `.int' files for the modules for
% which we read `.opt' files, and for the modules
% imported by those modules.
%
intermod_imports(ModuleName, Success1,
IntermodModules, Info1, Info2),
( { Success1 = no, KeepGoing = no } ->
{ Info3 = Info2 },
{ Success = no },
{ Modules = set__init }
;
foldl3_maybe_stop_at_error(Info2 ^ keep_going,
union_deps(non_intermod_direct_imports),
set__to_sorted_list(IntermodModules), Success2,
set__union(Modules0, IntermodModules), Modules1,
Info2, Info3),
{ Success = Success0 `and` Success1 `and` Success2 },
{ Modules = set__delete(Modules1, ModuleName) }
)
),
{ Info = Info3 ^ cached_direct_imports
^ elem(ModuleName) := 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(module_name::in, bool::out,
set(module_name)::out, make_info::in, make_info::out,
io__state::di, io__state::uo) is det.
non_intermod_direct_imports(ModuleName, Success, Modules, Info0, Info) -->
get_module_dependencies(ModuleName, MaybeImports, Info0, Info1),
(
{ MaybeImports = yes(Imports) },
%
% 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 interface
% imports of this module and for the *implementation*
% imports of its ancestors. This is because if this
% module is defined in the implementation section of
% its parent, then the interface of this module may
% depend on things imported only by its parent's
% implementation.
%
% If this module was actually defined in the interface
% section of one of its ancestors, then it should only
% depend on the interface imports of that ancestor,
% so the dependencies added here are in fact more
% conservative than they need to be in that case.
% However, that should not be a major problem.
% (This duplicates how this is handled by modules.m).
%
{ Modules0 = set__union(set__list_to_set(Imports ^ impl_deps),
set__list_to_set(Imports ^ int_deps)) },
( { ModuleName = qualified(ParentModule, _) } ->
non_intermod_direct_imports(ParentModule, Success,
ParentImports, Info1, Info),
{ Modules = set__union(ParentImports, Modules0) }
;
{ Success = yes },
{ Modules = Modules0 },
{ Info = Info1 }
)
;
{ MaybeImports = no },
{ Success = no },
{ Modules = set__init },
{ Info = Info1 }
).
%-----------------------------------------------------------------------------%
% Return the list of modules for which we should read `.int2' files.
:- pred indirect_imports(module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
indirect_imports(ModuleName, Success, Modules, Info0, Info) -->
indirect_imports_2(direct_imports, ModuleName,
Success, Modules, Info0, Info).
% 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(module_name::in, bool::out,
set(module_name)::out, make_info::in, make_info::out,
io__state::di, io__state::uo) is det.
non_intermod_indirect_imports(ModuleName, Success, Modules, Info0, Info) -->
indirect_imports_2(non_intermod_direct_imports, ModuleName,
Success, Modules, Info0, Info).
:- pred indirect_imports_2(find_module_deps(module_name)::in(find_module_deps),
module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
indirect_imports_2(FindDirectImports, ModuleName, Success, IndirectImports,
Info0, Info) -->
{ KeepGoing = Info1 ^ keep_going },
FindDirectImports(ModuleName, DirectSuccess,
DirectImports, Info0, Info1),
( { DirectSuccess = no, KeepGoing = no } ->
{ Success = no },
{ IndirectImports = set__init },
{ Info = Info1 }
;
foldl3_maybe_stop_at_error(Info1 ^ keep_going,
union_deps(find_transitive_implementation_imports),
set__to_sorted_list(DirectImports), IndirectSuccess,
set__init, IndirectImports0, Info1, Info),
{ IndirectImports = set__difference(
set__delete(IndirectImports0, ModuleName),
DirectImports) },
{ Success = DirectSuccess `and` IndirectSuccess }
).
%-----------------------------------------------------------------------------%
% Return the list of modules for which we should read `.opt' files.
:- pred intermod_imports(module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
intermod_imports(ModuleName, Success, Modules, Info0, Info) -->
globals__io_lookup_bool_option(intermodule_optimization, Intermod),
(
{ Intermod = yes },
globals__io_lookup_bool_option(read_opt_files_transitively,
Transitive),
(
{ Transitive = yes },
find_transitive_implementation_imports(ModuleName,
Success, Modules, Info0, Info)
;
{ Transitive = no },
non_intermod_direct_imports(ModuleName, Success,
Modules, Info0, Info)
)
;
{ Intermod = no },
{ Info = Info0 },
{ Success = yes },
{ Modules = set__init }
).
%-----------------------------------------------------------------------------%
:- pred foreign_imports(module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
foreign_imports(ModuleName, Success, Modules, Info0, Info) -->
%
% 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__io_get_globals(Globals),
{ globals__get_backend_foreign_languages(Globals, Languages) },
intermod_imports(ModuleName, IntermodSuccess, IntermodModules,
Info0, Info1),
foldl3_maybe_stop_at_error(Info1 ^ keep_going,
union_deps(find_module_foreign_imports(
set__list_to_set(Languages))),
[ModuleName | set__to_sorted_list(IntermodModules)],
ForeignSuccess, set__init, Modules, Info1, Info),
{ Success = IntermodSuccess `and` ForeignSuccess }.
:- pred find_module_foreign_imports(set(foreign_language)::in, module_name::in,
bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
find_module_foreign_imports(Languages, ModuleName,
Success, ForeignModules, Info0, Info) -->
find_transitive_implementation_imports(ModuleName, Success0,
ImportedModules, Info0, Info1),
( { Success0 = yes } ->
foldl3_maybe_stop_at_error(Info1 ^ keep_going,
union_deps(find_module_foreign_imports_2(Languages)),
[ModuleName | to_sorted_list(ImportedModules)],
Success, set__init, ForeignModules, Info1, Info)
;
{ Success = no },
{ ForeignModules = set__init },
{ Info = Info1 }
).
:- pred find_module_foreign_imports_2(set(foreign_language)::in,
module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
find_module_foreign_imports_2(Languages, ModuleName,
Success, ForeignModules, Info0, Info) -->
get_module_dependencies(ModuleName, MaybeImports, Info0, Info),
{
MaybeImports = yes(Imports),
ForeignModules = set__list_to_set(
get_foreign_imported_modules(Languages,
Imports ^ foreign_import_module_info)),
Success = yes
;
MaybeImports = no,
ForeignModules = set__init,
Success = no
}.
:- func get_foreign_imported_modules(foreign_import_module_info) =
list(module_name).
get_foreign_imported_modules(ForeignImportModules) =
get_foreign_imported_modules_2(no, ForeignImportModules).
:- func get_foreign_imported_modules(set(foreign_language),
foreign_import_module_info) = list(module_name).
get_foreign_imported_modules(Languages, ForeignImportModules) =
get_foreign_imported_modules_2(yes(Languages), ForeignImportModules).
:- func get_foreign_imported_modules_2(maybe(set(foreign_language)),
foreign_import_module_info) = list(module_name).
get_foreign_imported_modules_2(MaybeLanguages, ForeignImportModules) =
list__filter_map(
(func(ForeignImportModule) = ForeignModule is semidet :-
ForeignImportModule =
foreign_import_module(Language, ForeignModule, _),
(
MaybeLanguages = yes(Languages),
set__member(Language, Languages)
;
MaybeLanguages = no
)
), ForeignImportModules).
%-----------------------------------------------------------------------------%
%
% foreign_imports(Lang, ModuleName, Success, Modules, !Info, !IO)
%
% From the module, ModuleName, extract the set of modules, Modules,
% which are mentioned in foreign_import_module declarations with the
% specified language, Lang.
%
:- pred foreign_imports(foreign_language::in,
module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
foreign_imports(Lang, ModuleName, Success, Modules, !Info) -->
get_module_dependencies(ModuleName, MaybeImports, !Info),
{ MaybeImports = yes(Imports),
list__filter_map((pred(FI::in, M::out) is semidet :-
FI = foreign_import_module(Lang, M, _)
), Imports ^ foreign_import_module_info, ModulesList),
set__list_to_set(ModulesList, Modules),
Success = yes
; MaybeImports = no,
Modules = set__init,
Success = no
}.
%-----------------------------------------------------------------------------%
%
% filter(F, P, MN, S, Ms, I0, I, IO0, IO)
% Filter the set of module_names returned from P called with MN, I0, IO0
% as its input arguments with F. The first argument to F will be MN
% and the second argument be one of the module_names returned from P.
%
:- pred filter(pred(module_name, module_name)::pred(in, in) is semidet,
pred(module_name, bool, set(module_name), make_info, make_info,
io, io)::pred(in, out, out, in, out, di, uo) is det,
module_name::in, bool::out,
set(module_name)::out, make_info::in, make_info::out,
io::di, io::uo) is det.
filter(Filter, F, ModuleName, Success, Modules, !Info) -->
F(ModuleName, Success, Modules0, !Info),
{ Modules = set__filter(
(pred(M::in) is semidet :-
Filter(ModuleName, M)
), Modules0) }.
%
% If the current module we are compiling is not in the standard library
% and the module we are importing is then remove it, otherwise keep it.
% When compiling with `--target il', if the current module is not in the
% standard library, we link with mercury.dll rather than the DLL file for
% the imported module.
%
:- pred maybe_keep_std_lib_module(module_name::in,
module_name::in) is semidet.
maybe_keep_std_lib_module(CurrentModule, ImportedModule) :-
\+ (
\+ mercury_std_library_module_name(CurrentModule),
mercury_std_library_module_name(ImportedModule)
).
%-----------------------------------------------------------------------------%
:- pred fact_table(module_name::in,
bool::out, set(pair(file_name, maybe(option)))::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
fact_table(ModuleName, Success, Files, Info0, Info) -->
get_module_dependencies(ModuleName, MaybeImports, Info0, Info),
{
MaybeImports = yes(Imports),
Success = yes,
Files = set__list_to_set(
make_target_list(Imports ^ fact_table_deps, no))
;
MaybeImports = no,
Success = no,
Files = set__init
}.
%-----------------------------------------------------------------------------%
:- type transitive_dependencies_root
---> transitive_dependencies_root(
module_name,
transitive_dependencies_type,
module_locn
).
:- type transitive_deps_result == pair(bool, set(module_name)).
:- type transitive_dependencies_type
---> interface_imports
; all_dependencies % including parents and children
.
:- 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, transitive_deps_result).
init_cached_transitive_dependencies = map__init.
find_reachable_local_modules(ModuleName, Success, Modules, Info0, Info) -->
find_transitive_module_dependencies(all_dependencies, local_module,
ModuleName, Success, Modules, Info0, Info).
:- pred find_transitive_implementation_imports(module_name::in, bool::out,
set(module_name)::out, make_info::in, make_info::out,
io__state::di, io__state::uo) is det.
find_transitive_implementation_imports(ModuleName, Success, Modules,
Info0, Info) -->
find_transitive_module_dependencies(all_dependencies, any_module,
ModuleName, Success, Modules0, Info0, Info),
{ Modules = set__insert(Modules0, ModuleName) }.
:- pred find_transitive_interface_imports(module_name::in, bool::out,
set(module_name)::out, make_info::in, make_info::out,
io__state::di, io__state::uo) is det.
find_transitive_interface_imports(ModuleName,
Success, Modules, Info0, Info) -->
find_transitive_module_dependencies(interface_imports, any_module,
ModuleName, Success, Modules0, Info0, Info),
{ set__delete(Modules0, ModuleName, Modules) }.
:- pred find_transitive_module_dependencies(transitive_dependencies_type::in,
module_locn::in, module_name::in, bool::out, set(module_name)::out,
make_info::in, make_info::out, io__state::di, io__state::uo) is det.
find_transitive_module_dependencies(DependenciesType, ModuleLocn,
ModuleName, Success, Modules, Info0, Info) -->
globals__io_lookup_bool_option(keep_going, KeepGoing),
find_transitive_module_dependencies_2(KeepGoing,
DependenciesType, ModuleLocn, ModuleName,
Success, set__init, Modules, Info0, Info1),
{ DepsRoot = transitive_dependencies_root(ModuleName,
DependenciesType, ModuleLocn) },
{ Info = Info1 ^ cached_transitive_dependencies
^ elem(DepsRoot) := Success - Modules }.
:- pred find_transitive_module_dependencies_2(bool::in,
transitive_dependencies_type::in, module_locn::in,
module_name::in, bool::out, set(module_name)::in,
set(module_name)::out, make_info::in, make_info::out,
io__state::di, io__state::uo) is det.
find_transitive_module_dependencies_2(KeepGoing, DependenciesType,
ModuleLocn, ModuleName, Success, Modules0, Modules,
Info0, Info) -->
(
{ set__member(ModuleName, Modules0) }
->
{ Success = yes },
{ Modules = Modules0 },
{ Info = Info0 }
;
{ DepsRoot = transitive_dependencies_root(ModuleName,
DependenciesType, ModuleLocn) },
{ Result0 = Info0 ^ cached_transitive_dependencies ^ elem(DepsRoot) }
->
{ Result0 = Success - Modules1 },
{ Modules = set__union(Modules0, Modules1) },
{ Info = Info0 }
;
get_module_dependencies(ModuleName, MaybeImports, Info0, Info1),
(
{ MaybeImports = yes(Imports) },
(
{
ModuleLocn = any_module
;
ModuleLocn = local_module,
Imports ^ module_dir = dir__this_directory
}
->
{
% 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 ^ int_deps
;
DependenciesType = all_dependencies,
ImportsToCheck =
list__condense([
Imports ^ int_deps,
Imports ^ impl_deps,
Imports ^ parent_deps,
Imports ^ children,
get_foreign_imported_modules(
Imports ^ foreign_import_module_info)
])
},
{ ImportingModule = Info1 ^ importing_module },
{ Info2 = Info1 ^ importing_module := yes(ModuleName) },
foldl3_maybe_stop_at_error(KeepGoing,
find_transitive_module_dependencies_2(KeepGoing,
DependenciesType, ModuleLocn),
ImportsToCheck, Success,
set__insert(Modules0, ModuleName), Modules,
Info2, Info3),
{ Info = Info3 ^ importing_module := ImportingModule }
;
{ Success = yes },
{ Modules = Modules0 },
{ Info = Info1 }
)
;
{ MaybeImports = no },
{ Success = no } ,
{ Modules = Modules0 },
{ Info = Info1 }
)
).
%-----------------------------------------------------------------------------%
check_dependencies(TargetFileName, MaybeTimestamp, BuildDepsSucceeded,
DepFiles, DepsResult, Info0, Info) -->
list__map_foldl2(dependency_status, DepFiles,
DepStatusList, Info0, Info1),
{ assoc_list__from_corresponding_lists(DepFiles,
DepStatusList, DepStatusAL) },
{ list__filter(
(pred((_ - DepStatus)::in) is semidet :-
DepStatus \= up_to_date
), DepStatusAL, UnbuiltDependencies) },
( { UnbuiltDependencies \= [] } ->
{ Info = Info1 },
debug_msg(
(pred(di, uo) is det -->
io__write_string(TargetFileName),
io__write_string(
": dependencies could not be built.\n\t"),
io__write_list(UnbuiltDependencies,
",\n\t",
(pred((DepTarget - DepStatus)::in,
di, uo) is det -->
write_dependency_file(DepTarget),
io__write_string(" - "),
io__write(DepStatus)
)),
io__nl
)),
{ DepsResult = error }
;
debug_msg(
(pred(di, uo) is det -->
io__write_string(TargetFileName),
io__write_string(": finished dependencies\n")
)),
list__map_foldl2(get_dependency_timestamp, DepFiles,
DepTimestamps, Info1, Info),
check_dependency_timestamps(TargetFileName, MaybeTimestamp,
BuildDepsSucceeded, DepFiles, write_dependency_file,
DepTimestamps, DepsResult)
).
check_dependency_timestamps(TargetFileName, MaybeTimestamp, BuildDepsSucceeded,
DepFiles, WriteDepFile, DepTimestamps, DepsResult) -->
(
{ MaybeTimestamp = error(_) },
{ DepsResult = out_of_date },
debug_msg(
(pred(di, uo) is det -->
io__write_string(TargetFileName),
io__write_string(" does not exist.\n")
))
;
{ MaybeTimestamp = ok(Timestamp) },
globals__io_lookup_bool_option(rebuild, Rebuild),
(
{ list__member(MaybeDepTimestamp1, DepTimestamps) },
{ MaybeDepTimestamp1 = error(_) }
->
{ DepsResult = error },
{ WriteMissingDeps =
(pred(di, uo) is det -->
{ assoc_list__from_corresponding_lists(DepFiles,
DepTimestamps, DepTimestampAL) },
{ solutions(
(pred(DepFile::out) is nondet :-
list__member(DepFile - error(_),
DepTimestampAL)
), ErrorDeps) },
io__write_string("** dependencies for `"),
io__write_string(TargetFileName),
io__write_string("' do not exist: "),
io__write_list(ErrorDeps, ", ", WriteDepFile),
io__nl,
( { BuildDepsSucceeded = yes } ->
io__write_string(
"** This indicates a bug in `mmc --make'.\n")
;
[]
)
) },
(
{ BuildDepsSucceeded = yes },
%
% Something has gone wrong -- building the target has
% succeeded, but there are some files missing.
% Report an error.
%
WriteMissingDeps
;
{ BuildDepsSucceeded = no },
debug_msg(WriteMissingDeps)
)
;
{ Rebuild = yes }
->
%
% With `--rebuild', a target is always considered
% to be out-of-date, regardless of the timestamps
% of its dependencies.
%
{ DepsResult = out_of_date }
;
{ list__member(MaybeDepTimestamp2, DepTimestamps) },
{ MaybeDepTimestamp2 = ok(DepTimestamp) },
{ compare((>), DepTimestamp, Timestamp) }
->
debug_newer_dependencies(TargetFileName, MaybeTimestamp,
DepFiles, WriteDepFile, DepTimestamps),
{ DepsResult = out_of_date }
;
{ DepsResult = up_to_date }
)
).
:- pred debug_newer_dependencies(string::in, maybe_error(timestamp)::in,
list(T)::in, pred(T, io__state, io__state)::(pred(in, di, uo) is det),
list(maybe_error(timestamp))::in, io__state::di, io__state::uo) is det.
debug_newer_dependencies(TargetFileName, MaybeTimestamp,
DepFiles, WriteDepFile, DepTimestamps) -->
debug_msg(
(pred(di, uo) is det -->
io__write_string(TargetFileName),
io__write_string(": newer dependencies: "),
{ assoc_list__from_corresponding_lists(DepFiles,
DepTimestamps, DepTimestampAL) },
{ solutions(
(pred(DepFile::out) is nondet :-
list__member(DepFile - MaybeDepTimestamp,
DepTimestampAL),
(
MaybeDepTimestamp = error(_)
;
MaybeDepTimestamp = ok(DepTimestamp),
MaybeTimestamp = ok(Timestamp),
compare((>), DepTimestamp, Timestamp)
)), NewerDeps) },
io__write_list(NewerDeps, ",\n\t", WriteDepFile),
io__nl
)).
dependency_status(file(FileName, _) @ Dep, Status, Info0, Info) -->
( { Status0 = Info0 ^ dependency_status ^ elem(Dep) } ->
{ Info = Info0 },
{ Status = Status0 }
;
get_dependency_timestamp(Dep, MaybeTimestamp, Info0, Info1),
(
{ MaybeTimestamp = ok(_) },
{ Status = up_to_date }
;
{ MaybeTimestamp = error(Error) },
{ Status = error },
io__write_string("** Error: file `"),
io__write_string(FileName),
io__write_string("' not found: "),
io__write_string(Error),
io__nl
),
{ Info = Info1 ^ dependency_status ^ elem(Dep) := Status }
).
dependency_status(target(Target) @ Dep, Status, Info0, Info) -->
{ Target = ModuleName - FileType },
( { FileType = source } ->
% Source files are always up-to-date.
maybe_warn_up_to_date_target(ModuleName - module_target(source),
Info0, Info),
{ Status = up_to_date }
; { Status0 = Info0 ^ dependency_status ^ elem(Dep) } ->
{ Info = Info0 },
{ Status = Status0 }
;
get_module_dependencies(ModuleName, MaybeImports, Info0, Info1),
(
{ MaybeImports = no },
{ Status = error },
{ Info2 = Info1 }
;
{ MaybeImports = yes(Imports) },
( { Imports ^ module_dir \= dir__this_directory } ->
%
% Targets from libraries are always
% considered to be up-to-date if they
% exist.
%
get_target_timestamp(yes, Target,
MaybeTimestamp, Info1, Info2),
(
{ MaybeTimestamp = ok(_) },
{ Status = up_to_date }
;
{ MaybeTimestamp = error(Error) },
{ Status = error },
io__write_string("** Error: file `"),
write_target_file(Target),
io__write_string("' not found: "),
io__write_string(Error),
io__nl
)
;
{ Info2 = Info1 },
{ Status = not_considered }
)
),
{ Info = Info2 ^ dependency_status ^ elem(Dep) := Status }
).
%-----------------------------------------------------------------------------%
:- func this_file = string.
this_file = "make.dependencies.m".
%-----------------------------------------------------------------------------%