Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions lib/mob_dev/native_build.ex
Original file line number Diff line number Diff line change
Expand Up @@ -495,20 +495,10 @@ defmodule MobDev.NativeBuild do
end
end

defp ndk_sysroot do
# NDK ships only one prebuilt — darwin-x86_64 even on Apple Silicon
# (Apple's Rosetta 2 covers it). On Linux it'd be linux-x86_64.
host =
case :os.type() do
{:unix, :darwin} -> "darwin-x86_64"
{:unix, :linux} -> "linux-x86_64"
other -> raise "unsupported host for NDK: #{inspect(other)}"
end

sdk_root = System.get_env("ANDROID_HOME") || Path.expand("~/Library/Android/sdk")
ndk_version = MobDev.NdkVersion.effective()
Path.join([sdk_root, "ndk", ndk_version, "toolchains", "llvm", "prebuilt", host, "sysroot"])
end
# Single source of truth in MobDev.NdkVersion (honors ANDROID_HOME /
# ANDROID_SDK_ROOT + host detection) — shared with cpp_archive / nx_eigen_nif
# so the NDK path can't diverge again (MOB-89).
defp ndk_sysroot, do: MobDev.NdkVersion.sysroot()

defp resolve_driver_tab_android(mob_dir) do
resolve_driver_tab(mob_dir, "android", ["android", "jni"])
Expand Down
46 changes: 39 additions & 7 deletions lib/mob_dev/ndk_version.ex
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,47 @@ defmodule MobDev.NdkVersion do
default_sdk_root_for_os()
end

defp default_sdk_root_for_os do
@doc false
# The Android NDK toolchain host tag — the NDK ships a single prebuilt
# (darwin-x86_64 even on Apple Silicon; Apple's Rosetta 2 covers it).
@spec host() :: String.t()
def host do
case :os.type() do
{:unix, :darwin} -> Path.expand("~/Library/Android/sdk")
{:unix, _} -> Path.expand("~/Android/Sdk")
_ -> nil
{:unix, :darwin} -> "darwin-x86_64"
{:unix, :linux} -> "linux-x86_64"
other -> raise "unsupported host for NDK: #{inspect(other)}"
end
|> then(fn path ->
if path && File.dir?(path), do: path, else: nil
end)
end

@doc false
# NDK root for the effective version, honoring ANDROID_HOME / ANDROID_SDK_ROOT
# (falls back to the OS-conventional SDK dir even when it doesn't exist, so a
# "toolchain not found at <path>" error still names a sensible location). The
# single source of truth for `native_build`, `cpp_archive`, and `nx_eigen_nif`
# — none of them should re-derive this (see MOB-89).
@spec root() :: String.t()
def root, do: Path.join([sdk_root() || os_default_sdk_dir(), "ndk", effective()])

@doc false
@spec toolchain_bin() :: String.t()
def toolchain_bin, do: Path.join([root(), "toolchains", "llvm", "prebuilt", host(), "bin"])

@doc false
@spec sysroot() :: String.t()
def sysroot, do: Path.join([root(), "toolchains", "llvm", "prebuilt", host(), "sysroot"])

# The OS-conventional SDK dir (path only, no existence check) — used by
# `root/0` as the last-resort fallback so error messages name a real path.
defp os_default_sdk_dir do
case :os.type() do
{:unix, :linux} -> Path.expand("~/Android/Sdk")
_ -> Path.expand("~/Library/Android/sdk")
end
end

defp default_sdk_root_for_os do
path = os_default_sdk_dir()
if File.dir?(path), do: path, else: nil
end

