Making Erlang package management easy with EPM
Categories: erlang, epm
» http://github.com/JacobVorreuter/epm
A Quick Look
jvorreuter$ ./epm install dynamic_compile
epm v0.1.0, 2010
===============================
Install the following packages?
===============================
+ JacobVorreuter-dynamic_compile-master
[y/n] y
+ downloading http://github.com/JacobVorreuter/dynamic_compile/tarball/master
+ running dynamic_compile build command
+ running dynamic_compile install command
jvorreuter$ ./epm info dynamic_compile
epm v0.1.0, 2010
===============================
INSTALLED
===============================
name: dynamic_compile
owner: JacobVorreuter
vsn: master
install dir: /Users/jvorreuter/dev/dynamic_compile-0.1
homepage:
description: compile and load erlang modules from string input
jvorreuter$ ./epm remove dynamic_compile
epm v0.1.0, 2010
===============================
Remove the following packages?
===============================
+ JacobVorreuter-dynamic_compile-master
[y/n] y
+ removing package JacobVorreuter-dynamic_compile-master from /Users/jvorreuter/dev/dynamic_compile-0.1
Download
EPM is an executable escript. It has no dependencies other than Erlang. All you need to do to use it is download it:
curl "http://github.com/JacobVorreuter/epm/raw/master/epm" > epm
chmod +x epm
EPM uses GitHub
Currently, EPM only supports packages hosted on GitHub. This will probably change in the near future. Fortunately, nearly all popular Erlang apps are already on GitHub or are mirrored there. The advantage of using GitHub is that EPM is not responsible for hosting its own repository that would require maintaining.
The EPM GitHub user is given precedence
Some projects have been forked under the EPM GitHub account. If you install a project that has been forked by EPM and do not specify a user, the EPM repo will take precedence. Projects that were forked by EPM were most likely done so because they lacked an app file or did not properly list their dependencies. If you encounter a project on GitHub or even google code that does not conform to the EPM standards, feel free to let me know and I'll fork it under the EPM account.
Sometimes GitHub sucks
EPM relies on the GitHub API, which is prone to timeouts or sometimes worse. When GitHub is having problems then EPM is having problems.
Occasionally something like this will happen:
./epm install mochiweb_server_behavior
epm v0.1.0, 2010
- failed to locate remote repo for mochiweb_server_behavior
This is most likely because the GitHub API call failed. Try it again and it will probably work.
EPM is non-invasive
You don't have to change the structure of your project for it to be accessible to EPM. By default EPM assumes that a given project has no dependencies and can be built by running make in the project's root directory.
Determining dependencies
If a project relies on other projects it must list them in its epm file. EPM will look for a file named ProjectName.epm in the project's root directory. This file has the following structure:
excavator.epm:
[{deps, [
{"clones/mochiweb", []},
{"mochixpath", []},
{"dynamic_compile", []},
{"epm/etap", []},
{"mochiweb_server_behavior", []}
]}].
The argument list specifies project arguments, such as what version to install. Valid version arguments are tag, branch and sha. If the argument list is left empty the default value of {branch, "master"} is used.
example.epm:
[{deps, [
{"mochiweb", [{tag, "0.01"}]},
{"log_roller", [{branch, "stable"}]},
{"excavator", [{sha, "040092db3caa221bd0322430b580cb9f3b6ef919"}]}
]}].
Building and installing projects
The default behavior of EPM is to execute make in a project's root directory. Then the entire project directory is copied to your install location. This can be seen more clearly by including the --verbose tag when installing a package:
jvorreuter$ ./epm install emongo --verbose
epm v0.1.0, 2010
===============================
Packages already installed:
===============================
+ epm-etap-master (0.3.4)
===============================
Install the following packages?
===============================
+ JacobVorreuter-emongo-master
[y/n] y
+ downloading http://github.com/JacobVorreuter/emongo/tarball/master
+ running emongo build command
make
sh ebin/emongo.app.in 0.0.1
erl -make
Recompile: src/emongo_packet
Recompile: src/emongo_conn
Recompile: src/emongo_bson
Recompile: src/emongo_app
Recompile: src/emongo
+ running emongo test command
make test
sh ebin/emongo.app.in 0.0.1
erl -make
prove t/*.t
t/001-load..........ok
t/002-bson..........ok
t/003-find..........ok
t/004-cond-exprs....ok
All tests successful.
Files=4, Tests=65, 1 wallclock secs ( 0.60 cusr + 0.15 csys = 0.75 CPU)
+ running emongo install command
mkdir -p /Users/jvorreuter/dev/emongo-0.0.1; cp -R ./* /Users/jvorreuter/dev/emongo-0.0.1
emongo.epm looks like this:
[
{deps, [
{"etap", []}
]},
{test_command, "make test"}
].
make test is specified in the epm config file as the test command to run as part of the build process. The default make command is used to compile the code. The install command copies the project files and directories into an app directory in my ~/dev directory. This happens because I've set a custom install location in my ~/.epm file.
Customizing EPM's behavior with a ~/.epm file
my ~./epm file looks like this:
[ {build_dir, "/tmp"},
{install_dir, "/Users/jvorreuter/dev"} ].
This file is optional. If it is not present then the following values will be used by EPM:
{build_dir, "./"}
{install_dir, code:lib_dir()}
Override the default install_dir to avoid running sudo epm
The default install_dir is the value of code:lib_dir(), which on my machine is /usr/local/lib/erlang/lib and is owned by the root user. This means that epm would not have write access to that directory unless run as the super user.
Separate third-party apps from core libs and OTP apps
I recommend installing third-party apps in a location other than the value of code:lib_dir(). This will allow you to more easily manage these apps and keep your Erlang installation clean. On my machine I set the ERL_LIBS environmental variable to the same directory that I specify as my EPM install_dir:
export ERL_LIBS=/Users/jvorreuter/dev
This allows me to run epm not as the root user and it keeps the apps I install with EPM separate from system libs.
Specifying custom build commands on the command line
EPM allows you to override the prebuild, build and test commands as project args on the command line.
emongo doesn't have a configure script so this breaks:
jvorreuter$ ./epm install emongo --prebuild-command "./configure"
epm v0.1.0, 2010
...
+ downloading http://github.com/JacobVorreuter/emongo/tarball/master
+ running emongo prebuild command
- /bin/sh: line 1: ./configure: No such file or directory
now we try a non-existent make target as the build command:
jvorreuter$ ./epm install emongo --build-command "make bacon"
epm v0.1.0, 2010
...
+ downloading http://github.com/JacobVorreuter/emongo/tarball/master
+ running emongo build command
- make: *** No rule to make target `bacon'. Stop.
EPM Usage
install
Several packages can be installed at once by chaining package names together:
./epm install log_roller ibrowse etap
Also project owners can be included in the project names:
./epm install JacobVorreuter/log_roller cmullaparthi/ibrowse ngerakines/etap
Arguments can be associated with each project by placing them after the project name:
./epm install log_roller --tag 0.3 ngerakines/etap --branch stable
Non package specific tags can be included at the end of the epm command:
./epm install log_roller --tag 0.3 --verbose
By default, dependencies will be installed with packages. The --without-deps tag will prevent depedencies from being installed:
./epm install log_roller --without-deps
remove
The remove command has similar options to install. All packages that require the package being removed will also be removed:
./epm remove log_roller etap
./epm remove JacobVorreuter/log_roller
./epm remove JacobVorreuter/log_roller --tag 0.3
./epm remove log_roller --verbose
By default package dependencies will not be removed with a package. However, the --with-deps tag will remove child dependencies at the same time:
./epm remove log_roller --with-deps
info
The info command displays info about either installed or available packages:
./epm info emongo etap
===============================
INSTALLED
===============================
name: etap
owner: epm
vsn: master
install dir: /Users/jvorreuter/dev/etap-0.3.4
homepage:
description: etap is a simple erlang testing library that provides TAP compliant output.
===============================
AVAILABLE
===============================
name: emongo
owner: JacobVorreuter
followers: 25
homepage:
description: the most Emo of mongo drivers
branches:
master
list
The list command displays info about all installed packages
./epm list
===============================
INSTALLED
===============================
name: excavator
owner: JacobVorreuter
vsn: master
install dir: /Users/jvorreuter/dev/excavator-0.3
homepage:
description: An Erlang application for ingesting data from various sources (APIs, data feeds, web content, etc)
dependencies:
clones/mochiweb/master
mochixpath/master
dynamic_compile/master
epm/etap/master
mochiweb_server_behavior/master
name: mochixpath
owner: JacobVorreuter
vsn: master
install dir: /Users/jvorreuter/dev/mochixpath-0.1
homepage:
description: Mochiweb html parser xpath extension
name: dynamic_compile
owner: JacobVorreuter
vsn: master
install dir: /Users/jvorreuter/dev/dynamic_compile-0.1
homepage:
description: compile and load erlang modules from string input
name: mochiweb_server_behavior
owner: JacobVorreuter
vsn: master
install dir: /Users/jvorreuter/dev/mochiweb_server_behavior-0.1
homepage:
description: Erlang behavior for a simple mochiweb web server
dependencies:
clones/mochiweb/master
name: etap
owner: epm
vsn: master
install dir: /Users/jvorreuter/dev/etap-0.3.4
homepage:
description: etap is a simple erlang testing library that provides TAP compliant output.
name: mochiweb
owner: clones
vsn: master
install dir: /Users/jvorreuter/dev/mochiweb-0.01
homepage: http://code.google.com/p/mochiweb/
description: mochiweb clone
search
The search command searches the remote repository for available packages:
./epm search ibrowse
===============================
AVAILABLE
===============================
name: ibrowse
owner: cmullaparthi
followers: 47
homepage:
description: Erlang HTTP client
branches:
master
name: erl-couch
owner: bdionne
followers: 5
homepage:
description: erlang couchdb client based on ibrowse
branches:
master
name: ibrowse
owner: cstar
followers: 1
homepage:
description:
tags:
1.4.1-json
1.4.1
1.5.3-json
branches:
master
update
The update command re-installs the currently installed package
./epm update emongo
===============================
Update the following packages?
===============================
+ JacobVorreuter-emongo-master
([y]/n) y
+ downloading http://github.com/JacobVorreuter/emongo/tarball/master
+ running emongo build command
+ running emongo test command
+ running emongo install command
latest
The latest command downloads the latest version of epm and replaces the existing executable
jvorreuter$ ./epm latest
+ updated epm (./epm) to latest version
EPM is not a packaging and deployment solution
EPM is meant to be used for local development. It does not include functionality for packaging OTP releases or deploying releases.
EPM is beta software
Please give me input as to how to improve EPM and make it more useful.