This tutorial will go through a real world example of installing Erlware development tools, creating a release, creating an application, and publishing the release and application for others to use. The full cycle.
First thing to do is install the Erlware package manager; Faxien. Download the latest Faxien bootstrap installer for your platform or the Universal Bootstrapper from Google Code. Now bootstrap with one of the following commands.
$ python ./faxien-launcher-universal.py # installs to `/usr/local/erlware by default.`
or use an arch specific bootstrapper
$ ./faxien-launcher-<os-type-info>.sh # installs to `/usr/local/erlware.`
$ ./faxien-launcher-<os-type-info>.sh /home/martinjlogan/erlware # installs into the supplied path
Tip
Add /usr/local/erlware/bin to your path so that all applications you install
with Faxien, including Faxien itself, are easily accessible via the command
line.

You need to make sure that you have permissions to write to /usr/local to
install Faxien. Linux/Unix users will probably need to use the 'sudo' command.
Without the correct permissions the install will fail. If you cannot get write
permissions to /usr/local then use the prefix option to install into another directory.
The example below demonstrates installing faxien into /home/martinjlogan/erlware.
$ ./faxien-launcher-<os-type-info>.sh /home/martinjlogan/erlware # installs into the supplied path
Once faxien is installed, assuming the default install location, you can test the installation by typing:
$ /usr/local/erlware/bin/faxien version
0.18.1
Once Faxien is installed you can use it to easily install Sinan, our flagship build system.
$ faxien install-release sinan
This will install Sinan into your erlware directory and place a link
to the Sinan executable in erlware/bin which by now should be on your
path.

