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}