1#![doc(
89 html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
90 html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
91)]
92#![cfg_attr(docsrs, feature(doc_cfg))]
93#![warn(missing_docs)]
94
95use anyhow::Context;
96pub use anyhow::Result;
97use cargo_toml::Manifest;
98
99use tauri_utils::{
100 config::{BundleResources, Config, WebviewInstallMode},
101 resources::{ResourcePaths, external_binaries},
102};
103
104use std::{
105 collections::HashMap,
106 env,
107 ffi::OsStr,
108 fs,
109 path::{Path, PathBuf},
110};
111
112mod acl;
113#[cfg(feature = "codegen")]
114mod codegen;
115mod manifest;
116mod mobile;
117mod static_vcruntime;
118
119#[cfg(feature = "codegen")]
120#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
121pub use codegen::context::CodegenContext;
122
123pub use acl::{AppManifest, DefaultPermissionRule, InlinedPlugin};
124
125fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
126 let from = from.as_ref();
127 let to = to.as_ref();
128 if !from.exists() {
129 return Err(anyhow::anyhow!("{:?} does not exist", from));
130 }
131 if !from.is_file() {
132 return Err(anyhow::anyhow!("{:?} is not a file", from));
133 }
134 let dest_dir = to.parent().expect("No data in parent");
135 fs::create_dir_all(dest_dir)?;
136 fs::copy(from, to)?;
137 Ok(())
138}
139
140fn copy_binaries(
141 binaries: ResourcePaths,
142 target_triple: &str,
143 path: &Path,
144 package_name: Option<&str>,
145) -> Result<()> {
146 for src in binaries {
147 let src = src?;
148 println!("cargo:rerun-if-changed={}", src.display());
149 let file_name = src
150 .file_name()
151 .expect("failed to extract external binary filename")
152 .to_string_lossy()
153 .replace(&format!("-{target_triple}"), "");
154
155 if package_name == Some(&file_name) {
156 return Err(anyhow::anyhow!(
157 "Cannot define a sidecar with the same name as the Cargo package name `{}`. Please change the sidecar name in the filesystem and the Tauri configuration.",
158 file_name
159 ));
160 }
161
162 let dest = path.join(file_name);
163 if dest.exists() {
164 fs::remove_file(&dest).unwrap();
165 }
166 copy_file(&src, &dest)?;
167 }
168 Ok(())
169}
170
171fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> {
173 let path = path.canonicalize()?;
174 let mut resources = resources.iter();
175 for resource in resources.by_ref() {
176 let resource = resource?;
177
178 let src = resource.path().canonicalize()?;
180 let target = path.join(resource.target());
181 if src != target {
182 copy_file(src, target)?;
183 }
184 }
185
186 for path in resources.rerun_if_changed() {
187 println!("cargo:rerun-if-changed={}", path.display());
188 }
189
190 Ok(())
191}
192
193#[cfg(unix)]
194fn symlink_dir(src: &Path, dst: &Path) -> std::io::Result<()> {
195 std::os::unix::fs::symlink(src, dst)
196}
197
198#[cfg(windows)]
200fn symlink_dir(src: &Path, dst: &Path) -> std::io::Result<()> {
201 std::os::windows::fs::symlink_dir(src, dst)
202}
203
204#[cfg(unix)]
206fn symlink_file(src: &Path, dst: &Path) -> std::io::Result<()> {
207 std::os::unix::fs::symlink(src, dst)
208}
209
210#[cfg(windows)]
212fn symlink_file(src: &Path, dst: &Path) -> std::io::Result<()> {
213 std::os::windows::fs::symlink_file(src, dst)
214}
215
216fn copy_dir(from: &Path, to: &Path) -> Result<()> {
217 for entry in walkdir::WalkDir::new(from) {
218 let entry = entry?;
219 debug_assert!(entry.path().starts_with(from));
220 let rel_path = entry.path().strip_prefix(from)?;
221 let dest_path = to.join(rel_path);
222 if entry.file_type().is_symlink() {
223 let target = fs::read_link(entry.path())?;
224 if entry.path().is_dir() {
225 symlink_dir(&target, &dest_path)?;
226 } else {
227 symlink_file(&target, &dest_path)?;
228 }
229 } else if entry.file_type().is_dir() {
230 fs::create_dir(dest_path)?;
231 } else {
232 fs::copy(entry.path(), dest_path)?;
233 }
234 }
235 Ok(())
236}
237
238fn copy_framework_from(src_dir: &Path, framework: &str, dest_dir: &Path) -> Result<bool> {
240 let src_name = format!("{framework}.framework");
241 let src_path = src_dir.join(&src_name);
242 if src_path.exists() {
243 copy_dir(&src_path, &dest_dir.join(&src_name))?;
244 Ok(true)
245 } else {
246 Ok(false)
247 }
248}
249
250fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> {
252 fs::create_dir_all(dest_dir)
253 .with_context(|| format!("Failed to create frameworks output directory at {dest_dir:?}"))?;
254 for framework in frameworks.iter() {
255 if framework.ends_with(".framework") {
256 let src_path = Path::new(framework);
257 let src_name = src_path
258 .file_name()
259 .expect("Couldn't get framework filename");
260 let dest_path = dest_dir.join(src_name);
261 copy_dir(src_path, &dest_path)?;
262 continue;
263 } else if framework.ends_with(".dylib") {
264 let src_path = Path::new(framework);
265 if !src_path.exists() {
266 return Err(anyhow::anyhow!("Library not found: {}", framework));
267 }
268 let src_name = src_path.file_name().expect("Couldn't get library filename");
269 let dest_path = dest_dir.join(src_name);
270 copy_file(src_path, &dest_path)?;
271 continue;
272 } else if framework.contains('/') {
273 return Err(anyhow::anyhow!(
274 "Framework path should have .framework extension: {}",
275 framework
276 ));
277 }
278 if let Some(home_dir) = dirs::home_dir() {
279 if copy_framework_from(&home_dir.join("Library/Frameworks/"), framework, dest_dir)? {
280 continue;
281 }
282 }
283 if copy_framework_from("/Library/Frameworks/".as_ref(), framework, dest_dir)?
284 || copy_framework_from("/Network/Library/Frameworks/".as_ref(), framework, dest_dir)?
285 {
286 continue;
287 }
288 }
289 Ok(())
290}
291
292fn target_dir_from_out_dir(out_dir: &Path) -> Option<&Path> {
297 out_dir
298 .ancestors()
299 .find(|path| path.file_name() == Some(OsStr::new("build")))
300 .and_then(|build_dir| build_dir.parent())
301}
302
303fn cfg_alias(alias: &str, has_feature: bool) {
306 println!("cargo:rustc-check-cfg=cfg({alias})");
307 if has_feature {
308 println!("cargo:rustc-cfg={alias}");
309 }
310}
311
312#[allow(dead_code)]
314#[derive(Debug)]
315pub struct WindowsAttributes {
316 window_icon_path: Option<PathBuf>,
317 static_vc_runtime: Option<bool>,
319 #[doc = include_str!("windows-app-manifest.xml")]
324 app_manifest: Option<String>,
346 append_rc_content: Vec<String>,
348}
349
350impl Default for WindowsAttributes {
351 fn default() -> Self {
352 Self::new()
353 }
354}
355
356impl WindowsAttributes {
357 pub fn new() -> Self {
359 Self {
360 static_vc_runtime: None,
361 app_manifest: Some(include_str!("windows-app-manifest.xml").into()),
362 window_icon_path: None,
363 append_rc_content: Vec::new(),
364 }
365 }
366
367 #[must_use]
369 pub fn new_without_app_manifest() -> Self {
370 Self {
371 app_manifest: None,
372 window_icon_path: None,
373 static_vc_runtime: None,
374 append_rc_content: Vec::new(),
375 }
376 }
377
378 #[must_use]
383 pub fn window_icon_path<P: AsRef<Path>>(mut self, window_icon_path: P) -> Self {
384 self
385 .window_icon_path
386 .replace(window_icon_path.as_ref().into());
387 self
388 }
389
390 #[must_use]
394 pub fn static_vc_runtime(mut self, static_vc_runtime: bool) -> Self {
395 self.static_vc_runtime.replace(static_vc_runtime);
396 self
397 }
398
399 #[doc = include_str!("windows-app-manifest.xml")]
404 #[must_use]
451 pub fn app_manifest<S: AsRef<str>>(mut self, manifest: S) -> Self {
452 self.app_manifest = Some(manifest.as_ref().to_string());
453 self
454 }
455
456 #[must_use]
459 pub fn append_rc_content<S: Into<String>>(mut self, content: S) -> Self {
460 self.append_rc_content.push(content.into());
461 self
462 }
463}
464
465#[derive(Debug, Default)]
467pub struct Attributes {
468 #[allow(dead_code)]
469 windows_attributes: WindowsAttributes,
470 capabilities_path_pattern: Option<&'static str>,
471 config_path: Option<PathBuf>,
472 #[cfg(feature = "codegen")]
473 codegen: Option<codegen::context::CodegenContext>,
474 inlined_plugins: HashMap<&'static str, InlinedPlugin>,
475 app_manifest: AppManifest,
476}
477
478impl Attributes {
479 pub fn new() -> Self {
481 Self::default()
482 }
483
484 #[must_use]
503 pub fn windows_attributes(mut self, windows_attributes: WindowsAttributes) -> Self {
504 self.windows_attributes = windows_attributes;
505 self
506 }
507
508 #[must_use]
516 pub fn capabilities_path_pattern(mut self, pattern: &'static str) -> Self {
517 self.capabilities_path_pattern.replace(pattern);
518 self
519 }
520
521 pub fn plugin(mut self, name: &'static str, plugin: InlinedPlugin) -> Self {
525 self.inlined_plugins.insert(name, plugin);
526 self
527 }
528
529 pub fn plugins<I>(mut self, plugins: I) -> Self
533 where
534 I: IntoIterator<Item = (&'static str, InlinedPlugin)>,
535 {
536 self.inlined_plugins.extend(plugins);
537 self
538 }
539
540 pub fn config_path(mut self, config_path: impl Into<PathBuf>) -> Self {
546 self.config_path = Some(config_path.into());
547 self
548 }
549
550 pub fn app_manifest(mut self, manifest: AppManifest) -> Self {
554 self.app_manifest = manifest;
555 self
556 }
557
558 #[cfg(feature = "codegen")]
583 #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
584 #[must_use]
585 pub fn codegen(mut self, codegen: codegen::context::CodegenContext) -> Self {
586 self.codegen.replace(codegen);
587 self
588 }
589}
590
591pub fn is_dev() -> bool {
608 env::var_os("DEP_TAURI_DEV")
609 .expect("missing `cargo:dev` instruction, please update tauri to latest")
610 == "true"
611}
612
613pub fn build() {
636 if let Err(error) = try_build(Attributes::default()) {
637 let error = format!("{error:#}");
638 println!("{error}");
639 if error.starts_with("unknown field") {
640 print!(
641 "found an unknown configuration field. This usually means that you are using a CLI version that is newer than `tauri-build` and is incompatible. "
642 );
643 println!(
644 "Please try updating the Rust crates by running `cargo update` in the Tauri app folder."
645 );
646 }
647 std::process::exit(1);
648 }
649}
650
651#[allow(unused_variables)]
653pub fn try_build(attributes: Attributes) -> Result<()> {
654 use anyhow::anyhow;
655
656 println!("cargo:rerun-if-env-changed=TAURI_CONFIG");
657
658 let target_os = env::var_os("CARGO_CFG_TARGET_OS").unwrap();
659 let mobile = target_os == "ios" || target_os == "android";
660 cfg_alias("desktop", !mobile);
661 cfg_alias("mobile", mobile);
662
663 let target_triple = env::var("TARGET").unwrap();
664 let target = tauri_utils::platform::Target::from_triple(&target_triple);
665
666 let config_root = if let Some(config_path) = &attributes.config_path {
667 config_path.parent().with_context(|| {
668 format!(
669 "`config_path` '{}' doesn't have a parent directory",
670 config_path.display()
671 )
672 })?
673 } else {
674 &env::current_dir().unwrap()
675 };
676
677 let (mut config, config_paths) = tauri_utils::config::parse::read_from(target, config_root)?;
678
679 for config_file_path in config_paths {
680 println!("cargo:rerun-if-changed={}", config_file_path.display());
681 }
682 if let Ok(env) = env::var("TAURI_CONFIG") {
683 let merge_config: serde_json::Value = serde_json::from_str(&env)?;
684 json_patch::merge(&mut config, &merge_config);
685 }
686 let config: Config = serde_json::from_value(config)?;
687 let static_vc_runtime = should_static_link_vc_runtime(&config, &attributes);
688
689 let s = config.identifier.split('.');
690 let last = s.clone().count() - 1;
691 let mut android_package_prefix = String::new();
692 for (i, w) in s.enumerate() {
693 if i == last {
694 println!(
695 "cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_APP_NAME={}",
696 w.replace('-', "_")
697 );
698 } else {
699 android_package_prefix.push_str(&w.replace(['_', '-'], "_1"));
700 android_package_prefix.push('_');
701 }
702 }
703 android_package_prefix.pop();
704 println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_PREFIX={android_package_prefix}");
705
706 if let Some(project_dir) = env::var_os("TAURI_ANDROID_PROJECT_PATH").map(PathBuf::from) {
707 mobile::generate_gradle_files(project_dir)?;
708
709 if let Some(associations) = config.bundle.file_associations.as_ref() {
711 mobile::update_android_manifest_file_associations(associations)?;
712 }
713 }
714
715 cfg_alias("dev", is_dev());
716
717 let cargo_toml_path = Path::new("Cargo.toml").canonicalize()?;
718 let mut manifest = Manifest::<cargo_toml::Value>::from_path_with_metadata(cargo_toml_path)?;
719
720 let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
721
722 manifest::check(&config, &mut manifest)?;
723
724 acl::build(&out_dir, target, &config, &attributes)?;
725
726 tauri_utils::plugin::save_global_api_scripts_paths(&out_dir, None);
727
728 println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}");
729 unsafe { env::set_var("TAURI_ENV_TARGET_TRIPLE", &target_triple) };
732
733 let target_dir = target_dir_from_out_dir(&out_dir)
734 .with_context(|| format!("failed to resolve the target directory from {out_dir:?}"))?;
735
736 if let Some(paths) = &config.bundle.external_bin {
737 copy_binaries(
738 ResourcePaths::new(&external_binaries(paths, &target_triple, &target), true),
739 &target_triple,
740 target_dir,
741 manifest.package.as_ref().map(|p| p.name.as_ref()),
742 )?;
743 }
744
745 let mut resources = config
746 .bundle
747 .resources
748 .clone()
749 .unwrap_or(BundleResources::List(Vec::new()));
750 if target_triple.contains("windows") {
751 if let Some(fixed_webview2_runtime_path) = match &config.bundle.windows.webview_install_mode {
752 WebviewInstallMode::FixedRuntime { path } => Some(path),
753 _ => None,
754 } {
755 resources.push(fixed_webview2_runtime_path.display().to_string());
756 }
757 }
758 match resources {
759 BundleResources::List(res) => {
760 copy_resources(ResourcePaths::new(res.as_slice(), true), target_dir)?
761 }
762 BundleResources::Map(map) => copy_resources(ResourcePaths::from_map(&map, true), target_dir)?,
763 }
764
765 if target_triple.contains("darwin") {
766 if let Some(frameworks) = &config.bundle.macos.frameworks {
767 if !frameworks.is_empty() {
768 let frameworks_dir = target_dir.parent().unwrap().join("Frameworks");
769 let _ = fs::remove_dir_all(&frameworks_dir);
770 copy_frameworks(&frameworks_dir, frameworks)?;
773
774 println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks");
777 }
778 }
779
780 if !is_dev() {
781 if let Some(version) = &config.bundle.macos.minimum_system_version {
782 println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={version}");
783 }
784 }
785 }
786
787 if target_triple.contains("ios") {
788 println!(
789 "cargo:rustc-env=IPHONEOS_DEPLOYMENT_TARGET={}",
790 config.bundle.ios.minimum_system_version
791 );
792 }
793
794 if target_triple.contains("windows") {
795 use semver::Version;
796 use tauri_winres::{VersionInfo, WindowsResource};
797
798 let window_icon_path = attributes
799 .windows_attributes
800 .window_icon_path
801 .unwrap_or_else(|| {
802 config_root.join(
804 config
805 .bundle
806 .icon
807 .iter()
808 .find(|i| i.ends_with(".ico"))
809 .map(AsRef::as_ref)
810 .unwrap_or("icons/icon.ico"),
811 )
812 });
813
814 let mut res = WindowsResource::new();
815
816 if let Some(manifest) = attributes.windows_attributes.app_manifest {
817 res.set_manifest(&manifest);
818 }
819
820 for content in attributes.windows_attributes.append_rc_content {
821 res.append_rc_content(&content);
822 }
823
824 if let Some(version_str) = &config.version {
825 if let Ok(v) = Version::parse(version_str) {
826 let version = to_winres_version(&v);
827 res.set_version_info(VersionInfo::FILEVERSION, version);
828 res.set_version_info(VersionInfo::PRODUCTVERSION, version);
829 res.set("FileVersion", version_str);
830 res.set("ProductVersion", version_str);
831 }
832 }
833
834 if let Some(product_name) = &config.product_name {
835 res.set("ProductName", product_name);
836 }
837
838 let company_name = config.bundle.publisher.unwrap_or_else(|| {
839 config
840 .identifier
841 .split('.')
842 .nth(1)
843 .unwrap_or(&config.identifier)
844 .to_string()
845 });
846
847 res.set("CompanyName", &company_name);
848
849 let file_description = config
850 .product_name
851 .or_else(|| manifest.package.as_ref().map(|p| p.name.clone()))
852 .or_else(|| std::env::var("CARGO_PKG_NAME").ok());
853
854 res.set("FileDescription", &file_description.unwrap());
855
856 if let Some(copyright) = &config.bundle.copyright {
857 res.set("LegalCopyright", copyright);
858 }
859
860 if window_icon_path.exists() {
861 res.set_icon_with_id(
862 &window_icon_path.display().to_string(),
863 &tauri_utils::platform::WINDOWS_APP_ICON_RESOURCE_ID.to_string(),
864 );
865 } else {
866 return Err(anyhow!(format!(
867 "`{}` not found; required for generating a Windows Resource file during tauri-build",
868 window_icon_path.display()
869 )));
870 }
871
872 res.compile().with_context(|| {
873 format!(
874 "failed to compile `{}` into a Windows Resource file during tauri-build",
875 window_icon_path.display()
876 )
877 })?;
878
879 let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
880 match target_env.as_str() {
881 "gnu" => {
882 let target_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
883 "x86_64" => Some("x64"),
884 "x86" => Some("x86"),
885 "aarch64" => Some("arm64"),
886 arch => None,
887 };
888 if let Some(target_arch) = target_arch {
889 for entry in fs::read_dir(target_dir.join("build"))? {
890 let path = entry?.path();
891 let webview2_loader_path = path
892 .join("out")
893 .join(target_arch)
894 .join("WebView2Loader.dll");
895 if path.to_string_lossy().contains("webview2-com-sys") && webview2_loader_path.exists()
896 {
897 fs::copy(webview2_loader_path, target_dir.join("WebView2Loader.dll"))?;
898 break;
899 }
900 }
901 }
902 }
903 "msvc" if static_vc_runtime => {
904 static_vcruntime::build();
905 }
906 _ => (),
907 }
908 }
909
910 #[cfg(feature = "codegen")]
911 if let Some(mut codegen) = attributes.codegen {
912 if codegen.config_path.is_none() {
913 codegen.config_path = attributes.config_path;
914 }
915 codegen.try_build()?;
916 }
917
918 Ok(())
919}
920
921fn to_winres_version(v: &semver::Version) -> u64 {
922 let build = v.build.parse::<u16>().map(u64::from).unwrap_or(0);
923
924 (v.major << 48) | (v.minor << 32) | (v.patch << 16) | build
925}
926
927fn should_static_link_vc_runtime(config: &Config, attributes: &Attributes) -> bool {
928 if let Some(value) = env::var_os("STATIC_VCRUNTIME") {
929 println!(
930 "cargo:warning=STATIC_VCRUNTIME is deprecated; use build.windows.staticVCRuntime in tauri.conf.json or tauri_build::WindowsAttributes::static_vc_runtime instead."
931 );
932 value != "false"
933 } else {
934 attributes
935 .windows_attributes
936 .static_vc_runtime
937 .unwrap_or(config.build.windows.static_vc_runtime)
938 }
939}
940
941#[cfg(test)]
942mod tests {
943 use semver::Version;
944 use std::path::Path;
945
946 #[test]
947 fn target_dir_from_stable_out_dir() {
948 let out_dir = Path::new("/app/target/debug/build/app-63ba68eead531e35/out");
949
950 assert_eq!(
951 crate::target_dir_from_out_dir(out_dir),
952 Some(Path::new("/app/target/debug"))
953 );
954 }
955
956 #[test]
957 fn target_dir_from_nightly_out_dir() {
958 let out_dir = Path::new("/app/target/debug/build/app/63ba68eead531e35/out");
959
960 assert_eq!(
961 crate::target_dir_from_out_dir(out_dir),
962 Some(Path::new("/app/target/debug"))
963 );
964 }
965
966 #[test]
967 fn target_dir_from_out_dir_with_triple() {
968 let out_dir =
969 Path::new("/app/target/aarch64-apple-darwin/release/build/app/63ba68eead531e35/out");
970
971 assert_eq!(
972 crate::target_dir_from_out_dir(out_dir),
973 Some(Path::new("/app/target/aarch64-apple-darwin/release"))
974 );
975 }
976
977 #[test]
978 fn version_uses_numeric_build_metadata() {
979 let version = Version::parse("1.2.3+42").unwrap();
980
981 assert_eq!(
982 crate::to_winres_version(&version),
983 (1 << 48) | (2 << 32) | (3 << 16) | 42
984 );
985 }
986
987 #[test]
988 fn version_ignores_non_numeric_composite_build_metadata() {
989 let version = Version::parse("1.2.3+42.sha").unwrap();
990
991 assert_eq!(
992 crate::to_winres_version(&version),
993 (1 << 48) | (2 << 32) | (3 << 16)
994 );
995 }
996
997 #[test]
998 fn version_ignores_non_numeric_build_metadata() {
999 let version = Version::parse("1.2.3+abc").unwrap();
1000
1001 assert_eq!(
1002 crate::to_winres_version(&version),
1003 (1 << 48) | (2 << 32) | (3 << 16)
1004 );
1005 }
1006
1007 #[test]
1008 fn version_ignores_build_metadata_that_does_not_fit_in_u16() {
1009 let version = Version::parse("1.2.3+70000").unwrap();
1010
1011 assert_eq!(
1012 crate::to_winres_version(&version),
1013 (1 << 48) | (2 << 32) | (3 << 16)
1014 );
1015 }
1016
1017 #[test]
1018 #[serial_test::serial]
1019 fn static_vc_runtime_chain() {
1020 let config = tauri_utils::config::Config::default();
1022 let attributes = crate::Attributes::new();
1023 assert!(crate::should_static_link_vc_runtime(&config, &attributes));
1024
1025 unsafe { std::env::set_var("STATIC_VCRUNTIME", "qweqe") };
1027 let config = tauri_utils::config::Config::default();
1028 let attributes = crate::Attributes::new();
1029 assert!(crate::should_static_link_vc_runtime(&config, &attributes));
1030 unsafe { std::env::remove_var("STATIC_VCRUNTIME") };
1031
1032 unsafe { std::env::set_var("STATIC_VCRUNTIME", "false") };
1034 let config = tauri_utils::config::Config::default();
1035 let attributes = crate::Attributes::new();
1036 assert!(!crate::should_static_link_vc_runtime(&config, &attributes));
1037 unsafe { std::env::remove_var("STATIC_VCRUNTIME") };
1038
1039 let config = tauri_utils::config::Config::default();
1041 let attributes = crate::Attributes::new()
1042 .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(true));
1043 assert!(crate::should_static_link_vc_runtime(&config, &attributes));
1044
1045 let config = tauri_utils::config::Config::default();
1047 let attributes = crate::Attributes::new()
1048 .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(false));
1049 assert!(!crate::should_static_link_vc_runtime(&config, &attributes));
1050
1051 let config = tauri_utils::config::Config {
1053 build: tauri_utils::config::BuildConfig {
1054 windows: tauri_utils::config::WindowsBuildConfig {
1055 static_vc_runtime: true,
1056 },
1057 ..Default::default()
1058 },
1059 ..Default::default()
1060 };
1061 let attributes = crate::Attributes::new();
1062 assert!(crate::should_static_link_vc_runtime(&config, &attributes));
1063
1064 let config = tauri_utils::config::Config {
1066 build: tauri_utils::config::BuildConfig {
1067 windows: tauri_utils::config::WindowsBuildConfig {
1068 static_vc_runtime: false,
1069 },
1070 ..Default::default()
1071 },
1072 ..Default::default()
1073 };
1074 let attributes = crate::Attributes::new();
1075 assert!(!crate::should_static_link_vc_runtime(&config, &attributes));
1076
1077 let config = tauri_utils::config::Config {
1079 build: tauri_utils::config::BuildConfig {
1080 windows: tauri_utils::config::WindowsBuildConfig {
1081 static_vc_runtime: true,
1082 },
1083 ..Default::default()
1084 },
1085 ..Default::default()
1086 };
1087 let attributes = crate::Attributes::new()
1088 .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(false));
1089 assert!(!crate::should_static_link_vc_runtime(&config, &attributes));
1090
1091 unsafe { std::env::set_var("STATIC_VCRUNTIME", "false") };
1093 let config = tauri_utils::config::Config::default();
1094 let attributes = crate::Attributes::new()
1095 .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(true));
1096 assert!(!crate::should_static_link_vc_runtime(&config, &attributes));
1097 unsafe { std::env::remove_var("STATIC_VCRUNTIME") };
1098 }
1099}