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