diff --git a/crates/cactus-sys/build.rs b/crates/cactus-sys/build.rs index 00e0b5f802..721018d26c 100644 --- a/crates/cactus-sys/build.rs +++ b/crates/cactus-sys/build.rs @@ -1,74 +1,111 @@ use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let workspace_root = manifest_dir.parent().unwrap().parent().unwrap(); - let vendor_dir = workspace_root.join("vendor").join("cactus"); - let cactus_src_dir = vendor_dir.join("cactus"); + let cactus_src = locate_cactus_source(); + set_rebuild_triggers(&cactus_src); + apply_linux_compiler_workaround(); + + let build_dir = build_native_library(&cactus_src); + link_native_library(&build_dir); + link_platform_dependencies(); + + generate_bindings(&cactus_src); +} + +fn locate_cactus_source() -> PathBuf { + let path = env::var("CACTUS_SOURCE_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| char_workspace_fallback()); assert!( - cactus_src_dir.exists(), - "Cactus source not found at {cactus_src_dir:?}. Run: git submodule update --init --recursive" + path.exists(), + "Cactus source not found at {path:?}. Set CACTUS_SOURCE_DIR or run: git submodule update --init --recursive" ); + path +} + +fn char_workspace_fallback() -> PathBuf { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + manifest_dir + .ancestors() + .nth(2) + .unwrap() + .join("vendor/cactus/cactus") +} + +fn set_rebuild_triggers(cactus_src: &Path) { + println!("cargo:rerun-if-env-changed=CACTUS_SOURCE_DIR"); println!("cargo:rerun-if-changed=wrapper.h"); println!( "cargo:rerun-if-changed={}", - cactus_src_dir.join("ffi").join("cactus_ffi.h").display() + cactus_src.join("ffi/cactus_ffi.h").display() ); +} - // GCC on Linux doesn't include transitively through , - // but cactus/telemetry/telemetry.cpp uses std::setfill/setw without including it. - #[cfg(target_os = "linux")] - { - let existing = env::var("CXXFLAGS").unwrap_or_default(); - let patched = if existing.is_empty() { - "-include iomanip".to_string() - } else { - format!("-include iomanip {existing}") - }; - // Safety: build scripts run in an isolated process. - unsafe { env::set_var("CXXFLAGS", patched) }; - } +#[cfg(target_os = "linux")] +fn apply_linux_compiler_workaround() { + // GCC requires explicit ; upstream telemetry.cpp omits it. + let existing = env::var("CXXFLAGS").unwrap_or_default(); + let cxxflags = if existing.is_empty() { + "-include iomanip".to_string() + } else { + format!("-include iomanip {existing}") + }; + unsafe { env::set_var("CXXFLAGS", cxxflags) }; +} + +#[cfg(not(target_os = "linux"))] +fn apply_linux_compiler_workaround() {} - let dst = cmake::Config::new(&cactus_src_dir) +fn build_native_library(cactus_src: &Path) -> PathBuf { + cmake::Config::new(cactus_src) .define("BUILD_SHARED_LIBS", "OFF") .define("CMAKE_BUILD_TYPE", "Release") .build_target("cactus") - .build(); + .build() + .join("build") +} - println!( - "cargo:rustc-link-search=native={}", - dst.join("build").display() - ); +fn link_native_library(build_dir: &Path) { + println!("cargo:rustc-link-search=native={}", build_dir.display()); println!("cargo:rustc-link-lib=static=cactus"); +} - #[cfg(target_os = "macos")] - { - println!("cargo:rustc-link-lib=framework=Metal"); - println!("cargo:rustc-link-lib=framework=MetalPerformanceShaders"); - println!("cargo:rustc-link-lib=framework=Accelerate"); - println!("cargo:rustc-link-lib=framework=Foundation"); - println!("cargo:rustc-link-lib=framework=CoreML"); - println!("cargo:rustc-link-lib=curl"); - println!("cargo:rustc-link-lib=c++"); +#[cfg(target_os = "macos")] +fn link_platform_dependencies() { + for framework in [ + "Metal", + "MetalPerformanceShaders", + "Accelerate", + "Foundation", + "CoreML", + ] { + println!("cargo:rustc-link-lib=framework={framework}"); } + println!("cargo:rustc-link-lib=curl"); + println!("cargo:rustc-link-lib=c++"); +} - #[cfg(target_os = "linux")] - { - println!("cargo:rustc-link-lib=stdc++"); - println!("cargo:rustc-link-lib=m"); - println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=curl"); - } +#[cfg(target_os = "linux")] +fn link_platform_dependencies() { + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib=m"); + println!("cargo:rustc-link-lib=pthread"); + println!("cargo:rustc-link-lib=curl"); +} + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +fn link_platform_dependencies() {} - let wrapper_path = manifest_dir.join("wrapper.h"); +fn generate_bindings(cactus_src: &Path) { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let bindings = bindgen::Builder::default() - .header(wrapper_path.to_str().unwrap()) - .clang_arg(format!("-I{}", cactus_src_dir.display())) + bindgen::Builder::default() + .header(manifest_dir.join("wrapper.h").to_str().unwrap()) + .clang_arg(format!("-I{}", cactus_src.display())) .clang_arg("-x") .clang_arg("c++") .clang_arg("-std=c++20") @@ -78,9 +115,7 @@ fn main() { .derive_debug(true) .derive_default(true) .generate() - .expect("failed to generate bindings"); - - bindings + .expect("failed to generate bindings") .write_to_file(out_dir.join("bindings.rs")) .expect("failed to write bindings"); } diff --git a/crates/cactus-sys/src/lib.rs b/crates/cactus-sys/src/lib.rs index 0cf64d2a30..83ce996302 100644 --- a/crates/cactus-sys/src/lib.rs +++ b/crates/cactus-sys/src/lib.rs @@ -1,7 +1,9 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(clippy::all)] +#![allow( + non_upper_case_globals, + non_camel_case_types, + non_snake_case, + dead_code, + clippy::all +)] include!(concat!(env!("OUT_DIR"), "/bindings.rs"));