|
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 | // SPDX-License-Identifier: MIT |
4 | 4 |
|
5 | | -use std::{ |
6 | | - env::{var, var_os}, |
7 | | - fs::{copy, create_dir, create_dir_all, read_to_string, remove_dir_all, write}, |
8 | | - path::{Path, PathBuf}, |
9 | | -}; |
| 5 | +use std::{fs::write, path::PathBuf}; |
10 | 6 |
|
11 | 7 | use anyhow::{Context, Result}; |
12 | | -use serde::{Deserialize, Serialize}; |
13 | 8 |
|
14 | | -#[derive(Debug, Default, Deserialize, Serialize, Eq, PartialEq)] |
15 | | -pub(crate) struct PluginMetadata { |
16 | | - pub path: PathBuf, |
17 | | -} |
18 | | - |
19 | | -#[derive(Default)] |
20 | | -pub struct PluginBuilder { |
21 | | - android_path: Option<PathBuf>, |
22 | | - ios_path: Option<PathBuf>, |
23 | | -} |
24 | | - |
25 | | -impl PluginBuilder { |
26 | | - /// Creates a new builder for mobile plugin functionality. |
27 | | - pub fn new() -> Self { |
28 | | - Self::default() |
29 | | - } |
30 | | - |
31 | | - /// Sets the Android project path. |
32 | | - pub fn android_path<P: Into<PathBuf>>(mut self, android_path: P) -> Self { |
33 | | - self.android_path.replace(android_path.into()); |
34 | | - self |
35 | | - } |
36 | | - |
37 | | - /// Sets the iOS project path. |
38 | | - pub fn ios_path<P: Into<PathBuf>>(mut self, ios_path: P) -> Self { |
39 | | - self.ios_path.replace(ios_path.into()); |
40 | | - self |
41 | | - } |
42 | | - |
43 | | - /// Injects the mobile templates in the given path relative to the manifest root. |
44 | | - pub fn run(self) -> Result<()> { |
45 | | - let target_os = var("CARGO_CFG_TARGET_OS").unwrap(); |
46 | | - let mobile = target_os == "android" || target_os == "ios"; |
47 | | - crate::cfg_alias("mobile", mobile); |
48 | | - crate::cfg_alias("desktop", !mobile); |
49 | | - |
50 | | - match target_os.as_str() { |
51 | | - "android" => { |
52 | | - if let Some(path) = self.android_path { |
53 | | - let manifest_dir = var_os("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap(); |
54 | | - let source = manifest_dir.join(path); |
55 | | - |
56 | | - let tauri_library_path = std::env::var("DEP_TAURI_ANDROID_LIBRARY_PATH") |
57 | | - .expect("missing `DEP_TAURI_ANDROID_LIBRARY_PATH` environment variable. Make sure `tauri` is a dependency of the plugin."); |
58 | | - println!("cargo:rerun-if-env-changed=DEP_TAURI_ANDROID_LIBRARY_PATH"); |
59 | | - |
60 | | - create_dir_all(source.join(".tauri")).context("failed to create .tauri directory")?; |
61 | | - copy_folder( |
62 | | - Path::new(&tauri_library_path), |
63 | | - &source.join(".tauri").join("tauri-api"), |
64 | | - &[], |
65 | | - ) |
66 | | - .context("failed to copy tauri-api to the plugin project")?; |
67 | | - |
68 | | - println!("cargo:android_library_path={}", source.display()); |
69 | | - } |
70 | | - } |
71 | | - #[cfg(target_os = "macos")] |
72 | | - "ios" => { |
73 | | - if let Some(path) = self.ios_path { |
74 | | - let manifest_dir = var_os("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap(); |
75 | | - let tauri_library_path = std::env::var("DEP_TAURI_IOS_LIBRARY_PATH") |
76 | | - .expect("missing `DEP_TAURI_IOS_LIBRARY_PATH` environment variable. Make sure `tauri` is a dependency of the plugin."); |
77 | | - |
78 | | - let tauri_dep_path = path.parent().unwrap().join(".tauri"); |
79 | | - create_dir_all(&tauri_dep_path).context("failed to create .tauri directory")?; |
80 | | - copy_folder( |
81 | | - Path::new(&tauri_library_path), |
82 | | - &tauri_dep_path.join("tauri-api"), |
83 | | - &[".build", "Package.resolved", "Tests"], |
84 | | - ) |
85 | | - .context("failed to copy tauri-api to the plugin project")?; |
86 | | - link_swift_library(&var("CARGO_PKG_NAME").unwrap(), manifest_dir.join(path)); |
87 | | - } |
88 | | - } |
89 | | - _ => (), |
90 | | - } |
91 | | - |
92 | | - Ok(()) |
93 | | - } |
94 | | -} |
95 | | - |
96 | | -#[cfg(target_os = "macos")] |
97 | | -#[doc(hidden)] |
98 | | -pub fn link_swift_library(name: &str, source: impl AsRef<Path>) { |
99 | | - let source = source.as_ref(); |
100 | | - |
101 | | - let sdk_root = std::env::var_os("SDKROOT"); |
102 | | - std::env::remove_var("SDKROOT"); |
103 | | - |
104 | | - swift_rs::SwiftLinker::new( |
105 | | - &std::env::var("MACOSX_DEPLOYMENT_TARGET").unwrap_or_else(|_| "10.13".into()), |
106 | | - ) |
107 | | - .with_ios(&std::env::var("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or_else(|_| "13.0".into())) |
108 | | - .with_package(name, source) |
109 | | - .link(); |
110 | | - |
111 | | - if let Some(root) = sdk_root { |
112 | | - std::env::set_var("SDKROOT", root); |
113 | | - } |
114 | | -} |
115 | | - |
116 | | -fn copy_folder(source: &Path, target: &Path, ignore_paths: &[&str]) -> Result<()> { |
117 | | - let _ = remove_dir_all(target); |
118 | | - |
119 | | - for entry in walkdir::WalkDir::new(source) { |
120 | | - let entry = entry?; |
121 | | - let rel_path = entry.path().strip_prefix(source)?; |
122 | | - let rel_path_str = rel_path.to_string_lossy(); |
123 | | - if ignore_paths |
124 | | - .iter() |
125 | | - .any(|path| rel_path_str.starts_with(path)) |
126 | | - { |
127 | | - continue; |
128 | | - } |
129 | | - let dest_path = target.join(rel_path); |
130 | | - |
131 | | - if entry.file_type().is_dir() { |
132 | | - create_dir(&dest_path) |
133 | | - .with_context(|| format!("failed to create directory {}", dest_path.display()))?; |
134 | | - } else { |
135 | | - copy(entry.path(), &dest_path).with_context(|| { |
136 | | - format!( |
137 | | - "failed to copy {} to {}", |
138 | | - entry.path().display(), |
139 | | - dest_path.display() |
140 | | - ) |
141 | | - })?; |
142 | | - println!("cargo:rerun-if-changed={}", entry.path().display()); |
143 | | - } |
144 | | - } |
145 | | - |
146 | | - Ok(()) |
147 | | -} |
148 | | - |
149 | | -#[cfg(target_os = "macos")] |
150 | | -fn update_plist_file<P: AsRef<Path>, F: FnOnce(&mut plist::Dictionary)>( |
151 | | - path: P, |
152 | | - f: F, |
153 | | -) -> Result<()> { |
154 | | - use std::io::Cursor; |
155 | | - |
156 | | - let path = path.as_ref(); |
157 | | - if path.exists() { |
158 | | - let plist_str = read_to_string(path)?; |
159 | | - let mut plist = plist::Value::from_reader(Cursor::new(&plist_str))?; |
160 | | - if let Some(dict) = plist.as_dictionary_mut() { |
161 | | - f(dict); |
162 | | - let mut plist_buf = Vec::new(); |
163 | | - let writer = Cursor::new(&mut plist_buf); |
164 | | - plist::to_writer_xml(writer, &plist)?; |
165 | | - let new_plist_str = String::from_utf8(plist_buf)?; |
166 | | - if new_plist_str != plist_str { |
167 | | - write(path, new_plist_str)?; |
168 | | - } |
169 | | - } |
170 | | - } |
171 | | - |
172 | | - Ok(()) |
173 | | -} |
174 | | - |
175 | | -#[cfg(target_os = "macos")] |
176 | | -pub fn update_entitlements<F: FnOnce(&mut plist::Dictionary)>(f: F) -> Result<()> { |
177 | | - if let (Some(project_path), Ok(app_name)) = ( |
178 | | - var_os("TAURI_IOS_PROJECT_PATH").map(PathBuf::from), |
179 | | - var("TAURI_IOS_APP_NAME"), |
180 | | - ) { |
181 | | - update_plist_file( |
182 | | - project_path |
183 | | - .join(format!("{app_name}_iOS")) |
184 | | - .join(format!("{app_name}_iOS.entitlements")), |
185 | | - f, |
186 | | - )?; |
187 | | - } |
188 | | - |
189 | | - Ok(()) |
190 | | -} |
191 | | - |
192 | | -fn xml_block_comment(id: &str) -> String { |
193 | | - format!("<!-- {id}. AUTO-GENERATED. DO NOT REMOVE. -->") |
194 | | -} |
195 | | - |
196 | | -fn insert_into_xml(xml: &str, block_identifier: &str, parent_tag: &str, contents: &str) -> String { |
197 | | - let block_comment = xml_block_comment(block_identifier); |
198 | | - |
199 | | - let mut rewritten = Vec::new(); |
200 | | - let mut found_block = false; |
201 | | - let parent_closing_tag = format!("</{parent_tag}>"); |
202 | | - for line in xml.split('\n') { |
203 | | - if line.contains(&block_comment) { |
204 | | - found_block = !found_block; |
205 | | - continue; |
206 | | - } |
207 | | - |
208 | | - // found previous block which should be removed |
209 | | - if found_block { |
210 | | - continue; |
211 | | - } |
212 | | - |
213 | | - if let Some(index) = line.find(&parent_closing_tag) { |
214 | | - let identation = " ".repeat(index + 4); |
215 | | - rewritten.push(format!("{}{}", identation, block_comment)); |
216 | | - for l in contents.split('\n') { |
217 | | - rewritten.push(format!("{}{}", identation, l)); |
218 | | - } |
219 | | - rewritten.push(format!("{}{}", identation, block_comment)); |
220 | | - } |
221 | | - |
222 | | - rewritten.push(line.to_string()); |
223 | | - } |
224 | | - |
225 | | - rewritten.join("\n") |
226 | | -} |
227 | | - |
228 | | -pub fn update_android_manifest(block_identifier: &str, parent: &str, insert: String) -> Result<()> { |
229 | | - if let Some(project_path) = var_os("TAURI_ANDROID_PROJECT_PATH").map(PathBuf::from) { |
230 | | - let manifest_path = project_path.join("app/src/main/AndroidManifest.xml"); |
231 | | - let manifest = read_to_string(&manifest_path)?; |
232 | | - let rewritten = insert_into_xml(&manifest, block_identifier, parent, &insert); |
233 | | - if rewritten != manifest { |
234 | | - write(manifest_path, rewritten)?; |
235 | | - } |
236 | | - } |
237 | | - Ok(()) |
238 | | -} |
239 | | - |
240 | | -pub(crate) fn generate_gradle_files(project_dir: PathBuf) -> Result<()> { |
| 9 | +pub fn generate_gradle_files(project_dir: PathBuf) -> Result<()> { |
241 | 10 | let gradle_settings_path = project_dir.join("tauri.settings.gradle"); |
242 | 11 | let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts"); |
243 | 12 |
|
@@ -289,37 +58,3 @@ dependencies {" |
289 | 58 |
|
290 | 59 | Ok(()) |
291 | 60 | } |
292 | | - |
293 | | -#[cfg(test)] |
294 | | -mod tests { |
295 | | - #[test] |
296 | | - fn insert_into_xml() { |
297 | | - let manifest = r#"<manifest> |
298 | | - <application> |
299 | | - <intent-filter> |
300 | | - </intent-filter> |
301 | | - </application> |
302 | | -</manifest>"#; |
303 | | - let id = "tauritest"; |
304 | | - let new = super::insert_into_xml(manifest, id, "application", "<something></something>"); |
305 | | - |
306 | | - let block_id_comment = super::xml_block_comment(id); |
307 | | - let expected = format!( |
308 | | - r#"<manifest> |
309 | | - <application> |
310 | | - <intent-filter> |
311 | | - </intent-filter> |
312 | | - {block_id_comment} |
313 | | - <something></something> |
314 | | - {block_id_comment} |
315 | | - </application> |
316 | | -</manifest>"# |
317 | | - ); |
318 | | - |
319 | | - assert_eq!(new, expected); |
320 | | - |
321 | | - // assert it's still the same after an empty update |
322 | | - let new = super::insert_into_xml(&expected, id, "application", "<something></something>"); |
323 | | - assert_eq!(new, expected); |
324 | | - } |
325 | | -} |
0 commit comments