build.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use heck::AsShoutySnakeCase;
  5. use heck::AsSnakeCase;
  6. use heck::ToSnakeCase;
  7. use once_cell::sync::OnceCell;
  8. use std::env::var_os;
  9. use std::fs::read_dir;
  10. use std::fs::read_to_string;
  11. use std::fs::write;
  12. use std::{
  13. env::var,
  14. path::{Path, PathBuf},
  15. sync::Mutex,
  16. };
  17. static CHECKED_FEATURES: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
  18. // checks if the given Cargo feature is enabled.
  19. fn has_feature(feature: &str) -> bool {
  20. CHECKED_FEATURES
  21. .get_or_init(Default::default)
  22. .lock()
  23. .unwrap()
  24. .push(feature.to_string());
  25. // when a feature is enabled, Cargo sets the `CARGO_FEATURE_<name>` env var to 1
  26. // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
  27. std::env::var(format!("CARGO_FEATURE_{}", AsShoutySnakeCase(feature)))
  28. .map(|x| x == "1")
  29. .unwrap_or(false)
  30. }
  31. // creates a cfg alias if `has_feature` is true.
  32. // `alias` must be a snake case string.
  33. fn alias(alias: &str, has_feature: bool) {
  34. if has_feature {
  35. println!("cargo:rustc-cfg={alias}");
  36. }
  37. }
  38. fn main() {
  39. alias("custom_protocol", has_feature("custom-protocol"));
  40. alias("dev", !has_feature("custom-protocol"));
  41. alias("updater", has_feature("updater"));
  42. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  43. let mobile = target_os == "ios" || target_os == "android";
  44. alias("desktop", !mobile);
  45. alias("mobile", mobile);
  46. let api_all = has_feature("api-all");
  47. alias("api_all", api_all);
  48. alias_module(
  49. "fs",
  50. &[
  51. "read-file",
  52. "write-file",
  53. "read-dir",
  54. "copy-file",
  55. "create-dir",
  56. "remove-dir",
  57. "remove-file",
  58. "rename-file",
  59. "exists",
  60. ],
  61. api_all,
  62. );
  63. alias_module(
  64. "window",
  65. &[
  66. "create",
  67. "center",
  68. "request-user-attention",
  69. "set-resizable",
  70. "set-title",
  71. "maximize",
  72. "unmaximize",
  73. "minimize",
  74. "unminimize",
  75. "show",
  76. "hide",
  77. "close",
  78. "set-decorations",
  79. "set-shadow",
  80. "set-always-on-top",
  81. "set-content-protected",
  82. "set-size",
  83. "set-min-size",
  84. "set-max-size",
  85. "set-position",
  86. "set-fullscreen",
  87. "set-focus",
  88. "set-icon",
  89. "set-skip-taskbar",
  90. "set-cursor-grab",
  91. "set-cursor-visible",
  92. "set-cursor-icon",
  93. "set-cursor-position",
  94. "set-ignore-cursor-events",
  95. "start-dragging",
  96. "print",
  97. ],
  98. api_all,
  99. );
  100. alias_module("shell", &["execute", "sidecar", "open"], api_all);
  101. if !mobile {
  102. alias_module(
  103. "dialog",
  104. &["open", "save", "message", "ask", "confirm"],
  105. api_all,
  106. );
  107. }
  108. alias_module("http", &["request"], api_all);
  109. if !mobile {
  110. alias_module("notification", &[], api_all);
  111. alias_module("global-shortcut", &[], api_all);
  112. }
  113. alias_module("os", &[], api_all);
  114. alias_module("path", &[], api_all);
  115. alias_module("protocol", &["asset"], api_all);
  116. alias_module("process", &["relaunch", "exit"], api_all);
  117. alias_module("clipboard", &["write-text", "read-text"], api_all);
  118. alias_module("app", &["show", "hide"], api_all);
  119. let checked_features_out_path = Path::new(&var("OUT_DIR").unwrap()).join("checked_features");
  120. std::fs::write(
  121. checked_features_out_path,
  122. CHECKED_FEATURES.get().unwrap().lock().unwrap().join(","),
  123. )
  124. .expect("failed to write checked_features file");
  125. // workaround needed to prevent `STATUS_ENTRYPOINT_NOT_FOUND` error
  126. // see https://github.com/tauri-apps/tauri/pull/4383#issuecomment-1212221864
  127. let target_env = std::env::var("CARGO_CFG_TARGET_ENV");
  128. let is_tauri_workspace = std::env::var("__TAURI_WORKSPACE__").map_or(false, |v| v == "true");
  129. if is_tauri_workspace && target_os == "windows" && Ok("msvc") == target_env.as_deref() {
  130. add_manifest();
  131. }
  132. if target_os == "android" {
  133. if let Ok(kotlin_out_dir) = std::env::var("WRY_ANDROID_KOTLIN_FILES_OUT_DIR") {
  134. fn env_var(var: &str) -> String {
  135. std::env::var(var).unwrap_or_else(|_| {
  136. panic!(
  137. "`{}` is not set, which is needed to generate the kotlin files for android.",
  138. var
  139. )
  140. })
  141. }
  142. let package = env_var("WRY_ANDROID_PACKAGE");
  143. let library = env_var("WRY_ANDROID_LIBRARY");
  144. let kotlin_out_dir = PathBuf::from(&kotlin_out_dir)
  145. .canonicalize()
  146. .unwrap_or_else(move |_| {
  147. panic!("Failed to canonicalize `WRY_ANDROID_KOTLIN_FILES_OUT_DIR` path {kotlin_out_dir}")
  148. });
  149. let kotlin_files_path =
  150. PathBuf::from(env_var("CARGO_MANIFEST_DIR")).join("mobile/android-codegen");
  151. println!("cargo:rerun-if-changed={}", kotlin_files_path.display());
  152. let kotlin_files =
  153. read_dir(kotlin_files_path).expect("failed to read Android codegen directory");
  154. for file in kotlin_files {
  155. let file = file.unwrap();
  156. let content = read_to_string(file.path())
  157. .expect("failed to read kotlin file as string")
  158. .replace("{{package}}", &package)
  159. .replace("{{library}}", &library);
  160. let out_path = kotlin_out_dir.join(file.file_name());
  161. write(&out_path, content).expect("Failed to write kotlin file");
  162. println!("cargo:rerun-if-changed={}", out_path.display());
  163. }
  164. }
  165. if let Some(project_dir) = var_os("WRY_ANDROID_PROJECT_PATH").map(PathBuf::from) {
  166. let tauri_proguard = include_str!("./mobile/proguard-tauri.pro").replace(
  167. "$PACKAGE",
  168. &var("WRY_ANDROID_PACKAGE").expect("missing `WRY_ANDROID_PACKAGE` environment variable"),
  169. );
  170. std::fs::write(
  171. project_dir.join("app").join("proguard-tauri.pro"),
  172. tauri_proguard,
  173. )
  174. .expect("failed to write proguard-tauri.pro");
  175. }
  176. let lib_path =
  177. PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("mobile/android");
  178. println!("cargo:android_library_path={}", lib_path.display());
  179. }
  180. #[cfg(target_os = "macos")]
  181. {
  182. if target_os == "ios" {
  183. let lib_path =
  184. PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("mobile/ios-api");
  185. tauri_build::mobile::link_swift_library("Tauri", &lib_path);
  186. println!("cargo:ios_library_path={}", lib_path.display());
  187. }
  188. }
  189. }
  190. // create aliases for the given module with its apis.
  191. // each api is translated into a feature flag in the format of `<module>-<api>`
  192. // and aliased as `<module_snake_case>_<api_snake_case>`.
  193. //
  194. // The `<module>-all` feature is also aliased to `<module>_all`.
  195. //
  196. // If any of the features is enabled, the `<module_snake_case>_any` alias is created.
  197. //
  198. // Note that both `module` and `apis` strings must be written in kebab case.
  199. fn alias_module(module: &str, apis: &[&str], api_all: bool) {
  200. let all_feature_name = format!("{module}-all");
  201. let all = has_feature(&all_feature_name) || api_all;
  202. alias(&all_feature_name.to_snake_case(), all);
  203. let mut any = all;
  204. for api in apis {
  205. let has = has_feature(&format!("{module}-{api}")) || all;
  206. alias(
  207. &format!("{}_{}", AsSnakeCase(module), AsSnakeCase(api)),
  208. has,
  209. );
  210. any = any || has;
  211. }
  212. alias(&format!("{}_any", AsSnakeCase(module)), any);
  213. }
  214. fn add_manifest() {
  215. static WINDOWS_MANIFEST_FILE: &str = "window-app-manifest.xml";
  216. let manifest = std::env::current_dir()
  217. .unwrap()
  218. .join("../tauri-build/src")
  219. .join(WINDOWS_MANIFEST_FILE);
  220. println!("cargo:rerun-if-changed={}", manifest.display());
  221. // Embed the Windows application manifest file.
  222. println!("cargo:rustc-link-arg=/MANIFEST:EMBED");
  223. println!(
  224. "cargo:rustc-link-arg=/MANIFESTINPUT:{}",
  225. manifest.to_str().unwrap()
  226. );
  227. // Turn linker warnings into errors.
  228. println!("cargo:rustc-link-arg=/WX");
  229. }