aboutsummaryrefslogtreecommitdiff
path: root/build.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-04-06 14:19:43 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-04-06 14:19:43 +0300
commitca85cb732e1e8853f118e72ab1c2d6979dd9a37f (patch)
treeb5bfd63d637e041d706b5927d12824edadb921fa /build.rs
parent3371770669c81d836302313e2a6a309f3eba3c83 (diff)
build: link zig libc++ runtime for musl simd
Diffstat (limited to 'build.rs')
-rw-r--r--build.rs95
1 files changed, 93 insertions, 2 deletions
diff --git a/build.rs b/build.rs
index dc4031c5..b61d88ac 100644
--- a/build.rs
+++ b/build.rs
@@ -1,6 +1,6 @@
use std::env;
use std::fs;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::process::Command;
fn zig_target(target: &str) -> &str {
@@ -27,6 +27,75 @@ fn env_bool(name: &str) -> Option<bool> {
}
}
+/// Extract the final archive path for a named Zig runtime archive from
+/// verbose compiler/linker output.
+fn find_archive_path(output: &str, archive_name: &str) -> Option<PathBuf> {
+ output
+ .split_whitespace()
+ .map(|token| token.trim_matches(|c| matches!(c, '"' | '\'')))
+ .filter(|token| token.ends_with(archive_name) && token.contains('/'))
+ .map(PathBuf::from)
+ .last()
+}
+
+/// Ask Zig which musl-compatible C++ runtime archives it would use for a
+/// trivial target build.
+///
+/// This is necessary because `cargo:rustc-link-lib=stdc++` on a musl target
+/// resolves through the host toolchain and produces a mixed musl/glibc binary,
+/// while Zig's musl C++ driver resolves to its bundled libc++/libc++abi
+/// archives.
+fn zig_musl_cpp_runtime_archives(out_dir: &Path, zig_target: &str) -> (PathBuf, PathBuf) {
+ let probe_dir = out_dir.join("zig-cpp-runtime-probe");
+ fs::create_dir_all(&probe_dir).expect("failed to create Zig C++ runtime probe dir");
+
+ let source = probe_dir.join("probe.cpp");
+ fs::write(&source, "int main() { return 0; }\n")
+ .expect("failed to write Zig C++ runtime probe source");
+
+ let binary = probe_dir.join("probe");
+ let local_cache = probe_dir.join("local-cache");
+ let global_cache = probe_dir.join("global-cache");
+
+ let output = Command::new("zig")
+ .arg("c++")
+ .arg("-target")
+ .arg(zig_target)
+ .arg("-v")
+ .arg(&source)
+ .arg("-o")
+ .arg(&binary)
+ .env("ZIG_LOCAL_CACHE_DIR", &local_cache)
+ .env("ZIG_GLOBAL_CACHE_DIR", &global_cache)
+ .output()
+ .expect("failed to execute Zig C++ runtime probe");
+
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ let combined = format!("{stdout}\n{stderr}");
+
+ assert!(
+ output.status.success(),
+ "Zig C++ runtime probe failed: {}\nstdout:\n{}\nstderr:\n{}",
+ output.status,
+ stdout,
+ stderr
+ );
+
+ let libcxxabi = find_archive_path(&combined, "libc++abi.a").unwrap_or_else(|| {
+ panic!(
+ "failed to locate Zig musl libc++abi archive path for target {zig_target}; zig output was:\n{combined}"
+ )
+ });
+ let libcxx = find_archive_path(&combined, "libc++.a").unwrap_or_else(|| {
+ panic!(
+ "failed to locate Zig musl libc++ archive path for target {zig_target}; zig output was:\n{combined}"
+ )
+ });
+
+ (libcxxabi, libcxx)
+}
+
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=vendor/libghostty-vt.vendor.json");
@@ -37,6 +106,7 @@ fn main() {
println!("cargo:rerun-if-env-changed=LIBGHOSTTY_VT_SIMD");
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
+ let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
let vendored_dir = manifest_dir.join("vendor/libghostty-vt");
let optimize = env::var("LIBGHOSTTY_VT_OPTIMIZE").unwrap_or_else(|_| "ReleaseFast".into());
let simd = env_bool("LIBGHOSTTY_VT_SIMD").unwrap_or(true);
@@ -73,7 +143,28 @@ fn main() {
} else {
println!("cargo:rustc-link-lib=static=ghostty-vt");
if target.contains("linux") && simd {
- println!("cargo:rustc-link-lib=dylib=stdc++");
+ if target.contains("musl") {
+ // Keep musl SIMD builds entirely in Zig's musl/libc++ runtime
+ // world. Asking Cargo to link `stdc++` here resolves through the
+ // host toolchain and reintroduces the crashing mixed-runtime
+ // artifact shape.
+ let (libcxxabi, libcxx) = zig_musl_cpp_runtime_archives(&out_dir, zig_target);
+ let libcxxabi_dir = libcxxabi
+ .parent()
+ .expect("libc++abi archive should have a parent dir");
+ let libcxx_dir = libcxx
+ .parent()
+ .expect("libc++ archive should have a parent dir");
+
+ println!("cargo:rustc-link-search=native={}", libcxxabi_dir.display());
+ if libcxx_dir != libcxxabi_dir {
+ println!("cargo:rustc-link-search=native={}", libcxx_dir.display());
+ }
+ println!("cargo:rustc-link-lib=static=c++abi");
+ println!("cargo:rustc-link-lib=static=c++");
+ } else {
+ println!("cargo:rustc-link-lib=dylib=stdc++");
+ }
}
}
}