Skip to content

Commit 740b09b

Browse files
Merge pull request #229 from bernardladenthin/claude/friendly-mayer-nkqkjl
Fix Android API gating: use weak symbols instead of __ANDROID_API__
2 parents c8bc1b2 + d4d9dae commit 740b09b

3 files changed

Lines changed: 71 additions & 17 deletions

File tree

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ jobs:
164164
- name: Build libraries
165165
shell: bash
166166
run: |
167-
.github/dockcross/dockcross-android-arm64 .github/build.sh "-DANDROID_PLATFORM=android-28 -DOS_NAME=Linux-Android -DOS_ARCH=aarch64"
167+
.github/dockcross/dockcross-android-arm64 .github/build.sh "-DOS_NAME=Linux-Android -DOS_ARCH=aarch64"
168168
- name: Upload artifacts
169169
uses: actions/upload-artifact@v7
170170
with:
@@ -180,7 +180,7 @@ jobs:
180180
- name: Build libraries
181181
shell: bash
182182
run: |
183-
.github/dockcross/dockcross-android-arm64 .github/build_opencl_android.sh "-DANDROID_PLATFORM=android-28 -DOS_NAME=Linux-Android -DOS_ARCH=aarch64 -DGGML_OPENCL=ON -DGGML_OPENCL_EMBED_KERNELS=ON -DGGML_OPENCL_USE_ADRENO_KERNELS=ON"
183+
.github/dockcross/dockcross-android-arm64 .github/build_opencl_android.sh "-DOS_NAME=Linux-Android -DOS_ARCH=aarch64 -DGGML_OPENCL=ON -DGGML_OPENCL_EMBED_KERNELS=ON -DGGML_OPENCL_USE_ADRENO_KERNELS=ON"
184184
- name: Upload artifacts
185185
uses: actions/upload-artifact@v7
186186
with:

CLAUDE.md

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,46 @@ git commit -m "Upgrade CUDA from 13.2 to 13.3"
4242

4343
Current Android minimum API level: **28** (Android 9.0 Pie)
4444

45-
To change the minimum API level, update the following **three** places:
46-
47-
1. **`CMakeLists.txt`** — the `add_compile_definitions(__ANDROID_API__=28)` line (controls which NDK header symbols are exposed).
48-
2. **`.github/workflows/publish.yml`**`-DANDROID_PLATFORM=android-28` in both the `crosscompile-android-aarch64` and `crosscompile-android-aarch64-opencl` job steps.
49-
3. **`CLAUDE.md`** (this file) — the "Current Android minimum API level" line above and the `-DANDROID_PLATFORM` values in the local sanity-build examples.
50-
51-
Also update the minimum-API note in **`README.md`** (the `[!NOTE]` block near the Android classifier entries and the "Importing in Android" section).
52-
53-
**Why API 28?** `mtmd-helper.cpp` (part of the upstream llama.cpp `mtmd` multimodal library) includes `vendor/sheredom/subprocess.h`, which calls `posix_spawn`, `posix_spawnp`, and `posix_spawn_file_actions_*`. The Android NDK headers only expose those declarations when `__ANDROID_API__ >= 28`. The symbols exist in `libc.so` at all API levels; the define only gates their header visibility.
45+
This is enforced through bionic's **weak-symbol** mechanism, *not* by bumping
46+
`__ANDROID_API__` or passing `-DANDROID_PLATFORM`. See "How the API gate is
47+
satisfied" below for why. To change anything here, update:
48+
49+
1. **`CMakeLists.txt`** — the `add_compile_definitions(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__)`
50+
block and its Android-detection guard (`OS_NAME MATCHES "Android"` etc.).
51+
2. **`CLAUDE.md`** (this file) — the "Current Android minimum API level" line above.
52+
3. **`README.md`** — the minimum-API note (the `[!NOTE]` block near the Android
53+
classifier entries and the "Importing in Android" section).
54+
55+
**Why API 28?** `mtmd-helper.cpp` (part of the upstream llama.cpp `mtmd`
56+
multimodal library) includes `vendor/sheredom/subprocess.h`, which calls
57+
`posix_spawn`, `posix_spawnp`, and `posix_spawn_file_actions_*`. Bionic only
58+
exposes those `<spawn.h>` declarations once the minimum SDK is ≥ 28 (and
59+
`getifaddrs`/`freeifaddrs` in `<ifaddrs.h>`, used by cpp-httplib, at ≥ 24). The
60+
symbols exist in `libc.so` at all API levels; bionic only hides the
61+
*declarations* below the introducing API.
62+
63+
**How the API gate is satisfied (important — the obvious fixes do not work).**
64+
The CI cross-compiler is the `dockcross-android-arm64` image, which is **not**
65+
the Google NDK CMake toolchain — it is a Debian-style cross-clang at
66+
`/usr/aarch64-linux-android/bin/clang`. Consequently:
67+
68+
- It never sets the `ANDROID` / `ANDROID_ABI` CMake variables, so any
69+
`if(ANDROID_ABI)`-guarded logic silently does nothing.
70+
- It **ignores** `-DANDROID_PLATFORM=android-28` (CMake prints it as a
71+
"Manually-specified variables were not used by the project" warning).
72+
- `clang` predefines `__ANDROID_API__` from its baked-in target triple, so
73+
`-D__ANDROID_API__=28` would only clash with the builtin (`-Wmacro-redefined`)
74+
and would *not* move `__ANDROID_MIN_SDK_VERSION__`, which is what bionic's
75+
`__BIONIC_AVAILABILITY_GUARD(api)` actually tests.
76+
77+
The working fix is `add_compile_definitions(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__)`
78+
for the Android build. That macro forces `__BIONIC_AVAILABILITY_GUARD(api)` to
79+
`1` for every API level (declarations always visible) and makes any symbol newer
80+
than the toolchain's baked-in min-SDK a **weak** reference resolved by the
81+
dynamic linker at load time — present on every API-28+ device the artifact
82+
targets. It is never compiler-predefined, so defining it is clean. The guard
83+
detects Android via `OS_NAME MATCHES "Android"` (CI passes
84+
`-DOS_NAME=Linux-Android`) and the compiler path, not `ANDROID_ABI`.
5485

