MobDev.MLXDownloader (mob_dev v0.5.11)

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

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

Download URL for the target tarball.

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

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.

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