# `MobDev.Release.OTP`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/release/otp.ex#L1)

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

# `build`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

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

# `targets`

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

All cross-compile targets, in canonical order.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
