Is there any way to make cross-platform releases?

Hello,

I am doing some experiments with Elixir and I encountered an issue when I tried to execute a package. I built the package with exrm on my Mac and I tried to run it on my Raspberry Pi but all I got was this error message erts-8.0.2/bin/erlexec: line 1: syntax error: unexpected "(".

I Googled it and it looks like releases are OS/architecture specific. Elixir runs on the BEAM VM and, if I’m not wrong, it should use a platform independent byte code.

Is it possible to get exrm (or another packaging tool) to produce cross-platform executables?

If not I can setup a build container on my Raspberry Pi, but that’s a bit annoying.

5 Likes

In summary, while indeed the BEAM is a cross-platform bytecode platform, it is possible to call NIFs (Natively Implemented Functions) from it. These native dependencies (often compiled C libraries) usually are platform specific.

What also might play a role, is that a release usually contains the complete binary that runs the BEAM as well, which definitely is platform-specific.

There are two possibilities to make executables:

  1. Make a release on the same kind of platform as the target platform. Then you can get ‘true’ executable files.
  2. Make an escript archive of your code (mix escript.build). This packaged code basically is BEAM bytecode, but it still needs a compatible version of Erlang to be installed on the system you want to execute it on, so it is not fully an out-of-the-box executable.
1 Like

Thanks, I’ll have a look at escript

1 Like

Yes, it is absolutely possible to build releases cross-platform. You need to get the ERTS directory from the Erlang installation on the Pi (in other words, the root Erlang directory), and then configure your release to reference that ERTS when packaging (in the case of exrm, via rel/relx.config) with {include_erts, "path/to/pi_erts"}.. When you build your release, you can then deploy the tarball to the Pi and you’ll be set.

If you consume any NIFs, you will need a cross-compilation toolchain for those, which is really something you have to sort out based on the NIF and it’s build requirements.

2 Likes

Another option is to look into Nerves (nerves-project.org). It basically does what bitwalker says with the addition that it produces a bootable SDCard image for the Pi.

4 Likes

+1 to using Nerves for this!

1 Like

Thank you. Both grabbing the ERTS and using Nerves seem very good options.

Since I’d like to install my app alongside other things I’ll probably go for the former.

1 Like

So, I tried a few things but I’ve not been very successful.

Compilation on the Raspberry Pi worked fine, albeit it is very slow. Not that I wasn’t expecting to be fast, that’s way I’d cross compile if I manage to make it work. Since this is a toy project I can definitely live with it, but I’d like to do things properly.

I copying the files over from the Raspberry Pi and using {include_erts, "COPIED/FROM/RPI"} failed because of Unable to load asn1 nif library. Failed with error:~n"~p, ~s"~n In my previous post I missed to mention that I am building a Phoenix app. I’m not using any native dependency other than the ones already required by Phoenix and I guess this is something that Phoenix needs.

Googling I found that I can try to use whatever version of erts is available on the host device with {include_erts, false}. It failed even earlier with error msg: {"init terminating in do_boot",{load_failed,[supervisor,proc_lib,lists,gen_server,gen_event,gen,filename,ets,erl_parse,erl_lint,erl_eval]}

Does any of the error messages I posted suggest something to you?

3 Likes

I finally got cross compilation working thanks to this post I found.

It turns out that for applications that have binary dependencies relx.config should specify both include_erts and system_libs, making them point to the path containing the erlang grabbed from the device and its libraries.

{include_erts, "PATH/TO/ERLANG_FROM_DEVICE"}.
{system_libs, "PATH/TO/ERLANG_FROM_DEVICE/lib"}.
2 Likes