1use serde::Deserialize;
2use std::{
3 env,
4 path::{Path, PathBuf},
5 process::Command,
6};
7
8const SWIFT_PACKAGE_NAME: &'static 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";
36
37fn main() {
38 let swift_target = get_swift_target();
39
40 build_bridge(&swift_target);
41 link_swift_stdlib(&swift_target);
42 link_webrtc_framework(&swift_target);
43}
44
45fn build_bridge(swift_target: &SwiftTarget) {
46 println!("cargo:rerun-if-changed={}/Sources", SWIFT_PACKAGE_NAME);
47 println!(
48 "cargo:rerun-if-changed={}/Package.swift",
49 SWIFT_PACKAGE_NAME
50 );
51 println!(
52 "cargo:rerun-if-changed={}/Package.resolved",
53 SWIFT_PACKAGE_NAME
54 );
55 let swift_package_root = swift_package_root();
56 if !Command::new("swift")
57 .arg("build")
58 .args(&["--configuration", &env::var("PROFILE").unwrap()])
59 .args(&["--triple", &swift_target.target.triple])
60 .current_dir(&swift_package_root)
61 .status()
62 .unwrap()
63 .success()
64 {
65 panic!(
66 "Failed to compile swift package in {}",
67 swift_package_root.display()
68 );
69 }
70
71 println!(
72 "cargo:rustc-link-search=native={}",
73 swift_target.out_dir_path().display()
74 );
75 println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME);
76}
77
78fn link_swift_stdlib(swift_target: &SwiftTarget) {
79 swift_target
80 .paths
81 .runtime_library_paths
82 .iter()
83 .for_each(|path| {
84 println!("cargo:rustc-link-search=native={}", path);
85 });
86}
87
88fn link_webrtc_framework(swift_target: &SwiftTarget) {
89 let swift_out_dir_path = swift_target.out_dir_path();
90 println!("cargo:rustc-link-lib=framework=WebRTC");
91 println!(
92 "cargo:rustc-link-search=framework={}",
93 swift_out_dir_path.display()
94 );
95 // Find WebRTC.framework as a sibling of the executable when running tests.
96 println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
97
98 let source_path = swift_out_dir_path.join("WebRTC.framework");
99 let deps_dir_path =
100 PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework");
101 let target_dir_path =
102 PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework");
103 copy_dir(&source_path, &deps_dir_path);
104 copy_dir(&source_path, &target_dir_path);
105}
106
107fn get_swift_target() -> SwiftTarget {
108 let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
109 if arch == "aarch64" {
110 arch = "arm64".into();
111 }
112 let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION);
113
114 let swift_target_info_str = Command::new("swift")
115 .args(&["-target", &target, "-print-target-info"])
116 .output()
117 .unwrap()
118 .stdout;
119
120 serde_json::from_slice(&swift_target_info_str).unwrap()
121}
122
123fn swift_package_root() -> PathBuf {
124 env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME)
125}
126
127fn copy_dir(source: &Path, destination: &Path) {
128 assert!(
129 Command::new("cp")
130 .arg("-r")
131 .args(&[source, destination])
132 .status()
133 .unwrap()
134 .success(),
135 "could not copy {:?} to {:?}",
136 source,
137 destination
138 );
139}
140
141impl SwiftTarget {
142 fn out_dir_path(&self) -> PathBuf {
143 swift_package_root()
144 .join(".build")
145 .join(&self.target.unversioned_triple)
146 .join(env::var("PROFILE").unwrap())
147 }
148}