mirror of
https://github.com/openai/codex.git
synced 2026-11-01 19:33:35 +00:00
407 lines
15 KiB
Diff
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;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|