1use std::{fmt::Display, path::PathBuf};
8
9use serde::{Deserialize, Serialize};
10
11use crate::{config::BundleType, Env, PackageInfo};
12
13mod starting_binary;
14
15#[cfg(target_os = "android")]
20pub const ANDROID_ASSET_PROTOCOL_URI_PREFIX: &str = "asset://localhost/";
21
22#[derive(PartialEq, Eq, Copy, Debug, Clone, Serialize, Deserialize)]
24#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
25#[serde(rename_all = "camelCase")]
26#[non_exhaustive]
27pub enum Target {
28 #[serde(rename = "macOS")]
30 MacOS,
31 Windows,
33 Linux,
35 Android,
37 #[serde(rename = "iOS")]
39 Ios,
40}
41
42impl Display for Target {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(
45 f,
46 "{}",
47 match self {
48 Self::MacOS => "macOS",
49 Self::Windows => "windows",
50 Self::Linux => "linux",
51 Self::Android => "android",
52 Self::Ios => "iOS",
53 }
54 )
55 }
56}
57
58impl Target {
59 pub fn from_triple(target: &str) -> Self {
61 if target.contains("darwin") {
62 Self::MacOS
63 } else if target.contains("windows") {
64 Self::Windows
65 } else if target.contains("android") {
66 Self::Android
67 } else if target.contains("ios") {
68 Self::Ios
69 } else {
70 Self::Linux
71 }
72 }
73
74 pub fn current() -> Self {
76 if cfg!(target_os = "macos") {
77 Self::MacOS
78 } else if cfg!(target_os = "windows") {
79 Self::Windows
80 } else if cfg!(target_os = "ios") {
81 Self::Ios
82 } else if cfg!(target_os = "android") {
83 Self::Android
84 } else {
85 Self::Linux
86 }
87 }
88
89 pub fn is_mobile(&self) -> bool {
91 matches!(self, Target::Android | Target::Ios)
92 }
93
94 pub fn is_desktop(&self) -> bool {
96 !self.is_mobile()
97 }
98}
99
100pub fn current_exe() -> std::io::Result<PathBuf> {
173 self::starting_binary::STARTING_BINARY.cloned()
174}
175
176pub fn target_triple() -> crate::Result<String> {
186 let arch = if cfg!(target_arch = "x86") {
187 "i686"
188 } else if cfg!(target_arch = "x86_64") {
189 "x86_64"
190 } else if cfg!(target_arch = "arm") {
191 "armv7"
192 } else if cfg!(target_arch = "aarch64") {
193 "aarch64"
194 } else if cfg!(target_arch = "riscv64") {
195 "riscv64"
196 } else {
197 return Err(crate::Error::Architecture);
198 };
199
200 let os = if cfg!(target_os = "linux") {
201 "unknown-linux"
202 } else if cfg!(target_os = "macos") {
203 "apple-darwin"
204 } else if cfg!(target_os = "windows") {
205 "pc-windows"
206 } else if cfg!(target_os = "freebsd") {
207 "unknown-freebsd"
208 } else {
209 return Err(crate::Error::Os);
210 };
211
212 let os = if cfg!(target_os = "macos") || cfg!(target_os = "freebsd") {
213 String::from(os)
214 } else {
215 let env = if cfg!(target_env = "gnu") {
216 "gnu"
217 } else if cfg!(target_env = "musl") {
218 "musl"
219 } else if cfg!(target_env = "msvc") {
220 "msvc"
221 } else {
222 return Err(crate::Error::Environment);
223 };
224
225 format!("{os}-{env}")
226 };
227
228 Ok(format!("{arch}-{os}"))
229}
230
231#[cfg(all(not(test), not(target_os = "android")))]
232fn is_cargo_output_directory(path: &std::path::Path) -> bool {
233 path.join(".cargo-lock").exists()
234}
235
236#[cfg(test)]
237const CARGO_OUTPUT_DIRECTORIES: &[&str] = &["debug", "release", "custom-profile"];
238
239#[cfg(test)]
240fn is_cargo_output_directory(path: &std::path::Path) -> bool {
241 let Some(last_component) = path.components().next_back() else {
242 return false;
243 };
244 CARGO_OUTPUT_DIRECTORIES
245 .iter()
246 .any(|dirname| &last_component.as_os_str() == dirname)
247}
248
249pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<PathBuf> {
265 #[cfg(target_os = "android")]
266 return resource_dir_android(package_info, env);
267 #[cfg(not(target_os = "android"))]
268 {
269 let exe = current_exe()?;
270 resource_dir_from(exe, package_info, env)
271 }
272}
273
274#[cfg(target_os = "android")]
275fn resource_dir_android(_package_info: &PackageInfo, _env: &Env) -> crate::Result<PathBuf> {
276 Ok(PathBuf::from(ANDROID_ASSET_PROTOCOL_URI_PREFIX))
277}
278
279#[cfg(not(target_os = "android"))]
280#[allow(unused_variables)]
281fn resource_dir_from<P: AsRef<std::path::Path>>(
282 exe: P,
283 package_info: &PackageInfo,
284 env: &Env,
285) -> crate::Result<PathBuf> {
286 let exe_dir = exe.as_ref().parent().expect("failed to get exe directory");
287 let curr_dir = exe_dir.display().to_string();
288
289 let parts: Vec<&str> = curr_dir.split(std::path::MAIN_SEPARATOR).collect();
290 let len = parts.len();
291
292 if cfg!(target_os = "windows")
298 || ((len >= 2 && parts[len - 2] == "target") || (len >= 3 && parts[len - 3] == "target"))
299 && is_cargo_output_directory(exe_dir)
300 {
301 return Ok(exe_dir.to_path_buf());
302 }
303
304 #[allow(unused_mut, unused_assignments)]
305 let mut res = Err(crate::Error::UnsupportedPlatform);
306
307 #[cfg(target_os = "linux")]
308 {
309 res = if let Ok(bundle_dir) = exe_dir
311 .join(format!("../lib/{}", package_info.name))
312 .canonicalize()
313 {
314 Ok(bundle_dir)
315 } else if let Some(appdir) = &env.appdir {
316 let appdir: &std::path::Path = appdir.as_ref();
317 Ok(PathBuf::from(format!(
318 "{}/usr/lib/{}",
319 appdir.display(),
320 package_info.name
321 )))
322 } else {
323 Ok(PathBuf::from(format!("/usr/lib/{}", package_info.name)))
325 };
326 }
327
328 #[cfg(target_os = "macos")]
329 {
330 res = exe_dir
331 .join("../Resources")
332 .canonicalize()
333 .map_err(Into::into);
334 }
335
336 #[cfg(target_os = "ios")]
337 {
338 res = exe_dir.join("assets").canonicalize().map_err(Into::into);
339 }
340
341 res
342}
343
344#[used]
347static mut __TAURI_BUNDLE_TYPE: &str = "__TAURI_BUNDLE_TYPE_VAR_UNK";
350
351pub fn bundle_type() -> Option<BundleType> {
354 unsafe {
355 match __TAURI_BUNDLE_TYPE {
356 "__TAURI_BUNDLE_TYPE_VAR_DEB" => Some(BundleType::Deb),
357 "__TAURI_BUNDLE_TYPE_VAR_RPM" => Some(BundleType::Rpm),
358 "__TAURI_BUNDLE_TYPE_VAR_APP" => Some(BundleType::AppImage),
359 "__TAURI_BUNDLE_TYPE_VAR_MSI" => Some(BundleType::Msi),
360 "__TAURI_BUNDLE_TYPE_VAR_NSS" => Some(BundleType::Nsis),
361 _ => {
362 if cfg!(target_os = "macos") {
363 Some(BundleType::App)
364 } else {
365 None
366 }
367 }
368 }
369 }
370}
371
372#[cfg(feature = "build")]
373mod build {
374 use proc_macro2::TokenStream;
375 use quote::{quote, ToTokens, TokenStreamExt};
376
377 use super::*;
378
379 impl ToTokens for Target {
380 fn to_tokens(&self, tokens: &mut TokenStream) {
381 let prefix = quote! { ::tauri::utils::platform::Target };
382
383 tokens.append_all(match self {
384 Self::MacOS => quote! { #prefix::MacOS },
385 Self::Linux => quote! { #prefix::Linux },
386 Self::Windows => quote! { #prefix::Windows },
387 Self::Android => quote! { #prefix::Android },
388 Self::Ios => quote! { #prefix::Ios },
389 });
390 }
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use std::path::PathBuf;
397
398 use crate::{Env, PackageInfo};
399
400 #[test]
401 fn resolve_resource_dir() {
402 let package_info = PackageInfo {
403 name: "MyApp".into(),
404 version: "1.0.0".parse().unwrap(),
405 authors: "",
406 description: "",
407 crate_name: "my-app",
408 };
409 let env = Env::default();
410
411 let path = PathBuf::from("/path/to/target/aarch64-apple-darwin/debug/app");
412 let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap();
413 assert_eq!(resource_dir, path.parent().unwrap());
414
415 let path = PathBuf::from("/path/to/target/custom-profile/app");
416 let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap();
417 assert_eq!(resource_dir, path.parent().unwrap());
418
419 let path = PathBuf::from("/path/to/target/release/app");
420 let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap();
421 assert_eq!(resource_dir, path.parent().unwrap());
422
423 let path = PathBuf::from("/path/to/target/unknown-profile/app");
424 #[allow(clippy::needless_borrows_for_generic_args)]
425 let resource_dir = super::resource_dir_from(&path, &package_info, &env);
426 #[cfg(target_os = "macos")]
427 assert!(resource_dir.is_err());
428 #[cfg(target_os = "linux")]
429 assert_eq!(resource_dir.unwrap(), PathBuf::from("/usr/lib/MyApp"));
430 #[cfg(windows)]
431 assert_eq!(resource_dir.unwrap(), path.parent().unwrap());
432 }
433}