// We can use `std::process:Command` here because this is invoked within a build script, // _not_ within the Warp binary (where it could cause a terminal to temporarily flash on // Windows). #![allow(clippy::disallowed_types)] use std::env; use std::path::PathBuf; use std::process::Command; use cfg_aliases::cfg_aliases; fn main() { cfg_aliases! { macos: { target_os = "macos" }, // We use winit on all platforms other than mac, where we have a custom // AppKit-based platform implementation. winit: { not(macos) }, // We use wgpu for rendering on all platforms where we use winit, but // we can also use it on macOS, if enabled. wgpu: { any(winit, feature = "experimental-wgpu-renderer") }, native: { not(target_family = "wasm") }, } if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") { bindgen_shader_types(); compile_metal_shaders(); compile_objc_lib(); } } fn bindgen_shader_types() { let header_path = "src/platform/mac/rendering/metal/shaders/shader_types.h"; println!("cargo:rerun-if-changed={header_path}"); let bindings = bindgen::Builder::default() .header(header_path) .allowlist_type("vector_float2") .allowlist_type("Uniforms") .allowlist_type("PerRectUniforms") .allowlist_type("PerGlyphUniforms") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) // Disable intrinsic headers that define types containing 16-bit floats (`_Float16`, // `__m512h`, etc.) via preprocessor directive. `bindgen` doesn't know how to process // these types and panics at compile time. The types aren't used by our shader headers, // so suppressing the declarations is safe. // // - Xcode 15+: avx512fp16intrin.h, avx512vlfp16intrin.h // - Xcode 26+ (clang 21): amxavx512intrin.h, avx10_2convertintrin.h, // avx10_2_512convertintrin.h // // TODO(charlespierce): Remove once https://github.com/rust-lang/rust-bindgen/issues/2500 // is resolved. .clang_args([ "-D__AVX512VLFP16INTRIN_H", "-D__AVX512FP16INTRIN_H", "-D__AMX_AVX512INTRIN_H", "-D__AVX10_2CONVERTINTRIN_H", "-D__AVX10_2_512CONVERTINTRIN_H", "-D__AVX10_2_512MINMAXINTRIN_H", "-D__AVX10_2_512NIINTRIN_H", "-D__AVX10_2_512SATCVTINTRIN_H", ]) .generate() .unwrap_or_else(|_| panic!("unable to generate bindings for {header_path}")); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("shader_types.rs")) .expect("Couldn't write shader type bindings!"); } fn compile_metal_shaders() { let header_path = "src/platform/mac/rendering/metal/shaders/shader_types.h"; let metal_path = "src/platform/mac/rendering/metal/shaders/shaders.metal"; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let air_path = out_path.join("shaders.air"); let air_path = air_path.to_str().unwrap(); let lib_path = out_path.join("shaders.metallib"); let lib_path = lib_path.to_str().unwrap(); println!("cargo:rerun-if-changed={header_path}"); println!("cargo:rerun-if-changed={metal_path}"); println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET"); // Pin the AIR bytecode target to `MACOSX_DEPLOYMENT_TARGET`. Without an // explicit `-mmacosx-version-min`, `xcrun metal` on recent Xcode toolchains // emits AIR for the current SDK (e.g. `air64_v27-apple-macosx15.0.0`) // regardless of the env var, and older Metal driver stacks reject the // resulting `.metallib` during pipeline state creation. See #11700. let min_macos_version = env::var("MACOSX_DEPLOYMENT_TARGET") .expect("MACOSX_DEPLOYMENT_TARGET must be set for macOS builds"); let min_version_arg = format!("-mmacosx-version-min={min_macos_version}"); let mut compile_args = vec![ "-sdk", "macosx", "metal", "-c", metal_path, "-o", air_path, &min_version_arg, ]; if cfg!(feature = "enable-metal-frame-capture") { compile_args.push("-frecord-sources"); compile_args.push("-gline-tables-only"); } let result = Command::new("xcrun") .args(&compile_args) .output() .expect("error compiling metal shaders to .air"); assert!( result.status.success(), "error compiling metal shaders to .air; {}", std::str::from_utf8(&result.stderr).unwrap(), ); let result = Command::new("xcrun") .args(["-sdk", "macosx", "metallib", air_path, "-o", lib_path]) .output() .expect("error compiling metal shaders to .metallib"); assert!( result.status.success(), "error compling metal shaders to .metallib; {}", std::str::from_utf8(&result.stderr).unwrap(), ); } fn compile_objc_lib() { println!("cargo:rustc-link-lib=framework=UserNotifications"); println!("cargo:rustc-link-lib=framework=Carbon"); println!("cargo:rustc-link-lib=framework=SystemConfiguration"); println!("cargo:rustc-link-lib=framework=UniformTypeIdentifiers"); println!("cargo:rustc-link-lib=framework=AVFoundation"); println!("cargo:rustc-link-lib=framework=ServiceManagement"); println!("cargo:rerun-if-changed=src/platform/mac/objc/app.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/app.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/keycode.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/host_view.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/host_view.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/hotkey.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/hotkey.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/menus.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/menus.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/notifications/notifications.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/notifications/notifications.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/window.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/window_blur.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/window_blur.h"); // Referenced from https://github.com/tonymillion/Reachability println!("cargo:rerun-if-changed=src/platform/mac/objc/reachability.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/reachability.m"); println!("cargo:rerun-if-changed=src/platform/mac/objc/alert.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/fullscreen_queue.h"); println!("cargo:rerun-if-changed=src/platform/mac/objc/fullscreen_queue.m"); // Link against the clang_rt library so that the @available keyword // doesn't produce linker errors. // // See: https://github.com/alexcrichton/curl-rust/issues/279 if let Some(path) = macos_link_search_path() { println!("cargo:rustc-link-lib=clang_rt.osx"); println!("cargo:rustc-link-search={path}"); } cc::Build::new() .file("src/platform/mac/objc/app.m") .file("src/platform/mac/objc/host_view.m") .file("src/platform/mac/objc/hotkey.m") .file("src/platform/mac/objc/reachability.m") .file("src/platform/mac/objc/keycode.m") .file("src/platform/mac/objc/menus.m") .file("src/platform/mac/objc/notifications/notifications.m") .file("src/platform/mac/objc/window.m") .file("src/platform/mac/objc/fullscreen_queue.m") .file("src/platform/mac/objc/window_blur.m") .compile("warp_objc"); } /// Determine the path containing the macOS standard libraries by querying /// clang's library search paths. fn macos_link_search_path() -> Option { let output = Command::new("clang") .arg("--print-search-dirs") .output() .ok()?; if !output.status.success() { println!( "failed to run 'clang --print-search-dirs', continuing without a link search path" ); return None; } let stdout = String::from_utf8_lossy(&output.stdout); for line in stdout.lines() { if line.contains("libraries: =") { let path = line.split('=').nth(1)?; return Some(format!("{path}/lib/darwin")); } } println!("failed to determine link search path, continuing without it"); None }