Files
codex/patches/webrtc-audio-processing-sys_meson_env.patch
T
2026-04-05 14:08:57 -07:00

407 lines
15 KiB
Diff

diff --git a/build.rs b/build.rs
index 8578e9220a..bd5c9ba382 100644
--- a/build.rs
+++ b/build.rs
@@ -1,10 +1,14 @@
use anyhow::{bail, Context, Result};
use bindgen::callbacks::{AttributeInfo, DeriveInfo, ParseCallbacks};
+#[cfg(not(windows))]
+use std::os::unix::fs::symlink;
+#[cfg(windows)]
+use std::os::windows::fs::symlink_dir as symlink;
use std::{
env,
- fs::File,
+ fs::{self, File},
io::{BufWriter, Write},
- path::PathBuf,
+ path::{Path, PathBuf},
process::Command,
};
@@ -203,8 +207,136 @@ mod webrtc {
let webrtc_build_dir = build_dir.join(BUNDLED_SOURCE_PATH);
eprintln!("Building webrtc-audio-processing in {}", webrtc_build_dir.display());
- let mut meson = Command::new("meson");
+ let meson_launcher = env::var_os("MESON").expect("MESON is set by Bazel");
+ let cargo_manifest_dir =
+ env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is set by Bazel");
+ let runfiles_dir = Path::new(&cargo_manifest_dir)
+ .parent()
+ .expect("CARGO_MANIFEST_DIR points inside a Bazel runfiles tree");
+ let staged_meson = build_dir.join("meson");
+ fs::copy(&meson_launcher, &staged_meson).context("Failed to stage Meson launcher")?;
+ let meson_archive = PathBuf::from(&meson_launcher).with_extension("zip");
+ if meson_archive.exists() {
+ fs::copy(&meson_archive, build_dir.join("meson.zip"))
+ .context("Failed to stage Meson support archive")?;
+ }
+ let mut meson_runfiles = staged_meson.as_os_str().to_owned();
+ meson_runfiles.push(".runfiles");
+ let _ = fs::remove_file(&meson_runfiles);
+ symlink(runfiles_dir, &meson_runfiles).context("Failed to link Meson runfiles")?;
+
+ let mut meson = Command::new(staged_meson);
+ let mut meson_cross_file = None;
+ if let (Ok(host), Ok(target)) = (env::var("HOST"), env::var("TARGET")) {
+ if host != target {
+ let mut target_parts = target.split('-');
+ let cpu_family = target_parts
+ .next()
+ .expect("TARGET triple has an architecture");
+ let system = target_parts
+ .next_back()
+ .expect("TARGET triple has an operating system");
+ let endian = match cpu_family {
+ "aarch64" | "x86_64" => "little",
+ _ => bail!("Unsupported TARGET architecture for Meson cross file: {cpu_family}"),
+ };
+ let cross_file = build_dir.join("meson-cross-file.ini");
+ fs::write(
+ &cross_file,
+ format!(
+ concat!(
+ "[host_machine]\n",
+ "system = '{system}'\n",
+ "cpu_family = '{cpu_family}'\n",
+ "cpu = '{cpu_family}'\n",
+ "endian = '{endian}'\n",
+ "\n",
+ "[properties]\n",
+ "needs_exe_wrapper = true\n",
+ ),
+ system = system,
+ cpu_family = cpu_family,
+ endian = endian,
+ ),
+ )
+ .context("Failed to write Meson cross file")?;
+ meson_cross_file = Some(cross_file);
+ }
+ }
+
+ let mut ninja_launcher = env::var_os("NINJA");
+ if ninja_launcher.is_none() {
+ for candidate in ["ninja", "ninja-build"] {
+ let can_execute = Command::new(candidate)
+ .arg("--version")
+ .status()
+ .map(|status| status.success())
+ .unwrap_or(false);
+ if can_execute {
+ ninja_launcher = Some(candidate.into());
+ break;
+ }
+ }
+ }
+ if let Some(ninja_real) = env::var_os("NINJA_REAL") {
+ let mut temp_dirs = Vec::new();
+ if let Some(dir) = env::var_os("CODEX_NINJA_TMPDIR") {
+ temp_dirs.push(PathBuf::from(dir));
+ }
+ temp_dirs.extend([
+ PathBuf::from("/private/var/tmp"),
+ PathBuf::from("/var/tmp"),
+ env::temp_dir(),
+ PathBuf::from("/tmp"),
+ ]);
+ if let Some(home) = env::var_os("HOME") {
+ temp_dirs.push(PathBuf::from(home));
+ }
+
+ let mut staged_ninja = None;
+ for (attempt, temp_dir) in temp_dirs.into_iter().enumerate() {
+ if !temp_dir.is_dir() {
+ continue;
+ }
+
+ let stage_dir =
+ temp_dir.join(format!("codex-ninja-{}-{attempt}", std::process::id()));
+ let _ = fs::remove_dir_all(&stage_dir);
+ if fs::create_dir_all(&stage_dir).is_err() {
+ continue;
+ }
+
+ let candidate_ninja = stage_dir.join("ninja");
+ let stage_result =
+ fs::copy(&ninja_real, &candidate_ninja).and_then(|_| ensure_executable(&candidate_ninja));
+ if stage_result.is_ok() {
+ staged_ninja = Some(candidate_ninja.into_os_string());
+ break;
+ }
+ }
+ ninja_launcher = staged_ninja.or_else(|| Some(ninja_real));
+ }
+ let ninja_launcher =
+ ninja_launcher.ok_or_else(|| anyhow::anyhow!("Failed to locate ninja launcher"))?;
+
+ if let Some(ldflags) = env::var("LDFLAGS").ok() {
+ let execroot = determine_execroot(runfiles_dir)?;
+ let ldflags = ldflags
+ .split_whitespace()
+ .map(|token| {
+ if let Some(path) = token.strip_prefix("-Lbazel-out") {
+ format!("-L{}", execroot.join(format!("bazel-out{path}")).display())
+ } else if token.starts_with("bazel-out") {
+ execroot.join(token).display().to_string()
+ } else {
+ token.to_owned()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(" ");
+ meson.env("LDFLAGS", ldflags);
+ }
meson.args(["setup", "--prefix", install_dir.to_str().unwrap()]);
+ meson.env("NINJA", &ninja_launcher);
+ if let Some(cross_file) = meson_cross_file {
+ meson.arg("--cross-file");
+ meson.arg(cross_file);
+ }
meson.arg("--reconfigure");
if cfg!(target_os = "macos") {
@@ -221,14 +353,14 @@ mod webrtc {
.context("Failed to execute meson. Do you have it installed?")?;
assert!(status.success(), "Command failed: {:?}", &meson);
- let mut ninja = Command::new("ninja");
+ let mut ninja = Command::new(ninja_launcher.clone());
let status = ninja
.current_dir(&webrtc_build_dir)
.status()
.context("Failed to execute ninja. Do you have it installed?")?;
assert!(status.success(), "Command failed: {:?}", &ninja);
- let mut install = Command::new("ninja");
+ let mut install = Command::new(ninja_launcher);
let status = install
.current_dir(&webrtc_build_dir)
.arg("install")
@@ -267,15 +399,32 @@ mod webrtc {
/// Extract defined (non-external) symbols from a static library using nm.
fn get_defined_symbols(archive_path: &std::path::Path) -> Result<Vec<String>> {
- let output = Command::new("nm")
+ let nm = determine_nm_path();
+ let output = match Command::new(&nm)
.arg("--defined-only")
.arg("--format=posix")
.arg(archive_path)
.output()
- .context("Failed to execute nm")?;
+ {
+ Ok(output) => output,
+ Err(error)
+ if env::var("CARGO_CFG_TARGET_OS")
+ .map(|target_os| target_os == "macos")
+ .or_else(|_| env::var("TARGET").map(|target| target.contains("apple-darwin")))
+ .unwrap_or(false)
+ && error.kind() == std::io::ErrorKind::NotFound =>
+ {
+ println!(
+ "cargo:warning=Skipping symbol prefixing because {:?} is unavailable",
+ nm
+ );
+ return Ok(Vec::new());
+ }
+ Err(error) => return Err(error).context(format!("Failed to execute {:?}", nm)),
+ };
if !output.status.success() {
- anyhow::bail!("nm failed: {}", String::from_utf8_lossy(&output.stderr));
+ anyhow::bail!("{:?} failed: {}", nm, String::from_utf8_lossy(&output.stderr));
}
let stdout = String::from_utf8_lossy(&output.stdout);
@@ -400,6 +549,7 @@ fn main() -> Result<()> {
}
let binding_file = out_dir().join("bindings.rs");
+ maybe_set_libclang_path();
let mut builder = bindgen::Builder::default()
.header("src/wrapper.hpp")
.clang_args(&["-x", "c++", "-std=c++17", "-fparse-all-comments"])
@@ -461,3 +611,180 @@ fn determine_objcopy_path() -> Result<PathBuf> {
Ok(objcopy)
}
+
+fn determine_execroot(path: &Path) -> Result<PathBuf> {
+ for ancestor in path.ancestors() {
+ if ancestor.file_name().and_then(|name| name.to_str()) == Some("bazel-out") {
+ return ancestor
+ .parent()
+ .map(Path::to_path_buf)
+ .context("bazel-out directory is missing its execroot parent");
+ }
+ }
+
+ env::current_dir().context("Failed to determine execroot")
+}
+
+#[cfg(not(windows))]
+fn ensure_executable(path: &Path) -> std::io::Result<()> {
+ use std::os::unix::fs::PermissionsExt;
+
+ let mut permissions = fs::metadata(path)?.permissions();
+ permissions.set_mode(0o755);
+ fs::set_permissions(path, permissions)
+}
+
+#[cfg(windows)]
+fn ensure_executable(_path: &Path) -> std::io::Result<()> {
+ Ok(())
+}
+
+fn determine_nm_path() -> PathBuf {
+ if let Some(nm) = env::var_os("NM") {
+ return nm.into();
+ }
+
+ if env::var("CARGO_CFG_TARGET_OS")
+ .map(|target_os| target_os == "macos")
+ .or_else(|_| env::var("TARGET").map(|target| target.contains("apple-darwin")))
+ .unwrap_or(false)
+ {
+ let xcrun_output = Command::new("/usr/bin/xcrun")
+ .args(["--find", "llvm-nm"])
+ .output();
+ if let Ok(output) = xcrun_output {
+ if output.status.success() {
+ let path = String::from_utf8_lossy(&output.stdout).trim().to_owned();
+ if !path.is_empty() {
+ return PathBuf::from(path);
+ }
+ }
+ }
+ }
+
+ for tool_env in ["AR", "CC", "CXX"] {
+ if let Some(tool_path) = env::var_os(tool_env) {
+ let tool_path = PathBuf::from(tool_path);
+ if let Some(parent) = tool_path.parent() {
+ for candidate_name in ["llvm-nm", "nm"] {
+ let candidate = parent.join(candidate_name);
+ if candidate.exists() {
+ return candidate;
+ }
+ }
+ }
+
+ if let Some(file_name) = tool_path.file_name().and_then(|name| name.to_str()) {
+ let candidate_name = file_name.replace("llvm-ar", "llvm-nm");
+ if candidate_name != file_name {
+ let candidate = tool_path.with_file_name(candidate_name);
+ if candidate.exists() {
+ return candidate;
+ }
+ }
+
+ if file_name != "llvm-nm" {
+ let candidate = tool_path.with_file_name("llvm-nm");
+ if candidate.exists() {
+ return candidate;
+ }
+ }
+ }
+ }
+ }
+
+ for candidate in ["llvm-nm", "nm"] {
+ if Command::new(candidate)
+ .arg("--version")
+ .status()
+ .map(|status| status.success())
+ .unwrap_or(false)
+ {
+ return PathBuf::from(candidate);
+ }
+ }
+
+ PathBuf::from("nm")
+}
+
+fn maybe_set_libclang_path() {
+ if env::var_os("LIBCLANG_PATH").is_some() {
+ return;
+ }
+
+ for tool_env in ["CC", "CXX"] {
+ let Some(tool_value) = env::var_os(tool_env) else {
+ continue;
+ };
+ let tool_path = PathBuf::from(tool_value);
+ let tool_path = if tool_path.is_absolute() {
+ tool_path
+ } else if let Ok(current_dir) = env::current_dir() {
+ let current_dir_candidate = current_dir.join(&tool_path);
+ if current_dir_candidate.exists() {
+ current_dir_candidate
+ } else if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from) {
+ let mut resolved = tool_path.clone();
+ for ancestor in manifest_dir.ancestors() {
+ let ancestor_candidate = ancestor.join(&tool_path);
+ if ancestor_candidate.exists() {
+ resolved = ancestor_candidate;
+ break;
+ }
+ }
+ resolved
+ } else {
+ tool_path
+ }
+ } else {
+ tool_path
+ };
+
+ let Some(tool_dir) = tool_path.parent() else {
+ continue;
+ };
+ let Some(tool_root) = tool_dir.parent() else {
+ continue;
+ };
+
+ let mut directories = vec![tool_root.join("lib"), tool_root.join("lib64")];
+ if let Ok(read_dir) = fs::read_dir(tool_root) {
+ directories.extend(
+ read_dir
+ .filter_map(std::result::Result::ok)
+ .map(|entry| entry.path())
+ .filter(|path| path.is_dir()),
+ );
+ }
+
+ while let Some(directory) = directories.pop() {
+ let read_dir = match fs::read_dir(&directory) {
+ Ok(read_dir) => read_dir,
+ Err(_) => continue,
+ };
+
+ for entry in read_dir.filter_map(std::result::Result::ok) {
+ let path = entry.path();
+ if path.is_dir() {
+ directories.push(path);
+ continue;
+ }
+
+ let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else {
+ continue;
+ };
+ let is_libclang = matches!(file_name, "libclang.so" | "libclang.dll" | "libclang.dylib")
+ || file_name.starts_with("libclang.so.")
+ || file_name.starts_with("libclang-");
+ if !is_libclang {
+ continue;
+ }
+
+ if let Some(parent) = path.parent() {
+ env::set_var("LIBCLANG_PATH", parent);
+ return;
+ }
+ }
+ }
+ }
+}