diff options
author | Siri Hansen <[email protected]> | 2011-05-11 15:57:51 +0200 |
---|---|---|
committer | Siri Hansen <[email protected]> | 2011-05-17 11:04:38 +0200 |
commit | 06f99b287fbb79e89831bedf1b2e64e7185f66c8 (patch) | |
tree | 0fda082fa28322df482ecff103324462a2aaf396 | |
parent | 430056cfd0d8202337c87cb110818b8491caa41a (diff) | |
download | otp-06f99b287fbb79e89831bedf1b2e64e7185f66c8.tar.gz otp-06f99b287fbb79e89831bedf1b2e64e7185f66c8.tar.bz2 otp-06f99b287fbb79e89831bedf1b2e64e7185f66c8.zip |
Allow same module name in multiple applications if explicitely excluded
Earlier, reltool expected all module names detected under the lib
directories to have unique names. If this was not the case, the result
was undefined - i.e. the beam file of the duplicated module might be
included in multiple applications in the target area, or it might even
be excluded from all applications.
This commit adds awareness in reltool that a module might occur in
multiple applications, and it is allowed as long as the module or it's
application is explicitely excluded in all but one of the containing
applications.
11 files changed, 321 insertions, 72 deletions
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 9743289ca6..692baea0a4 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -465,16 +465,16 @@ analyse(#state{common = C, [MissingApp2 | Apps2] end, app_propagate_is_used_by(C, Apps3), - Apps4 = read_apps(C, Sys, Apps3, []), + {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), %% io:format("Missing app: ~p\n", %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), Sys2 = Sys#sys{apps = Apps4}, - case verify_config(RelApps2, Sys2, Status3) of - {ok, _Warnings} = Status4 -> - {S#state{sys = Sys2}, Status4}; - {error, _} = Status4 -> - {S, Status4} + case verify_config(RelApps2, Sys2, Status4) of + {ok, _Warnings} = Status5 -> + {S#state{sys = Sys2}, Status5}; + {error, _} = Status5 -> + {S, Status5} end. apps_in_rels(Rels, Apps, Status) -> @@ -548,21 +548,24 @@ app_init_is_included(C, {derived, [_ | _]} -> % App is included in at least one rel {true, undefined, true, Status} end, - A2 = A#app{is_pre_included = IsPreIncl, + {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> + mod_init_is_included(C, + Mod, + ModCond, + AppCond, + Default, + Acc) + end, + Status2, + Mods), + A2 = A#app{mods = Mods2, + is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, ets:insert(C#common.app_tab, A2), - lists:foreach(fun(Mod) -> - mod_init_is_included(C, - Mod, - ModCond, - AppCond, - Default) - end, - Mods), - {A2, Status2}. - -mod_init_is_included(C, M, ModCond, AppCond, Default) -> + {A2, Status3}. + +mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -595,9 +598,52 @@ mod_init_is_included(C, M, ModCond, AppCond, Default) -> Default end end, + M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, + + Status2 = + case ets:lookup(C#common.mod_tab,M#mod.name) of + [Existing] -> + case {Existing#mod.is_included,IsIncl} of + {false,_} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + M#mod.app_name, "."]), + ets:insert(C#common.mod_tab, M2), + reltool_utils:add_warning(Status,Warning); + {_,false} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + Existing#mod.app_name, "."]), + + %% Don't insert in mod_tab - using Existing + reltool_utils:add_warning(Status,Warning); + {_,_} -> + Error = + lists:concat( + ["Module ",M#mod.name, + " potentially included by ", + "two different applications: ", + Existing#mod.app_name, " and ", + M#mod.app_name, "."]), + %% Don't insert in mod_tab - using Existing + reltool_utils:return_first_error(Status,Error) + end; + [] -> + ets:insert(C#common.mod_tab, M2), + Status + end, + %% print(M#mod.name, hipe, "~p -> ~p\n", [M2, IsIncl]), - ets:insert(C#common.mod_tab, M2). + {M2,Status2}. false_to_undefined(Bool) -> case Bool of @@ -612,21 +658,27 @@ app_propagate_is_included(_C, _Sys, [], Acc) -> Acc. mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), Acc2 = - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C, Sys, ModName, M2#mod.uses_mods, Acc); - false -> - Acc; - undefined -> - Acc - end, + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + %% print(ModName, file, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + case M2#mod.is_included of + true -> + %% Propagate include mark + mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); + false -> + Acc; + undefined -> + Acc + end; + [_] -> + %% This module is currently used from a different application + %% Ignore + Acc + end, mod_propagate_is_included(C, Sys, A, Mods, Acc2); mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> Acc. @@ -740,9 +792,10 @@ mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> mod_propagate_is_used_by(_C, []) -> ok. -read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> - {Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl), - Status = +app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> + {Mods2, IsIncl2, Status2} = + mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), + AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; false -> ok @@ -759,34 +812,52 @@ read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, - status = Status, + status = AppStatus, uses_mods = UsesMods2, used_by_mods = UsedByMods2, uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - read_apps(C, Sys, Apps, [A2 | Acc]); -read_apps(_C, _Sys, [], Acc) -> - lists:reverse(Acc). - -read_apps(C, Sys, A, [#mod{name = ModName} | Mods], Acc, IsIncl) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - Status = do_get_status(M2), - %% print(M2#mod.name, hipe, "status -> ~p\n", [Status]), - {IsIncl2, M3} = - case M2#mod.is_included of - true -> - UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, - ModName)], - {true, M2#mod{status = Status, used_by_mods = UsedByMods}}; - _ -> - {IsIncl, M2#mod{status = Status, used_by_mods = []}} - end, - ets:insert(C#common.mod_tab, M3), - read_apps(C, Sys, A, Mods, [M3 | Acc], IsIncl2); -read_apps(_C, _Sys, _A, [], Acc, IsIncl) -> - {lists:reverse(Acc), IsIncl}. + ets:insert(C#common.app_tab,A2), + app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); +app_recap_dependencies(_C, _Sys, [], Acc, Status) -> + {lists:reverse(Acc), Status}. + +mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + ModStatus = do_get_status(M2), + %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), + {IsIncl2, M3} = + case M2#mod.is_included of + true -> + UsedByMods = + [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + ModName)], + {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; + _ -> + {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} + end, + ets:insert(C#common.mod_tab, M3), + mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); + [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> + %% App is explicitely excluded so it is ok that the module + %% record does not exist for this module in this + %% application. + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + [M2] -> + %% A module is potensially included by multiple + %% applications. This is not allowed! + Error = + lists:concat( + ["Module ",ModName, + " potentially included by two different applications: ", + A#app.name, " and ", M2#mod.app_name, "."]), + Status2 = reltool_utils:return_first_error(Status,Error), + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + end; +mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> + {lists:reverse(Acc), IsIncl, Status}. do_get_status(M) -> if diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index dbb8e32aa2..76c064f1e7 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1092,17 +1092,23 @@ move_app(S, {_ItemNo, AppBase}, Action) -> OldApp#app{incl_cond = AppCond}. do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> - {ok, AnalysedApp, Warnings} = reltool_server:set_app(ServerPid, NewApp), + Result = reltool_server:set_app(ServerPid, NewApp), [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], S2 = redraw_apps(S), - case Warnings of - [] -> - ignore; - _ -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING) - end, - {ok, AnalysedApp, S2}. + ReturnApp = + case Result of + {ok, AnalysedApp, []} -> + AnalysedApp; + {ok, AnalysedApp, Warnings} -> + Msg = lists:flatten([[W, $\n] || W <- Warnings]), + display_message(Msg, ?wxICON_WARNING), + AnalysedApp; + {error, Reason} -> + display_message(Reason, ?wxICON_ERROR), + {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), + OldApp + end, + {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, source = SourceCtrl, diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 62fe05238b..767454b66a 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -76,8 +76,8 @@ release_tests_spec: opt $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR) $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + chmod -R u+w $(RELSYSDIR) + @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index b77560db94..9ed79e8c95 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). -define(WORK_DIR, "reltool_work_dir"). @@ -53,7 +54,7 @@ all() -> [start_server, set_config, create_release, create_script, create_target, create_embedded, create_standalone, create_old_target, - otp_9135]. + otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. groups() -> []. @@ -361,6 +362,114 @@ create_old_target(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% OTP-9229 - handle duplicated module names, i.e. same module name +%% exists in two applications. + +%% Include on app, exclude the other +otp_9229_exclude_app(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclApp = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), + ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(false, filelib:is_file(AbsYArchive)), + ?m(non_existing, mod_path(Node,y)), + + ?msym(ok, stop_node(Node)), + + ok. + +%% Include both apps, but exclude common module from one app +otp_9229_exclude_mod(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclMod = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include},{mod, mylib,[{incl_cond,exclude}]}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_mod"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), + ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + YEbin = ["ebin","y-1.0",YArchive], + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(true, filelib:is_file(AbsYArchive)), + ?m(YEbin, mod_path(Node,y)), + + %% Remove path to XEbin and check that mylib is not located in YEbin + Mylib = rpc:call(Node,code,which,[mylib]), + rpc:call(Node,code,del_path,[filename:dirname(Mylib)]), + ?m(non_existing, mod_path(Node,mylib)), + + ?msym(ok, stop_node(Node)), + + ok. + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Library functions erl_libs() -> @@ -407,6 +516,20 @@ os_cmd(Cmd) when is_list(Cmd) -> end end. +%% Returns the location (directory) of the given module. Split, +%% reverted and relative to the lib dir. +mod_path(Node,Mod) -> + case rpc:call(Node,code,which,[Mod]) of + Path when is_list(Path) -> + lists:takewhile( + fun("lib") -> false; + (_) -> true + end, + lists:reverse(filename:split(filename:dirname(Path)))); + Other -> + Other + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Node handling diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src new file mode 100644 index 0000000000..049e8dd6cc --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -0,0 +1,19 @@ +EFLAGS=+debug_info + +OTP9229= \ + otp_9229/x-1.0/ebin/x.@EMULATOR@ \ + otp_9229/x-1.0/ebin/mylib.@EMULATOR@ \ + otp_9229/y-1.0/ebin/y.@EMULATOR@ \ + otp_9229/y-1.0/ebin/mylib.@EMULATOR@ + + +all: $(OTP9229) + +otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl +otp_9229/x-1.0/ebin/mylib.@EMULATOR@: otp_9229/x-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/mylib.erl +otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl +otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app new file mode 100644 index 0000000000..e597704b19 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, x, + [{description, "X CXC 138 11"}, + {vsn, "1.0"}, + {modules, [x, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl new file mode 100644 index 0000000000..17ff84f08f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl @@ -0,0 +1,4 @@ +-module(x). +-export([x/0]). + +x() ->mylib:foo(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app new file mode 100644 index 0000000000..5b327862e3 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, y, + [{description, "Y CXC 138 11"}, + {vsn, "1.0"}, + {modules, [y, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl new file mode 100644 index 0000000000..342e7da7d5 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl @@ -0,0 +1,4 @@ +-module(y). +-export([y/0]). + +y() ->mylib:foo(). |