use std::env;
use std::path::PathBuf;
use std::process::Command;
fn shim_skipped(reason: String) {
if std::env::var("CAR_REQUIRE_APPLE_SHIMS").as_deref() == Ok("1") {
println!("cargo::error={reason}");
} else {
println!("cargo:warning={reason}");
println!(
"cargo:warning=ScreenCaptureKit shim skipped — display capture will use the \
deprecated CGDisplayCreateImage path. Set CAR_REQUIRE_APPLE_SHIMS=1 to fail \
the build instead."
);
}
}
fn main() {
let target = env::var("TARGET").unwrap_or_default();
let is_macos = target.ends_with("-apple-darwin");
let is_apple_silicon = target.starts_with("aarch64-");
println!("cargo::rustc-check-cfg=cfg(car_sck_swift_built)");
println!("cargo:rerun-if-changed=swift/CarScreenCapture.swift");
println!("cargo:rerun-if-changed=build.rs");
if !is_macos {
return;
}
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let swift_src = manifest_dir.join("swift").join("CarScreenCapture.swift");
if !swift_src.exists() {
shim_skipped(format!(
"ScreenCaptureKit Swift shim not found at {}",
swift_src.display()
));
return;
}
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let obj_path = out_dir.join("CarScreenCapture.o");
let lib_path = out_dir.join("libCarScreenCapture.a");
let swift_target = if is_apple_silicon {
"arm64-apple-macosx14.0"
} else {
"x86_64-apple-macosx14.0"
};
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=14.0");
let status = Command::new("xcrun")
.arg("--sdk")
.arg("macosx")
.arg("swiftc")
.arg("-parse-as-library")
.arg("-O")
.arg("-target")
.arg(swift_target)
.arg("-emit-object")
.arg("-o")
.arg(&obj_path)
.arg(&swift_src)
.status();
let status = match status {
Ok(s) => s,
Err(e) => {
shim_skipped(format!(
"swiftc invocation failed ({e}); SCK backend unavailable. \
Install full Xcode (not just Command Line Tools) to enable it."
));
return;
}
};
if !status.success() {
shim_skipped(format!(
"swiftc returned non-zero status ({:?}); SCK backend unavailable",
status.code()
));
return;
}
let ar_status = Command::new("ar")
.arg("crus")
.arg(&lib_path)
.arg(&obj_path)
.status();
match ar_status {
Ok(s) if s.success() => {}
_ => {
shim_skipped(format!(
"ar failed to pack {} into {}; SCK backend unavailable",
obj_path.display(),
lib_path.display()
));
return;
}
}
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=CarScreenCapture");
println!("cargo:rustc-link-arg=-Wl,-weak_framework,ScreenCaptureKit");
println!("cargo:rustc-link-arg=-Wl,-framework,CoreVideo");
println!("cargo:rustc-link-arg=-Wl,-framework,CoreGraphics");
println!("cargo:rustc-link-arg=-Wl,-framework,Foundation");
println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift");
if let Ok(swift_lib) = swift_runtime_path() {
println!("cargo:rustc-link-search=native={}", swift_lib);
}
println!("cargo:rustc-link-lib=dylib=swiftCore");
println!("cargo:rustc-link-lib=dylib=swift_Concurrency");
println!("cargo:rustc-link-lib=dylib=swiftFoundation");
println!("cargo:rustc-cfg=car_sck_swift_built");
}
fn swift_runtime_path() -> Result<String, std::io::Error> {
let out = Command::new("xcrun").arg("--show-sdk-path").output()?;
if !out.status.success() {
return Err(std::io::Error::other("xcrun --show-sdk-path failed"));
}
let sdk = String::from_utf8_lossy(&out.stdout).trim().to_string();
Ok(format!("{}/usr/lib/swift", sdk))
}