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!(all(
 39        target_os = "macos",
 40        not(any(test, feature = "test-support", feature = "no-webrtc")),
 41    )) {
 42        let swift_target = get_swift_target();
 43
 44        build_bridge(&swift_target);
 45        link_swift_stdlib(&swift_target);
 46        link_webrtc_framework(&swift_target);
 47
 48        // Register exported Objective-C selectors, protocols, etc when building example binaries.
 49        println!("cargo:rustc-link-arg=-Wl,-ObjC");
 50    }
 51}
 52
 53fn build_bridge(swift_target: &SwiftTarget) {
 54    println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET");
 55    println!("cargo:rerun-if-changed={}/Sources", SWIFT_PACKAGE_NAME);
 56    println!(
 57        "cargo:rerun-if-changed={}/Package.swift",
 58        SWIFT_PACKAGE_NAME
 59    );
 60    println!(
 61        "cargo:rerun-if-changed={}/Package.resolved",
 62        SWIFT_PACKAGE_NAME
 63    );
 64
 65    let swift_package_root = swift_package_root();
 66    let swift_target_folder = swift_target_folder();
 67    let swift_cache_folder = swift_cache_folder();
 68    if !Command::new("swift")
 69        .arg("build")
 70        .arg("--disable-automatic-resolution")
 71        .args(["--configuration", &env::var("PROFILE").unwrap()])
 72        .args(["--triple", &swift_target.target.triple])
 73        .args(["--build-path".into(), swift_target_folder])
 74        .args(["--cache-path".into(), swift_cache_folder])
 75        .current_dir(&swift_package_root)
 76        .status()
 77        .unwrap()
 78        .success()
 79    {
 80        panic!(
 81            "Failed to compile swift package in {}",
 82            swift_package_root.display()
 83        );
 84    }
 85
 86    println!(
 87        "cargo:rustc-link-search=native={}",
 88        swift_target.out_dir_path().display()
 89    );
 90    println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME);
 91}
 92
 93fn link_swift_stdlib(swift_target: &SwiftTarget) {
 94    for path in &swift_target.paths.runtime_library_paths {
 95        println!("cargo:rustc-link-search=native={}", path);
 96    }
 97}
 98
 99fn link_webrtc_framework(swift_target: &SwiftTarget) {
100    let swift_out_dir_path = swift_target.out_dir_path();
101    println!("cargo:rustc-link-lib=framework=WebRTC");
102    println!(
103        "cargo:rustc-link-search=framework={}",
104        swift_out_dir_path.display()
105    );
106    // Find WebRTC.framework as a sibling of the executable when running tests.
107    println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
108    // Find WebRTC.framework in parent directory of the executable when running examples.
109    println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/..");
110
111    let source_path = swift_out_dir_path.join("WebRTC.framework");
112    let deps_dir_path =
113        PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework");
114    let target_dir_path =
115        PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework");
116    copy_dir(&source_path, &deps_dir_path);
117    copy_dir(&source_path, &target_dir_path);
118}
119
120fn get_swift_target() -> SwiftTarget {
121    let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
122    if arch == "aarch64" {
123        arch = "arm64".into();
124    }
125    let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION);
126
127    let swift_target_info_str = Command::new("swift")
128        .args(["-target", &target, "-print-target-info"])
129        .output()
130        .unwrap()
131        .stdout;
132
133    serde_json::from_slice(&swift_target_info_str).unwrap()
134}
135
136fn swift_package_root() -> PathBuf {
137    env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME)
138}
139
140fn swift_target_folder() -> PathBuf {
141    let target = env::var("TARGET").unwrap();
142    env::current_dir()
143        .unwrap()
144        .join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_target"))
145}
146
147fn swift_cache_folder() -> PathBuf {
148    let target = env::var("TARGET").unwrap();
149    env::current_dir()
150        .unwrap()
151        .join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_cache"))
152}
153
154fn copy_dir(source: &Path, destination: &Path) {
155    assert!(
156        Command::new("rm")
157            .arg("-rf")
158            .arg(destination)
159            .status()
160            .unwrap()
161            .success(),
162        "could not remove {:?} before copying",
163        destination
164    );
165
166    assert!(
167        Command::new("cp")
168            .arg("-R")
169            .args([source, destination])
170            .status()
171            .unwrap()
172            .success(),
173        "could not copy {:?} to {:?}",
174        source,
175        destination
176    );
177}
178
179impl SwiftTarget {
180    fn out_dir_path(&self) -> PathBuf {
181        swift_target_folder()
182            .join(&self.target.unversioned_triple)
183            .join(env::var("PROFILE").unwrap())
184    }
185}