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

openSUSE Build Service is sponsored by