Skip to content

feat(library): expose embeddable Android NDK configuration API - #215

Closed
psyche314 wants to merge 1 commit into
bbqsrc:mainfrom
psyche314:codex/library-api
Closed

psyche314 wants to merge 1 commit into
bbqsrc:mainfrom
psyche314:codex/library-api

Conversation

@psyche314

@psyche314 psyche314 commented Aug 29, 2026 •

Copy link
Copy Markdown

Summary

This PR makes cargo-ndk usable as a regular Rust library for build tools such as xtask. Consumers can configure and launch their own Cargo commands while reusing cargo-ndk's Android NDK toolchain compatibility logic.

Motivation

The previous implementation kept environment setup behind the CLI and assumed that a cargo-ndk executable would be available to act as the Rust linker wrapper. That makes library embedding awkward: an xtask would need an additional installed or sibling cargo-ndk executable.

What changed

  • Add a small structured API: Ndk, NdkVersion, AndroidTarget, ApiLevel, BuildConfig, and BuildEnvironment.
  • Reuse NDK discovery, version validation, target/ABI/Clang/sysroot mapping, tool paths, Cargo/cc/bindgen environment variables, builtins and libc++_shared linking, and 16 KiB page-size compatibility flags.
  • Move the linker-wrapper implementation into the library as run_linker_wrapper(), returning a Result instead of terminating the process.
  • Keep the existing CLI commands and argument handling. The CLI remains responsible for presentation and process exit behavior, and continues to use its legacy sibling executable paths where required.
  • Keep the existing single-package architecture; this does not add a cargo-ndk-core crate, new crate types, or new dependencies.

Embedding model

An embedding executable can dispatch linker invocations to cargo-ndk before its normal command handling:

if std::env::var_os("_CARGO_NDK_LINK_TARGET").is_some() {
    let status = cargo_ndk::run_linker_wrapper()?;
    std::process::exit(status.code().unwrap_or(1));
}

It can then configure and launch Cargo itself:

let ndk = Ndk::discover()?
    .ok_or_else(|| std::io::Error::other("Android NDK was not found"))?;
let target = AndroidTarget::Arm64V8a;
let config = BuildConfig::new(ndk, target, ApiLevel::new(28));

let mut cargo = Command::new("cargo");
cargo.args(["build", "--target", target.triple()]);
config.configure_command(&mut cargo)?;
cargo.status()?;

The default linker is the current embedding executable, so this path does not look up or require a sibling cargo-ndk binary.

Compatibility and tests

  • NDK releases older than r23 are rejected at the library configuration boundary through Result.
  • An independent tests/fixtures/embedding crate consumes cargo-ndk through a path dependency and runs as its own executable.
  • Integration coverage verifies linker-wrapper dispatch without a cargo-ndk sibling, target/toolchain environment generation, NDK builtins and page-size flags, and cargo ndk-env CMake environment output.
  • The existing CI workflow is unchanged; no additional GitHub Actions test job is added.

Validation

  • cargo fmt --all -- --check
  • cargo build --locked --all-targets
  • cargo test --locked --all-targets
  • cargo clippy --locked --all-targets -- -D warnings
  • cargo doc --locked --no-deps

Refactor NDK discovery, version compatibility checks, target mapping, and Cargo environment generation into a reusable library API. Make the CLI share the linker-wrapper implementation.

Allow external Rust executables to consume cargo-ndk directly and act as their own linker wrapper without requiring a sibling cargo-ndk executable. Add an independent embedding fixture, CLI environment compatibility tests, and documentation.

Co-authored-by: Codex <noreply@openai.com>
@bbqsrc bbqsrc closed this Aug 29, 2026
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.

2 participants