<!--
%%
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 2025. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
-->

Cross Compiling Erlang/OTP - IOS
====================================

Introduction
------------

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.

### Get Xcode Tools ###

iOS binaries can only be cross-compiled from Apple macOS computers. For compilation the Xcode tools must be used.
### Environment Variables ###

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

### Configure Erlang/OTP ###

    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


### Compile Erlang/OTP ###

    $ ./otp_build boot

### Linking a binary with your App ###
    
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

## Starting Erlang

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);

### Reference Example

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.

