diff options
-rw-r--r-- | lib/kernel/doc/src/code.xml | 28 | ||||
-rw-r--r-- | lib/kernel/src/code_server.erl | 66 | ||||
-rw-r--r-- | lib/kernel/test/code_SUITE.erl | 15 | ||||
-rw-r--r-- | system/doc/general_info/scheduled_for_removal_26.inc | 13 |
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> |