MobDev.Release.OTP (mob_dev v0.5.6)

Copy Markdown View Source

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

Targetxcomp-confSSL strategyInstall
android_arm64xcomp/erl-xcomp-arm64-android.conf--with-ssl=<prefix> --disable-dynamic-ssl-lib./otp_build release -a <root>
android_arm32xcomp/erl-xcomp-arm-android.confsamesame
ios_simxcomp/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_devicexcomp/erl-xcomp-arm64-ios.confsame as ios_simsame

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-ssl would break with undefined references to RAND_seed / OSSL_PROVIDER_load / etc. We side-step by building crypto separately via MobDev.Release.OpenSSL.CryptoNif, then the tarball stage (iter 4) ships crypto.a + libcrypto.a and 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

  1. make distclean (tolerant of failure — first-time builds have nothing to clean)
  2. ./otp_build configure --xcomp-conf=<conf> <ssl-flags>
  3. ./otp_build boot (the long step, ~5-10 minutes)
  4. rm -rf <release_root> (idempotency — re-runs replace)
  5. Install: ./otp_build release -a (Android) OR make release RELEASE_ROOT= (iOS)
  6. 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-ssl was 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

build(target_id, opts \\ [])

@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_SRC env or ~/code/otp)
  • :openssl_prefix — OpenSSL install (required for Android targets; ignored for iOS)
  • :release_root — where to install (default: target's default_release_root)
  • :ndk_root — Android NDK override (Android targets only)

build_all(opts \\ [])

@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-arm64
  • android_arm32/tmp/openssl-android-arm32
  • iOS targets → not required (build uses --without-ssl)

configure_args(target, openssl_prefix)

@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.

install_args(target, release_root)

@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]

target_spec(atom)

@spec target_spec(atom()) :: MobDev.Release.OTP.Target.t()

Per-target spec. Public for testing — surface lock-down.

targets()

@spec targets() :: [atom()]

All cross-compile targets, in canonical order.