Skip to main content

tauri_plugin/build/
mobile.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! Mobile-specific build utilities.
6
7use std::{
8  fs::{copy, create_dir, create_dir_all, remove_dir_all},
9  path::{Path, PathBuf},
10};
11
12use anyhow::{Context, Result};
13
14use super::{build_var, cfg_alias};
15
16#[cfg(target_os = "macos")]
17pub fn update_entitlements<F: FnOnce(&mut plist::Dictionary)>(f: F) -> Result<()> {
18  if let (Some(project_path), Ok(app_name)) = (
19    std::env::var_os("TAURI_IOS_PROJECT_PATH").map(PathBuf::from),
20    std::env::var("TAURI_IOS_APP_NAME"),
21  ) {
22    update_plist_file(
23      project_path
24        .join(format!("{app_name}_iOS"))
25        .join(format!("{app_name}_iOS.entitlements")),
26      f,
27    )?;
28  }
29
30  Ok(())
31}
32
33#[cfg(target_os = "macos")]
34pub fn update_info_plist<F: FnOnce(&mut plist::Dictionary)>(f: F) -> Result<()> {
35  if let (Some(project_path), Ok(app_name)) = (
36    std::env::var_os("TAURI_IOS_PROJECT_PATH").map(PathBuf::from),
37    std::env::var("TAURI_IOS_APP_NAME"),
38  ) {
39    update_plist_file(
40      project_path
41        .join(format!("{app_name}_iOS"))
42        .join("Info.plist"),
43      f,
44    )?;
45  }
46
47  Ok(())
48}
49
50/// Updates the Android manifest by inserting XML content into a specified parent tag.
51pub fn update_android_manifest(block_identifier: &str, parent: &str, insert: String) -> Result<()> {
52  tauri_utils::build::update_android_manifest(block_identifier, parent, insert)
53}
54
55pub(crate) fn setup(
56  android_path: Option<PathBuf>,
57  #[allow(unused_variables)] ios_path: Option<PathBuf>,
58) -> Result<()> {
59  let target_os = build_var("CARGO_CFG_TARGET_OS")?;
60  let mobile = target_os == "android" || target_os == "ios";
61  cfg_alias("mobile", mobile);
62  cfg_alias("desktop", !mobile);
63
64  match target_os.as_str() {
65    "android" => {
66      if let Some(path) = android_path {
67        let manifest_dir = build_var("CARGO_MANIFEST_DIR").map(PathBuf::from)?;
68        let source = manifest_dir.join(path);
69
70        let tauri_library_path = std::env::var("DEP_TAURI_ANDROID_LIBRARY_PATH")
71            .expect("missing `DEP_TAURI_ANDROID_LIBRARY_PATH` environment variable. Make sure `tauri` is a dependency of the plugin.");
72        println!("cargo:rerun-if-env-changed=DEP_TAURI_ANDROID_LIBRARY_PATH");
73
74        create_dir_all(source.join(".tauri")).context("failed to create .tauri directory")?;
75        copy_folder(
76          Path::new(&tauri_library_path),
77          &source.join(".tauri").join("tauri-api"),
78          &[],
79        )
80        .context("failed to copy tauri-api to the plugin project")?;
81
82        println!("cargo:android_library_path={}", source.display());
83      }
84    }
85    #[cfg(target_os = "macos")]
86    "ios" => {
87      if let Some(path) = ios_path {
88        let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
89          .map(PathBuf::from)
90          .unwrap();
91        let tauri_library_path = std::env::var("DEP_TAURI_IOS_LIBRARY_PATH")
92            .expect("missing `DEP_TAURI_IOS_LIBRARY_PATH` environment variable. Make sure `tauri` is a dependency of the plugin.");
93
94        let tauri_dep_path = path.parent().unwrap().join(".tauri");
95        create_dir_all(&tauri_dep_path).context("failed to create .tauri directory")?;
96        copy_folder(
97          Path::new(&tauri_library_path),
98          &tauri_dep_path.join("tauri-api"),
99          &[".build", "Package.resolved", "Tests"],
100        )
101        .context("failed to copy tauri-api to the plugin project")?;
102        tauri_utils::build::link_apple_library(
103          &std::env::var("CARGO_PKG_NAME").unwrap(),
104          manifest_dir.join(path),
105        );
106      }
107    }
108    _ => (),
109  }
110
111  Ok(())
112}
113
114fn copy_folder(source: &Path, target: &Path, ignore_paths: &[&str]) -> Result<()> {
115  let _ = remove_dir_all(target);
116
117  for entry in walkdir::WalkDir::new(source) {
118    let entry = entry?;
119    let rel_path = entry.path().strip_prefix(source)?;
120    let rel_path_str = rel_path.to_string_lossy();
121    if ignore_paths
122      .iter()
123      .any(|path| rel_path_str.starts_with(path))
124    {
125      continue;
126    }
127    let dest_path = target.join(rel_path);
128
129    if entry.file_type().is_dir() {
130      create_dir(&dest_path)
131        .with_context(|| format!("failed to create directory {}", dest_path.display()))?;
132    } else {
133      copy(entry.path(), &dest_path).with_context(|| {
134        format!(
135          "failed to copy {} to {}",
136          entry.path().display(),
137          dest_path.display()
138        )
139      })?;
140      println!("cargo:rerun-if-changed={}", entry.path().display());
141    }
142  }
143
144  Ok(())
145}
146
147#[cfg(target_os = "macos")]
148fn update_plist_file<P: AsRef<Path>, F: FnOnce(&mut plist::Dictionary)>(
149  path: P,
150  f: F,
151) -> Result<()> {
152  use std::io::Cursor;
153
154  let path = path.as_ref();
155  if path.exists() {
156    let plist_str = std::fs::read_to_string(path)?;
157    let mut plist = plist::Value::from_reader(Cursor::new(&plist_str))?;
158    if let Some(dict) = plist.as_dictionary_mut() {
159      f(dict);
160      let mut plist_buf = Vec::new();
161      let writer = Cursor::new(&mut plist_buf);
162      plist::to_writer_xml(writer, &plist)?;
163      let new_plist_str = String::from_utf8(plist_buf)?;
164      if new_plist_str != plist_str {
165        std::fs::write(path, new_plist_str)?;
166      }
167    }
168  }
169
170  Ok(())
171}
172
173#[cfg(test)]
174mod tests {
175  #[test]
176  fn update_android_manifest() {
177    use tauri_utils::build::update_android_manifest;
178
179    // This test would require setting up the environment, so we just verify it compiles
180    // The actual implementation is tested in tauri-utils
181    let _result = update_android_manifest("test", "activity", "<test></test>".to_string());
182  }
183}