diff --git a/.changes/bundle-vc-runtime.md b/.changes/bundle-vc-runtime.md deleted file mode 100644 index 15d7fa313c29..000000000000 --- a/.changes/bundle-vc-runtime.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"tauri-bundler": "minor:feat" -"tauri-cli": "minor:feat" -"tauri-utils": "minor:feat" ---- - -Added `bundle.windows.bundleVCRuntime` to copy the Visual C++ runtime DLLs into Windows MSI and NSIS installers. The bundler locates the runtime through `VCTOOLS_REDIST_DIR` or the bundled `vswhere.exe`. diff --git a/.changes/no-dest-resource-target.md b/.changes/no-dest-resource-target.md new file mode 100644 index 000000000000..926b8e8c8cf3 --- /dev/null +++ b/.changes/no-dest-resource-target.md @@ -0,0 +1,17 @@ +--- +"tauri-utils": "patch:bug" +--- + +Fix a regression in tauri-utils 2.8.3 that made empty path an invalid resource target, e.g. + +```json +{ + "bundle": { + "resources": { + "README.md": "", + } + } +} +``` + +(this means `README.md` -> `$RESOURCE/README.md`, note this is a confusing behavior, and will be changed in v3) diff --git a/.changes/resources-after-empty-diretory.md b/.changes/resources-after-empty-diretory.md new file mode 100644 index 000000000000..58087ef0b68b --- /dev/null +++ b/.changes/resources-after-empty-diretory.md @@ -0,0 +1,18 @@ +--- +"tauri-utils": "patch:bug" +--- + +Fix a regression in tauri-utils 2.8.3 that made an empty directory makes it skip all the following entries, e.g. + +```json +{ + "bundle": { + "resources": [ + "empty-directory", + "README.md" + ] + } +} +``` + +if `empty-directory` is empty, the `README.md` will not be copied to the resource directory (skipped) diff --git a/.changes/static-vc-runtime.md b/.changes/static-vc-runtime.md deleted file mode 100644 index 22f770e27167..000000000000 --- a/.changes/static-vc-runtime.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"tauri-cli": "minor:feat" -"tauri-utils": "minor:feat" ---- - -Added `build.windows.staticVCRuntime` to control MSVC static runtime linking. The `STATIC_VCRUNTIME` environment variable is now deprecated and emits a migration warning when used. diff --git a/.changes/tauri-build-static-vc-runtime.md b/.changes/tauri-build-static-vc-runtime.md deleted file mode 100644 index 98f0d0517137..000000000000 --- a/.changes/tauri-build-static-vc-runtime.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"tauri-build": "minor:feat" ---- - -Added `tauri_build::WindowsAttributes::static_vc_runtime` to control MSVC static runtime linking from build scripts. diff --git a/crates/tauri-build/src/lib.rs b/crates/tauri-build/src/lib.rs index db62722ef275..6c171a3005f9 100644 --- a/crates/tauri-build/src/lib.rs +++ b/crates/tauri-build/src/lib.rs @@ -215,8 +215,6 @@ fn cfg_alias(alias: &str, has_feature: bool) { #[derive(Debug)] pub struct WindowsAttributes { window_icon_path: Option, - /// Whether to statically link the Visual C++ runtime into the application binary on Windows MSVC targets - static_vc_runtime: Option, /// A string containing an [application manifest] to be included with the application on Windows. /// /// Defaults to: @@ -259,7 +257,6 @@ impl WindowsAttributes { pub fn new() -> Self { Self { window_icon_path: Default::default(), - static_vc_runtime: None, app_manifest: Some(include_str!("windows-app-manifest.xml").into()), append_rc_content: Vec::new(), } @@ -271,7 +268,6 @@ impl WindowsAttributes { Self { app_manifest: None, window_icon_path: Default::default(), - static_vc_runtime: None, append_rc_content: Vec::new(), } } @@ -286,15 +282,6 @@ impl WindowsAttributes { self } - /// Sets whether to statically link the Visual C++ runtime into the application binary on Windows MSVC targets. - /// - /// If unset, this is read from `build > windows > staticVCRuntime` in the Tauri configuration. - #[must_use] - pub fn static_vc_runtime(mut self, static_vc_runtime: bool) -> Self { - self.static_vc_runtime.replace(static_vc_runtime); - self - } - /// Sets the [application manifest] to be included with the application on Windows. /// /// Defaults to: @@ -502,7 +489,6 @@ pub fn try_build(attributes: Attributes) -> Result<()> { json_patch::merge(&mut config, &merge_config); } let config: Config = serde_json::from_value(config)?; - let static_vc_runtime = should_static_link_vc_runtime(&config, &attributes); let s = config.identifier.split('.'); let last = s.clone().count() - 1; @@ -719,7 +705,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> { } } } - "msvc" if static_vc_runtime => { + "msvc" if env::var_os("STATIC_VCRUNTIME").is_some_and(|v| v == "true") => { static_vcruntime::build(); } _ => (), @@ -740,20 +726,6 @@ fn to_winres_version(v: &semver::Version) -> u64 { (v.major << 48) | (v.minor << 32) | (v.patch << 16) | build } -fn should_static_link_vc_runtime(config: &Config, attributes: &Attributes) -> bool { - if let Some(value) = env::var_os("STATIC_VCRUNTIME") { - println!( - "cargo:warning=STATIC_VCRUNTIME is deprecated; use build.windows.staticVCRuntime in tauri.conf.json or tauri_build::WindowsAttributes::static_vc_runtime instead." - ); - value != "false" - } else { - attributes - .windows_attributes - .static_vc_runtime - .unwrap_or(config.build.windows.static_vc_runtime) - } -} - #[cfg(test)] mod tests { use semver::Version; @@ -797,86 +769,4 @@ mod tests { (1 << 48) | (2 << 32) | (3 << 16) ); } - - #[test] - fn static_vc_runtime_chain() { - // 1. Nothing is set, should default to true - let config = tauri_utils::config::Config::default(); - let attributes = crate::Attributes::new(); - assert!(crate::should_static_link_vc_runtime(&config, &attributes)); - - // 2. Set to anything but "false" in env, should be true - std::env::set_var("STATIC_VCRUNTIME", "qweqe"); - let config = tauri_utils::config::Config::default(); - let attributes = crate::Attributes::new(); - assert!(crate::should_static_link_vc_runtime(&config, &attributes)); - std::env::remove_var("STATIC_VCRUNTIME"); - - // 3. Set to "false" in env, should be false - std::env::set_var("STATIC_VCRUNTIME", "false"); - let config = tauri_utils::config::Config::default(); - let attributes = crate::Attributes::new(); - assert!(!crate::should_static_link_vc_runtime(&config, &attributes)); - std::env::remove_var("STATIC_VCRUNTIME"); - - // 4. Set to true in attributes, should be true - let config = tauri_utils::config::Config::default(); - let attributes = crate::Attributes::new() - .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(true)); - assert!(crate::should_static_link_vc_runtime(&config, &attributes)); - - // 5. Set to false in attributes, should be false - let config = tauri_utils::config::Config::default(); - let attributes = crate::Attributes::new() - .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(false)); - assert!(!crate::should_static_link_vc_runtime(&config, &attributes)); - - // 6. Set to true in config, should be true - let config = tauri_utils::config::Config { - build: tauri_utils::config::BuildConfig { - windows: tauri_utils::config::WindowsBuildConfig { - static_vc_runtime: true, - }, - ..Default::default() - }, - ..Default::default() - }; - let attributes = crate::Attributes::new(); - assert!(crate::should_static_link_vc_runtime(&config, &attributes)); - - // 7. Set to false in config, should be false - let config = tauri_utils::config::Config { - build: tauri_utils::config::BuildConfig { - windows: tauri_utils::config::WindowsBuildConfig { - static_vc_runtime: false, - }, - ..Default::default() - }, - ..Default::default() - }; - let attributes = crate::Attributes::new(); - assert!(!crate::should_static_link_vc_runtime(&config, &attributes)); - - // 8. Set to true in config and false in attributes, should be false because attributes takes precedence over config - let config = tauri_utils::config::Config { - build: tauri_utils::config::BuildConfig { - windows: tauri_utils::config::WindowsBuildConfig { - static_vc_runtime: true, - }, - ..Default::default() - }, - ..Default::default() - }; - let attributes = crate::Attributes::new() - .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(false)); - assert!(!crate::should_static_link_vc_runtime(&config, &attributes)); - - // 9. Set to false in env and true in attributes, should be false because env takes precedence over attributes - std::env::set_var("STATIC_VCRUNTIME", "false"); - let config = tauri_utils::config::Config::default(); - let attributes = crate::Attributes::new() - .windows_attributes(crate::WindowsAttributes::new().static_vc_runtime(true)); - assert!(!crate::should_static_link_vc_runtime(&config, &attributes)); - std::env::remove_var("STATIC_VCRUNTIME"); - } } diff --git a/crates/tauri-bundler/Cargo.toml b/crates/tauri-bundler/Cargo.toml index ba4b38a684e9..5dc1cdb117b3 100644 --- a/crates/tauri-bundler/Cargo.toml +++ b/crates/tauri-bundler/Cargo.toml @@ -50,6 +50,7 @@ plist = "1" [target."cfg(target_os = \"windows\")".dependencies] bitness = "0.4" windows-registry = "0.5" +glob = "0.3" [target."cfg(target_os = \"windows\")".dependencies.windows-sys] version = "0.60" @@ -66,9 +67,6 @@ ar = "0.9" md5 = "0.8" rpm = { version = "0.16", features = ["bzip2-compression"] } -[target."cfg(any(target_os = \"windows\", target_os = \"macos\", target_os = \"linux\"))".dependencies] -glob = "0.3" - [target."cfg(unix)".dependencies] which = "8" diff --git a/crates/tauri-bundler/src/bundle.rs b/crates/tauri-bundler/src/bundle.rs index bda981e6fc4f..ab0a45032f93 100644 --- a/crates/tauri-bundler/src/bundle.rs +++ b/crates/tauri-bundler/src/bundle.rs @@ -14,8 +14,6 @@ mod settings; mod updater_bundle; mod windows; -pub use windows::vswhere_path; - use tauri_utils::{display_path, platform::Target as TargetPlatform}; const BUNDLE_VAR_TOKEN: &[u8] = b"__TAURI_BUNDLE_TYPE_VAR_UNK"; diff --git a/crates/tauri-bundler/src/bundle/settings.rs b/crates/tauri-bundler/src/bundle/settings.rs index 62e6557863a1..7580a0adbc95 100644 --- a/crates/tauri-bundler/src/bundle/settings.rs +++ b/crates/tauri-bundler/src/bundle/settings.rs @@ -603,13 +603,6 @@ pub struct WindowsSettings { /// if the user's WebView2 is older than this version, /// the installer will try to trigger a WebView2 update. pub minimum_webview2_version: Option, - /// Whether to bundle the Visual C++ runtime DLLs alongside the application. - /// - /// This can be particularly useful when the application includes sidecars or DLLs that do not - /// statically link the Visual C++ runtime and require the runtime DLLs at runtime, and users - /// should not be required to install the Visual C++ Redistributable. This can also be useful - /// when `static_vc_runtime` is set to `false`. - pub bundle_vc_runtime: bool, } impl WindowsSettings { @@ -636,7 +629,6 @@ mod _default { allow_downgrades: true, sign_command: None, minimum_webview2_version: None, - bundle_vc_runtime: false, } } } diff --git a/crates/tauri-bundler/src/bundle/windows/mod.rs b/crates/tauri-bundler/src/bundle/windows/mod.rs index 2d59a28f7d00..b92fb5f56216 100644 --- a/crates/tauri-bundler/src/bundle/windows/mod.rs +++ b/crates/tauri-bundler/src/bundle/windows/mod.rs @@ -11,6 +11,6 @@ pub mod sign; mod util; pub use util::{ - vswhere_path, NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, WIX_OUTPUT_FOLDER_NAME, + NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME, }; diff --git a/crates/tauri-bundler/src/bundle/windows/msi/mod.rs b/crates/tauri-bundler/src/bundle/windows/msi/mod.rs index 2b7452291f40..ad0533601686 100644 --- a/crates/tauri-bundler/src/bundle/windows/msi/mod.rs +++ b/crates/tauri-bundler/src/bundle/windows/msi/mod.rs @@ -9,7 +9,7 @@ use crate::{ windows::{ sign::{should_sign, try_sign}, util::{ - download_webview2_bootstrapper, download_webview2_offline_installer, vc_runtime_dlls, + download_webview2_bootstrapper, download_webview2_offline_installer, WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME, }, }, @@ -1066,21 +1066,6 @@ fn generate_resource_data(settings: &Settings) -> crate::Result { let mut dlls = Vec::new(); - if settings.windows().bundle_vc_runtime { - for dll in vc_runtime_dlls(settings.binary_arch())? { - let resource_path = dunce::simplified(&dll); - if added_resources.contains(&resource_path.to_path_buf()) { - continue; - } - added_resources.push(resource_path.to_path_buf()); - dlls.push(ResourceFile { - id: format!("I{}", Uuid::new_v4().as_simple()), - guid: Uuid::new_v4().to_string(), - path: resource_path.to_path_buf(), - }); - } - } - // TODO: The bundler should not include all DLLs it finds. Instead it should only include WebView2Loader.dll if present and leave the rest to the resources config. let out_dir = settings.project_out_directory(); for dll in glob::glob( diff --git a/crates/tauri-bundler/src/bundle/windows/nsis/mod.rs b/crates/tauri-bundler/src/bundle/windows/nsis/mod.rs index b78334426c6a..0ad87c188cab 100644 --- a/crates/tauri-bundler/src/bundle/windows/nsis/mod.rs +++ b/crates/tauri-bundler/src/bundle/windows/nsis/mod.rs @@ -8,7 +8,7 @@ use crate::{ windows::{ sign::{should_sign, sign_command, try_sign}, util::{ - download_webview2_bootstrapper, download_webview2_offline_installer, vc_runtime_dlls, + download_webview2_bootstrapper, download_webview2_offline_installer, NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, }, }, @@ -774,22 +774,6 @@ fn generate_resource_data(settings: &Settings) -> crate::Result { } } - if settings.windows().bundle_vc_runtime { - for dll in vc_runtime_dlls(settings.binary_arch())? { - let dll = dunce::simplified(&dll).to_path_buf(); - if added_resources.contains(&dll) { - continue; - } - let target = PathBuf::from( - dll - .file_name() - .expect("failed to extract Visual C++ runtime DLL filename"), - ); - added_resources.push(dll.clone()); - resources.insert(dll, (PathBuf::new(), target)); - } - } - for resource in settings.resource_files().iter() { let resource = resource?; diff --git a/crates/tauri-bundler/src/bundle/windows/util.rs b/crates/tauri-bundler/src/bundle/windows/util.rs index 4e606f04e090..e716ad7bbef5 100644 --- a/crates/tauri-bundler/src/bundle/windows/util.rs +++ b/crates/tauri-bundler/src/bundle/windows/util.rs @@ -2,16 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -#[cfg(windows)] -use std::process::Command; use std::{ - fs, - io::Write, + fs::create_dir_all, path::{Path, PathBuf}, }; use ureq::ResponseExt; -use crate::bundle::settings::Arch; use crate::utils::http_utils::{base_ureq_agent, download}; pub const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703"; @@ -26,11 +22,6 @@ pub const NSIS_UPDATER_OUTPUT_FOLDER_NAME: &str = "nsis-updater"; pub const WIX_OUTPUT_FOLDER_NAME: &str = "msi"; pub const WIX_UPDATER_OUTPUT_FOLDER_NAME: &str = "msi-updater"; -const VSWHERE: &[u8] = include_bytes!("vswhere.exe"); -const VCTOOLS_REDIST_DIR_ENV_VAR: &str = "VCTOOLS_REDIST_DIR"; -#[cfg(windows)] -const VC_REDIST_COMPONENT: &str = "Microsoft.VisualStudio.Component.VC.Redist.14.Latest"; - pub fn webview2_guid_path(url: &str) -> crate::Result<(String, String)> { let agent = base_ureq_agent(); let response = agent.head(url).call().map_err(Box::new)?; @@ -66,168 +57,12 @@ pub fn download_webview2_offline_installer(base_path: &Path, arch: &str) -> crat let dir_path = base_path.join(guid); let file_path = dir_path.join(filename); if !file_path.exists() { - fs::create_dir_all(dir_path)?; + create_dir_all(dir_path)?; std::fs::write(&file_path, download(url)?)?; } Ok(file_path) } -/// Finds the Visual C++ runtime DLLs for the given architecture. -pub fn vc_runtime_dlls(arch: Arch) -> crate::Result> { - let arch = vc_runtime_arch(arch)?; - let redist_dir = vc_redist_dir()?; - let runtime_dir = vc_runtime_dir(&redist_dir, arch)?; - - let dlls = glob::glob(&glob_path(&runtime_dir, "*.dll"))?.collect::, _>>()?; - if dlls.is_empty() { - return Err(crate::Error::GenericError(format!( - "no Visual C++ runtime DLLs found in `{}`", - runtime_dir.display() - ))); - } - - Ok(dlls) -} - -#[inline(always)] -fn vc_runtime_arch(arch: Arch) -> crate::Result<&'static str> { - match arch { - Arch::X86_64 => Ok("x64"), - Arch::X86 => Ok("x86"), - Arch::AArch64 => Ok("arm64"), - _ => Err(crate::Error::GenericError( - "bundling the Visual C++ runtime is only supported for Windows x86, x64 and arm64 targets" - .into(), - )), - } -} - -#[cfg(windows)] -fn visual_studio_dir() -> crate::Result { - let Some(vswhere) = vswhere_path() else { - return Err(crate::Error::GenericError( - "failed to prepare bundled vswhere.exe".into(), - )); - }; - - let output = Command::new(vswhere) - .args([ - "-latest", - "-prerelease", - "-products", - "*", - "-requires", - VC_REDIST_COMPONENT, - "-property", - "installationPath", - "-format", - "value", - "-utf8", - ]) - .output()?; - - if !output.status.success() { - return Err(crate::Error::GenericError(format!( - "failed to locate Visual Studio with the {VC_REDIST_COMPONENT} component" - ))); - } - - let stdout = String::from_utf8_lossy(&output.stdout); - let Some(vs_dir) = stdout.lines().map(str::trim).find(|line| !line.is_empty()) else { - return Err(crate::Error::GenericError(format!( - "failed to locate Visual Studio with the {VC_REDIST_COMPONENT} component" - ))); - }; - - Ok(PathBuf::from(vs_dir)) -} - -fn vc_redist_dir() -> crate::Result { - if let Ok(redist_dir) = std::env::var(VCTOOLS_REDIST_DIR_ENV_VAR) { - return Ok(PathBuf::from(redist_dir)); - } - - #[cfg(windows)] - { - let vs_dir = visual_studio_dir()?; - Ok(vs_dir.join("VC/Redist/MSVC")) - } - - #[cfg(not(windows))] - { - Err(crate::Error::GenericError(format!( - "failed to find Visual C++ runtime redist directory; set {VCTOOLS_REDIST_DIR_ENV_VAR} when bundling the Visual C++ runtime from non-Windows hosts" - ))) - } -} - -fn vc_runtime_dir(redist_dir: &Path, arch: &str) -> crate::Result { - let Some(latest_version_dir) = latest_vc_redist_version_dir(redist_dir)? else { - return Err(crate::Error::GenericError(format!( - "failed to find Visual C++ runtime versions in `{}`", - redist_dir.display() - ))); - }; - - let arch_dir = latest_version_dir.join(arch); - let Some(runtime_dir) = glob::glob(&glob_path(&arch_dir, "Microsoft.VC*.CRT"))? - .filter_map(Result::ok) - .find(|path| path.is_dir()) - else { - return Err(crate::Error::GenericError(format!( - "failed to find Visual C++ runtime directory for `{arch}` in `{}`", - arch_dir.display() - ))); - }; - - Ok(runtime_dir) -} - -fn latest_vc_redist_version_dir(redist_dir: &Path) -> crate::Result> { - let dir = fs::read_dir(redist_dir)? - .flatten() - .map(|entry| entry.path()) - .filter(|path| path.is_dir()) - .filter_map(|path| { - let version = path - .file_name()? - .to_str()? - .parse::() - .ok()?; - Some((version, path)) - }) - .max_by(|(a, _), (b, _)| a.cmp(b)) - .map(|(_, path)| path); - Ok(dir) -} - -/// Builds a glob pattern from a literal base path and an unescaped glob suffix. -/// -/// The base path is escaped so Visual Studio paths containing glob metacharacters are treated as -/// literal directories, while `pattern` remains active glob syntax. -fn glob_path(path: &Path, pattern: &str) -> String { - PathBuf::from(glob::Pattern::escape(&path.to_string_lossy())) - .join(pattern) - .to_string_lossy() - .into_owned() -} - -/// Returns the bundled `vswhere.exe` path. -/// -/// The executable is written to a temporary file so callers do not depend on a system-installed -/// `vswhere.exe`. -pub fn vswhere_path() -> Option { - let mut vswhere = std::env::temp_dir(); - vswhere.push("vswhere.exe"); - - if !vswhere.exists() { - let mut file = std::fs::File::create(&vswhere).ok()?; - file.write_all(VSWHERE).ok()?; - } - - Some(vswhere) -} - #[cfg(target_os = "windows")] pub fn processor_architecture<'a>() -> Option<&'a str> { use windows_sys::Win32::System::SystemInformation::{ diff --git a/crates/tauri-bundler/src/error.rs b/crates/tauri-bundler/src/error.rs index 211b5148dcd9..40547a3eea39 100644 --- a/crates/tauri-bundler/src/error.rs +++ b/crates/tauri-bundler/src/error.rs @@ -79,11 +79,11 @@ pub enum Error { #[error("`{0}`")] HttpError(#[from] Box), /// Invalid glob pattern. - #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] + #[cfg(windows)] #[error("{0}")] GlobPattern(#[from] glob::PatternError), /// Failed to use glob pattern. - #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] + #[cfg(windows)] #[error("`{0}`")] Glob(#[from] glob::GlobError), /// Failed to parse the URL diff --git a/crates/tauri-cli/ENVIRONMENT_VARIABLES.md b/crates/tauri-cli/ENVIRONMENT_VARIABLES.md index f6f400ff7678..5a8a18100a18 100644 --- a/crates/tauri-cli/ENVIRONMENT_VARIABLES.md +++ b/crates/tauri-cli/ENVIRONMENT_VARIABLES.md @@ -24,8 +24,6 @@ These environment variables are inputs to the CLI which may have an equivalent C - `TAURI_SIGNING_RPM_KEY` — The private GPG key used to sign the RPM bundle, exported to its ASCII-armored format. - `TAURI_SIGNING_RPM_KEY_PASSPHRASE` — The GPG key passphrase for `TAURI_SIGNING_RPM_KEY`, if needed. - `TAURI_WINDOWS_SIGNTOOL_PATH` — Specify a path to `signtool.exe` used for code signing the application on Windows. -- `STATIC_VCRUNTIME` — Deprecated. Set `build > windows > staticVCRuntime` in `tauri.conf.json` instead. -- `VCTOOLS_REDIST_DIR` — Override the Visual C++ redistributable root directory used when `bundle > windows > bundleVCRuntime` is enabled. If unset, Tauri uses its bundled `vswhere.exe` to locate Visual Studio and derive this directory. - `APPLE_CERTIFICATE` — Base64 encoded of the `.p12` certificate for code signing. To get this value, run `openssl base64 -in MyCertificate.p12 -out MyCertificate-base64.txt`. - `APPLE_CERTIFICATE_PASSWORD` — The password you used to export the certificate. - `APPLE_ID` — The Apple ID used to notarize the application. If this environment variable is provided, `APPLE_PASSWORD` and `APPLE_TEAM_ID` must also be set. Alternatively, `APPLE_API_KEY` and `APPLE_API_ISSUER` can be used to authenticate. diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index 64a7109d95cc..dbbd5dff137e 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -71,10 +71,7 @@ "description": "The build configuration.", "default": { "additionalWatchFolders": [], - "removeUnusedCommands": false, - "windows": { - "staticVCRuntime": true - } + "removeUnusedCommands": false }, "allOf": [ { @@ -132,7 +129,6 @@ "useLocalToolsDir": false, "windows": { "allowDowngrades": true, - "bundleVCRuntime": false, "certificateThumbprint": null, "digestAlgorithm": null, "minimumWebview2Version": null, @@ -1975,17 +1971,6 @@ "items": { "type": "string" } - }, - "windows": { - "description": "Windows-specific build configuration.", - "default": { - "staticVCRuntime": true - }, - "allOf": [ - { - "$ref": "#/definitions/WindowsBuildConfig" - } - ] } }, "additionalProperties": false @@ -2113,18 +2098,6 @@ } ] }, - "WindowsBuildConfig": { - "description": "Windows-specific build configuration.", - "type": "object", - "properties": { - "staticVCRuntime": { - "description": "Whether to statically link the Visual C++ runtime into the application binary on Windows MSVC targets.", - "default": true, - "type": "boolean" - } - }, - "additionalProperties": false - }, "BundleConfig": { "description": "Configuration for tauri-bundler.\n\n See more: ", "type": "object", @@ -2256,7 +2229,6 @@ "description": "Configuration for the Windows bundles.", "default": { "allowDowngrades": true, - "bundleVCRuntime": false, "certificateThumbprint": null, "digestAlgorithm": null, "minimumWebview2Version": null, @@ -2777,11 +2749,6 @@ "type": "null" } ] - }, - "bundleVCRuntime": { - "description": "Whether to bundle the Visual C++ runtime DLLs alongside the application.\n\n This can be particularly useful when your application includes sidecars or DLLs that do\n not statically link the Visual C++ runtime and require the runtime DLLs at runtime, and\n you do not want to require users to install the Visual C++ Redistributable. This can also\n be useful when `build > windows > staticVCRuntime` is set to `false`.", - "default": false, - "type": "boolean" } }, "additionalProperties": false diff --git a/crates/tauri-bundler/src/bundle/windows/vswhere.exe b/crates/tauri-cli/scripts/vswhere.exe similarity index 100% rename from crates/tauri-bundler/src/bundle/windows/vswhere.exe rename to crates/tauri-cli/scripts/vswhere.exe diff --git a/crates/tauri-cli/src/info/env_system.rs b/crates/tauri-cli/src/info/env_system.rs index fb429c0db8f5..0087319eb820 100644 --- a/crates/tauri-cli/src/info/env_system.rs +++ b/crates/tauri-cli/src/info/env_system.rs @@ -17,10 +17,20 @@ struct VsInstanceInfo { display_name: String, } +#[cfg(windows)] +const VSWHERE: &[u8] = include_bytes!("../../scripts/vswhere.exe"); + #[cfg(windows)] fn build_tools_version() -> crate::Result> { - let vswhere = - tauri_bundler::bundle::vswhere_path().context("failed to find or prepare vswhere.exe")?; + let mut vswhere = std::env::temp_dir(); + vswhere.push("vswhere.exe"); + + if !vswhere.exists() { + if let Ok(mut file) = std::fs::File::create(&vswhere) { + use std::io::Write; + let _ = file.write_all(VSWHERE); + } + } // Check if there are Visual Studio installations that have the "MSVC - C++ Buildtools" and "Windows SDK" components. // Both the Windows 10 and Windows 11 SDKs work so we need to query it twice. diff --git a/crates/tauri-cli/src/interface/rust.rs b/crates/tauri-cli/src/interface/rust.rs index 0b76a19ba0d7..e18e8308f444 100644 --- a/crates/tauri-cli/src/interface/rust.rs +++ b/crates/tauri-cli/src/interface/rust.rs @@ -1658,7 +1658,6 @@ fn tauri_config_to_bundle_settings( allow_downgrades: config.windows.allow_downgrades, sign_command: config.windows.sign_command.map(custom_sign_settings), minimum_webview2_version: config.windows.minimum_webview2_version, - bundle_vc_runtime: config.windows.bundle_vc_runtime, }, license: config.license.or_else(|| { settings diff --git a/crates/tauri-cli/src/interface/rust/desktop.rs b/crates/tauri-cli/src/interface/rust/desktop.rs index ed252fb10c83..ddde0a4f0a44 100644 --- a/crates/tauri-cli/src/interface/rust/desktop.rs +++ b/crates/tauri-cli/src/interface/rust/desktop.rs @@ -159,6 +159,10 @@ pub fn build( let out_dir = app_settings.out_dir(&options, tauri_dir)?; let bin_path = app_settings.app_binary_path(&options, tauri_dir)?; + if !std::env::var_os("STATIC_VCRUNTIME").is_some_and(|v| v == "false") { + std::env::set_var("STATIC_VCRUNTIME", "true"); + } + if options.target == Some("universal-apple-darwin".into()) { std::fs::create_dir_all(&out_dir) .fs_context("failed to create project out directory", out_dir.clone())?; diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index 64a7109d95cc..dbbd5dff137e 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -71,10 +71,7 @@ "description": "The build configuration.", "default": { "additionalWatchFolders": [], - "removeUnusedCommands": false, - "windows": { - "staticVCRuntime": true - } + "removeUnusedCommands": false }, "allOf": [ { @@ -132,7 +129,6 @@ "useLocalToolsDir": false, "windows": { "allowDowngrades": true, - "bundleVCRuntime": false, "certificateThumbprint": null, "digestAlgorithm": null, "minimumWebview2Version": null, @@ -1975,17 +1971,6 @@ "items": { "type": "string" } - }, - "windows": { - "description": "Windows-specific build configuration.", - "default": { - "staticVCRuntime": true - }, - "allOf": [ - { - "$ref": "#/definitions/WindowsBuildConfig" - } - ] } }, "additionalProperties": false @@ -2113,18 +2098,6 @@ } ] }, - "WindowsBuildConfig": { - "description": "Windows-specific build configuration.", - "type": "object", - "properties": { - "staticVCRuntime": { - "description": "Whether to statically link the Visual C++ runtime into the application binary on Windows MSVC targets.", - "default": true, - "type": "boolean" - } - }, - "additionalProperties": false - }, "BundleConfig": { "description": "Configuration for tauri-bundler.\n\n See more: ", "type": "object", @@ -2256,7 +2229,6 @@ "description": "Configuration for the Windows bundles.", "default": { "allowDowngrades": true, - "bundleVCRuntime": false, "certificateThumbprint": null, "digestAlgorithm": null, "minimumWebview2Version": null, @@ -2777,11 +2749,6 @@ "type": "null" } ] - }, - "bundleVCRuntime": { - "description": "Whether to bundle the Visual C++ runtime DLLs alongside the application.\n\n This can be particularly useful when your application includes sidecars or DLLs that do\n not statically link the Visual C++ runtime and require the runtime DLLs at runtime, and\n you do not want to require users to install the Visual C++ Redistributable. This can also\n be useful when `build > windows > staticVCRuntime` is set to `false`.", - "default": false, - "type": "boolean" } }, "additionalProperties": false diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index f5483f3b96a1..a54c619e3f6f 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -1078,19 +1078,6 @@ pub struct WindowsConfig { /// need to use another tool like `osslsigncode`. #[serde(alias = "sign-command")] pub sign_command: Option, - /// Whether to bundle the Visual C++ runtime DLLs alongside the application. - /// - /// This can be particularly useful when your application includes sidecars or DLLs that do - /// not statically link the Visual C++ runtime and require the runtime DLLs at runtime, and - /// you do not want to require users to install the Visual C++ Redistributable. This can also - /// be useful when `build > windows > staticVCRuntime` is set to `false`. - #[serde( - default, - rename = "bundleVCRuntime", - alias = "bundle-vc-runtime", - alias = "bundleVcRuntime" - )] - pub bundle_vc_runtime: bool, } impl Default for WindowsConfig { @@ -1106,7 +1093,6 @@ impl Default for WindowsConfig { wix: None, nsis: None, sign_command: None, - bundle_vc_runtime: false, } } } @@ -3462,32 +3448,6 @@ pub struct BuildConfig { /// Additional paths to watch for changes when running `tauri dev`. #[serde(alias = "additional-watch-directories", default)] pub additional_watch_folders: Vec, - /// Windows-specific build configuration. - #[serde(default)] - pub windows: WindowsBuildConfig, -} - -/// Windows-specific build configuration. -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[cfg_attr(feature = "schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct WindowsBuildConfig { - /// Whether to statically link the Visual C++ runtime into the application binary on Windows MSVC targets. - #[serde( - default = "default_true", - rename = "staticVCRuntime", - alias = "static-vc-runtime", - alias = "staticVcRuntime" - )] - pub static_vc_runtime: bool, -} - -impl Default for WindowsBuildConfig { - fn default() -> Self { - Self { - static_vc_runtime: true, - } - } } #[derive(Debug, PartialEq, Eq)] @@ -4166,7 +4126,6 @@ mod build { let features = quote!(None); let remove_unused_commands = quote!(false); let additional_watch_folders = quote!(Vec::new()); - let windows = &self.windows; literal_struct!( tokens, @@ -4179,20 +4138,7 @@ mod build { before_bundle_command, features, remove_unused_commands, - additional_watch_folders, - windows - ); - } - } - - impl ToTokens for WindowsBuildConfig { - fn to_tokens(&self, tokens: &mut TokenStream) { - let static_vc_runtime = self.static_vc_runtime; - - literal_struct!( - tokens, - ::tauri::utils::config::WindowsBuildConfig, - static_vc_runtime + additional_watch_folders ); } } @@ -4536,7 +4482,6 @@ mod test { features: None, remove_unused_commands: false, additional_watch_folders: Vec::new(), - windows: WindowsBuildConfig::default(), }; // create a bundle config diff --git a/crates/tauri-utils/src/resources.rs b/crates/tauri-utils/src/resources.rs index 3fd0290b78ac..6c30b89de486 100644 --- a/crates/tauri-utils/src/resources.rs +++ b/crates/tauri-utils/src/resources.rs @@ -209,7 +209,21 @@ impl ResourcePathsIter<'_> { // preserving the file name as it is ResourcePathsInnerIter::Glob { .. } => dest.join(path.file_name().unwrap()), }, - None => dest.clone(), + None => { + if dest.components().count() == 0 { + // if current_dest is empty while processing a file pattern + // we preserve the file name as it is + // + // e.g. `{ "README.md": "" }` is `README.md` -> `$RESOURCE/README.md` + // + // TODO: This behavior is a confusing special case, + // remove this in v3 or make other cases like this work + // > `{ "README.md": "./folder/" }` is `README.md` -> `$RESOURCE/folder/README.md` (this gives `$RESOURCE/folder` today) + PathBuf::from(path.file_name().unwrap()) + } else { + dest.clone() + } + } } } else { // If [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Slice`] @@ -221,6 +235,7 @@ impl ResourcePathsIter<'_> { fn next_pattern(&mut self) -> Option> { self.current_dest = None; + self.current_iter = None; let pattern = match &mut self.pattern_iter { PatternIter::Slice(iter) => iter.next()?, @@ -237,10 +252,10 @@ impl ResourcePathsIter<'_> { Err(error) => return Some(Err(error.into())), }; match self.next_current_iter() { - Some(r) => return Some(r), + Some(r) => Some(r), None => { self.current_iter = None; - return Some(Err(crate::Error::GlobPathNotFound(pattern.clone()))); + Some(Err(crate::Error::GlobPathNotFound(pattern.clone()))) } } } else { @@ -257,12 +272,12 @@ impl ResourcePathsIter<'_> { None }, }); + // If the directory is empty, skip and continue to the next pattern + self.next_current_iter().or_else(|| self.next_pattern()) } else { - return Some(self.resource_from_path(path)); + Some(self.resource_from_path(path)) } } - - self.next_current_iter() } } @@ -358,6 +373,7 @@ mod tests { fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::write(path, "").unwrap(); } + fs::create_dir_all("empty-directory").unwrap(); } fn resources_map(literal: &[(&str, &str)]) -> HashMap { @@ -377,6 +393,8 @@ mod tests { let resources = ResourcePaths::new( &[ + // `empty-directory` should not affect anything + "../empty-directory".into(), "../src/script.js".into(), "../src/assets".into(), "../src/index.html".into(),