use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::{env, fs};
fn build_backend() {
println!("Building Backend!");
println!("cargo::rerun-if-changed=src/thread_local.c");
cc::Build::new()
.file("src/thread_local.c")
.flags([
"-mno-mmx",
"-mno-sse",
"-mno-sse2",
"-mno-sse3",
"-mno-ssse3",
"-mno-sse4.1",
"-mno-sse4.2",
"-mno-avx",
"-mno-avx2",
"-msoft-float",
])
.compile("thread_local");
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("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.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() {
let is_staticlib = env::var_os("CARGO_FEATURE_STATICLIB").is_some();
let is_clippy = env::var_os("CLIPPY_ARGS").is_some();
if is_clippy | is_staticlib {
return;
}
build_backend();
}
pub fn cargo() -> Command {
sanitize("cargo")
}
fn sanitize(cmd: &str) -> Command {
let cmd = {
let exe = format!("{cmd}{}", env::consts::EXE_SUFFIX);
let mut cargo_home = home::cargo_home().unwrap();
cargo_home.push("bin");
cargo_home.push(&exe);
if cargo_home.exists() {
cargo_home
} else {
PathBuf::from(exe)
}
};
let mut cmd = Command::new(cmd);
cmd.current_dir(env::var_os("CARGO_MANIFEST_DIR").unwrap());
cmd.env_remove("LD_LIBRARY_PATH");
env::vars()
.filter(|(key, _value)| {
key.starts_with("CARGO") && !key.starts_with("CARGO_HOME")
|| key.starts_with("RUST") && !key.starts_with("RUSTUP_HOME")
})
.for_each(|(key, _value)| {
cmd.env_remove(&key);
});
cmd
}
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 =>
format!("Could not find llvm-tools component\n\
\n\
Maybe the rustup component `llvm-tools` is missing? Install it through: `rustup component add --toolchain={} llvm-tools`", env::var("RUSTUP_TOOLCHAIN").unwrap()).to_string()
,
err => format!("{err:?}"),
})?
.tool(&exe)
.ok_or_else(|| format!("could not find {exe}"))?;
Ok(path)
}