File 2852-kernel-Add-code-all_available-that-returns-all-modul.patch of Package erlang
From cb5747109fb6506ab42e98779dfbd724458abe60 Mon Sep 17 00:00:00 2001
From: Lukas Larsson <lukas@erlang.org>
Date: Wed, 29 Jan 2020 15:50:02 +0100
Subject: [PATCH 02/27] kernel: Add code:all_available that returns all modules
This function returns all modules that can be loaded
or already have been loaded.
---
lib/kernel/doc/src/code.xml | 15 +++++++++++++
lib/kernel/src/code.erl | 48 ++++++++++++++++++++++++++++++++++++++++++
lib/kernel/test/code_SUITE.erl | 45 +++++++++++++++++++++++++++++++++++++--
3 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index 268eb5e839..01068c7263 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -704,6 +704,21 @@ ok = code:finish_loading(Prepared),
<c>Loaded==cover_compiled</c>.</p>
</desc>
</func>
+ <func>
+ <name name="all_available" arity="0"/>
+ <fsummary>Get all available modules.</fsummary>
+ <type name="loaded_filename"/>
+ <type name="loaded_ret_atoms"/>
+ <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute
+ filename.</type_desc>
+ <desc>
+ <p>Returns a list of tuples <c>{<anno>Module</anno>, <anno>Filename</anno>,
+ <anno>Loaded</anno>}</c> for all available modules. A module is considered
+ to be available if it either is loaded or would be loaded if called.
+ <c><anno>Filename</anno></c> is normally the absolute filename, as described for
+ <seealso marker="#is_loaded/1"><c>is_loaded/1</c></seealso>.</p>
+ </desc>
+ </func>
<func>
<name name="all_loaded" arity="0"/>
<fsummary>Get all loaded modules.</fsummary>
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index e205d6450c..a8cf6b55ea 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -44,6 +44,7 @@
soft_purge/1,
is_loaded/1,
all_loaded/0,
+ all_available/0,
stop/0,
root_dir/0,
lib_dir/0,
@@ -220,6 +221,53 @@ get_object_code(Mod) when is_atom(Mod) -> call({get_object_code, Mod}).
Loaded :: loaded_filename().
all_loaded() -> call(all_loaded).
+-spec all_available() -> [{Module, Filename, Loaded}] when
+ Module :: string(),
+ Filename :: loaded_filename(),
+ Loaded :: boolean().
+all_available() ->
+ case code:get_mode() of
+ interactive ->
+ all_available(get_path(), #{});
+ embedded ->
+ all_available([], #{})
+ end.
+all_available([Path|Tail], Acc) ->
+ case erl_prim_loader:list_dir(Path) of
+ {ok, Files} ->
+ all_available(Tail, all_available(Path, Files, Acc));
+ _Error ->
+ all_available(Tail, Acc)
+ end;
+all_available([], AllModules) ->
+ AllLoaded = [{atom_to_list(M),Path,true} || {M,Path} <- all_loaded()],
+ AllAvailable =
+ maps:fold(
+ fun(File, Path, Acc) ->
+ [{filename:rootname(File), filename:append(Path, File), false} | Acc]
+ end, [], AllModules),
+ OrderFun = fun F({A,_,_},{B,_,_}) ->
+ F(A,B);
+ F(A,B) ->
+ A =< B
+ end,
+ lists:umerge(OrderFun, lists:sort(OrderFun, AllLoaded), lists:sort(OrderFun, AllAvailable)).
+
+all_available(Path, [File | T], Acc) ->
+ case filename:extension(File) of
+ ".beam" ->
+ case maps:is_key(File, Acc) of
+ false ->
+ all_available(Path, T, Acc#{ File => Path });
+ true ->
+ all_available(Path, T, Acc)
+ end;
+ _Else ->
+ all_available(Path, T, Acc)
+ end;
+all_available(_Path, [], Acc) ->
+ Acc.
+
-spec stop() -> no_return().
stop() -> call(stop).
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 9e532be29f..ed1976d912 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -26,7 +26,7 @@
-export([set_path/1, get_path/1, add_path/1, add_paths/1, del_path/1,
replace_path/1, load_file/1, load_abs/1, ensure_loaded/1,
delete/1, purge/1, purge_many_exits/1, soft_purge/1, is_loaded/1,
- all_loaded/1,
+ all_loaded/1, all_available/1,
load_binary/1, dir_req/1, object_code/1, set_path_file/1,
upgrade/0, upgrade/1,
sticky_dir/1, pa_pz_option/1, add_del_path/1,
@@ -61,7 +61,7 @@ all() ->
[set_path, get_path, add_path, add_paths, del_path,
replace_path, load_file, load_abs, ensure_loaded,
delete, purge, purge_many_exits, soft_purge, is_loaded, all_loaded,
- load_binary, dir_req, object_code, set_path_file,
+ all_available, load_binary, dir_req, object_code, set_path_file,
upgrade,
sticky_dir, pa_pz_option, add_del_path, dir_disappeared,
ext_mod_dep, clash, where_is_file,
@@ -504,6 +504,47 @@ all_unique([]) -> ok;
all_unique([_]) -> ok;
all_unique([{X,_}|[{Y,_}|_]=T]) when X < Y -> all_unique(T).
+all_available(Config) when is_list(Config) ->
+ case test_server:is_cover() of
+ true -> {skip,"Cover is running"};
+ false -> all_available_1(Config)
+ end.
+
+all_available_1(Config) ->
+
+ %% Add an ez dir to make sure the modules in there are found
+ DDir = proplists:get_value(data_dir,Config)++"clash/",
+ true = code:add_path(DDir++"foobar-0.1.ez/foobar-0.1/ebin"),
+
+ Available = code:all_available(),
+
+ %% Test that baz and blarg that are part of the .ez archive are found
+ {value, _} =
+ lists:search(fun({Name,_,Loaded}) -> not Loaded andalso Name =:= "baz" end, Available),
+ {value, _} =
+ lists:search(fun({Name,_,Loaded}) -> not Loaded andalso Name =:= "blarg" end, Available),
+
+ %% Test that all loaded are part of all available
+ Loaded = [{atom_to_list(M),P,true} || {M,P} <- code:all_loaded()],
+ [] = Loaded -- Available,
+
+ {value, {ModStr,_Path,false} = NotLoaded} =
+ lists:search(fun({Name,_,Loaded}) -> not is_atom(Name) end, Available),
+ ct:log("Testing with ~p",[NotLoaded]),
+
+ Mod = list_to_atom(ModStr),
+
+ %% Test that the module is actually not loaded
+ false = code:is_loaded(Mod),
+
+ %% Load it
+ Mod:module_info(),
+
+ {value, {ModStr,_Path,true}} =
+ lists:search(fun({Name,_,_}) -> Name =:= ModStr end, code:all_available()),
+
+ ok.
+
load_binary(Config) when is_list(Config) ->
TestDir = test_dir(),
File = TestDir ++ "/code_b_test" ++ code:objfile_extension(),
--
2.16.4