summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/kernel/doc/src/code.xml28
-rw-r--r--lib/kernel/src/code_server.erl66
-rw-r--r--lib/kernel/test/code_SUITE.erl15
-rw-r--r--system/doc/general_info/scheduled_for_removal_26.inc13
4 files changed, 99 insertions, 23 deletions
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index cc70f9e860..1c8b01598c 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -162,6 +162,34 @@ zip:create("mnesia-4.4.7.ez",
would list the contents of a directory inside an archive.
See <seeerl marker="erts:erl_prim_loader"><c>erl_prim_loader(3)</c></seeerl>.</p>
+ <p>An application archive file and a regular application directory
+ can coexist. This can be useful when it is needed to have
+ parts of the application as regular files. A typical case is the
+ <c>priv</c> directory, which must reside as a regular directory
+ to link in drivers dynamically and start port programs.
+ For other applications that do not need this, directory
+ <c>priv</c> can reside in the archive and the files
+ under the directory <c>priv</c> can be read through
+ <c>erl_prim_loader</c>.</p>
+
+ <p>When a directory is added to the code path and
+ when the entire code path is (re)set, the code server
+ decides which subdirectories in an application that are to be
+ read from the archive and which that are to be read as regular
+ files. If directories are added or removed afterwards, the file
+ access can fail if the code path is not updated (possibly to the
+ same path as before, to trigger the directory resolution
+ update).</p>
+
+ <p>For each directory on the second level in the application archive
+ (<c>ebin</c>, <c>priv</c>, <c>src</c>, and so on), the code server first
+ chooses the regular directory if it exists and second from the
+ archive. Function <c>code:lib_dir/2</c> returns the path to the
+ subdirectory. For example, <c>code:lib_dir(megaco,ebin)</c> can return
+ <c>/otp/root/lib/megaco-3.9.1.1.ez/megaco-3.9.1.1/ebin</c> while
+ <c>code:lib_dir(megaco,priv)</c> can return
+ <c>/otp/root/lib/megaco-3.9.1.1/priv</c>.</p>
+
<p>When an <c>escript</c> file contains an archive, there are
no restrictions on the name of the <c>escript</c> and no restrictions
on how many applications that can be stored in the embedded
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 0dc80a4951..af8531271f 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -780,7 +780,7 @@ create_namedb(Path, Root) ->
init_namedb(lists:reverse(Path), Db),
case lookup_name("erts", Db) of
- {ok, _} ->
+ {ok, _, _, _} ->
%% erts is part of code path
ok;
false ->
@@ -828,9 +828,34 @@ insert_name(Name, Dir, Db) ->
do_insert_name(Name, AppDir, Db).
do_insert_name(Name, AppDir, Db) ->
- ets:insert(Db, {Name, AppDir}),
+ {Base, SubDirs} = archive_subdirs(AppDir),
+ ets:insert(Db, {Name, AppDir, Base, SubDirs}),
true.
+archive_subdirs(AppDir) ->
+ Base = filename:basename(AppDir),
+ Dirs = case split_base(Base) of
+ {Name, _} -> [Name, Base];
+ _ -> [Base]
+ end,
+ Ext = archive_extension(),
+ try_archive_subdirs(AppDir ++ Ext, Base, Dirs).
+
+try_archive_subdirs(Archive, Base, [Dir | Dirs]) ->
+ ArchiveDir = filename:append(Archive, Dir),
+ case erl_prim_loader:list_dir(ArchiveDir) of
+ {ok, Files} ->
+ IsDir = fun(RelFile) ->
+ File = filename:append(ArchiveDir, RelFile),
+ is_dir(File)
+ end,
+ {Dir, lists:filter(IsDir, Files)};
+ _ ->
+ try_archive_subdirs(Archive, Base, Dirs)
+ end;
+try_archive_subdirs(_Archive, Base, []) ->
+ {Base, []}.
+
%%
%% Delete a directory from Path.
%% Name can be either the the name in .../Name[-*] or
@@ -920,7 +945,21 @@ del_ebin(Dir) ->
filename:join(del_ebin_1(filename:split(Dir))).
del_ebin_1([Parent,App,"ebin"]) ->
- [Parent,App];
+ case filename:basename(Parent) of
+ [] ->
+ %% Parent is the root directory
+ [Parent,App];
+ _ ->
+ Ext = archive_extension(),
+ case filename:basename(Parent, Ext) of
+ Parent ->
+ %% Plain directory.
+ [Parent,App];
+ Archive ->
+ %% Archive.
+ [Archive]
+ end
+ end;
del_ebin_1(Path = [_App,"ebin"]) ->
del_ebin_1(filename:split(absname(filename:join(Path))));
del_ebin_1(["ebin"]) ->
@@ -948,7 +987,7 @@ delete_name_dir(Dir, Db) ->
Name ->
Dir0 = del_ebin(Dir),
case lookup_name(Name, Db) of
- {ok, Dir0} ->
+ {ok, Dir0, _Base, _SubDirs} ->
ets:delete(Db, Name),
true;
_ -> false
@@ -957,7 +996,7 @@ delete_name_dir(Dir, Db) ->
lookup_name(Name, Db) ->
case ets:lookup(Db, Name) of
- [{Name, Dir}] -> {ok, Dir};
+ [{Name, Dir, Base, SubDirs}] -> {ok, Dir, Base, SubDirs};
_ -> false
end.
@@ -970,19 +1009,28 @@ do_dir(Root,root_dir,_) ->
Root;
do_dir(_Root,compiler_dir,NameDb) ->
case lookup_name("compiler", NameDb) of
- {ok, Dir} -> Dir;
+ {ok, Dir, _Base, _SubDirs} -> Dir;
_ -> ""
end;
do_dir(_Root,{lib_dir,Name},NameDb) ->
case catch lookup_name(to_list(Name), NameDb) of
- {ok, Dir} -> Dir;
+ {ok, Dir, _Base, _SubDirs} -> Dir;
_ -> {error, bad_name}
end;
do_dir(_Root,{lib_dir,Name,SubDir0},NameDb) ->
SubDir = atom_to_list(SubDir0),
case catch lookup_name(to_list(Name), NameDb) of
- {ok, Dir} ->
- filename:join([Dir, SubDir]);
+ {ok, Dir, Base, SubDirs} ->
+ case lists:member(SubDir, SubDirs) of
+ true ->
+ %% Subdir is in archive
+ filename:join([Dir ++ archive_extension(),
+ Base,
+ SubDir]);
+ false ->
+ %% Subdir is regular directory
+ filename:join([Dir, SubDir])
+ end;
_ ->
{error, bad_name}
end;
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 0f985a43ba..7059d477d9 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1118,6 +1118,12 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) ->
{ok, _} = zip:create(Archive, [Base],
[{compress, []}, {cwd, PrivDir}]),
+ %% Create a directory and a file outside of the archive.
+ OtherFile = filename:join([RootDir,VsnBase,"other","other.txt"]),
+ OtherContents = ?MODULE:module_info(md5),
+ filelib:ensure_dir(OtherFile),
+ ok = file:write_file(OtherFile, OtherContents),
+
%% Set up ERL_LIBS and start a peer node.
{ok, Peer, Node} = ?CT_PEER(["-env", "ERL_LIBS", RootDir]),
CodePath = rpc:call(Node, code, get_path, []),
@@ -1133,7 +1139,7 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) ->
%% Get the lib dir for the app.
AppLibDir = rpc:call(Node, code, lib_dir, [App]),
io:format("AppLibDir: ~p\n", [AppLibDir]),
- AppLibDir = filename:join(Archive, Base),
+ AppLibDir = filename:join(RootDir, VsnBase),
%% Access the app priv dir
AppPrivDir = rpc:call(Node, code, priv_dir, [App]),
@@ -1142,6 +1148,13 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) ->
{ok, _Bin, _} =
rpc:call(Node, erl_prim_loader, get_file, [AppPrivFile]),
+ %% Read back the other text file.
+ OtherDirPath = rpc:call(Node, code, lib_dir, [App,other]),
+ OtherFilePath = filename:join(OtherDirPath, "other.txt"),
+ io:format("OtherFilePath: ~p\n", [OtherFilePath]),
+ {ok, OtherContents, _} =
+ rpc:call(Node, erl_prim_loader, get_file, [OtherFilePath]),
+
%% Use the app
Tab = code_archive_tab,
Key = foo,
diff --git a/system/doc/general_info/scheduled_for_removal_26.inc b/system/doc/general_info/scheduled_for_removal_26.inc
index 8a4a5daa94..1d59136ce5 100644
--- a/system/doc/general_info/scheduled_for_removal_26.inc
+++ b/system/doc/general_info/scheduled_for_removal_26.inc
@@ -28,16 +28,3 @@
inconsistent state.
</p>
</section>
-
- <section>
- <title>Archive fallback directory</title>
- <p>
- This feature makes archives more unpredictable, since you might
- have a application folder that contains files that overrides the files in the archive.
- It also makes packaging archives not as straight forward as you would have
- to bundle files in an additional archive layer. We will change this
- so that all files related to an App can be packaged in the .ez archive.
- Additionally dynamically linked libraries such as NIFs will also be loaded
- from the archive.
- </p>
- </section>