[erlang-questions] unhappy interaction between rebar and appl. controller
Ulf Wiger
ulf@REDACTED
Sun Dec 16 10:59:42 CET 2012
Is 'included_applications' really an environment variable?
I have spent some time trying to figure out why one of my eunit tests crashed at the end with the tasty error message:
{error_logger,{{2012,12,16},{10,33,28}},"~s~n",["Error in process <0.551.0> with exit value: {terminated,[{io,format,[<0.22.0>,\"Internal error: ~P.\n\",[{error,terminated,[{io,format,[<0.22.0>,\"~s\n\",[undefined]],[]},{eunit_tty,handle_cancel,3,[{file,\"eunit_tty.erl\"},{line,159}]},{eunit_listener,call,3,[{file,\"eunit_listener.erl\"},{line... \n"]}
{"Kernel pid terminated",application_controller,{{badmatch,undefined},[{application_controller,unload,2,[{file,"application_controller.erl"},{line,1287}]},{application_controller,handle_call,3,[{file,"application_controller.erl"},{line,650}]},{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]}]}}
The crash happens because application_controller:unload/2 is a bit particular about requiring the presence of the 'included_applications' entry:
https://github.com/erlang/otp/blob/master/lib/kernel/src/application_controller.erl#L1276
unload(AppName, S) ->
{ok, IncApps} = get_env(AppName, included_applications),
It took me some time to figure out who actually deleted this entry in the first place. After a bit of tracing (using the {message,caller} match body), I discovered that it was rebar, in rebar_eunit:reset_after_eunit/1.
https://github.com/basho/rebar/blob/master/src/rebar_eunit.erl#L612
OldApps = [App || {App, _} <- OldAppEnvs],
Apps = get_app_names(),
_ = [begin
_ = case lists:member(App, OldApps) of
true -> ok;
false -> application:stop(App)
end,
ok = application:unset_env(App, K)
end || App <- Apps, App /= rebar,
{K, _V} <- application:get_all_env(App)],
After this, rebar rebuilds the application environment by re-reading the .app file from disk and trying to emulate application:load/1.
I see two problems with this:
- For historical reasons (originally my 'fault'), 'included_applications' is represented as an environment variable, although it no longer really is one. Rebar has to either exclude it from the unset_env operation or explicitly reconstruct it later.
- Application controller needs to make up its mind about whether 'included_applications' is a critical element. If it is, perhaps it shouldn't so readily allow people to delete it?
BTW, why doesn't rebar simply call application:unload(App)? Or, if it really needs to reconstruct an app env that wasn't there from the beginning, perhaps application:unload(App) followed by application:load(App)?
The problem on my side was that a previous eunit test had done start and stop on an app, but forgot unload. A later test tried the unload and crashed.
- I will add unload calls to my first eunit test.
- Rebar should not try to call application:unset_env(App, included_applications).
- Application_controller should perhaps not crash if included_applications is not present at unload, or better yet, not allow users to delete it through the official API in the first place.
- I should find more fun things to do on a Sunday.
BR,
Ulf W
Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.
http://feuerlabs.com
More information about the erlang-questions
mailing list