mobile.rs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. use std::{
  2. env::{var, var_os},
  3. fs::{self, rename},
  4. path::{Path, PathBuf},
  5. };
  6. use anyhow::Result;
  7. #[derive(Default)]
  8. pub struct PluginBuilder {
  9. android_path: Option<PathBuf>,
  10. ios_path: Option<PathBuf>,
  11. }
  12. impl PluginBuilder {
  13. /// Creates a new builder for mobile plugin functionality.
  14. pub fn new() -> Self {
  15. Self::default()
  16. }
  17. /// Sets the Android project path.
  18. pub fn android_path<P: Into<PathBuf>>(mut self, android_path: P) -> Self {
  19. self.android_path.replace(android_path.into());
  20. self
  21. }
  22. /// Sets the iOS project path.
  23. pub fn ios_path<P: Into<PathBuf>>(mut self, ios_path: P) -> Self {
  24. self.ios_path.replace(ios_path.into());
  25. self
  26. }
  27. /// Injects the mobile templates in the given path relative to the manifest root.
  28. pub fn run(self) -> Result<()> {
  29. let target_os = var("CARGO_CFG_TARGET_OS").unwrap();
  30. match target_os.as_str() {
  31. "android" => {
  32. if let Some(path) = self.android_path {
  33. let manifest_dir = var_os("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap();
  34. let source = manifest_dir.join(path);
  35. let tauri_library_path = std::env::var("DEP_TAURI_ANDROID_LIBRARY_PATH")
  36. .expect("missing `DEP_TAURI_ANDROID_LIBRARY_PATH` environment variable. Make sure `tauri` is a dependency of the plugin.");
  37. copy_folder(
  38. Path::new(&tauri_library_path),
  39. &source.join("tauri-api"),
  40. &[],
  41. )?;
  42. if let Ok(project_dir) = var("TAURI_ANDROID_PROJECT_PATH") {
  43. let pkg_name = var("CARGO_PKG_NAME").unwrap();
  44. println!("cargo:rerun-if-env-changed=TAURI_ANDROID_PROJECT_PATH");
  45. let project_dir = PathBuf::from(project_dir);
  46. inject_android_project(
  47. source,
  48. project_dir.join("tauri-plugins").join(&pkg_name),
  49. &["tauri-api"],
  50. )?;
  51. let gradle_settings_path = project_dir.join("tauri.settings.gradle");
  52. let gradle_settings = fs::read_to_string(&gradle_settings_path)?;
  53. let include = format!(
  54. "include ':{pkg_name}'
  55. project(':{pkg_name}').projectDir = new File('./tauri-plugins/{pkg_name}')"
  56. );
  57. if !gradle_settings.contains(&include) {
  58. fs::write(
  59. &gradle_settings_path,
  60. format!("{gradle_settings}\n{include}"),
  61. )?;
  62. }
  63. let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts");
  64. let app_build_gradle = fs::read_to_string(&app_build_gradle_path)?;
  65. let implementation = format!(r#"implementation(project(":{pkg_name}"))"#);
  66. let target = "dependencies {";
  67. if !app_build_gradle.contains(&implementation) {
  68. fs::write(
  69. &app_build_gradle_path,
  70. app_build_gradle.replace(target, &format!("{target}\n {implementation}")),
  71. )?
  72. }
  73. }
  74. }
  75. }
  76. #[cfg(target_os = "macos")]
  77. "ios" => {
  78. if let Some(path) = self.ios_path {
  79. let manifest_dir = var_os("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap();
  80. let tauri_library_path = std::env::var("DEP_TAURI_IOS_LIBRARY_PATH")
  81. .expect("missing `DEP_TAURI_IOS_LIBRARY_PATH` environment variable. Make sure `tauri` is a dependency of the plugin.");
  82. copy_folder(
  83. &Path::new(&tauri_library_path),
  84. &path.join("tauri-api"),
  85. &[".build", "Package.resolved", "Tests"],
  86. )?;
  87. link_swift_library(&var("CARGO_PKG_NAME").unwrap(), manifest_dir.join(path));
  88. }
  89. }
  90. _ => (),
  91. }
  92. Ok(())
  93. }
  94. }
  95. #[cfg(target_os = "macos")]
  96. #[doc(hidden)]
  97. pub fn link_swift_library(name: &str, source: impl AsRef<Path>) {
  98. let source = source.as_ref();
  99. println!("cargo:rerun-if-changed={}", source.display());
  100. let curr_dir = std::env::current_dir().unwrap();
  101. std::env::set_current_dir(&source).unwrap();
  102. swift_rs::build::SwiftLinker::new("10.13")
  103. .with_ios("11")
  104. .with_package(name, source)
  105. .link();
  106. std::env::set_current_dir(&curr_dir).unwrap();
  107. }
  108. #[doc(hidden)]
  109. pub fn inject_android_project(
  110. source: impl AsRef<Path>,
  111. target: impl AsRef<Path>,
  112. ignore_paths: &[&str],
  113. ) -> Result<()> {
  114. let source = source.as_ref();
  115. let target = target.as_ref();
  116. // keep build folder if it exists
  117. let build_path = target.join("build");
  118. let out_dir = if build_path.exists() {
  119. let out_dir = target.parent().unwrap().join(".tauri-tmp-build");
  120. rename(&build_path, &out_dir)?;
  121. Some(out_dir)
  122. } else {
  123. None
  124. };
  125. copy_folder(source, target, ignore_paths)?;
  126. if let Some(out_dir) = out_dir {
  127. rename(out_dir, &build_path)?;
  128. }
  129. Ok(())
  130. }
  131. fn copy_folder(source: &Path, target: &Path, ignore_paths: &[&str]) -> Result<()> {
  132. let _ = fs::remove_dir_all(target);
  133. for entry in walkdir::WalkDir::new(source) {
  134. let entry = entry?;
  135. let rel_path = entry.path().strip_prefix(source)?;
  136. let rel_path_str = rel_path.to_string_lossy();
  137. if ignore_paths
  138. .iter()
  139. .any(|path| rel_path_str.starts_with(path))
  140. {
  141. continue;
  142. }
  143. let dest_path = target.join(rel_path);
  144. if entry.file_type().is_dir() {
  145. fs::create_dir(dest_path)?;
  146. } else {
  147. fs::copy(entry.path(), dest_path)?;
  148. }
  149. }
  150. Ok(())
  151. }