@doc """
Expand Down
11 changes: 6 additions & 5 deletions lib/mob_dev/nx_eigen_nif.ex
Original file line number Diff line number Diff line change
Expand Up @@ -455,12 +455,13 @@ defmodule MobDev.NxEigenNif do
defp ios_min_version_flag(:ios_sim), do: "-mios-simulator-version-min=#{@ios_min_version}"
defp ios_min_version_flag(:ios_device), do: "-miphoneos-version-min=#{@ios_min_version}"

defp default_ndk_root do
Path.join([System.user_home!(), "Library/Android/sdk/ndk", NdkVersion.effective()])
end
# Honors ANDROID_HOME / ANDROID_SDK_ROOT via the shared NdkVersion helper (MOB-89).
defp default_ndk_root, do: NdkVersion.root()

defp android_toolchain_bin(opts) do
ndk_root = opts[:ndk_root] || default_ndk_root()
Path.join([ndk_root, "toolchains/llvm/prebuilt/darwin-x86_64/bin"])
case opts[:ndk_root] do
nil -> NdkVersion.toolchain_bin()
root -> Path.join([root, "toolchains", "llvm", "prebuilt", NdkVersion.host(), "bin"])
end
end
end
13 changes: 8 additions & 5 deletions lib/mob_dev/plugin/cpp_archive.ex
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,15 @@ defmodule MobDev.Plugin.CppArchive do
defp ios_min_flag(:ios_sim), do: "-mios-simulator-version-min=#{@ios_min_version}"
defp ios_min_flag(:ios_device), do: "-miphoneos-version-min=#{@ios_min_version}"

defp default_ndk_root do
Path.join([System.user_home!(), "Library/Android/sdk/ndk", NdkVersion.effective()])
end
# Honors ANDROID_HOME / ANDROID_SDK_ROOT via the shared NdkVersion helper —
# do NOT re-derive the SDK path here (MOB-89: this used to hardcode
# ~/Library/Android/sdk, breaking builds where the NDK lives elsewhere).
defp default_ndk_root, do: NdkVersion.root()

defp android_toolchain_bin(opts) do
ndk_root = opts[:ndk_root] || default_ndk_root()
Path.join([ndk_root, "toolchains/llvm/prebuilt/darwin-x86_64/bin"])
case opts[:ndk_root] do
nil -> NdkVersion.toolchain_bin()
root -> Path.join([root, "toolchains", "llvm", "prebuilt", NdkVersion.host(), "bin"])
end
end
end
47 changes: 47 additions & 0 deletions test/mob_dev/ndk_version_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,51 @@ defmodule MobDev.NdkVersionTest do
assert cmd =~ NdkVersion.recommended()
end
end

# The single source of truth cpp_archive / nx_eigen_nif / native_build all use.
# MOB-89: cpp_archive + nx_eigen_nif used to hardcode ~/Library/Android/sdk,
# ignoring ANDROID_HOME — so a cpp_archive build failed wherever the NDK lived
# elsewhere. These pin that root/sysroot/toolchain honor the SDK env.
describe "root/0 + host/0 + sysroot/0 (shared NDK path — MOB-89)" do
setup do
prev_home = System.get_env("ANDROID_HOME")
prev_root = System.get_env("ANDROID_SDK_ROOT")

on_exit(fn ->
restore = fn k, v -> if v, do: System.put_env(k, v), else: System.delete_env(k) end
restore.("ANDROID_HOME", prev_home)
restore.("ANDROID_SDK_ROOT", prev_root)
end)

:ok
end

test "root/0 honors ANDROID_HOME, not a hardcoded ~/Library path" do
System.delete_env("ANDROID_SDK_ROOT")
System.put_env("ANDROID_HOME", "/opt/custom-sdk")

assert NdkVersion.root() == Path.join(["/opt/custom-sdk", "ndk", NdkVersion.effective()])
refute NdkVersion.root() =~ "Library/Android/sdk"
end

test "root/0 falls back to ANDROID_SDK_ROOT when ANDROID_HOME is unset" do
System.delete_env("ANDROID_HOME")
System.put_env("ANDROID_SDK_ROOT", "/opt/sdkroot")

assert NdkVersion.root() =~ "/opt/sdkroot/ndk/"
end

test "host/0 is the single NDK prebuilt tag for this OS" do
assert NdkVersion.host() in ["darwin-x86_64", "linux-x86_64"]
end

test "sysroot/0 and toolchain_bin/0 compose root + host" do
System.delete_env("ANDROID_SDK_ROOT")
System.put_env("ANDROID_HOME", "/opt/custom-sdk")
base = Path.join([NdkVersion.root(), "toolchains", "llvm", "prebuilt", NdkVersion.host()])

assert NdkVersion.sysroot() == Path.join(base, "sysroot")
assert NdkVersion.toolchain_bin() == Path.join(base, "bin")
end
end
end
Loading