use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
fn search_dirs(target: &str) -> Vec<PathBuf> {
let arch = target.split('-').next().unwrap_or("x86_64");
let mut dirs = Vec::new();
if let Ok(explicit) = env::var("OTEL_BOOTSTRAP_MUSL_LIBUNWIND_DIR") {
dirs.push(PathBuf::from(explicit));
}
dirs.push(PathBuf::from(format!("/usr/local/musl/{arch}/lib")));
dirs.push(PathBuf::from(format!("/usr/lib/{arch}-linux-musl")));
dirs.push(PathBuf::from("/usr/lib"));
dirs
}
fn needs_lzma(archive: &Path) -> bool {
Command::new("nm")
.arg("--undefined-only")
.arg(archive)
.output()
.map(|o| String::from_utf8_lossy(&o.stdout).contains("lzma_"))
.unwrap_or(false)
}
fn member_defining_unw_backtrace(archive: &Path) -> Option<String> {
let out = Command::new("nm")
.arg("--print-armap")
.arg(archive)
.output()
.ok()?;
if !out.status.success() {
return None;
}
String::from_utf8_lossy(&out.stdout)
.lines()
.find_map(|line| {
let rest = line.strip_prefix("unw_backtrace in ")?;
Some(rest.trim().to_owned())
})
}
fn main() {
println!("cargo:rerun-if-env-changed=OTEL_BOOTSTRAP_MUSL_LIBUNWIND_DIR");
if env::var("CARGO_FEATURE_PROFILING_MEMORY_JEMALLOC").is_err() {
return;
}
if env::var("CARGO_CFG_TARGET_ENV").as_deref() != Ok("musl") {
return;
}
let target = env::var("TARGET").unwrap_or_default();
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
let searched = search_dirs(&target);
let Some((dir, archive)) = searched.clone().into_iter().find_map(|d| {
let a = d.join("libunwind.a");
a.is_file().then_some((d, a))
}) else {
panic!(
"otel-bootstrap: heap profiling (profiling-memory-jemalloc) is \
enabled for target {target}, but no musl-built libunwind.a was \
found in any of: {searched}.\n\
\n\
jemalloc calls unw_backtrace on every sampled allocation and a \
static musl binary has no working unwinder without this. Building \
anyway produces a binary that segfaults at runtime rather than one \
that fails here.\n\
\n\
Provide a musl-built libunwind (the brefwiz CI image ships one at \
/usr/local/musl/<arch>/lib), or point OTEL_BOOTSTRAP_MUSL_LIBUNWIND_DIR \
at one, or build without the feature.",
target = target,
searched = searched
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(", "),
);
};
let Some(member) = member_defining_unw_backtrace(&archive) else {
println!(
"cargo:warning=otel-bootstrap: {} defines no unw_backtrace; heap \
profiling will not link for this target.",
archive.display()
);
return;
};
let work = out_dir.join("unwind-shim");
let _ = fs::remove_dir_all(&work);
fs::create_dir_all(&work).expect("create shim dir");
let extracted = Command::new("ar")
.current_dir(&work)
.arg("x")
.arg(&archive)
.arg(&member)
.status();
if !matches!(extracted, Ok(s) if s.success()) {
println!(
"cargo:warning=otel-bootstrap: could not extract {member} from {}",
archive.display()
);
return;
}
let shim = work.join("libunwind_backtrace_shim.a");
let packed = Command::new("ar")
.current_dir(&work)
.arg("rcs")
.arg("libunwind_backtrace_shim.a")
.arg(&member)
.status();
if !matches!(packed, Ok(s) if s.success()) || !shim.is_file() {
println!("cargo:warning=otel-bootstrap: could not package the unwind shim");
return;
}
println!("cargo:rustc-link-search=native={}", work.display());
println!("cargo:rustc-link-search=native={}", dir.display());
println!("cargo:rustc-link-lib=static=unwind_backtrace_shim");
let arch = target.split('-').next().unwrap_or("x86_64");
println!("cargo:rustc-link-lib=static=unwind");
println!("cargo:rustc-link-lib=static=unwind-{arch}");
for (lib, file) in [("lzma", "liblzma.a"), ("z", "libz.a")] {
if needs_lzma(&archive) && dir.join(file).is_file() {
println!("cargo:rustc-link-lib=static={lib}");
}
}
}