This document describes how to cross compile Erlang/OTP to iOS platforms. iOS has the peculiarity that it does not support the creation of shared libraries. So the build process needs to be amended to generate statically linkable and executable Erlang binaries.
iOS binaries can only be cross-compiled from Apple macOS computers. For compilation the Xcode tools must be used.
In order to inform the build system that it should generate the static linkable libbeam.a
add the following environment variable during configuration and compilation:
$ export RELEASE_LIBBEAM=yes
To build without OpenSSL support, run `configure` like this:
$ ./otp_build configure \
--xcomp-conf=./xcomp/erl-xcomp-arm64-ios.conf \
--without-ssl
To build with OpenSSL statically linked, run configure
like this (note that 1.1.1k works, but needs a manual patch in ios cross build config to include engines):
$ ./otp_build configure \
--xcomp-conf=./xcomp/erl-xcomp-arm64-ios.conf \
--with-ssl=/path/to/libcrypto.a \
--disable-dynamic-ssl-lib
$ ./otp_build boot
To use the resulting libbeam.a
it needs to be packaged into a Xcode .xcframework
package together with all other required .a
files. These include:
libbeam.a
libz.a
libcrypto.a
Basically all .a files that have been generated by the build.
One way of creating a combined archive out of all of these is to use libtool
:
$ libtool -static -o liberlang.a /Path/to/libbeam.a /Path/to/....a
This list should also include static nifs you want to include:
Finally packaging the archive into a .xcframework
directory:
$ xcodebuild -create-xcframework -output ./liberlang.xcframework -library liberlang.a
If you're building for multiple iOS targets (e.g. simulator and phone):
$ xcodebuild -create-xcframework -output ./liberlang.xcframework -library /Path/to/phonelib/liberlang.a -library /Path/to/simulatorlib/liberlang.a
To execute erlang from within an iOS project a native C/C++ wrapper is needed that can call the function:
erl_start(int argc, char *argv[]);
An example call could look like this:
$ const char *args[] = {
"my_main",
"-sbwt",
"none",
"--",
"-root",
root_dir.c_str(),
"-progname",
"erl",
"--",
"-home",
home_dir.c_str(),
"--",
"-kernel",
"shell_history",
"enabled",
"--",
"-start_epmd",
"false",
"-elixir",
"ansi_enabled",
"true",
"-noshell",
"-s",
"elixir",
"start_cli",
"-mode",
"interactive",
"-config",
config_path.c_str(),
"-boot",
boot_path.c_str(),
"-boot_var",
"RELEASE_LIB",
lib_path.c_str(),
"--",
"--",
"-extra",
"--no-halt",
};
erl_start(sizeof(args) / sizeof(args[0]), (char **)args);
At the time of writing (October 2021) there is a full reference iOS application available at https://github.com/elixir-desktop/ios-example-app.
An implementation of the native wrapper can be viewed at https://github.com/elixir-desktop/ios-example-app/blob/main/native-lib.cpp.