From 3b13e084a5c4367aadc33be5f11fa686096c7618 Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Sun, 2 Nov 2025 03:49:25 +0100 Subject: [PATCH] Support target-specific rustflags --- Cargo.toml | 1 + README.md | 4 ++++ src/build.rs | 33 ++++++++++++++++++++++++++++----- src/build_targets.rs | 8 ++++++++ src/target.rs | 32 ++++++++++++++++++++++++++++---- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 718551b..2162808 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ glob = "0.3" itertools = "0.14" implib = "0.4.0" object = { version = "0.37.1", default-features = false, features = ["std", "read_core", "pe"] } +cargo-platform = "0.3.1" [features] default = [] diff --git a/README.md b/README.md index 3e38d0d..c14accc 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,10 @@ rustflags = "-Cpanic=abort" # Used to disable the generation of additional import library file in platforms # that have the concept such as Windows import_library = false + +[package.metadata.capi.library.target.'cfg(target_os = "linux")'] +# Add target-specific rustflags, the are folded with the main rustflags above +rustflags = "-Clink-arg=-Wl,--version-script=assets/version.map" ``` ### Custom data install diff --git a/src/build.rs b/src/build.rs index 1adea0c..3e2e352 100644 --- a/src/build.rs +++ b/src/build.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::{ErrorKind, Read}; use std::path::{Path, PathBuf}; +use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -14,6 +15,7 @@ use cargo::util::interning::InternedString; use cargo::{CliResult, GlobalContext}; use anyhow::Context as _; +use cargo_platform::Platform; use cargo_util::paths::{copy, create_dir_all, open, read, read_bytes, write}; use implib::def::ModuleDef; use implib::{Flavor, ImportLibrary, MachineType}; @@ -648,17 +650,38 @@ fn load_manifest_capi_config( }); } + fn make_args(args: &str) -> impl Iterator + use<'_> { + args.split(' ') + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(str::to_string) + } + import_library = library .get("import_library") .and_then(|v| v.as_bool()) .unwrap_or(true); + if let Some(args) = library.get("rustflags").and_then(|v| v.as_str()) { + rustflags.extend(make_args(args)); + } + + if let Some(args) = library.get("target").and_then(|v| v.as_table()) { let args = args - .split(' ') - .map(str::trim) - .filter(|s| !s.is_empty()) - .map(str::to_string); - rustflags.extend(args); + .iter() + .filter_map(|(p, v)| { + if Platform::from_str(p) + .ok() + .is_some_and(|p| p.matches(name, &rustc_target.cfg)) + { + v.as_str() + } else { + None + } + }) + .flat_map(make_args); + + rustflags.extend(args) } } diff --git a/src/build_targets.rs b/src/build_targets.rs index 052355b..5628807 100644 --- a/src/build_targets.rs +++ b/src/build_targets.rs @@ -237,6 +237,8 @@ mod test { arch: String::from(""), os: os.to_string(), env: String::from(""), + cfg: Vec::new(), + target: None, }; let file_names = FileNames::from_target(&target, "ferris", Path::new("/foo/bar"), false); @@ -261,6 +263,8 @@ mod test { arch: String::from(""), os: os.to_string(), env: String::from(""), + cfg: Vec::new(), + target: None, }; let file_names = FileNames::from_target(&target, "ferris", Path::new("/foo/bar"), false); @@ -284,6 +288,8 @@ mod test { arch: String::from(""), os: String::from("windows"), env: String::from("msvc"), + cfg: Vec::new(), + target: None, }; let file_names = FileNames::from_target(&target, "ferris", Path::new("/foo/bar"), false); @@ -305,6 +311,8 @@ mod test { arch: String::from(""), os: String::from("windows"), env: String::from("gnu"), + cfg: Vec::new(), + target: None, }; let file_names = FileNames::from_target(&target, "ferris", Path::new("/foo/bar"), false); diff --git a/src/target.rs b/src/target.rs index a13c48e..a622257 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,9 +1,11 @@ use std::env::consts; use std::path::{Path, PathBuf}; - -use anyhow::*; +use std::str::FromStr; use crate::build::CApiConfig; +use anyhow::*; +use cargo::core::compiler::CompileTarget; +use cargo_platform::Cfg; /// Split a target string to its components /// @@ -16,15 +18,18 @@ pub struct Target { // pub vendor: String, pub os: String, pub env: String, + pub target: Option, + pub cfg: Vec, } impl Target { - pub fn new>( + pub fn new + AsRef>( target: Option, is_target_overridden: bool, - ) -> Result { + ) -> Result { let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".into()); let mut cmd = std::process::Command::new(rustc); + let target = target.as_ref(); cmd.arg("--print").arg("cfg"); if let Some(target) = target { @@ -46,18 +51,37 @@ impl Target { let s = std::str::from_utf8(&out.stdout).unwrap(); + let lines = s.lines(); + + let cfg = lines + .map(|line| Ok(Cfg::from_str(line)?)) + .collect::>>() + .with_context(|| { + format!( + "failed to parse the cfg from `rustc --print=cfg`, got:\n{}", + s + ) + })?; + Ok(Target { arch: match_re(arch_re, s), // vendor: match_re(vendor_re, s), os: match_re(os_re, s), env: match_re(env_re, s), is_target_overridden, + target: target.map(|t| CompileTarget::new(t.as_ref())).transpose()?, + cfg, }) } else { Err(anyhow!("Cannot run {:?}", cmd)) } } + /// Produce the target name, if known + pub fn name(&self) -> Option<&str> { + self.target.as_ref().map(|t| t.short_name()) + } + /// Build a list of linker arguments pub fn shared_object_link_args( &self,