cmdfiles

Scott Lystig Fritchie <>
Sat Jan 29 23:29:04 CET 2000


>>>>> "cm" == Chandrashekhar M <> writes:

cm> You'll have to make a boot file for your application and use the
cm> -boot option of erl.

I was wondering how the heck the Erlang application packaging stuff
worked.  The Erlang documentation works fairly well as a reference,
but it's short on tutorial.  While I'm certainly no expert with Erlang
packaging, I've managed to figure out enough to get an application to
run all on its own, and enough to write....

The Guerrilla 10 Minute Erlang Packaging School
===============================================

(Constructive criticism is welcome.)

As an example, I've got a simple incrementing counter server that I'd
like to run non-interactively.  I might want to extend the application
later to include a decrementing counter, or a counter that I could
specify my own increment, or perhaps a counter in floating point, or
something else ... but I'll worry about that later.

The source code is split into two directory hierarchies.

	count_server
		Contains the application stuff.  If I want to have
		other counter types later, this is where they'll be
		started and managed.  If I add more, I'd probably want
		to use a supervisor tree to make things more
		manageable, and I'd put the supervisor stuff here, too.
	increment
		Contains the code for the simple incrementing server.

Since this is a really simple example, there is only one Erlang source
file in each hierarchy.  Hopefully they are enough for demonstration
purposes.

When I was writing & debugging this code, the interactive shell was
the tool to use.  The directory I worked out of was count_server/src,
and in it I had a .erlang file to assist code auto-loading:

	code:add_path("../ebin").    
	code:add_path("../../increment/ebin").

When it comes time to try to run this non-interactively, the steps I
took were:

	1. Create a .app file for each hierarchy in the "ebin" subdir.
	systools:make_script() seems to want the .app file in "ebin"
	instead of "src".  I'd prefer "src", but I haven't figured out
	how.  Perhaps if I look at the Eddie source distribution a bit
	harder....

	2. Create a .rel file for the application.

	3. Generate the .boot file.

	4. Run the silly thing.

	5. Test it.

All of the files I mention are included in a "shar" archive below.  My
apologies to Windows users, but it shouldn't be too difficult to pick
apart the archive manually.

1. Creating the .app files.

The two I created are count_server/ebin/count_server.app and
increment/ebin/increment.app.  I don't fully understand all of the
entries, so for those that I also thought were mandatory, I just stuck
something in using "HUH" as a marker that I don't know what I'm doing.

The count_server/ebin/count_server.app file looks like:

	{application, count_server,
	 [
	  {description, "Ridiculously brute-force Erlang packaging example"},
	  {vsn, "0.01"},
	  {id, "count_server HUH"},
	  {modules, [
	             count_server
	            ]
	  },
	  {registered, [ count_server ] },
	  %% NOTE: It seems as if you don't want to list below libraries
	  %% that are load-only.  This is only a list of applications that must
	  %% be started before this application is started.  In the case of
	  %% increment, we'll be starting it ourselves.
	  {applications, [ kernel, stdlib ] },
	
	  %% This is the statement that triggers the loading process to call
	  %% the start function (and arguments) for the application.
	  {mod, {count_server, [arg1, arg2]} }
	 ]
	}.

If the count_server app contained more than one module, I'd include
them in the "modules" list.  Most everything else is boilerplate.

The same holds true for the increment/ebin/increment.app file.  The
only significant difference is that it doesn't have a "mod" entry,
because the count_server application is responsible for starting the
increment server.

2. Create the .rel file.

This is count_server/src/count_server.rel:

	{release, {"count_server", "0.01"}, {erts, "47.4.1"},
	 [{kernel,"2.5"},
	  {stdlib,"1.8.1"},
	  {count_server, "0.01"},
	  {increment, "0.01"}]}.

This lists version dependencies.  If you're using an older Erlang
distribution, you may have to tweak the version numbers for erts,
kernel, and stdlib.  If I recall correctly, make_script will complain,
but it will tell you what versions are installed.  Edit the .rel file,
then try again.

3. Generate the .boot file.

I've put this in a Makefile rule in count_server/src/Makefile:

	count_server.boot count_server.script: $(ESRC)/count_server.rel \
	    $(EBIN)/count_server.app $(INCREMENT_EBIN)/increment.app
	        erl -pa $(EBIN) -pa $(INCREMENT_EBIN) \
	        -s systools make_script count_server -s erlang halt -noshell

