use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
fn build_backend() {
println!("Building Backend!");
let out_dir = env::var("OUT_DIR").unwrap();
let full_target_dir = format!("{}/target_static", out_dir);
let target = "x86_64-unknown-none";
let mut cmd = cargo();
cmd.arg("+nightly");
cmd.arg("rustc");
cmd.args(&["--target", target]);
cmd.args(&["--target-dir", &full_target_dir]);
cmd.arg("--features=staticlib");
if env::var_os("CARGO_FEATURE_INTERRUPTSAFE").is_some() {
cmd.arg("--features=interruptsafe");
}
cmd.args(&["--color", "always"]);
cmd.stdout(Stdio::inherit());
cmd.stderr(Stdio::inherit());
cmd.args(&[
"-Zbuild-std=core",
"-Zbuild-std-features=compiler-builtins-mem",
]);
cmd.arg("--release");
cmd.arg("--");
cmd.arg("-Cpanic=abort");
cmd.env_remove("RUSTFLAGS");
cmd.env_remove("CARGO_ENCODED_RUSTFLAGS");
println!("Starting sub-cargo");
let status = cmd.status().expect("Unable to build tracer's static lib!");
assert!(status.success(), "Unable to build tracer's static lib!");
println!("Sub-cargo successful!");
let dist_dir = format!("{}/{}/release", &full_target_dir, &target);
retain_symbols(
Path::new(&format!("{}/librftrace.a", &dist_dir)),
HashSet::from([
"mcount",
"rftrace_backend_disable",
"rftrace_backend_enable",
"rftrace_backend_get_events",
"rftrace_backend_get_events_index",
"rftrace_backend_init",
]),
);
println!("cargo:rustc-link-search=native={}", &dist_dir);
println!("cargo:rustc-link-lib=static=rftrace");
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=src/backend.rs");
println!("cargo:rerun-if-changed=src/interface.rs");
println!("cargo:rerun-if-changed=src/lib.rs");
}
fn main() {
if env::var_os("CARGO_FEATURE_STATICLIB").is_some() {
return;
}
build_backend();
}
fn cargo() -> Command {
let cargo = {
let exe = format!("cargo{}", env::consts::EXE_SUFFIX);
let mut cargo_home = PathBuf::from(env::var_os("CARGO_HOME").unwrap());
cargo_home.push("bin");
cargo_home.push(&exe);
if cargo_home.exists() {
cargo_home
} else {
PathBuf::from(exe)
}
};
let mut cargo = Command::new(cargo);
cargo.env_remove("LD_LIBRARY_PATH");
env::vars()
.filter(|(key, _value)| key.starts_with("CARGO") || key.starts_with("RUST"))
.for_each(|(key, _value)| {
cargo.env_remove(&key);
});
cargo
}
pub fn retain_symbols(archive: &Path, mut exported_symbols: HashSet<&str>) {
use std::fmt::Write;
let prefix = "rftrace";
let all_symbols = {
let objcopy = binutil("nm").unwrap();
let output = Command::new(&objcopy)
.arg("--export-symbols")
.arg(archive)
.output()
.unwrap();
assert!(output.status.success());
String::from_utf8(output.stdout).unwrap()
};
let symbol_renames = all_symbols
.lines()
.fold(String::new(), |mut output, symbol| {
if exported_symbols.remove(symbol) {
return output;
}
if let Some(symbol) = symbol.strip_prefix("_ZN") {
let prefix_len = prefix.len();
let _ = writeln!(output, "_ZN{symbol} _ZN{prefix_len}{prefix}{symbol}",);
} else {
let _ = writeln!(output, "{symbol} {prefix}_{symbol}");
}
output
});
assert!(exported_symbols.is_empty());
let rename_path = archive.with_extension("redefine_syms");
fs::write(&rename_path, symbol_renames).unwrap();
let objcopy = binutil("objcopy").unwrap();
let status = Command::new(&objcopy)
.arg("--redefine-syms")
.arg(&rename_path)
.arg(archive)
.status()
.unwrap();
assert!(status.success());
fs::remove_file(&rename_path).unwrap();
}
fn binutil(name: &str) -> Result<PathBuf, String> {
let exe_suffix = env::consts::EXE_SUFFIX;
let exe = format!("llvm-{name}{exe_suffix}");
let path = llvm_tools::LlvmTools::new()
.map_err(|err| match err {
llvm_tools::Error::NotFound =>
"Could not find llvm-tools component\n\
\n\
Maybe the rustup component `llvm-tools` is missing? Install it through: `rustup component add llvm-tools`".to_string()
,
err => format!("{err:?}"),
})?
.tool(&exe)
.ok_or_else(|| format!("could not find {exe}"))?;
Ok(path)
}