use serde::Deserialize; use std::{ env, path::{Path, PathBuf}, process::Command, }; const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge"; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SwiftTargetInfo { pub triple: String, pub unversioned_triple: String, pub module_triple: String, pub swift_runtime_compatibility_version: String, #[serde(rename = "librariesRequireRPath")] pub libraries_require_rpath: bool, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SwiftPaths { pub runtime_library_paths: Vec, pub runtime_library_import_paths: Vec, pub runtime_resource_path: String, } #[derive(Debug, Deserialize)] pub struct SwiftTarget { pub target: SwiftTargetInfo, pub paths: SwiftPaths, } const MACOS_TARGET_VERSION: &str = "10.15.7"; fn main() { if cfg!(not(any(test, feature = "test-support"))) { let swift_target = get_swift_target(); build_bridge(&swift_target); link_swift_stdlib(&swift_target); link_webrtc_framework(&swift_target); // Register exported Objective-C selectors, protocols, etc when building example binaries. println!("cargo:rustc-link-arg=-Wl,-ObjC"); } } fn build_bridge(swift_target: &SwiftTarget) { println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET"); println!("cargo:rerun-if-changed={}/Sources", SWIFT_PACKAGE_NAME); println!( "cargo:rerun-if-changed={}/Package.swift", SWIFT_PACKAGE_NAME ); println!( "cargo:rerun-if-changed={}/Package.resolved", SWIFT_PACKAGE_NAME ); let swift_package_root = swift_package_root(); if !Command::new("swift") .arg("build") .args(["--configuration", &env::var("PROFILE").unwrap()]) .args(["--triple", &swift_target.target.triple]) .current_dir(&swift_package_root) .status() .unwrap() .success() { panic!( "Failed to compile swift package in {}", swift_package_root.display() ); } println!( "cargo:rustc-link-search=native={}", swift_target.out_dir_path().display() ); println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME); } fn link_swift_stdlib(swift_target: &SwiftTarget) { for path in &swift_target.paths.runtime_library_paths { println!("cargo:rustc-link-search=native={}", path); } } fn link_webrtc_framework(swift_target: &SwiftTarget) { let swift_out_dir_path = swift_target.out_dir_path(); println!("cargo:rustc-link-lib=framework=WebRTC"); println!( "cargo:rustc-link-search=framework={}", swift_out_dir_path.display() ); // Find WebRTC.framework as a sibling of the executable when running tests. println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); // Find WebRTC.framework in parent directory of the executable when running examples. println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/.."); let source_path = swift_out_dir_path.join("WebRTC.framework"); let deps_dir_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework"); let target_dir_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework"); copy_dir(&source_path, &deps_dir_path); copy_dir(&source_path, &target_dir_path); } fn get_swift_target() -> SwiftTarget { let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); if arch == "aarch64" { arch = "arm64".into(); } let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION); let swift_target_info_str = Command::new("swift") .args(["-target", &target, "-print-target-info"]) .output() .unwrap() .stdout; serde_json::from_slice(&swift_target_info_str).unwrap() } fn swift_package_root() -> PathBuf { env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME) } fn copy_dir(source: &Path, destination: &Path) { assert!( Command::new("rm") .arg("-rf") .arg(destination) .status() .unwrap() .success(), "could not remove {:?} before copying", destination ); assert!( Command::new("cp") .arg("-R") .args([source, destination]) .status() .unwrap() .success(), "could not copy {:?} to {:?}", source, destination ); } impl SwiftTarget { fn out_dir_path(&self) -> PathBuf { swift_package_root() .join(".build") .join(&self.target.unversioned_triple) .join(env::var("PROFILE").unwrap()) } }