MobDev.NdkVersion (mob_dev v0.5.0)

Copy Markdown View Source

Single source of truth for the Android NDK version Mob's bundled OTP runtime was cross-compiled against.

The on-device libbeam.a (in the otp-android-* tarballs) embeds C++ stdlib symbols using libc++'s versioned inline namespace. NDK 27.2 uses std::__ne180000::; NDK 25 uses std::__ne140000::. These don't link cross-version: an app's libpigeon.so built with the wrong NDK fails with undefined symbol: __cxa_allocate_exception (or similar libc++ ABI symbols).

This module is consulted by:

  • mix mob.doctor — checks the recommended NDK is installed and that the project's gradle pin (or override) doesn't drift.
  • mix mob.install — same check during onboarding.
  • mix mob.new's gradle template (via MobNew.NdkVersion) — sets the ndkVersion literal so AGP picks deterministically.
  • scripts/release/openssl/*.sh — sources NDK_VERSION from _lib.sh so the host-side OpenSSL cross-compile uses the same NDK as the bundled tarballs.

recommended/0 is the version Mob's tarballs were built against. It changes only when we cross-compile new tarballs.

effective/0 returns the recommended version unless the user has overridden it. Two override mechanisms:

  1. Environment variable (MOB_ANDROID_NDK_VERSION=...) — machine-local. Use when one developer needs a specific NDK on their box and the team's project config should stay clean.

  2. Per-project config in mob.exs:

    config :mob_dev,
      android_ndk_version: "25.1.8937393"

    Travels with the project. Use when the whole team needs to build against a non-recommended NDK (legacy library dependency, hardware-specific toolchain, etc).

Precedence: env var > mob.exs > recommended.

Override caveat

When an override is active the user opts out of the libc++ ABI guarantee against the bundled tarballs. They're navigating that alone — mob.doctor warns but does not fail. Cryptic link errors against libbeam.a are then their problem to debug. See ~/code/mob/common_fixes.md "NDK 27 / clang 18 split libc++" for the symptom and the diagnostic.

Summary

Functions

The NDK version the user's build should target.

Build the suggested install command for the recommended NDK. Used by mob.doctor and mix mob.install to give the user a one-liner.

True if the given NDK version is installed under the local Android SDK.

Returns all NDK versions present under the local SDK, newest-first by string sort.

Returns {:env, version}, {:mob_exs, version}, or :none describing which override mechanism is active (if any).

Reads the project's android/app/build.gradle (or .kts) for the ndkVersion literal. Returns the string or nil if not pinned.

The NDK version the bundled OTP tarballs were cross-compiled with.

Returns the absolute path to the recommended NDK install if present, or nil.

Functions

effective()

@spec effective() :: String.t()

The NDK version the user's build should target.

Returns recommended/0 unless overridden via MOB_ANDROID_NDK_VERSION env var or :android_ndk_version in mob.exs's :mob_dev config.

install_command()

@spec install_command() :: String.t()

Build the suggested install command for the recommended NDK. Used by mob.doctor and mix mob.install to give the user a one-liner.

installed?(version)

@spec installed?(String.t()) :: boolean()

True if the given NDK version is installed under the local Android SDK.

Looks for <sdk>/ndk/<version>/source.properties since the directory alone can be a half-extracted partial install.

installed_versions()

@spec installed_versions() :: [String.t()]

Returns all NDK versions present under the local SDK, newest-first by string sort.

override()

@spec override() :: {:env | :mob_exs, String.t()} | :none

Returns {:env, version}, {:mob_exs, version}, or :none describing which override mechanism is active (if any).

Used by mob.doctor to explain why the effective version differs from the recommendation.

project_pinned(project_root \\ File.cwd!())

@spec project_pinned(String.t()) :: String.t() | nil

Reads the project's android/app/build.gradle (or .kts) for the ndkVersion literal. Returns the string or nil if not pinned.

Accepts an optional project root; defaults to the current working directory.

recommended()

@spec recommended() :: String.t()

The NDK version the bundled OTP tarballs were cross-compiled with.