The first two lines specify "make" dependencies.  The last two lines
give the "erl" command line to run systools:make_script(count_server),
then erlang:halt().

4. Run the silly thing.

Run using:

	% cd count_server/src
	% erl -boot count_server -pa ../ebin -pa ../../increment/ebin \
		-sname count -noinput

If I'd really installed the compiled .beam files in the proper place,
such as /usr/local/lib/erlang somewhere, the "-pa" flags wouldn't be
necessary.  The "-sname" flag is necessary if you want this Erlang
node to be able to communicate with other Erlang nodes.  The
"-noinput" flag keeps the interpreter from running.

To save wear and tear on your fingers and keyboard, it's probably a
good idea to put that command line in a shell/batch script.  :-)

5. Test it.

Once the "count" node is running the count_server application, I'll
start another Erlang node on the same machine, bigbird, to test it
out.

    % erl -sname foo
    Erlang (BEAM) emulator version 4.9.1 [source]
    
    Eshell V4.9.1  (abort with ^G)
    ()1> gen_server:call({increment, ''}, {get1}).
    0
    ()2> gen_server:call({increment, ''}, {get1}).
    1
    ()3> gen_server:call({increment, ''}, {get1}).
    2
    ()4> gen_server:call({increment, ''}, {reset, 40}).
    3
    ()5> gen_server:call({increment, ''}, {get1}).     
    40
    ()6> gen_server:call({increment, ''}, {get1}).
    41

-Scott
---
Scott Lystig Fritchie, <>
5401 - 10th Ave S, Minneapolis, MN 55417 USA
office: 612.827.2835, cell: 612.805.1383
Professional Governing: Is It Faked?
    
--- snip --- snip --- snip --- snip --- snip --- snip --- snip --- 

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	count_server/Makefile
#	count_server/ebin/count_server.app
#	count_server/src/Makefile
#	count_server/src/count_server.erl
#	count_server/src/count_server.rel
#	increment/Makefile
#	increment/ebin/increment.app
#	increment/src/Makefile
#	increment/src/increment.erl
#
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
	echo x - 'Makefile'
	sed 's/^X//' >'Makefile' << 'SHAR_EOF'
XSUBDIRS = increment count_server
X
Xall install clean:
X	@for d in ${SUBDIRS}; do (cd $$d; ${MAKE} $@); done
SHAR_EOF
if test 106 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 106 characters)'
fi
fi
if test ! -d 'count_server'
then
	mkdir 'count_server'
fi
if test -f 'count_server/Makefile'
then
	echo shar: will not over-write existing file "'count_server/Makefile'"
else
	echo x - 'count_server/Makefile'
	sed 's/^X//' >'count_server/Makefile' << 'SHAR_EOF'
XSUBDIRS = src
X
Xall install clean:
X	@for d in ${SUBDIRS}; do (cd $$d; ${MAKE} $@); done
SHAR_EOF
if test 87 -ne "`wc -c < 'count_server/Makefile'`"
then
	echo shar: error transmitting "'count_server/Makefile'" '(should have been 87 characters)'
fi
fi
if test ! -d 'count_server'
then
	mkdir 'count_server'
fi
if test ! -d 'count_server/ebin'
then
	mkdir 'count_server/ebin'
fi
if test -f 'count_server/ebin/count_server.app'
then
	echo shar: will not over-write existing file "'count_server/ebin/count_server.app'"
else
	echo x - 'count_server/ebin/count_server.app'
	sed 's/^X//' >'count_server/ebin/count_server.app' << 'SHAR_EOF'
X{application, count_server,
X [
X  {description, "Ridiculously brute-force Erlang packaging example"},
X  {vsn, "0.01"},
X  {id, "count_server HUH"},
X  {modules, [
X	     count_server
X	    ]
X  },
X  {registered, [ count_server ] },
X  %% NOTE: It seems as if you don't want to list below libraries
X  %% that are load-only.  This is only a list of applications that must
X  %% be started before this application is started.  In the case of
X  %% increment, we'll be starting it ourselves.
X  {applications, [ kernel, stdlib ] },
X
X  %% This is the statement that triggers the loading process to call
X  %% the start function (and arguments) for the application.
X  {mod, {count_server, [arg1, arg2]} }
X ]
X}.
X
SHAR_EOF
if test 695 -ne "`wc -c < 'count_server/ebin/count_server.app'`"
then
	echo shar: error transmitting "'count_server/ebin/count_server.app'" '(should have been 695 characters)'
