Skip to content

ci(sdk_v2): build the C++ core for Android in the native pipeline - #954

Draft
sheetalarkadam wants to merge 5 commits into
fix/genai-aar-cache-stalenessfrom
ci/android-native-build
Draft

ci(sdk_v2): build the C++ core for Android in the native pipeline#954
sheetalarkadam wants to merge 5 commits into
fix/genai-aar-cache-stalenessfrom
ci/android-native-build

Conversation

@sheetalarkadam

@sheetalarkadam sheetalarkadam commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Adds Android stages to CI

Scope / decisions

  • Build-only. Emulator tests are opt-in via runEmulatorTests on the x86_64 leg.
  • Not wired into the pack stages. The stages publish cpp-native-android-<abi>
    artifacts, but neither the NuGet package nor the C++ SDK tgz has an Android consumer
    today, and Android's differing dependency origins make packaging a separate design question.
  • All four .so files are staged (libfoundry_local.so, libonnxruntime.so,
    libonnxruntime-genai.so, and libmat.so), unlike the desktop legs which stage only
    libfoundry_local.so. Desktop consumers get ORT/GenAI out of band (pip / NuGet); an
    Android consumer has no such channel. A readelf DT_NEEDED-closure check fails the
    stage if a staged library needs a build-produced .so that wasn't staged.

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
foundry-local Ready Ready Preview Aug 10, 2026 8:30pm

Request Review

sheetalarkadam and others added 4 commits August 10, 2026 12:45
Adds two cross-compiled Android stages (arm64-v8a, x86_64) to the sdk_v2
native build graph, running on the existing Linux pool via the NDK.

Android resolves its native dependencies from different origins than the
desktop platforms: ORT comes from the same Microsoft.ML.OnnxRuntime NuGet
package, but GenAI ships as a standalone AAR on GitHub Releases. A version
that exists on NuGet therefore does not necessarily exist for Android, and
today nothing in CI notices - every desktop leg goes green while the Android
build cannot configure at all.

The stages publish `cpp-native-android-<abi>` artifacts but are deliberately
not wired into either pack stage; neither the NuGet package nor the C++ SDK
tgz has an Android consumer yet, so packaging is left as a separate question.

Build-only for now. Emulator tests are plumbed behind a `runEmulatorTests`
parameter on the x86_64 leg and can be enabled once the leg is proven stable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24ef037b-246f-4fea-a1a3-4fae5223cb0c
The Android staging step copied three .so files and its comment claimed that
was all of them. GenAI 0.15.0 split most of libonnxruntime-genai.so into
libmat.so and records it as a DT_NEEDED, so the published artifact was missing
a library its own contents require -- a consuming APK would build green and
then fail to dlopen at runtime.

Stage libmat.so, guarded on existence to mirror the copy in
sdk_v2/cpp/CMakeLists.txt so 0.14.x keeps working unchanged.

To stop this class of bug returning, assert DT_NEEDED closure over the staged
set. The check keys off "the build produced this library in bin/", so system
libraries are excluded without a whitelist and it cannot fail spuriously on an
agent whose image differs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24ef037b-246f-4fea-a1a3-4fae5223cb0c
Every other native producer stage uses the same arch token in its stage id and
its artifact name (cpp_build_linux_arm64 -> cpp-native-linux-arm64). The
Android arm64 stage shortened the ABI to "arm64" while publishing
cpp-native-arm64-v8a, and did not even agree with its own x86_64 sibling,
which already carries the full ABI string.

Renaming is free right now because the stage has never run on main, so no
branch policy can reference it as a required check yet. That stops being true
once it merges.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24ef037b-246f-4fea-a1a3-4fae5223cb0c
The NDK version was pinned, but the way it was obtained was not. If the pool
image lacked the pinned version the step shelled out to sdkmanager, which
requires cmdline-tools on the image, prompts for licence acceptance, and
performs no integrity check on what it downloads.

Resolve it in two paths instead. Fast path: use the image's NDK, but only
after confirming source.properties reports exactly the pinned revision -- the
previous code inferred the version from the directory name alone. Otherwise
download Google's published archive and verify its SHA-1 before use.

The archive name and checksum become parameters alongside the version, since
all three come from one entry in Google's package manifest and have to move
together. A partially-updated triple cannot slip through: the extracted NDK's
Pkg.Revision is asserted against ndkVersion, so bumping the version without
the archive yields a checksum that still matches but a revision that does not.

Extraction uses unzip rather than python -m zipfile, which discards the
executable bit and would leave every toolchain binary unusable.

Also stop requiring an Android SDK to build. A cross-compile needs only the
NDK; the SDK is an emulator concern, so it is now resolved in a step guarded
by runEmulatorTests. This removes a hard dependency on pool image contents
that were never verified, and lets an image without an SDK still build.

Verified against the real toolchain and the live archive:
  - pinned NDK on the image is detected and its revision asserted
  - a different NDK version is rejected rather than silently used
  - no NDK present falls through to the download path
  - the URL returns HTTP 200, Content-Length 783549481 matching the manifest,
    Content-Type application/zip, and PK\x03\x04 magic
  - the archive's first entry is android-ndk-r29/, confirming the single
    top-level directory the extraction assumes
  - sha1sum parsing yields a 40-char digest and detects a mismatch
  - all eight bash step bodies pass bash -n

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24ef037b-246f-4fea-a1a3-4fae5223cb0c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant