config.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use json_patch::merge;
  5. use serde_json::Value as JsonValue;
  6. pub use tauri_utils::{config::*, platform::Target};
  7. use std::{
  8. collections::HashMap,
  9. env::{current_dir, set_current_dir, set_var, var_os},
  10. ffi::OsStr,
  11. process::exit,
  12. sync::{Arc, Mutex, OnceLock},
  13. };
  14. pub const MERGE_CONFIG_EXTENSION_NAME: &str = "--config";
  15. pub struct ConfigMetadata {
  16. /// The current target.
  17. target: Target,
  18. /// The actual configuration, merged with any extension.
  19. inner: Config,
  20. /// The config extensions (platform-specific config files or the config CLI argument).
  21. /// Maps the extension name to its value.
  22. extensions: HashMap<String, JsonValue>,
  23. }
  24. impl std::ops::Deref for ConfigMetadata {
  25. type Target = Config;
  26. #[inline(always)]
  27. fn deref(&self) -> &Config {
  28. &self.inner
  29. }
  30. }
  31. impl ConfigMetadata {
  32. /// Checks which config is overwriting the bundle identifier.
  33. pub fn find_bundle_identifier_overwriter(&self) -> Option<String> {
  34. for (ext, config) in &self.extensions {
  35. if let Some(identifier) = config
  36. .as_object()
  37. .and_then(|config| config.get("tauri"))
  38. .and_then(|tauri_config| tauri_config.as_object())
  39. .and_then(|tauri_config| tauri_config.get("bundle"))
  40. .and_then(|bundle_config| bundle_config.as_object())
  41. .and_then(|bundle_config| bundle_config.get("identifier"))
  42. .and_then(|id| id.as_str())
  43. {
  44. if identifier == self.inner.identifier {
  45. return Some(ext.clone());
  46. }
  47. }
  48. }
  49. None
  50. }
  51. }
  52. pub type ConfigHandle = Arc<Mutex<Option<ConfigMetadata>>>;
  53. pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
  54. tauri_bundler::WixSettings {
  55. language: tauri_bundler::WixLanguage(match config.language {
  56. WixLanguage::One(lang) => vec![(lang, Default::default())],
  57. WixLanguage::List(languages) => languages
  58. .into_iter()
  59. .map(|lang| (lang, Default::default()))
  60. .collect(),
  61. WixLanguage::Localized(languages) => languages
  62. .into_iter()
  63. .map(|(lang, config)| {
  64. (
  65. lang,
  66. tauri_bundler::WixLanguageConfig {
  67. locale_path: config.locale_path.map(Into::into),
  68. },
  69. )
  70. })
  71. .collect(),
  72. }),
  73. template: config.template,
  74. fragment_paths: config.fragment_paths,
  75. component_group_refs: config.component_group_refs,
  76. component_refs: config.component_refs,
  77. feature_group_refs: config.feature_group_refs,
  78. feature_refs: config.feature_refs,
  79. merge_refs: config.merge_refs,
  80. enable_elevated_update_task: config.enable_elevated_update_task,
  81. banner_path: config.banner_path,
  82. dialog_image_path: config.dialog_image_path,
  83. fips_compliant: var_os("TAURI_BUNDLER_WIX_FIPS_COMPLIANT").map_or(false, |v| v == "true"),
  84. }
  85. }
  86. pub fn nsis_settings(config: NsisConfig) -> tauri_bundler::NsisSettings {
  87. tauri_bundler::NsisSettings {
  88. template: config.template,
  89. header_image: config.header_image,
  90. sidebar_image: config.sidebar_image,
  91. installer_icon: config.installer_icon,
  92. install_mode: config.install_mode,
  93. languages: config.languages,
  94. custom_language_files: config.custom_language_files,
  95. display_language_selector: config.display_language_selector,
  96. compression: config.compression,
  97. start_menu_folder: config.start_menu_folder,
  98. installer_hooks: config.installer_hooks,
  99. }
  100. }
  101. fn config_handle() -> &'static ConfigHandle {
  102. static CONFIG_HANDLE: OnceLock<ConfigHandle> = OnceLock::new();
  103. CONFIG_HANDLE.get_or_init(Default::default)
  104. }
  105. /// Gets the static parsed config from `tauri.conf.json`.
  106. fn get_internal(
  107. merge_config: Option<&serde_json::Value>,
  108. reload: bool,
  109. target: Target,
  110. ) -> crate::Result<ConfigHandle> {
  111. if !reload && config_handle().lock().unwrap().is_some() {
  112. return Ok(config_handle().clone());
  113. }
  114. let tauri_dir = super::app_paths::tauri_dir();
  115. let (mut config, config_path) =
  116. tauri_utils::config::parse::parse_value(target, tauri_dir.join("tauri.conf.json"))?;
  117. let config_file_name = config_path.file_name().unwrap().to_string_lossy();
  118. let mut extensions = HashMap::new();
  119. if let Some((platform_config, config_path)) =
  120. tauri_utils::config::parse::read_platform(target, tauri_dir)?
  121. {
  122. merge(&mut config, &platform_config);
  123. extensions.insert(
  124. config_path.file_name().unwrap().to_str().unwrap().into(),
  125. platform_config,
  126. );
  127. }
  128. if let Some(merge_config) = merge_config {
  129. let merge_config_str = serde_json::to_string(&merge_config).unwrap();
  130. set_var("TAURI_CONFIG", merge_config_str);
  131. merge(&mut config, merge_config);
  132. extensions.insert(MERGE_CONFIG_EXTENSION_NAME.into(), merge_config.clone());
  133. };
  134. if config_path.extension() == Some(OsStr::new("json"))
  135. || config_path.extension() == Some(OsStr::new("json5"))
  136. {
  137. let schema: JsonValue = serde_json::from_str(include_str!("../../schema.json"))?;
  138. let schema = jsonschema::JSONSchema::compile(&schema).unwrap();
  139. let result = schema.validate(&config);
  140. if let Err(errors) = result {
  141. for error in errors {
  142. let path = error.instance_path.clone().into_vec().join(" > ");
  143. if path.is_empty() {
  144. log::error!("`{}` error: {}", config_file_name, error);
  145. } else {
  146. log::error!("`{}` error on `{}`: {}", config_file_name, path, error);
  147. }
  148. }
  149. if !reload {
  150. exit(1);
  151. }
  152. }
  153. }
  154. // the `Config` deserializer for `package > version` can resolve the version from a path relative to the config path
  155. // so we actually need to change the current working directory here
  156. let current_dir = current_dir()?;
  157. set_current_dir(config_path.parent().unwrap())?;
  158. let config: Config = serde_json::from_value(config)?;
  159. // revert to previous working directory
  160. set_current_dir(current_dir)?;
  161. for (plugin, conf) in &config.plugins.0 {
  162. set_var(
  163. format!(
  164. "TAURI_{}_PLUGIN_CONFIG",
  165. plugin.to_uppercase().replace('-', "_")
  166. ),
  167. serde_json::to_string(&conf)?,
  168. );
  169. }
  170. *config_handle().lock().unwrap() = Some(ConfigMetadata {
  171. target,
  172. inner: config,
  173. extensions,
  174. });
  175. Ok(config_handle().clone())
  176. }
  177. pub fn get(
  178. target: Target,
  179. merge_config: Option<&serde_json::Value>,
  180. ) -> crate::Result<ConfigHandle> {
  181. get_internal(merge_config, false, target)
  182. }
  183. pub fn reload(merge_config: Option<&serde_json::Value>) -> crate::Result<ConfigHandle> {
  184. let target = config_handle()
  185. .lock()
  186. .unwrap()
  187. .as_ref()
  188. .map(|conf| conf.target);
  189. if let Some(target) = target {
  190. get_internal(merge_config, true, target)
  191. } else {
  192. Err(anyhow::anyhow!("config not loaded"))
  193. }
  194. }