MobDev.MLXDownloader (mob_dev v0.7.7)

Copy Markdown View Source

Downloads and caches pre-built Apple MLX + EMLX NIF static archives so iOS Mob apps can ship EMLX.Backend as an Nx backend without cross-compiling MLX themselves.

Mirrors the MobDev.OtpDownloader / MobDev.PythonAppleSupport pattern: hashed URL + cached download at ~/.mob/cache/mlx-<version>-<target>/, validated against the expected layout. Reused across projects.

Used by MobDev.NativeBuild when the project's deps include :emlx or mob.exs declares mlx_enabled: true. The build template sources MLX_DIR from dir/1 and links libmlx.a + libemlx.a from there.

Scope (v1)

CPU-only MLX. Metal-on-iOS is gated behind a separate tarball variant (libmlx-<ver>-ios-device-metal.tar.gz) that requires the iOS-Metal CMakeLists patch and the Xcode Metal Toolchain installed at build time. v1 ships CPU + Accelerate framework — already ~10-50x faster than Nx.BinaryBackend for typical Nx workloads.

Local-build override

Set MOB_MLX_LOCAL_TARBALL_DIR=/path/to/dir to bypass the GitHub download and use locally-built tarballs (named exactly as tarball_name/1 returns). Useful when iterating on the cross-compile scripts in mob_dev/scripts/release/mlx/.

Summary

Types

Target slice this downloader supports.

Functions

Checks a project's resolved EMLX version against the bundle's libemlx.a. Returns {:error, message} with the migration steps when they don't match, because the app would link but EMLX.NIF would fail to load at runtime. nil (version couldn't be determined) passes.

Cached MLX root directory for target. May not exist if ensure/1 hasn't been called.

Download URL for the target tarball.

The :emlx dependency requirement this bundle's libemlx.a matches (e.g. "~> 0.2.0").

Ensure the MLX bundle for target is cached and extracted. Returns {:ok, path} where path is the unpacked root containing lib/libmlx.a, lib/libemlx.a, include/mlx/..., VERSION.

Convenience for iOS device.

Convenience for iOS simulator.

Path to mlx.metallib if this bundle ships Metal GPU kernels, or nil if it's a CPU-only bundle.

Pinned MLX upstream version (e.g. 0.25.1).

Bundle name (no extension, no path) for target — libmlx-0.25.1-ios-device etc.

GitHub release tag this downloader targets.

Tarball file name for target (e.g. libmlx-0.25.1-ios-device.tar.gz).

Returns true if the cache directory has the expected layout (lib/libmlx.a, lib/libemlx.a, include/mlx/, VERSION).

Types

target()

@type target() :: :ios_device | :ios_sim

Target slice this downloader supports.

Functions

check_emlx_version(vsn)

@spec check_emlx_version(String.t() | nil) :: :ok | {:error, String.t()}

Checks a project's resolved EMLX version against the bundle's libemlx.a. Returns {:error, message} with the migration steps when they don't match, because the app would link but EMLX.NIF would fail to load at runtime. nil (version couldn't be determined) passes.

dir(target)

@spec dir(target()) :: String.t()

Cached MLX root directory for target. May not exist if ensure/1 hasn't been called.

download_url(target)

@spec download_url(target()) :: String.t()

Download URL for the target tarball.

emlx_requirement()

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

The :emlx dependency requirement this bundle's libemlx.a matches (e.g. "~> 0.2.0").

ensure(target)

@spec ensure(target()) :: {:ok, String.t()} | {:error, term()}

Ensure the MLX bundle for target is cached and extracted. Returns {:ok, path} where path is the unpacked root containing lib/libmlx.a, lib/libemlx.a, include/mlx/..., VERSION.

ensure_ios_device()

@spec ensure_ios_device() :: {:ok, String.t()} | {:error, term()}

Convenience for iOS device.

ensure_ios_sim()

@spec ensure_ios_sim() :: {:ok, String.t()} | {:error, term()}

Convenience for iOS simulator.

metallib_path(dir)

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

Path to mlx.metallib if this bundle ships Metal GPU kernels, or nil if it's a CPU-only bundle.

The CPU build (scripts/release/mlx/ios_device.sh) doesn't produce a metallib. The Metal build (ios_device_metal.sh) puts one at lib/mlx.metallib alongside the static archives. Callers use this to decide whether to copy the metallib into the iOS .app bundle so EMLX's runtime device: :gpu path can find it (via MLX's load_colocated_library).

mlx_version()

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

Pinned MLX upstream version (e.g. 0.25.1).

name(atom)

@spec name(target()) :: String.t()

Bundle name (no extension, no path) for target — libmlx-0.25.1-ios-device etc.

release_tag()

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

GitHub release tag this downloader targets.

tarball_name(target)

@spec tarball_name(target()) :: String.t()

Tarball file name for target (e.g. libmlx-0.25.1-ios-device.tar.gz).

valid_dir?(dir)

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

Returns true if the cache directory has the expected layout (lib/libmlx.a, lib/libemlx.a, include/mlx/, VERSION).

Public for testing and so NativeBuild can cheaply probe for a partial cache without parsing the VERSION file.