What in the world is Mix? Why would anyone want to do this?
These are the sorts of questions that any readers of this post may be asking themselves based on the title. The answer to the former is that Mix is the build tool of the Elixir ecosystem. For me the answer to the second question is simply curiosity and this is a brief summary of my experience following that curiosity.
As an Erlang developer for the last five years I admit to having paid very little attention to Elixir until very recently. The inspiration to actually take a closer look came from listening to an interview with José Valim on an episode of the Functional Geekery podcast. There were several aspects of Elixir discussed that piqued my interest and one of them was the build tool known as Mix.
Mix provides several features that are appealing having experienced a lot of frustration with rebar, the most popular build tool for Erlang projects. Most of the frustration with rebar comes from the dependency handling and so it was very interesting to see what Mix offered in this area in particular. Some of the features I found notable were:
- Built-in dependency locking behavior to aid in repeatable builds.
- Ability to resolve a dependency conflict in the top level configuration by explicitly overriding any other version of the same dependency encountered in the dependency tree.
- The concept of environments (dev, test, and prod are the defaults) to facilitate things like omitting test dependencies from the development or production builds.
- An umbrella mode for creating new projects that do not directly contain source code, but are compositions of other applications and dependencies.
- Ability to specify binary packages from the Hex package manager as dependencies in addition to git repositories.
In Erlang projects that use rebar some of these things can be accomplished with plugins or various other workarounds, but I like that they are baked into Mix. I suspect many of these features will be part of rebar3, but it is still in the development stages whereas Mix can be used today. Another benefit from using Mix is the possibility for people to contribute to a project using either Erlang or Elixir. This certainly will not be applicable to all projects or desired by some, but in general I do count greater flexibility as a benefit.
Specifying a Mix configuration
After hearing the description of Mix on the podcast as mentioned previously I decided to play around with it a bit. I was very interested in the fact that Mix could be used to build regular Erlang projects so I decided to test out what it would take to actually make this happen for a real project. I have quite a bit of familiarity with Basho’s riak_cs project so I decided to use it as my test project.
First here is the rebar.config
file from riak_cs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
Now here is the mostly equivalent translation to a mix.exs
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
The two files are very similar, but the thing that sticks out most is the
cleaniness of the dependency specification in the Mix file versus the
rebar configuration. The github
shorthand is very nice since it is the
most common home for Erlang projects. Also, notice that Mix gathers the
information to generate the project’s .app
file from information in
the mix.exs
file rather than an app.src
file.
The dependencies that specify override: true
are ones that had
version conflicts and I was able to select the correct version
explicitly at the top level. This is also the reason that the set of
dependencies specified differs slightly between the rebar and Mix
configurations.
In order to build the project I also had to specify a basic mix.exs
file in the apps/riak_cs directory. Here is the listing of that file:
1 2 3 4 5 6 7 8 9 |
|
Testing it out
Elixir and Mix require at least Erlang 17 so I did have to alter the
versions some of the dependencies in order to get a working build due
to some version-related build errors and for some of the Basho
dependencies I had to manually edit the rebar.config file to allow
using version 17 or disable warnings_as_errors
(also related to
building with Erlang 17). Beyond that everything built as expected with
very minimal overall effort.
Here are the steps to follow to build the project:
mix deps.get
-
Edit
deps/velvet/rebar.config
in the following manner:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
diff --git a/rebar.config b/rebar.config index 9a03432..56d7170 100644 --- a/rebar.config +++ b/rebar.config @@ -1,10 +1,10 @@ -{require_otp_vsn, "R14B0[234]|R15|R16"}. +{require_otp_vsn, "R14B0[234]|R15|R16|17"}. {cover_enabled, true}. {lib_dirs, ["apps"]}. -{erl_opts, [debug_info, warnings_as_errors, {parse_transform, lager_transform}]}. +{erl_opts, [debug_info, {parse_transform, lager_transform}]}. {reset_after_eunit, true}.
-
Edit
deps/riakc/rebar.config
in the following manner:1 2 3 4 5 6 7 8 9 10 11 12
diff --git a/rebar.config b/rebar.config index f09a602..9765dc3 100644 --- a/rebar.config +++ b/rebar.config @@ -1,6 +1,6 @@ {cover_enabled, true}. {eunit_opts, [verbose]}. -{erl_opts, [warnings_as_errors, debug_info]}. +{erl_opts, [debug_info]}. {deps, [ {riak_pb, ".*", {git, "git://github.com/basho/riak_pb", {tag, "1.4.4.0"}}} ]}.
mix deps.compile && mix compile
That is all there is to it. The results should be in the local _build
directory.
Conclusion and further explorations
Mix appears to be a well-implemented, flexible build tool for building Elixir or Erlang projects. From an Erlang developer’s perspective, it definitely addresses some pain points that frequently arise with dependency handling with rebar.
I also like that the build tool is a core part of the Elixir ecosystem. Many Erlang projects include a copy of the rebar executable as part of the git repository because rebar is not the official Erlang build tool and is not distributed as part of the language installation. This, in my view, is a problematic and sloppy solution so Elixir definitely improves upon that with Mix.
I have not explored how Elixir/Mix handle interaction with
reltool for creating
releases yet. An interesting next step will be to explore this more
and attempt to get an actual release of the project created. One missing
thing from the rebar.config
in the example of riak_cs
is the
handling of the rebar plugin for executing customized testing
functions. Translating the plugin actions into Mix tasks would be
another interesting learning experience.