Replaces scripts/release/xcompile_*.sh × 3 + the misplaced
scripts/release/openssl/_build_otp_android_arm64.sh. Cross-compiles
the Erlang OTP runtime for one of four target ABIs and stages the
install tree at a release root.
iex> MobDev.Release.OTP.build(:android_arm64,
...> openssl_prefix: "/tmp/openssl-android-arm64")
{:ok, %{release_root: "/tmp/otp-android", erts_vsn: "17.0", ...}}Phase 6c iter 13d deferral preserved
The C compiler stays xcrun cc / NDK clang — iter 13d's research
established that swapping in zig cc here breaks on OTP's emulator
Makefile.in dep-generation pass. This module is orchestration
only: it wraps ./otp_build configure && make with typed errors
- testable invocations, but the OTP build itself runs with the toolchain it always has.
Per-target deltas
| Target | xcomp-conf | SSL strategy | Install |
|---|---|---|---|
| android_arm64 | xcomp/erl-xcomp-arm64-android.conf | --with-ssl=<prefix> --disable-dynamic-ssl-lib | ./otp_build release -a <root> |
| android_arm32 | xcomp/erl-xcomp-arm-android.conf | same | same |
| ios_sim | xcomp/erl-xcomp-arm64-iossimulator.conf | --without-ssl (the xcomp conf sets --enable-static-nifs; OTP doesn't propagate --with-ssl to beam.emu's link in that mode) | make release RELEASE_ROOT=<root> |
| ios_device | xcomp/erl-xcomp-arm64-ios.conf | same as ios_sim | same |
Why the iOS targets diverge on install method + SSL flag
Trial-and-error knowledge from the shell scripts, preserved here inline so it's available to anyone reading the spec:
- iOS xcomp confs set
--enable-static-nifs, which static-links crypto into beam.emu at OTP-build time. OTP's build system doesn't propagate--with-ssl=<prefix>into beam.emu's link line, so--with-sslwould break with undefined references toRAND_seed/OSSL_PROVIDER_load/ etc. We side-step by building crypto separately viaMobDev.Release.OpenSSL.CryptoNif, then the tarball stage (iter 4) shipscrypto.a+libcrypto.aand the user's app links them at app-build time. make release RELEASE_ROOT=is the install incantation that works for iOS xcomp;./otp_build release -a(used by Android) hits a layout mismatch.
Don't try to "unify" these without re-running the experiments — the divergence is load-bearing.
What "build" does end-to-end
make distclean(tolerant of failure — first-time builds have nothing to clean)./otp_build configure --xcomp-conf=<conf> <ssl-flags>./otp_build boot(the long step, ~5-10 minutes)rm -rf <release_root>(idempotency — re-runs replace)- Install:
./otp_build release -a(Android) ORmake release RELEASE_ROOT=(iOS) - Verify: per-target sanity checks (release tree exists, expected
arch-specific config.h / libs are produced, Android-only:
crypto/public_key/ssl apps are present in the install tree
— i.e.
--with-sslwas wired correctly)
Summary
Functions
Cross-compile OTP for one target. Returns {:ok, info} where info
names the produced release root + erts version, or a tagged error.
Build all four targets in sequence. Returns [{target_id, result}, ...]
in canonical order. Doesn't short-circuit on first failure — callers
can decide what to do with partial results.
Assemble the ./otp_build configure argv (excluding the program) for
a target + opts. Pure for testability — silent flag drops here would
ship a runtime that can't load crypto / can't be cross-linked / etc.
Assemble the install-step argv. Returns one of
Per-target spec. Public for testing — surface lock-down.
All cross-compile targets, in canonical order.
Functions
@spec build( atom(), keyword() ) :: {:ok, map()} | MobDev.Release.Errors.t()
Cross-compile OTP for one target. Returns {:ok, info} where info
names the produced release root + erts version, or a tagged error.
Options:
:otp_src— OTP source checkout (default:$OTP_SRCenv or~/code/otp):openssl_prefix— OpenSSL install (required for Android targets; ignored for iOS):release_root— where to install (default: target'sdefault_release_root):ndk_root— Android NDK override (Android targets only)
@spec build_all(keyword()) :: [{atom(), {:ok, map()} | MobDev.Release.Errors.t()}]
Build all four targets in sequence. Returns [{target_id, result}, ...]
in canonical order. Doesn't short-circuit on first failure — callers
can decide what to do with partial results.
Per-target :openssl_prefix defaults from MobDev.Release.OpenSSL:
android_arm64→/tmp/openssl-android-arm64android_arm32→/tmp/openssl-android-arm32- iOS targets → not required (build uses
--without-ssl)
@spec configure_args(MobDev.Release.OTP.Target.t(), Path.t() | nil) :: [String.t()]
Assemble the ./otp_build configure argv (excluding the program) for
a target + opts. Pure for testability — silent flag drops here would
ship a runtime that can't load crypto / can't be cross-linked / etc.
@spec install_args(MobDev.Release.OTP.Target.t(), Path.t()) :: [String.t()]
Assemble the install-step argv. Returns one of:
["./otp_build", "release", "-a", release_root]["make", "release", "RELEASE_ROOT=" <> release_root]
@spec target_spec(atom()) :: MobDev.Release.OTP.Target.t()
Per-target spec. Public for testing — surface lock-down.
@spec targets() :: [atom()]
All cross-compile targets, in canonical order.