build.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use heck::AsShoutySnakeCase;
  5. use std::env::var_os;
  6. use std::fs::create_dir_all;
  7. use std::fs::read_dir;
  8. use std::fs::read_to_string;
  9. use std::fs::write;
  10. use std::{
  11. env::var,
  12. path::{Path, PathBuf},
  13. sync::{Mutex, OnceLock},
  14. };
  15. static CHECKED_FEATURES: OnceLock<Mutex<Vec<String>>> = OnceLock::new();
  16. const PLUGINS: &[(&str, &[(&str, bool)])] = &[
  17. // (plugin_name, &[(command, enabled-by_default)])
  18. (
  19. "path",
  20. &[
  21. ("resolve_directory", true),
  22. ("resolve", true),
  23. ("normalize", true),
  24. ("join", true),
  25. ("dirname", true),
  26. ("extname", true),
  27. ("basename", true),
  28. ("is_absolute", true),
  29. ],
  30. ),
  31. (
  32. "event",
  33. &[
  34. ("listen", true),
  35. ("unlisten", true),
  36. ("emit", true),
  37. ("emit_to", true),
  38. ],
  39. ),
  40. (
  41. "window",
  42. &[
  43. ("create", false),
  44. // getters
  45. ("scale_factor", true),
  46. ("inner_position", true),
  47. ("outer_position", true),
  48. ("inner_size", true),
  49. ("outer_size", true),
  50. ("is_fullscreen", true),
  51. ("is_minimized", true),
  52. ("is_maximized", true),
  53. ("is_focused", true),
  54. ("is_decorated", true),
  55. ("is_resizable", true),
  56. ("is_maximizable", true),
  57. ("is_minimizable", true),
  58. ("is_closable", true),
  59. ("is_visible", true),
  60. ("title", true),
  61. ("current_monitor", true),
  62. ("primary_monitor", true),
  63. ("available_monitors", true),
  64. ("theme", true),
  65. // setters
  66. ("center", false),
  67. ("request_user_attention", false),
  68. ("set_resizable", false),
  69. ("set_maximizable", false),
  70. ("set_minimizable", false),
  71. ("set_closable", false),
  72. ("set_title", false),
  73. ("maximize", false),
  74. ("unmaximize", false),
  75. ("minimize", false),
  76. ("unminimize", false),
  77. ("show", false),
  78. ("hide", false),
  79. ("close", false),
  80. ("destroy", false),
  81. ("set_decorations", false),
  82. ("set_shadow", false),
  83. ("set_effects", false),
  84. ("set_always_on_top", false),
  85. ("set_always_on_bottom", false),
  86. ("set_visible_on_all_workspaces", false),
  87. ("set_content_protected", false),
  88. ("set_size", false),
  89. ("set_min_size", false),
  90. ("set_max_size", false),
  91. ("set_position", false),
  92. ("set_fullscreen", false),
  93. ("set_focus", false),
  94. ("set_skip_taskbar", false),
  95. ("set_cursor_grab", false),
  96. ("set_cursor_visible", false),
  97. ("set_cursor_icon", false),
  98. ("set_cursor_position", false),
  99. ("set_ignore_cursor_events", false),
  100. ("start_dragging", false),
  101. ("set_progress_bar", false),
  102. ("set_icon", false),
  103. ("toggle_maximize", false),
  104. // internal
  105. ("internal_toggle_maximize", true),
  106. ],
  107. ),
  108. (
  109. "webview",
  110. &[
  111. ("create_webview", false),
  112. ("create_webview_window", false),
  113. // getters
  114. ("webview_position", true),
  115. ("webview_size", true),
  116. // setters
  117. ("webview_close", false),
  118. ("set_webview_size", false),
  119. ("set_webview_position", false),
  120. ("set_webview_focus", false),
  121. ("print", false),
  122. ("reparent", false),
  123. // internal
  124. ("internal_toggle_devtools", true),
  125. ],
  126. ),
  127. (
  128. "app",
  129. &[
  130. ("version", true),
  131. ("name", true),
  132. ("tauri_version", true),
  133. ("app_show", false),
  134. ("app_hide", false),
  135. ],
  136. ),
  137. ("resources", &[("close", true)]),
  138. (
  139. "menu",
  140. &[
  141. ("new", false),
  142. ("append", false),
  143. ("prepend", false),
  144. ("insert", false),
  145. ("remove", false),
  146. ("remove_at", false),
  147. ("items", false),
  148. ("get", false),
  149. ("popup", false),
  150. ("create_default", false),
  151. ("set_as_app_menu", false),
  152. ("set_as_window_menu", false),
  153. ("text", false),
  154. ("set_text", false),
  155. ("is_enabled", false),
  156. ("set_enabled", false),
  157. ("set_accelerator", false),
  158. ("set_as_windows_menu_for_nsapp", false),
  159. ("set_as_help_menu_for_nsapp", false),
  160. ("is_checked", false),
  161. ("set_checked", false),
  162. ("set_icon", false),
  163. ],
  164. ),
  165. (
  166. "tray",
  167. &[
  168. ("new", false),
  169. ("set_icon", false),
  170. ("set_menu", false),
  171. ("set_tooltip", false),
  172. ("set_title", false),
  173. ("set_visible", false),
  174. ("set_temp_dir_path", false),
  175. ("set_icon_as_template", false),
  176. ("set_show_menu_on_left_click", false),
  177. ],
  178. ),
  179. ];
  180. // checks if the given Cargo feature is enabled.
  181. fn has_feature(feature: &str) -> bool {
  182. CHECKED_FEATURES
  183. .get_or_init(Default::default)
  184. .lock()
  185. .unwrap()
  186. .push(feature.to_string());
  187. // when a feature is enabled, Cargo sets the `CARGO_FEATURE_<name>` env var to 1
  188. // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
  189. std::env::var(format!("CARGO_FEATURE_{}", AsShoutySnakeCase(feature)))
  190. .map(|x| x == "1")
  191. .unwrap_or(false)
  192. }
  193. // creates a cfg alias if `has_feature` is true.
  194. // `alias` must be a snake case string.
  195. fn alias(alias: &str, has_feature: bool) {
  196. if has_feature {
  197. println!("cargo:rustc-cfg={alias}");
  198. }
  199. }
  200. fn main() {
  201. let custom_protocol = has_feature("custom-protocol");
  202. let dev = !custom_protocol;
  203. alias("custom_protocol", custom_protocol);
  204. alias("dev", dev);
  205. println!("cargo:dev={}", dev);
  206. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  207. let mobile = target_os == "ios" || target_os == "android";
  208. alias("desktop", !mobile);
  209. alias("mobile", mobile);
  210. let out_dir = PathBuf::from(var("OUT_DIR").unwrap());
  211. let checked_features_out_path = out_dir.join("checked_features");
  212. std::fs::write(
  213. checked_features_out_path,
  214. CHECKED_FEATURES.get().unwrap().lock().unwrap().join(","),
  215. )
  216. .expect("failed to write checked_features file");
  217. // workaround needed to prevent `STATUS_ENTRYPOINT_NOT_FOUND` error
  218. // see https://github.com/tauri-apps/tauri/pull/4383#issuecomment-1212221864
  219. let target_env = std::env::var("CARGO_CFG_TARGET_ENV");
  220. let is_tauri_workspace = std::env::var("__TAURI_WORKSPACE__").map_or(false, |v| v == "true");
  221. if is_tauri_workspace && target_os == "windows" && Ok("msvc") == target_env.as_deref() {
  222. add_manifest();
  223. }
  224. if target_os == "android" {
  225. if let Ok(kotlin_out_dir) = std::env::var("WRY_ANDROID_KOTLIN_FILES_OUT_DIR") {
  226. fn env_var(var: &str) -> String {
  227. std::env::var(var).unwrap_or_else(|_| {
  228. panic!(
  229. "`{}` is not set, which is needed to generate the kotlin files for android.",
  230. var
  231. )
  232. })
  233. }
  234. let package = env_var("WRY_ANDROID_PACKAGE");
  235. let library = env_var("WRY_ANDROID_LIBRARY");
  236. let kotlin_out_dir = PathBuf::from(&kotlin_out_dir)
  237. .canonicalize()
  238. .unwrap_or_else(move |_| {
  239. panic!("Failed to canonicalize `WRY_ANDROID_KOTLIN_FILES_OUT_DIR` path {kotlin_out_dir}")
  240. });
  241. let kotlin_files_path =
  242. PathBuf::from(env_var("CARGO_MANIFEST_DIR")).join("mobile/android-codegen");
  243. println!("cargo:rerun-if-changed={}", kotlin_files_path.display());
  244. let kotlin_files =
  245. read_dir(kotlin_files_path).expect("failed to read Android codegen directory");
  246. for file in kotlin_files {
  247. let file = file.unwrap();
  248. let content = read_to_string(file.path())
  249. .expect("failed to read kotlin file as string")
  250. .replace("{{package}}", &package)
  251. .replace("{{library}}", &library);
  252. let out_path = kotlin_out_dir.join(file.file_name());
  253. write(&out_path, content).expect("Failed to write kotlin file");
  254. println!("cargo:rerun-if-changed={}", out_path.display());
  255. }
  256. }
  257. if let Some(project_dir) = var_os("TAURI_ANDROID_PROJECT_PATH").map(PathBuf::from) {
  258. let tauri_proguard = include_str!("./mobile/proguard-tauri.pro").replace(
  259. "$PACKAGE",
  260. &var("WRY_ANDROID_PACKAGE").expect("missing `WRY_ANDROID_PACKAGE` environment variable"),
  261. );
  262. std::fs::write(
  263. project_dir.join("app").join("proguard-tauri.pro"),
  264. tauri_proguard,
  265. )
  266. .expect("failed to write proguard-tauri.pro");
  267. }
  268. let lib_path =
  269. PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("mobile/android");
  270. println!("cargo:android_library_path={}", lib_path.display());
  271. }
  272. #[cfg(target_os = "macos")]
  273. {
  274. if target_os == "ios" {
  275. let lib_path =
  276. PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("mobile/ios-api");
  277. tauri_utils::build::link_swift_library("Tauri", &lib_path);
  278. println!("cargo:ios_library_path={}", lib_path.display());
  279. }
  280. }
  281. define_permissions(&out_dir);
  282. }
  283. fn define_permissions(out_dir: &Path) {
  284. let license_header = r#"# Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  285. # SPDX-License-Identifier: Apache-2.0
  286. # SPDX-License-Identifier: MIT
  287. "#;
  288. for (plugin, commands) in PLUGINS {
  289. let permissions_out_dir = out_dir.join("permissions").join(plugin);
  290. let autogenerated =
  291. permissions_out_dir.join(tauri_utils::acl::build::AUTOGENERATED_FOLDER_NAME);
  292. let commands_dir = autogenerated.join("commands");
  293. tauri_utils::acl::build::autogenerate_command_permissions(
  294. &commands_dir,
  295. &commands.iter().map(|(cmd, _)| *cmd).collect::<Vec<_>>(),
  296. license_header,
  297. false,
  298. );
  299. let default_permissions = commands
  300. .iter()
  301. .filter(|(_cmd, default)| *default)
  302. .map(|(cmd, _)| {
  303. let slugified_command = cmd.replace('_', "-");
  304. format!("\"allow-{}\"", slugified_command)
  305. })
  306. .collect::<Vec<_>>()
  307. .join(", ");
  308. let default_toml = format!(
  309. r###"{license_header}# Automatically generated - DO NOT EDIT!
  310. [default]
  311. description = "Default permissions for the plugin."
  312. permissions = [{default_permissions}]
  313. "###,
  314. );
  315. let out_path = autogenerated.join("default.toml");
  316. if default_toml != read_to_string(&out_path).unwrap_or_default() {
  317. std::fs::write(out_path, default_toml)
  318. .unwrap_or_else(|_| panic!("unable to autogenerate default permissions"));
  319. }
  320. let permissions = tauri_utils::acl::build::define_permissions(
  321. &permissions_out_dir
  322. .join("**")
  323. .join("*.toml")
  324. .to_string_lossy(),
  325. &format!("tauri:{plugin}"),
  326. out_dir,
  327. |_| true,
  328. )
  329. .unwrap_or_else(|e| panic!("failed to define permissions for {plugin}: {e}"));
  330. let docs_out_dir = Path::new("permissions").join(plugin).join("autogenerated");
  331. create_dir_all(&docs_out_dir).expect("failed to create plugin documentation directory");
  332. tauri_utils::acl::build::generate_docs(&permissions, &docs_out_dir)
  333. .expect("failed to generate plugin documentation page");
  334. }
  335. }
  336. fn add_manifest() {
  337. static WINDOWS_MANIFEST_FILE: &str = "window-app-manifest.xml";
  338. let manifest = std::env::current_dir()
  339. .unwrap()
  340. .join("../tauri-build/src")
  341. .join(WINDOWS_MANIFEST_FILE);
  342. println!("cargo:rerun-if-changed={}", manifest.display());
  343. // Embed the Windows application manifest file.
  344. println!("cargo:rustc-link-arg=/MANIFEST:EMBED");
  345. println!(
  346. "cargo:rustc-link-arg=/MANIFESTINPUT:{}",
  347. manifest.to_str().unwrap()
  348. );
  349. // Turn linker warnings into errors.
  350. println!("cargo:rustc-link-arg=/WX");
  351. }