5586
## OpenCL / Adreno backend on Android
5687

@@ -76,7 +107,7 @@ Three places wire it together (mirrors the CUDA classifier pattern):
76107
Local sanity build:
77108
```bash
78109
.github/dockcross/dockcross-android-arm64 .github/build_opencl_android.sh \
79-
"-DANDROID_PLATFORM=android-28 -DOS_NAME=Linux-Android -DOS_ARCH=aarch64 \
110+
"-DOS_NAME=Linux-Android -DOS_ARCH=aarch64 \
80111
-DGGML_OPENCL=ON -DGGML_OPENCL_EMBED_KERNELS=ON \
81112
-DGGML_OPENCL_USE_ADRENO_KERNELS=ON"
82113
```

CMakeLists.txt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,37 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_L
4747
link_libraries(stdc++fs)
4848
endif()
4949

50-
# Android NDK headers gate symbols on __ANDROID_API__:
50+
# Android bionic gates several libc declarations behind a minimum API level:
5151
# >= 24: getifaddrs/freeifaddrs (<ifaddrs.h>), needed by cpp-httplib.
5252
# >= 28: posix_spawn / posix_spawnp / posix_spawn_file_actions_* (<spawn.h>),
5353
# needed by mtmd-helper.cpp (vendor/sheredom/subprocess.h).
54-
# NDK unified sysroots (r14b+) include all these symbols in libc regardless of
55-
# level; the define only controls which declarations are visible in the headers.
56-
if(ANDROID_ABI)
57-
add_compile_definitions(__ANDROID_API__=28)
54+
# The symbols exist in libc.so at all API levels; bionic only hides the
55+
# *declarations* below the introducing API via:
56+
# #if __BIONIC_AVAILABILITY_GUARD(api) // <android/versioning.h>
57+
# which expands to (__ANDROID_MIN_SDK_VERSION__ >= api) UNLESS
58+
# __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ is defined, in which case it is
59+
# always 1 (all declarations visible, newer-API symbols become weak references
60+
# resolved at load time — present on every API-28+ device we target).
61+
#
62+
# We define __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ rather than bumping
63+
# __ANDROID_API__ because the dockcross-android-arm64 image used by CI does NOT
64+
# use the Google NDK CMake toolchain: it is a Debian-style cross-clang
65+
# (/usr/aarch64-linux-android/bin/clang) that (a) never sets the ANDROID /
66+
# ANDROID_ABI CMake variables and (b) ignores -DANDROID_PLATFORM (CMake reports
67+
# it as an unused variable). On that toolchain clang predefines __ANDROID_API__
68+
# from the baked-in target triple, so -D__ANDROID_API__=28 would only clash with
69+
# the builtin (a -Wmacro-redefined) without moving __ANDROID_MIN_SDK_VERSION__.
70+
# The weak-symbols macro is never compiler-predefined, so defining it is clean
71+
# and deterministically exposes the guarded declarations. It also covers the
72+
# getifaddrs (API 24) case, replacing the old __ANDROID_API__ bump entirely.
73+
#
74+
# Detection must not rely on ANDROID_ABI (unset by this toolchain). OS_NAME is
75+
# passed as -DOS_NAME=Linux-Android by the CI cmake invocation and is already
76+
# used the same way at the server-models.cpp exclusion below; we also accept the
77+
# NDK-style ANDROID/ANDROID_ABI signals so a future switch back to the NDK
78+
# toolchain keeps working.
79+
if(ANDROID OR ANDROID_ABI OR OS_NAME MATCHES "Android" OR CMAKE_CXX_COMPILER MATCHES "android")
80+
add_compile_definitions(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__)
5881
endif()
5982

6083
set(LLAMA_BUILD_COMMON ON)

0 commit comments

Comments
 (0)