fi
fi
if test ! -d 'count_server'
then
	mkdir 'count_server'
fi
if test ! -d 'count_server/src'
then
	mkdir 'count_server/src'
fi
if test -f 'count_server/src/Makefile'
then
	echo shar: will not over-write existing file "'count_server/src/Makefile'"
else
	echo x - 'count_server/src/Makefile'
	sed 's/^X//' >'count_server/src/Makefile' << 'SHAR_EOF'
XESRC = .
XEBIN = ../ebin
XINCREMENT_EBIN = ../../increment/ebin
X
XERL_FLAGS = -W
X
Xall: $(EBIN)/count_server.beam count_server.boot count_server.script
X
X$(EBIN)/count_server.beam: $(ESRC)/count_server.erl
X	erlc $(ERL_FLAGS) -o$(EBIN) $(ESRC)/count_server.erl
X
Xcount_server.boot count_server.script: $(ESRC)/count_server.rel \
X    $(EBIN)/count_server.app $(INCREMENT_EBIN)/increment.app
X	erl -pa $(EBIN) -pa $(INCREMENT_EBIN) \
X	-s systools make_script count_server -s erlang halt -noshell
X
Xclean:
X	rm -f $(EBIN)/*.beam count_server.boot count_server.script
X
SHAR_EOF
if test 555 -ne "`wc -c < 'count_server/src/Makefile'`"
then
	echo shar: error transmitting "'count_server/src/Makefile'" '(should have been 555 characters)'
fi
fi
if test ! -d 'count_server'
then
	mkdir 'count_server'
fi
if test ! -d 'count_server/src'
then
	mkdir 'count_server/src'
fi
if test -f 'count_server/src/count_server.erl'
then
	echo shar: will not over-write existing file "'count_server/src/count_server.erl'"
else
	echo x - 'count_server/src/count_server.erl'
	sed 's/^X//' >'count_server/src/count_server.erl' << 'SHAR_EOF'
X%%%----------------------------------------------------------------------
X%%% File    : count_server.erl
X%%% Author  : Scott Lystig Fritchie <>
X%%% Purpose : Brute-force demo Erlang packaging and boot file making.
X%%%----------------------------------------------------------------------
X
X-module(count_server).
X-author('').
X-compile([verbose, report_errors, report_warnings, trace]).
X-vsn("0.01").
X
X-behaviour(application).
X
X%% application callbacks
X-export([start/2, stop/1]).
X
X%%%----------------------------------------------------------------------
X%%% Callback functions from application
X%%%----------------------------------------------------------------------
X
X%%----------------------------------------------------------------------
X%% Func: start/2
X%% Returns: {ok, Pid}        |
X%%          {ok, Pid, State} |
X%%          {error, Reason}   
X%%----------------------------------------------------------------------
Xstart(Type, StartArgs) ->
X    %% Trying to be simple here, so won't use a supervisor tree.
X    case increment:start_link() of
X	{ok, Pid} -> 
X	    {ok, Pid};
X	Error ->
X	    Error
X    end.
X
X%%----------------------------------------------------------------------
X%% Func: stop/1
X%% Returns: any 
X%%----------------------------------------------------------------------
Xstop(State) ->
X    ok.
X
X%%%----------------------------------------------------------------------
X%%% Internal functions
X%%%----------------------------------------------------------------------
SHAR_EOF
if test 1523 -ne "`wc -c < 'count_server/src/count_server.erl'`"
then
	echo shar: error transmitting "'count_server/src/count_server.erl'" '(should have been 1523 characters)'
fi
fi
if test ! -d 'count_server'
then
	mkdir 'count_server'
fi
if test ! -d 'count_server/src'
then
	mkdir 'count_server/src'
fi
if test -f 'count_server/src/count_server.rel'
then
	echo shar: will not over-write existing file "'count_server/src/count_server.rel'"
else
	echo x - 'count_server/src/count_server.rel'
	sed 's/^X//' >'count_server/src/count_server.rel' << 'SHAR_EOF'
X{release, {"count_server", "0.01"}, {erts, "47.4.1"},
X [{kernel,"2.5"},
X  {stdlib,"1.8.1"},
X  {count_server, "0.01"},
X  {increment, "0.01"}]}.
SHAR_EOF
if test 143 -ne "`wc -c < 'count_server/src/count_server.rel'`"
then
	echo shar: error transmitting "'count_server/src/count_server.rel'" '(should have been 143 characters)'
fi
fi
if test ! -d 'increment'
then
	mkdir 'increment'
fi
if test -f 'increment/Makefile'
then
	echo shar: will not over-write existing file "'increment/Makefile'"
else
	echo x - 'increment/Makefile'
	sed 's/^X//' >'increment/Makefile' << 'SHAR_EOF'
XSUBDIRS = src
X
Xall install clean:
X	@for d in ${SUBDIRS}; do (cd $$d; ${MAKE} $@); done
SHAR_EOF
if test 87 -ne "`wc -c < 'increment/Makefile'`"
then
	echo shar: error transmitting "'increment/Makefile'" '(should have been 87 characters)'
fi
fi
if test ! -d 'increment'
then
	mkdir 'increment'
fi
if test ! -d 'increment/ebin'
then
	mkdir 'increment/ebin'
fi
if test -f 'increment/ebin/increment.app'
then
	echo shar: will not over-write existing file "'increment/ebin/increment.app'"
else
	echo x - 'increment/ebin/increment.app'
	sed 's/^X//' >'increment/ebin/increment.app' << 'SHAR_EOF'
X{application, increment,
X [
X  {description, "Ridiculously brute-force Erlang packaging example"},
X  {vsn, "0.01"},
X  {id, "increment HUH"},
X  {modules, [
X  	     increment
X	    ]
X  },
X  {registered, [  ] },
X  %% NOTE: It seems as if you don't want to list below libraries
X  %% that are load-only.  This is only a list of applications that must
X  %% be started before this application is started.  In the case of
X  %% incr_server, we'll be starting it ourselves.
X  {applications, [ kernel, stdlib ] }
X
X  %% We don't need a mod item to start this app.
X ]
X}.
X
SHAR_EOF
if test 557 -ne "`wc -c < 'increment/ebin/increment.app'`"
then
	echo shar: error transmitting "'increment/ebin/increment.app'" '(should have been 557 characters)'
fi
fi
if test ! -d 'increment'
then
	mkdir 'increment'
fi
if test ! -d 'increment/src'
then
	mkdir 'increment/src'
fi
if test -f 'increment/src/Makefile'
then
	echo shar: will not over-write existing file "'increment/src/Makefile'"
else
	echo x - 'increment/src/Makefile'
	sed 's/^X//' >'increment/src/Makefile' << 'SHAR_EOF'
XESRC = .
XEBIN = ../ebin
X
XERL_FLAGS = -W
X
Xall: $(EBIN)/increment.beam
X
X$(EBIN)/increment.beam: $(ESRC)/increment.erl
X	erlc $(ERL_FLAGS) -o$(EBIN) $(ESRC)/increment.erl
X
Xclean:
X	rm -f $(EBIN)/*.beam
X
SHAR_EOF
if test 198 -ne "`wc -c < 'increment/src/Makefile'`"
then
	echo shar: error transmitting "'increment/src/Makefile'" '(should have been 198 characters)'
fi
fi
if test ! -d 'increment'
then
	mkdir 'increment'
fi
if test ! -d 'increment/src'
then
	mkdir 'increment/src'
fi
if test -f 'increment/src/increment.erl'
then
	echo shar: will not over-write existing file "'increment/src/increment.erl'"
else
	echo x - 'increment/src/increment.erl'
	sed 's/^X//' >'increment/src/increment.erl' << 'SHAR_EOF'
X%%%----------------------------------------------------------------------
X%%% File    : increment.erl
X%%% Author  : Scott Lystig Fritchie <>
X%%% Purpose : A brute-force attempt to show how quickly Erlang's VM can
X%%%           handle a couple of processes busily chatting with each other.
X%%%----------------------------------------------------------------------
X
X-module(increment).
X-compile([verbose, report_errors, report_warnings, trace]).
X-behaviour(gen_server).
X
X-define(NAME, ?MODULE).
X-define(Timeout, infinity).
X
X%% External exports
X-export([start_link/0]).
X-export([get1/0, get_many/1, reset/1]).
X
X%% gen_server callbacks
X-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
X	code_change/3]).
X
X%%%----------------------------------------------------------------------
X%%% API
X%%%----------------------------------------------------------------------
Xstart_link() ->
X    gen_server:start_link({local, ?NAME}, ?MODULE, [], []).
X
Xget1() ->
X    Timeout = ?Timeout,
X    gen_server:call(?NAME, {get1}, Timeout).
X
Xget_many(Total) ->
X    get_many(0, Total, now()).
Xget_many(Total, Total, {_, StartS, StartMs}) ->
X    %% Ignoring Megaseconds not good but it's simple....
X    {_, FinS, FinMs} = now(),
X    Finish = FinS * 1000000 + FinMs,
X    Start = StartS * 1000000 + StartMs,
X    io:format("Start = ~w, Finish = ~w\n", [Start, Finish]),
X    io:format("Looped ~w times in ~w secs, ~w usec/loop\n",
X	      [Total, (Finish - Start) / 1000000, (Finish - Start) / Total]),
X    ok;
Xget_many(N, Total, Start) ->
X    get(),
X    get_many(N + 1, Total, Start).
X
Xreset(N) ->
X    Timeout = ?Timeout,
X    gen_server:call(?NAME, {reset, N}, Timeout).
X
X%%%----------------------------------------------------------------------
X%%% Callback functions from gen_server
X%%%----------------------------------------------------------------------
X
X%%----------------------------------------------------------------------
X%% Func: init/1
X%% Returns: {ok, State}          |
X%%          {ok, State, Timeout} |
X%%          ignore               |
X%%          {stop, Reason}
X%%----------------------------------------------------------------------
Xinit([]) ->
X    {ok, 0}.
X
X%%----------------------------------------------------------------------
X%% Func: handle_call/3
X%% Returns: {reply, Reply, State}          |
X%%          {reply, Reply, State, Timeout} |
X%%          {noreply, State}               |
X%%          {noreply, State, Timeout}      |
X%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
X%%          {stop, Reason, State}            (terminate/2 is called)
X%%----------------------------------------------------------------------
Xhandle_call({get1}, From, State) ->
X    {reply, State, State + 1};
X
Xhandle_call({reset, N}, From, State) ->
X    {reply, State, N}.
X
X%%----------------------------------------------------------------------
X%% Func: handle_cast/2
X%% Returns: {noreply, State}          |
X%%          {noreply, State, Timeout} |
X%%          {stop, Reason, State}            (terminate/2 is called)
X%%----------------------------------------------------------------------
Xhandle_cast(Msg, State) ->
X    error_logger:info_msg("~w: ~s:handle_cast got ~w\n",
X			  [self(), ?MODULE, Msg]),
X    {noreply, State}.
X
X%%----------------------------------------------------------------------
X%% Func: handle_info/2
X%% Returns: {noreply, State}          |
X%%          {noreply, State, Timeout} |
X%%          {stop, Reason, State}            (terminate/2 is called)
X%%----------------------------------------------------------------------
Xhandle_info(Info, State) ->
X    error_logger:info_msg("~w: ~s:handle_info got ~w\n",
X			  [self(), ?MODULE, Info]),
X    {noreply, State}.
X
X%%----------------------------------------------------------------------
X%% Func: terminate/2
X%% Purpose: Shutdown the server
X%% Returns: any (ignored by gen_server)
X%%----------------------------------------------------------------------
Xterminate(Reason, State) ->
X    ok.
X
X%%----------------------------------------------------------------------
X%% Func: code_change/3
X%% Purpose: Preserve server state across code upgrades
X%% Returns: {ok, NewState}
X%%----------------------------------------------------------------------
Xcode_change(OldVsn, State, Extra) ->
X    {ok, State}.
X
X%%%----------------------------------------------------------------------
X%%% Internal functions
X%%%----------------------------------------------------------------------
SHAR_EOF
if test 4473 -ne "`wc -c < 'increment/src/increment.erl'`"
then
	echo shar: error transmitting "'increment/src/increment.erl'" '(should have been 4473 characters)'
fi
fi
echo Done
exit 0




More information about the erlang-questions mailing list