Technical Musings: Erlang auto reload code in (w)erl shell

Wednesday, March 30, 2011

Erlang auto reload code in (w)erl shell

UPDATE: I found an existing, more mature implimentation of this idea:
http://www.rustyrazorblade.com/2010/12/erlang-code-auto-reloader/. Please disregard the rest.

I develop most of my code in eclipse, because most of my professional work deals with Java, and I develop in many different languages, including erlang.

One of the reasons I use and IDE instead of just and editor is auto compilation. This doesn't work out that great with a erl or werl shell because they don't auto reload code that is compiled outside of the shell.

I found a cool method to reload compiled code here, but it didn't auto reload; you still had to type a command to reload, so not much better than just running c().

So, I figured, like many problems, Erlang could solve this with a process. I was right!

1) One thing to note is that you have to exclude recompiling itself, because that kills that running process. And you'll have to update that code path manually. You could also use a relative path to os:getenv("PWD") if that makes sense in your env.

2) Also note that moving reload.erl into a seperate directory than the path mentioned in the code will not stop it from being reloaded; erlang stores all paths added via -pa as relative to your main path. Say you put reload.erl in ~/test and the rest of your code in ~/erlang, start erl in ~/test/src, and add ~/test/ebin to that code path:

$/home/user/erlang: erl -pa ../../test/ebin

the reload module would be loaded from:
/home/user/erlang/ebin/../../test/ebin

To start:
reload:start() (defaults to reload every 1 second)
or
reload:start(N) (reloads every N milliseconds)

To stop:
reload:stop() (yes, just throws an exception, could be nicer)

CODE:
%% Author: Drew Gulino: drew dot gulino at gmail dot com
%% Created: Mar 30, 2011
%% Description: Process to auto reload compiled code in shell

-module(reload).

%%
%% Exported Functions
%%
-export([start/0, start/1, stop/0, reloadAll/0]).

%% API Functions
start() ->
    start(1000).

start(Milliseconds) ->
    register(reload, spawn_link(fun() -> loop(Milliseconds) end)).

stop() ->
    throw(reload_stopped).

%% Local Functions
loop(Milliseconds) ->
    reloadAll(),
    timer:sleep(1000),
    loop(Milliseconds).

reload(M) ->
    code:purge(M),
    code:soft_purge(M),
    {module, M} = code:load_file(M),
    {ok, M}.

reloadAll() ->
    Modules = [M || {M, P} <- code:all_loaded(), is_list(P)
        andalso M =/= reload
        andalso string:str(P, "/home/drew/workspace/erlang/") > 0],
    [reload(M) || M <- Modules].

No comments: