[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:


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.


    OldApps = [App || {App, _} <- OldAppEnvs],
    Apps = get_app_names(),
    _ = [begin
             _ = case lists:member(App, OldApps) of
                     true  -> ok;
                     false -> application:stop(App)
             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.

Ulf W

Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.

More information about the erlang-questions mailing list