Skip to content

Commit 03c7386

Browse files
committed
Add --link-builtins flag
1 parent b14bca4 commit 03c7386

4 files changed

Lines changed: 70 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This version of `cargo-ndk` is much closer to a thin passthrough to `cargo` than
1111
- **Breaking change**: Removed Cargo.toml-based configuration property handling
1212
- **Breaking change**: Bump minimum supported Rust version (MSRV) to 1.86 and use Rust edition 2024.
1313
- **Breaking change**: `--bindgen` has been removed, and relevant environment variables are set by default
14+
- Enhancement: Clang builtins can now be linked automatically by adding `--link-builtins` or its equivalent environment variable
1415
- Enhancement: Can now be built for an Android host (i.e. Termux) if built with `CARGO_NDK_ON_ANDROID` environment variable set
1516
- Enhancement: CLI flags can now be used in any order (e.g., `cargo ndk -t x86 build` and `cargo ndk build --target x86` are equivalent).
1617
- Enhancement: Added environment variable support for configuration flags with `CARGO_NDK_` prefix (e.g., `CARGO_NDK_TARGET`, `CARGO_NDK_PLATFORM`, `CARGO_NDK_OUTPUT_DIR`)

src/bin/cargo-ndk.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,29 @@ use std::process::exit;
1515
/// Cargo.
1616
fn clang_linker_wrapper() -> ! {
1717
let args = std::env::args_os().skip(1);
18+
1819
let clang = std::env::var("_CARGO_NDK_LINK_CLANG")
1920
.expect("cargo-ndk rustc linker: didn't find _CARGO_NDK_LINK_CLANG env var");
2021
let target = std::env::var("_CARGO_NDK_LINK_TARGET")
2122
.expect("cargo-ndk rustc linker: didn't find _CARGO_NDK_LINK_TARGET env var");
2223

23-
let mut child = std::process::Command::new(&clang)
24-
.arg(target)
25-
.args(args)
26-
.spawn()
27-
.unwrap_or_else(|err| {
28-
eprintln!("cargo-ndk: Failed to spawn {clang:?} as linker: {err}");
29-
std::process::exit(1)
30-
});
24+
let mut cmd = std::process::Command::new(&clang);
25+
cmd.arg(target);
26+
27+
if let Ok(builtins_path) = std::env::var("_CARGO_NDK_LINK_BUILTINS") {
28+
cmd.arg(format!("-L{}", builtins_path));
29+
30+
// Extract arch from target triple (e.g., "aarch64-linux-android21" -> "aarch64")
31+
let arch = std::env::var("CARGO_NDK_SYSROOT_TARGET")
32+
.expect("cargo-ndk rustc linker: didn't find CARGO_NDK_SYSROOT_TARGET");
33+
let arch = arch.split('-').next().unwrap();
34+
cmd.arg(format!("-lclang_rt.builtins-{}-android", arch));
35+
}
36+
37+
let mut child = cmd.args(args).spawn().unwrap_or_else(|err| {
38+
eprintln!("cargo-ndk: Failed to spawn {clang:?} as linker: {err}");
39+
std::process::exit(1)
40+
});
3141
let status = child.wait().unwrap_or_else(|err| {
3242
eprintln!("cargo-ndk (as linker): Failed to wait for {clang:?} to complete: {err}");
3343
std::process::exit(1);

src/cargo.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
};
99

1010
use anyhow::{Context, Result};
11-
use cargo_metadata::{Artifact, Message, camino::Utf8PathBuf, semver::Version};
11+
use cargo_metadata::{Artifact, Message, semver::Version};
1212

1313
use crate::shell::Shell;
1414

@@ -86,10 +86,35 @@ fn cc_env(var_base: &str, triple: &str) -> (String, Option<String>) {
8686
.unwrap_or_else(|| (most_specific_key, None))
8787
}
8888

89+
#[inline]
90+
// {}/toolchains/llvm/prebuilt/{ARCH}/lib/clang/{clang_version}/lib/linux
91+
fn clang_lib_path(ndk_home: &Path) -> PathBuf {
92+
let clang_folder: PathBuf = ndk_home
93+
.join("toolchains")
94+
.join("llvm")
95+
.join("prebuilt")
96+
.join(ARCH)
97+
.join("lib")
98+
.join("clang");
99+
100+
let clang_lib_version = std::fs::read_dir(&clang_folder)
101+
.expect("Unable to get clang target directory")
102+
.filter_map(|a| a.ok())
103+
.max_by(|a, b| a.file_name().cmp(&b.file_name()))
104+
.expect("Unable to get clang target")
105+
.path();
106+
107+
clang_folder
108+
.join(clang_lib_version)
109+
.join("lib")
110+
.join("linux")
111+
}
112+
89113
pub(crate) fn build_env(
90114
triple: &str,
91115
ndk_home: &Path,
92116
clang_target: &str,
117+
link_builtins: bool,
93118
) -> BTreeMap<String, OsString> {
94119
let self_path = std::fs::canonicalize(env::args().next().unwrap())
95120
.expect("Failed to canonicalize absolute path to cargo-ndk")
@@ -168,6 +193,11 @@ pub(crate) fn build_env(
168193
.into_iter()
169194
.collect::<BTreeMap<String, OsString>>();
170195

196+
if link_builtins {
197+
let builtins_path = clang_lib_path(ndk_home);
198+
envs.insert("_CARGO_NDK_LINK_BUILTINS".to_string(), builtins_path.into());
199+
}
200+
171201
if env::var("MSYSTEM").is_ok() || env::var("CYGWIN").is_ok() {
172202
envs = envs
173203
.into_iter()
@@ -214,9 +244,9 @@ pub(crate) fn run(
214244
version: &Version,
215245
triple: &str,
216246
platform: u8,
247+
link_builtins: bool,
217248
cargo_args: &[String],
218249
cargo_manifest: &Path,
219-
#[allow(unused_variables)] out_dir: &Utf8PathBuf,
220250
) -> Result<(std::process::ExitStatus, Vec<Artifact>)> {
221251
if version.major < 23 {
222252
shell.error("NDK versions less than r23 are not supported. Install an up-to-date version of the NDK.").unwrap();
@@ -235,7 +265,7 @@ pub(crate) fn run(
235265
let clang_target = clang_target(triple, platform);
236266
let cargo_bin = env::var("CARGO").unwrap_or_else(|_| "cargo".into());
237267
let mut cargo_cmd = Command::new(&cargo_bin);
238-
let envs = build_env(triple, ndk_home, &clang_target);
268+
let envs = build_env(triple, ndk_home, &clang_target, link_builtins);
239269

240270
shell
241271
.very_verbose(|shell| {

src/cli.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ struct ArgsEnv {
3535
#[arg(long, default_value_t = 21, env = "CARGO_NDK_PLATFORM")]
3636
platform: u8,
3737

38+
/// Links Clang builtins library
39+
#[arg(long, default_value_t = false, env = "CARGO_NDK_LINK_BUILTINS")]
40+
link_builtins: bool,
41+
3842
/// use PowerShell syntax
3943
#[arg(long)]
4044
powershell: bool,
@@ -54,6 +58,10 @@ struct Args {
5458
#[arg(long, default_value_t = 21, env = "CARGO_NDK_PLATFORM")]
5559
platform: u8,
5660

61+
/// Links Clang builtins library
62+
#[arg(long, default_value_t = false, env = "CARGO_NDK_LINK_BUILTINS")]
63+
link_builtins: bool,
64+
5765
/// output to a jniLibs directory in the correct sub-directories
5866
#[arg(short, long, value_name = "DIR", env = "CARGO_NDK_OUTPUT_DIR")]
5967
output_dir: Option<PathBuf>,
@@ -331,10 +339,15 @@ pub fn run_env(args: Vec<String>) -> anyhow::Result<()> {
331339
let clang_target = clang_target(args.target.triple(), args.platform);
332340

333341
// Try command line, then config. Config falls back to defaults in any case.
334-
let env = build_env(args.target.triple(), &ndk_home, &clang_target)
335-
.into_iter()
336-
.filter(|(k, _)| !k.starts_with('_'))
337-
.collect::<BTreeMap<_, _>>();
342+
let env = build_env(
343+
args.target.triple(),
344+
&ndk_home,
345+
&clang_target,
346+
args.link_builtins,
347+
)
348+
.into_iter()
349+
.filter(|(k, _)| !k.starts_with('_'))
350+
.collect::<BTreeMap<_, _>>();
338351

339352
if args.json {
340353
println!(
@@ -719,9 +732,9 @@ pub fn run(args: Vec<String>) -> anyhow::Result<()> {
719732
&ndk_version,
720733
triple,
721734
platform,
735+
args.link_builtins,
722736
&args.cargo_args,
723737
&cargo_manifest,
724-
&out_dir,
725738
)?;
726739
let code = status.code().unwrap_or(-1);
727740

0 commit comments

Comments
 (0)