1use serde::Deserialize;
2use std::{
3 env,
4 path::{Path, PathBuf},
5 process::Command,
6};
7
8const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge2";
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 let swift_cache_folder = swift_cache_folder();
65 if !Command::new("swift")
66 .arg("build")
67 .arg("--disable-automatic-resolution")
68 .args(["--configuration", &env::var("PROFILE").unwrap()])
69 .args(["--triple", &swift_target.target.triple])
70 .args(["--build-path".into(), swift_target_folder])
71 .args(["--cache-path".into(), swift_cache_folder])
72 .current_dir(&swift_package_root)
73 .status()
74 .unwrap()
75 .success()
76 {
77 panic!(
78 "Failed to compile swift package in {}",
79 swift_package_root.display()
80 );
81 }
82
83 println!(
84 "cargo:rustc-link-search=native={}",
85 swift_target.out_dir_path().display()
86 );
87 println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME);
88}
89
90fn link_swift_stdlib(swift_target: &SwiftTarget) {
91 for path in &swift_target.paths.runtime_library_paths {
92 println!("cargo:rustc-link-search=native={}", path);
93 }
94}
95
96fn link_webrtc_framework(swift_target: &SwiftTarget) {
97 let swift_out_dir_path = swift_target.out_dir_path();
98 println!("cargo:rustc-link-lib=framework=WebRTC");
99 println!(
100 "cargo:rustc-link-search=framework={}",
101 swift_out_dir_path.display()
102 );
103 // Find WebRTC.framework as a sibling of the executable when running tests.
104 println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
105 // Find WebRTC.framework in parent directory of the executable when running examples.
106 println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/..");
107
108 let source_path = swift_out_dir_path.join("WebRTC.framework");
109 let deps_dir_path =
110 PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework");
111 let target_dir_path =
112 PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework");
113 copy_dir(&source_path, &deps_dir_path);
114 copy_dir(&source_path, &target_dir_path);
115}
116
117fn get_swift_target() -> SwiftTarget {
118 let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
119 if arch == "aarch64" {
120 arch = "arm64".into();
121 }
122 let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION);
123
124 let swift_target_info_str = Command::new("swift")
125 .args(["-target", &target, "-print-target-info"])
126 .output()
127 .unwrap()
128 .stdout;
129
130 serde_json::from_slice(&swift_target_info_str).unwrap()
131}
132
133fn swift_package_root() -> PathBuf {
134 env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME)
135}
136
137fn swift_target_folder() -> PathBuf {
138 let target = env::var("TARGET").unwrap();
139 env::current_dir()
140 .unwrap()
141 .join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_target"))
142}
143
144fn swift_cache_folder() -> PathBuf {
145 let target = env::var("TARGET").unwrap();
146 env::current_dir()
147 .unwrap()
148 .join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_cache"))
149}
150
151fn copy_dir(source: &Path, destination: &Path) {
152 assert!(
153 Command::new("rm")
154 .arg("-rf")
155 .arg(destination)
156 .status()
157 .unwrap()
158 .success(),
159 "could not remove {:?} before copying",
160 destination
161 );
162
163 assert!(
164 Command::new("cp")
165 .arg("-R")
166 .args([source, destination])
167 .status()
168 .unwrap()
169 .success(),
170 "could not copy {:?} to {:?}",
171 source,
172 destination
173 );
174}
175
176impl SwiftTarget {
177 fn out_dir_path(&self) -> PathBuf {
178 swift_target_folder()
179 .join(&self.target.unversioned_triple)
180 .join(env::var("PROFILE").unwrap())
181 }
182}