If your Sinan install halts with the following error:
$ faxien install-release sinan
Installing Application sgte-0.2.1 -> ok
Installing Application projgen-0.1.3 -> ok
Installing Application syntax_tools-1.5.3 -> ok
Installing Application compiler-4.4.4 -> ok
Installing Application edoc-0.7.2 -> ok
Installing Application fconf-0.1.1 -> ok
Installing Application tools-2.5.4faxien:install/1 exited with: installation_failed
- Make sure you have the correct permissions to run Faxien
- For error_logger information look at "/usr/local/erlware/log/faxien.err_log"
- For sasl log information look at "/usr/local/erlware/log/faxien.sasl_log"
This most probably indicates that for your architecture and operating
system a compiled version of the tools application, one that Sinan
requires to function, is not present. The fix for this is easy, help
us and publish it. Enter the following command to publish the tools
application (that's if you have erlang installed at
/usr/local/lib/erlang though of course you don't have to install
Erlang from source at all with Erlware)
$ faxien publish /usr/local/lib/erlang/lib/tools-2.5.4 infinity
where infinity is the remote request timeout length. The above example is
assuming, of course, that erlang is installed at /usr/local/lib/erlang. Now
re-run the install script to see a successful installation.
The first step in building our software is creating a project to work in. A project is at its heart an organized collection of configuration and code. To create the project we will be using for this tutorial we are going to use the sinan gen utility that is packaged with the Sinan release. In this case the project will be called "full_cycle" and the only app you will include in the project when prompted will be called "hello_world".
A project is at its heart an organized collection of configuration and code. To really understand what a project is we need to dig a little deeper though. First let me say, projects are simple; just want to set the tone.
Let's change directories into our newly created project and run a directory listing.
$ cd full_cycle
$ ls # directory listing on windows you may type dir
_build.cfg doc lib
We see three names, one file, _build.cfg, and two directories, doc, and
lib. _build.cfg is the project level configuration file. Edit the file to
contain the following code:
project : {
name : full_cycle
vsn : "0.1.0"
},
repositories : ["http://repo.erlware.org/pub",
"http://www.martinjlogan.com/pub"],
_build.cfg has a number of other options allowing you to control all phases of
Sinan project interaction. For most cases however the basic file you see above
will suffice. The first thing we see is the project meta information, name
and version. Following that is a list of repositories that Sinan will seek to
download dependencies from; kernel, stdlib, sasl, gas, fslib to name a few.
The last bit of configuration for dist is out of the scope of this tutorial.
The doc directory is self explanatory so we will move on to the final
directory in our listing which is lib. So change directories into lib and do
a listing. You will see that lib contains a single directory, gas. Rename it
to hello_world, delete the erl files, and rename all other files with gas in
in the name to read hello_world. This tutorial is going to assume you know
at least a little something about application structure. If you don't you can
either look at our docs on the subject, or just keep going, you should not
have a terrible time following.
hello_world Applicationhello_world.app)First we need to edit hello_world.app our application specification file.
This file is found in hello_world/ebin/. So lets open it up:
%% This is the application resource file (.app file) for the hello_world,
%% application.
{application, hello_world,
[{description, "Your Desc HERE"},
{vsn, "0.1.0"},
{modules, []},
{registered,[hw_sup]},
{applications, [kernel, stdlib]},
{mod, {hw_app,[]}},
{start_phases, []}]}.
Replace the value for the description key with "Says hello to the world".
Notice that we have two modules already in our module listing. Sinan gen has
created our application entry point file "hello_world_app.erl" and our top
level supervisor "hello_world_sup" for us. These files are listed in
modules because Sinan will only compile files for a given application that
are listed within the modules list. The registered list tells OTP which
registered processes this application contains in order to avoid conflict with
other applications in our release. applications tells Sinan what our
dependency set is. If we were dependent on fslib and gas in addition to
kernel and stdlib we would list both additional apps under
applications. The meaning of the rest of the .app file is left as an
exercise to the reader.
Lets write our hello world code now.
Our hello world application is going to be very simple. It will contain a
single simple server that is started by the top level supervisor. This server
will respond to a message consisting of a single Pid by sending back the message
{ok, hello_world}.
Create the file hw_server.erl in full_cycle/lib/hello_world/src and enter
the following code into it.
%%%-------------------------------------------------------------------
%%% @doc The hello world server.
%%% @end
%%%-------------------------------------------------------------------
-module(hw_server).
%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
start_link/0,
init/1
]).
%%--------------------------------------------------------------------
%% macro definitions
%%--------------------------------------------------------------------
-define(SERVER, hello_world_server).
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%% @spec start_link() -> {ok, pid()} | {error, Reason}
%% @end
%%--------------------------------------------------------------------
start_link() ->
proc_lib:start(?MODULE, init, [self()]).
%%====================================================================
%% Callback Functions
%%====================================================================
%%--------------------------------------------------------------------
%% @doc Called before the process start functions can return.
%% @spec init(Parent::pid()) -> void()
%% @end
%%--------------------------------------------------------------------
init(Parent) ->
register(?SERVER, self()),
proc_lib:init_ack(Parent, {ok, self()}),
loop().
%%====================================================================
%% Internal Functions
%%====================================================================
loop() ->
receive
Pid when is_pid(Pid) -> Pid ! {ok, hello_world};
_Msg -> ok
end,
loop().
Now create the files hw_app and hw_sup which are toe be your application
and supervisor behaviour start files. The file contents are as follows:
hw_app.erl
%%%-------------------------------------------------------------------
%%% @author
%%% @doc
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(hw_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%%%===================================================================
%%% Application callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called whenever an application is started using
%% application:start/[1,2], and should start the processes of the
%% application. If the application is structured according to the OTP
%% design principles as a supervision tree, this means starting the
%% top supervisor of the tree.
%%
%% @spec start(Type, StartArgs) -> {ok, Pid} |
%% {ok, Pid, State} |
%% {error, Reason}
%% @end
%%--------------------------------------------------------------------
start(_Type, StartArgs) ->
case 'TopSupervisor':start_link(StartArgs) of
{ok, Pid} ->
{ok, Pid};
Error ->
Error
end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called whenever an application has stopped. It
%% is intended to be the opposite of Module:start/2 and should do
%% any necessary cleaning up. The return value is ignored.
%%
%% @spec stop(State) -> void()
%% @end
%%--------------------------------------------------------------------
stop(_State) ->
ok.
and hw_sup.erl
%%%-------------------------------------------------------------------
%%% @author
%%% @copyright (C) 2008,
%%% @doc
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(hw_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API functions
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Starts the supervisor
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a supervisor is started using supervisor:start_link/[2,3],
%% this function is called by the new process to find out about
%% restart strategy, maximum restart frequency and child
%% specifications.
%%
%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
RestartStrategy = one_for_one,
MaxRestarts = 1000,
MaxSecondsBetweenRestarts = 3600,
SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
Restart = permanent,
Shutdown = 2000,
Type = worker,
AChild = {'AName', {'AModule', start_link, []},
Restart, Shutdown, Type, ['AModule']},
{ok, {SupFlags, [AChild]}}.
Now if you recall Sinan will only compile modules that are listed in the
modules list within our .app file; ebin/hello_world.app.
{modules, [hw_app, hw_sup, hw_server]}, # add this entry into modules
Next we need to tell our top level supervisor to start our server. Edit
hello_world_sup.erl altering the init/1 function to look like the example
below:
init([]) ->
AChild = {hw_server,{hw_server,start_link,[]},
permanent,2000,worker,[hw_server]},
{ok,{{one_for_all,0,1}, [AChild]}}.
Now to compile
$ sinan
Using task build
Executing task discover ...
Discovering project layout and structure ...
Executing task check_depends ...
Dependencies are out of date. Should I run dependecies now? yes
Pulling eunit-2.0 from repository if non-local
Pulling stdlib-1.14.4 from repository if non-local
Pulling kernel-2.11.4 from repository if non-local
Executing task build ...
Starting compilation process ...
Building /home/martinjlogan/work/full_cycle/lib/hello_world/src/hw_app.erl
Building /home/martinjlogan/work/full_cycle/lib/hello_world/src/hw_sup.erl
Building /home/martinjlogan/work/full_cycle/lib/hello_world/src/hw_server.erl
run complete
Looks good, our application is complete. At this point we have a working
application. Lets assume that we have been good programmers and tested this
thing somewhat. Lets further assume that the full_cycle release, which
contains our hello_world application among other bits of functionality is
something that we want the community at large to be able to download and run
similar to the way we downloaded and used Sinan. To do this we need to take our
project create a release and then publish it.
The first thing we will need for a functioning release is a startup script.
Create a directory named cmds right underneath the full_cycle directory. We
are going to be launching with faxien so we don't really have to worry about any
of the platform specific shells.
Lets create the full_cycle executable start script under the bin directory.
#!/bin/sh
#### Fill in values for these variables ####
REL_NAME=full_cycle
REL_VSN=0.1.0
ERTS_VSN=5.5.5
INVOCATION_SUFFIX=""
###########################################
PROG=$0
test -h $0 && PROG=$(readlink $0)
PREFIX=$(cd $(dirname $(dirname $PROG)); pwd)
export ROOTDIR=$PREFIX/application_packages/$ERTS_VSN
export BINDIR=$PREFIX/erts_packages/erts-$ERTS_VSN/bin
export EMU=beam
export PROGNAME=erl
export LD_LIBRARY_PATH=$PREFIX/erts/$ERTS_VSN/lib
REL_DIR=$PREFIX/release_packages/$REL_NAME-$REL_VSN/release
$BINDIR/erlexec -config $REL_DIR/faxien.config -boot $REL_DIR/$REL_NAME $INVOCATION_SUFFIX
Create a release directory called full_cycle-<vsn> so in this case
full_cycle-0.1.0. This version number is essential, you cannot publish a
release if your release directory is not of the form
<release_name>-<release_vsn>.
$ sinan release
This will prompt Sinan to create a .rel file under the directory
full_cycle/_build/development/releases/0.1.0. We are going to need to copy the
entire "releases" directory under full_cycle-0.1.0. Now copy the bin
directory under full_cycle-0.1.0. Finally copy your LICENSE and README files
if you have them into full_cycle-0.1.0.
Running a directory listing in the full_cycle-0.1.0 directory should yield the
following:
$ ls
cmds LICENCE README releases
To publish our release to the world we type
$ faxien publish ./full_cycle-0.1.0
Wait a minute though, not so fast, unless you configured Faxien to publish to a repository running on your local network someone else doing this tutorial could publish right over your release by the time you try to install and run it.
So lets just pretend we published our version of full_cycle into
repo.erlware.org/writable and instead utilize the ability of Faxien to install
locally resident release packages just as well as it installs them from remote
repositories. To do this we need to take one more step before installing. We
need to create a lib directory under full_cycle-0.1.0 and place the
hello_world application underneath it. Faxien will first look in the lib
directory for applications to install and then in remote repo's for further
dependencies that do not exist within the lib directory. hello_world.app
specifies that it depends on kernel and stdlib in the applications
dependency list which are apps that are definitely present in remote
repositories which is why hello_world is the only app we need to place in the
lib directory. The compiled version of hello_world can be found in
_build/development/apps/hello_world-0.1.0. It is important to note that all
apps in published and installed packages must have the
$ faxien install-release full_cycle-0.1.0/
Installing Application hello_world-0.1.0 -> ok
Installing Release full_cycle-0.1.0 -> ok
ok
We can now stand up our release and give it a test.
When we execute the full_cycle command, which sits as a soft link in
erlware/bin, we are starting up the entire release we have created. This means
that every application in the full_cycle.rel file starts up in the order they
are listed.
$ cat /usr/local/erlware/full_cycle-0.1.0/releases/0.1.0/full_cycle.rel
{release,{"full_cycle","0.1.0"},
{erts,"5.5.5"},
[{kernel,"2.11.4"},
{stdlib,"1.14.4"},
{hello_world,"0.1.0"},
{eunit,"2.0"}]}.
The apps listed will all be started if they can be, eunit for example is not an
app that gets started but in this case it will be loaded into memory. This is a
dependency we may want to remove in the future, but for now, it does not
hurt. To start and test the release we will run the hello_world command then
fire up a Erlang shell to interact with it.
$ full_cycle
Starting Full Cycle
$ erl -name try
Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.5.5 (abort with ^G)
(try@test.erlware.org)1> net_adm:ping('full_cycle@test.erlware.org').
pong
(try@test.erlware.org)2> {hello_world_server, 'full_cycle@test.erlware.org'} ! self().
<0.37.0>
(try@test.erlware.org)3> receive Msg -> Msg end.
{ok,hello_world}
Looks like we now have the full_cycle service running on our network.
Today we have run through the full cycle of developing, publishing, and
installing an Erlang release. If we had actually run the publish command on
full_cycle-0.1.0 the rest of the world could have installed it by typing
faxien install-release full_cycle. The combination of Sinan and Faxien is
quite powerful. We have scratched the surface of what is possible with these
Erlware tools we encourage you to dig deeper into the documentation if you want
to learn more.
Happy Erlanging.
You can download the source code for the full cycle application and for the full cycle release.