diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-06 14:19:43 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-06 14:19:43 +0300 |
| commit | ca85cb732e1e8853f118e72ab1c2d6979dd9a37f (patch) | |
| tree | b5bfd63d637e041d706b5927d12824edadb921fa /build.rs | |
| parent | 3371770669c81d836302313e2a6a309f3eba3c83 (diff) | |
build: link zig libc++ runtime for musl simd
Diffstat (limited to 'build.rs')
| -rw-r--r-- | build.rs | 95 |
1 files changed, 93 insertions, 2 deletions
@@ -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++"); + } } } } |
