use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CaptureShims {
pub rustc_shim: PathBuf,
pub linker_shim: PathBuf,
pub rustc_cache_dir: PathBuf,
pub linker_cache_dir: PathBuf,
pub real_linker: PathBuf,
pub target_triple: Option<String>,
}
pub fn capture_env_vars(c: &CaptureShims) -> Vec<(String, String)> {
capture_env_vars_for_triple(c, c.target_triple.as_deref())
}
pub fn capture_env_vars_for_triple(
c: &CaptureShims,
triple_override: Option<&str>,
) -> Vec<(String, String)> {
let mut out = vec![
(
"RUSTC_WORKSPACE_WRAPPER".into(),
c.rustc_shim.to_string_lossy().into(),
),
(
"WHISKER_RUSTC_CACHE_DIR".into(),
c.rustc_cache_dir.to_string_lossy().into(),
),
(
"WHISKER_LINKER_CACHE_DIR".into(),
c.linker_cache_dir.to_string_lossy().into(),
),
(
"WHISKER_REAL_LINKER".into(),
c.real_linker.to_string_lossy().into(),
),
];
let shim = c.linker_shim.to_string_lossy().to_string();
let export_dynamic = match triple_override {
Some(t) if t.contains("apple") => "-Clink-arg=-Wl,-export_dynamic",
_ => "-Clink-arg=-Wl,--export-dynamic",
};
let save_temps = format!("-Csave-temps=y -Cdebug-assertions=on {export_dynamic}");
let save_temps = save_temps.as_str();
match triple_override {
Some(triple) => {
out.push((target_linker_env_var(triple), shim));
let prior = std::env::var(target_rustflags_env_var(triple)).unwrap_or_default();
let mut rustflags = String::new();
if !prior.is_empty() {
rustflags.push_str(&prior);
rustflags.push(' ');
}
rustflags.push_str(save_temps);
out.push((target_rustflags_env_var(triple), rustflags));
}
None => {
let prior = std::env::var("RUSTFLAGS").unwrap_or_default();
let mut rustflags = String::new();
if !prior.is_empty() {
rustflags.push_str(&prior);
rustflags.push(' ');
}
rustflags.push_str(&format!("-Clinker={shim} {save_temps}"));
out.push(("RUSTFLAGS".into(), rustflags));
}
}
out
}
pub fn target_rustflags_env_var(triple: &str) -> String {
let mut s = String::with_capacity(triple.len() + 24);
s.push_str("CARGO_TARGET_");
for ch in triple.chars() {
if ch.is_ascii_alphanumeric() {
s.push(ch.to_ascii_uppercase());
} else {
s.push('_');
}
}
s.push_str("_RUSTFLAGS");
s
}
pub fn target_linker_env_var(triple: &str) -> String {
let mut s = String::with_capacity(triple.len() + 22);
s.push_str("CARGO_TARGET_");
for ch in triple.chars() {
if ch.is_ascii_alphanumeric() {
s.push(ch.to_ascii_uppercase());
} else {
s.push('_');
}
}
s.push_str("_LINKER");
s
}
#[cfg(test)]
mod tests {
use super::*;
fn shim_for_triple(triple: Option<&str>) -> CaptureShims {
CaptureShims {
rustc_shim: PathBuf::from("/tmp/rustc-shim"),
linker_shim: PathBuf::from("/tmp/linker-shim"),
rustc_cache_dir: PathBuf::from("/tmp/rustc-cache"),
linker_cache_dir: PathBuf::from("/tmp/linker-cache"),
real_linker: PathBuf::from("/usr/bin/cc"),
target_triple: triple.map(String::from),
}
}
#[test]
fn target_linker_env_var_uppercases_and_replaces_separators() {
assert_eq!(
target_linker_env_var("aarch64-linux-android"),
"CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER",
);
}
#[test]
fn target_rustflags_env_var_matches_cargo_convention() {
assert_eq!(
target_rustflags_env_var("aarch64-apple-ios-sim"),
"CARGO_TARGET_AARCH64_APPLE_IOS_SIM_RUSTFLAGS",
);
}
#[test]
fn capture_env_vars_emits_workspace_wrapper_and_cache_dirs() {
let vars = capture_env_vars(&shim_for_triple(Some("aarch64-linux-android")));
let names: std::collections::HashSet<&str> = vars.iter().map(|(k, _)| k.as_str()).collect();
assert!(names.contains("RUSTC_WORKSPACE_WRAPPER"));
assert!(names.contains("WHISKER_RUSTC_CACHE_DIR"));
assert!(names.contains("WHISKER_LINKER_CACHE_DIR"));
assert!(names.contains("WHISKER_REAL_LINKER"));
assert!(names.contains("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER"));
assert!(names.contains("CARGO_TARGET_AARCH64_LINUX_ANDROID_RUSTFLAGS"));
}
#[test]
fn capture_env_vars_picks_apple_export_dynamic_for_ios_triples() {
let vars = capture_env_vars(&shim_for_triple(Some("aarch64-apple-ios-sim")));
let rustflags = vars
.iter()
.find(|(k, _)| k == "CARGO_TARGET_AARCH64_APPLE_IOS_SIM_RUSTFLAGS")
.map(|(_, v)| v.as_str())
.unwrap();
assert!(rustflags.contains("-Wl,-export_dynamic"));
assert!(!rustflags.contains("-Wl,--export-dynamic"));
}
#[test]
fn capture_env_vars_picks_gnu_export_dynamic_for_android_triples() {
let vars = capture_env_vars(&shim_for_triple(Some("aarch64-linux-android")));
let rustflags = vars
.iter()
.find(|(k, _)| k == "CARGO_TARGET_AARCH64_LINUX_ANDROID_RUSTFLAGS")
.map(|(_, v)| v.as_str())
.unwrap();
assert!(rustflags.contains("-Wl,--export-dynamic"));
}
#[test]
fn capture_env_vars_no_triple_falls_back_to_global_rustflags() {
let vars = capture_env_vars(&shim_for_triple(None));
let names: std::collections::HashSet<&str> = vars.iter().map(|(k, _)| k.as_str()).collect();
assert!(names.contains("RUSTFLAGS"));
assert!(!names.iter().any(|k| k.contains("CARGO_TARGET_")));
}
}