build.rs

  1use serde::Deserialize;
  2use std::{
  3    env,
  4    path::{Path, PathBuf},
  5    process::Command,
  6};
  7
  8const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge";
  9
 10#[derive(Debug, Deserialize)]
 11#[serde(rename_all = "camelCase")]
 12pub struct SwiftTargetInfo {
 13    pub triple: String,
 14    pub unversioned_triple: String,
 15    pub module_triple: String,
 16    pub swift_runtime_compatibility_version: String,
 17    #[serde(rename = "librariesRequireRPath")]
 18    pub libraries_require_rpath: bool,
 19}
 20
 21#[derive(Debug, Deserialize)]
 22#[serde(rename_all = "camelCase")]
 23pub struct SwiftPaths {
 24    pub runtime_library_paths: Vec<String>,
 25    pub runtime_library_import_paths: Vec<String>,
 26    pub runtime_resource_path: String,
 27}
 28
 29#[derive(Debug, Deserialize)]
 30pub struct SwiftTarget {
 31    pub target: SwiftTargetInfo,
 32    pub paths: SwiftPaths,
 33}
 34
 35const MACOS_TARGET_VERSION: &str = "10.15.7";
 36
 37fn main() {
 38    if cfg!(not(any(test, feature = "test-support"))) {
 39        let swift_target = get_swift_target();
 40
 41        build_bridge(&swift_target);
 42        link_swift_stdlib(&swift_target);
 43        link_webrtc_framework(&swift_target);
 44
 45        // Register exported Objective-C selectors, protocols, etc when building example binaries.
 46        println!("cargo:rustc-link-arg=-Wl,-ObjC");
 47    }
 48}
 49
 50fn build_bridge(swift_target: &SwiftTarget) {
 51    println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET");
 52    println!("cargo:rerun-if-changed={}/Sources", SWIFT_PACKAGE_NAME);
 53    println!(
 54        "cargo:rerun-if-changed={}/Package.swift",
 55        SWIFT_PACKAGE_NAME
 56    );
 57    println!(
 58        "cargo:rerun-if-changed={}/Package.resolved",
 59        SWIFT_PACKAGE_NAME
 60    );
 61
 62    let swift_package_root = swift_package_root();
 63    let swift_target_folder = swift_target_folder();
 64    if !Command::new("swift")
 65        .arg("build")
 66        .arg("--disable-automatic-resolution")
 67        .args(["--configuration", &env::var("PROFILE").unwrap()])
 68        .args(["--triple", &swift_target.target.triple])
 69        .args(["--build-path".into(), swift_target_folder])
 70        .current_dir(&swift_package_root)
 71        .status()
 72        .unwrap()
 73        .success()
 74    {
 75        panic!(
 76            "Failed to compile swift package in {}",
 77            swift_package_root.display()
 78        );
 79    }
 80
 81    println!(
 82        "cargo:rustc-link-search=native={}",
 83        swift_target.out_dir_path().display()
 84    );
 85    println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME);
 86}
 87
 88fn link_swift_stdlib(swift_target: &SwiftTarget) {
 89    for path in &swift_target.paths.runtime_library_paths {
 90        println!("cargo:rustc-link-search=native={}", path);
 91    }
 92}
 93
 94fn link_webrtc_framework(swift_target: &SwiftTarget) {
 95    let swift_out_dir_path = swift_target.out_dir_path();
 96    println!("cargo:rustc-link-lib=framework=WebRTC");
 97    println!(
 98        "cargo:rustc-link-search=framework={}",
 99        swift_out_dir_path.display()
100    );
101    // Find WebRTC.framework as a sibling of the executable when running tests.
102    println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
103    // Find WebRTC.framework in parent directory of the executable when running examples.
104    println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/..");
105
106    let source_path = swift_out_dir_path.join("WebRTC.framework");
107    let deps_dir_path =
108        PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework");
109    let target_dir_path =
110        PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework");
111    copy_dir(&source_path, &deps_dir_path);
112    copy_dir(&source_path, &target_dir_path);
113}
114
115fn get_swift_target() -> SwiftTarget {
116    let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
117    if arch == "aarch64" {
118        arch = "arm64".into();
119    }
120    let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION);
121
122    let swift_target_info_str = Command::new("swift")
123        .args(["-target", &target, "-print-target-info"])
124        .output()
125        .unwrap()
126        .stdout;
127
128    serde_json::from_slice(&swift_target_info_str).unwrap()
129}
130
131fn swift_package_root() -> PathBuf {
132    env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME)
133}
134
135fn swift_target_folder() -> PathBuf {
136    env::current_dir()
137        .unwrap()
138        .join(format!("../../target/{SWIFT_PACKAGE_NAME}"))
139}
140
141fn copy_dir(source: &Path, destination: &Path) {
142    assert!(
143        Command::new("rm")
144            .arg("-rf")
145            .arg(destination)
146            .status()
147            .unwrap()
148            .success(),
149        "could not remove {:?} before copying",
150        destination
151    );
152
153    assert!(
154        Command::new("cp")
155            .arg("-R")
156            .args([source, destination])
157            .status()
158            .unwrap()
159            .success(),
160        "could not copy {:?} to {:?}",
161        source,
162        destination
163    );
164}
165
166impl SwiftTarget {
167    fn out_dir_path(&self) -> PathBuf {
168        swift_target_folder()
169            .join(&self.target.unversioned_triple)
170            .join(env::var("PROFILE").unwrap())
171    }
172}