123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770 |
- // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-License-Identifier: MIT
- //! [](https://tauri.app)
- //!
- //! This applies the macros at build-time in order to rig some special features needed by `cargo`.
- #![doc(
- html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
- html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
- )]
- #![cfg_attr(docsrs, feature(doc_cfg))]
- use anyhow::Context;
- pub use anyhow::Result;
- use cargo_toml::Manifest;
- use tauri_utils::{
- acl::build::parse_capabilities,
- config::{BundleResources, Config, WebviewInstallMode},
- resources::{external_binaries, ResourcePaths},
- };
- use std::{
- collections::HashMap,
- env::var_os,
- fs::copy,
- path::{Path, PathBuf},
- };
- mod acl;
- #[cfg(feature = "codegen")]
- mod codegen;
- mod manifest;
- mod mobile;
- mod static_vcruntime;
- #[cfg(feature = "codegen")]
- #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
- pub use codegen::context::CodegenContext;
- const PLUGIN_MANIFESTS_FILE_NAME: &str = "plugin-manifests.json";
- const CAPABILITIES_FILE_NAME: &str = "capabilities.json";
- fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
- let from = from.as_ref();
- let to = to.as_ref();
- if !from.exists() {
- return Err(anyhow::anyhow!("{:?} does not exist", from));
- }
- if !from.is_file() {
- return Err(anyhow::anyhow!("{:?} is not a file", from));
- }
- let dest_dir = to.parent().expect("No data in parent");
- std::fs::create_dir_all(dest_dir)?;
- std::fs::copy(from, to)?;
- Ok(())
- }
- fn copy_binaries(
- binaries: ResourcePaths,
- target_triple: &str,
- path: &Path,
- package_name: Option<&String>,
- ) -> Result<()> {
- for src in binaries {
- let src = src?;
- println!("cargo:rerun-if-changed={}", src.display());
- let file_name = src
- .file_name()
- .expect("failed to extract external binary filename")
- .to_string_lossy()
- .replace(&format!("-{target_triple}"), "");
- if package_name.map_or(false, |n| n == &file_name) {
- return Err(anyhow::anyhow!(
- "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.",
- file_name
- ));
- }
- let dest = path.join(file_name);
- if dest.exists() {
- std::fs::remove_file(&dest).unwrap();
- }
- copy_file(&src, &dest)?;
- }
- Ok(())
- }
- /// Copies resources to a path.
- fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> {
- for resource in resources.iter() {
- let resource = resource?;
- println!("cargo:rerun-if-changed={}", resource.path().display());
- copy_file(resource.path(), path.join(resource.target()))?;
- }
- Ok(())
- }
- #[cfg(unix)]
- fn symlink_dir(src: &Path, dst: &Path) -> std::io::Result<()> {
- std::os::unix::fs::symlink(src, dst)
- }
- /// Makes a symbolic link to a directory.
- #[cfg(windows)]
- fn symlink_dir(src: &Path, dst: &Path) -> std::io::Result<()> {
- std::os::windows::fs::symlink_dir(src, dst)
- }
- /// Makes a symbolic link to a file.
- #[cfg(unix)]
- fn symlink_file(src: &Path, dst: &Path) -> std::io::Result<()> {
- std::os::unix::fs::symlink(src, dst)
- }
- /// Makes a symbolic link to a file.
- #[cfg(windows)]
- fn symlink_file(src: &Path, dst: &Path) -> std::io::Result<()> {
- std::os::windows::fs::symlink_file(src, dst)
- }
- fn copy_dir(from: &Path, to: &Path) -> Result<()> {
- for entry in walkdir::WalkDir::new(from) {
- let entry = entry?;
- debug_assert!(entry.path().starts_with(from));
- let rel_path = entry.path().strip_prefix(from)?;
- let dest_path = to.join(rel_path);
- if entry.file_type().is_symlink() {
- let target = std::fs::read_link(entry.path())?;
- if entry.path().is_dir() {
- symlink_dir(&target, &dest_path)?;
- } else {
- symlink_file(&target, &dest_path)?;
- }
- } else if entry.file_type().is_dir() {
- std::fs::create_dir(dest_path)?;
- } else {
- std::fs::copy(entry.path(), dest_path)?;
- }
- }
- Ok(())
- }
- // Copies the framework under `{src_dir}/{framework}.framework` to `{dest_dir}/{framework}.framework`.
- fn copy_framework_from(src_dir: &Path, framework: &str, dest_dir: &Path) -> Result<bool> {
- let src_name = format!("{}.framework", framework);
- let src_path = src_dir.join(&src_name);
- if src_path.exists() {
- copy_dir(&src_path, &dest_dir.join(&src_name))?;
- Ok(true)
- } else {
- Ok(false)
- }
- }
- // Copies the macOS application bundle frameworks to the target folder
- fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> {
- std::fs::create_dir_all(dest_dir).with_context(|| {
- format!(
- "Failed to create frameworks output directory at {:?}",
- dest_dir
- )
- })?;
- for framework in frameworks.iter() {
- if framework.ends_with(".framework") {
- let src_path = PathBuf::from(framework);
- let src_name = src_path
- .file_name()
- .expect("Couldn't get framework filename");
- let dest_path = dest_dir.join(src_name);
- copy_dir(&src_path, &dest_path)?;
- continue;
- } else if framework.ends_with(".dylib") {
- let src_path = PathBuf::from(framework);
- if !src_path.exists() {
- return Err(anyhow::anyhow!("Library not found: {}", framework));
- }
- let src_name = src_path.file_name().expect("Couldn't get library filename");
- let dest_path = dest_dir.join(src_name);
- copy_file(&src_path, &dest_path)?;
- continue;
- } else if framework.contains('/') {
- return Err(anyhow::anyhow!(
- "Framework path should have .framework extension: {}",
- framework
- ));
- }
- if let Some(home_dir) = dirs_next::home_dir() {
- if copy_framework_from(&home_dir.join("Library/Frameworks/"), framework, dest_dir)? {
- continue;
- }
- }
- if copy_framework_from(&PathBuf::from("/Library/Frameworks/"), framework, dest_dir)?
- || copy_framework_from(
- &PathBuf::from("/Network/Library/Frameworks/"),
- framework,
- dest_dir,
- )?
- {
- continue;
- }
- }
- Ok(())
- }
- // creates a cfg alias if `has_feature` is true.
- // `alias` must be a snake case string.
- fn cfg_alias(alias: &str, has_feature: bool) {
- if has_feature {
- println!("cargo:rustc-cfg={alias}");
- }
- }
- /// Attributes used on Windows.
- #[allow(dead_code)]
- #[derive(Debug, Default)]
- pub struct WindowsAttributes {
- window_icon_path: Option<PathBuf>,
- /// A string containing an [application manifest] to be included with the application on Windows.
- ///
- /// Defaults to:
- /// ```text
- #[doc = include_str!("window-app-manifest.xml")]
- /// ```
- ///
- /// ## Warning
- ///
- /// if you are using tauri's dialog APIs, you need to specify a dependency on Common Control v6 by adding the following to your custom manifest:
- /// ```text
- /// <dependency>
- /// <dependentAssembly>
- /// <assemblyIdentity
- /// type="win32"
- /// name="Microsoft.Windows.Common-Controls"
- /// version="6.0.0.0"
- /// processorArchitecture="*"
- /// publicKeyToken="6595b64144ccf1df"
- /// language="*"
- /// />
- /// </dependentAssembly>
- /// </dependency>
- /// ```
- ///
- /// [application manifest]: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests
- app_manifest: Option<String>,
- }
- impl WindowsAttributes {
- /// Creates the default attribute set.
- pub fn new() -> Self {
- Self::default()
- }
- /// Sets the icon to use on the window. Currently only used on Windows.
- /// It must be in `ico` format. Defaults to `icons/icon.ico`.
- #[must_use]
- pub fn window_icon_path<P: AsRef<Path>>(mut self, window_icon_path: P) -> Self {
- self
- .window_icon_path
- .replace(window_icon_path.as_ref().into());
- self
- }
- /// Sets the [application manifest] to be included with the application on Windows.
- ///
- /// Defaults to:
- /// ```text
- #[doc = include_str!("window-app-manifest.xml")]
- /// ```
- ///
- /// ## Warning
- ///
- /// if you are using tauri's dialog APIs, you need to specify a dependency on Common Control v6 by adding the following to your custom manifest:
- /// ```text
- /// <dependency>
- /// <dependentAssembly>
- /// <assemblyIdentity
- /// type="win32"
- /// name="Microsoft.Windows.Common-Controls"
- /// version="6.0.0.0"
- /// processorArchitecture="*"
- /// publicKeyToken="6595b64144ccf1df"
- /// language="*"
- /// />
- /// </dependentAssembly>
- /// </dependency>
- /// ```
- ///
- /// # Example
- ///
- /// The following manifest will brand the exe as requesting administrator privileges.
- /// Thus, everytime it is executed, a Windows UAC dialog will appear.
- ///
- /// ```rust,no_run
- /// let mut windows = tauri_build::WindowsAttributes::new();
- /// windows = windows.app_manifest(r#"
- /// <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
- /// <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- /// <security>
- /// <requestedPrivileges>
- /// <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
- /// </requestedPrivileges>
- /// </security>
- /// </trustInfo>
- /// </assembly>
- /// "#);
- /// let attrs = tauri_build::Attributes::new().windows_attributes(windows);
- /// tauri_build::try_build(attrs).expect("failed to run build script");
- /// ```
- ///
- /// Note that you can move the manifest contents to a separate file and use `include_str!("manifest.xml")`
- /// instead of the inline string.
- ///
- /// [manifest]: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests
- #[must_use]
- pub fn app_manifest<S: AsRef<str>>(mut self, manifest: S) -> Self {
- self.app_manifest = Some(manifest.as_ref().to_string());
- self
- }
- }
- /// Definition of a plugin that is part of the Tauri application instead of having its own crate.
- ///
- /// By default it generates a plugin manifest that parses permissions from the `permissions/$plugin-name` directory.
- /// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`].
- ///
- /// To autogenerate permissions for each of the plugin commands, see [`Self::commands`].
- #[derive(Debug, Default)]
- pub struct InlinedPlugin {
- commands: &'static [&'static str],
- permissions_path_pattern: Option<&'static str>,
- }
- impl InlinedPlugin {
- pub fn new() -> Self {
- Self::default()
- }
- /// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command`
- /// where $command is the command in kebab-case.
- pub fn commands(mut self, commands: &'static [&'static str]) -> Self {
- self.commands = commands;
- self
- }
- /// Sets a glob pattern that is used to find the permissions of this inlined plugin.
- ///
- /// **Note:** You must emit [rerun-if-changed] instructions for the plugin permissions directory.
- ///
- /// By default it is `./permissions/$plugin-name/**/*`
- pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self {
- self.permissions_path_pattern.replace(pattern);
- self
- }
- }
- /// The attributes used on the build.
- #[derive(Debug, Default)]
- pub struct Attributes {
- #[allow(dead_code)]
- windows_attributes: WindowsAttributes,
- capabilities_path_pattern: Option<&'static str>,
- #[cfg(feature = "codegen")]
- codegen: Option<codegen::context::CodegenContext>,
- inlined_plugins: HashMap<&'static str, InlinedPlugin>,
- }
- impl Attributes {
- /// Creates the default attribute set.
- pub fn new() -> Self {
- Self::default()
- }
- /// Sets the icon to use on the window. Currently only used on Windows.
- #[must_use]
- pub fn windows_attributes(mut self, windows_attributes: WindowsAttributes) -> Self {
- self.windows_attributes = windows_attributes;
- self
- }
- /// Set the glob pattern to be used to find the capabilities.
- ///
- /// **Note:** You must emit [rerun-if-changed] instructions for your capabilities directory.
- ///
- /// [rerun-if-changed]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed
- #[must_use]
- pub fn capabilities_path_pattern(mut self, pattern: &'static str) -> Self {
- self.capabilities_path_pattern.replace(pattern);
- self
- }
- /// Adds the given plugin to the list of inlined plugins (a plugin that is part of your application).
- ///
- /// See [`InlinedPlugin`] for more information.
- pub fn plugin(mut self, name: &'static str, plugin: InlinedPlugin) -> Self {
- self.inlined_plugins.insert(name, plugin);
- self
- }
- #[cfg(feature = "codegen")]
- #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
- #[must_use]
- pub fn codegen(mut self, codegen: codegen::context::CodegenContext) -> Self {
- self.codegen.replace(codegen);
- self
- }
- }
- pub fn dev() -> bool {
- std::env::var("DEP_TAURI_DEV")
- .expect("missing `cargo:dev` instruction, please update tauri to latest")
- == "true"
- }
- /// Run all build time helpers for your Tauri Application.
- ///
- /// The current helpers include the following:
- /// * Generates a Windows Resource file when targeting Windows.
- ///
- /// # Platforms
- ///
- /// [`build()`] should be called inside of `build.rs` regardless of the platform:
- /// * New helpers may target more platforms in the future.
- /// * Platform specific code is handled by the helpers automatically.
- /// * A build script is required in order to activate some cargo environmental variables that are
- /// used when generating code and embedding assets - so [`build()`] may as well be called.
- ///
- /// In short, this is saying don't put the call to [`build()`] behind a `#[cfg(windows)]`.
- ///
- /// # Panics
- ///
- /// If any of the build time helpers fail, they will [`std::panic!`] with the related error message.
- /// This is typically desirable when running inside a build script; see [`try_build`] for no panics.
- pub fn build() {
- if let Err(error) = try_build(Attributes::default()) {
- let error = format!("{error:#}");
- println!("{error}");
- if error.starts_with("unknown field") {
- print!("found an unknown configuration field. This usually means that you are using a CLI version that is newer than `tauri-build` and is incompatible. ");
- println!(
- "Please try updating the Rust crates by running `cargo update` in the Tauri app folder."
- );
- }
- std::process::exit(1);
- }
- }
- /// Non-panicking [`build()`].
- #[allow(unused_variables)]
- pub fn try_build(attributes: Attributes) -> Result<()> {
- use anyhow::anyhow;
- println!("cargo:rerun-if-env-changed=TAURI_CONFIG");
- #[cfg(feature = "config-json")]
- println!("cargo:rerun-if-changed=tauri.conf.json");
- #[cfg(feature = "config-json5")]
- println!("cargo:rerun-if-changed=tauri.conf.json5");
- #[cfg(feature = "config-toml")]
- println!("cargo:rerun-if-changed=Tauri.toml");
- let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
- let mobile = target_os == "ios" || target_os == "android";
- cfg_alias("desktop", !mobile);
- cfg_alias("mobile", mobile);
- let target_triple = std::env::var("TARGET").unwrap();
- let target = tauri_utils::platform::Target::from_triple(&target_triple);
- let mut config = serde_json::from_value(tauri_utils::config::parse::read_from(
- target,
- std::env::current_dir().unwrap(),
- )?)?;
- if let Ok(env) = std::env::var("TAURI_CONFIG") {
- let merge_config: serde_json::Value = serde_json::from_str(&env)?;
- json_patch::merge(&mut config, &merge_config);
- }
- let config: Config = serde_json::from_value(config)?;
- let s = config.identifier.split('.');
- let last = s.clone().count() - 1;
- let mut android_package_prefix = String::new();
- for (i, w) in s.enumerate() {
- if i == 0 || i != last {
- android_package_prefix.push_str(w);
- android_package_prefix.push('_');
- }
- }
- android_package_prefix.pop();
- println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_PREFIX={android_package_prefix}");
- if let Some(project_dir) = var_os("TAURI_ANDROID_PROJECT_PATH").map(PathBuf::from) {
- mobile::generate_gradle_files(project_dir)?;
- }
- cfg_alias("dev", dev());
- let ws_path = get_workspace_dir()?;
- let mut manifest =
- Manifest::<cargo_toml::Value>::from_slice_with_metadata(&std::fs::read("Cargo.toml")?)?;
- if let Ok(ws_manifest) = Manifest::from_path(ws_path.join("Cargo.toml")) {
- Manifest::complete_from_path_and_workspace(
- &mut manifest,
- Path::new("Cargo.toml"),
- Some((&ws_manifest, ws_path.as_path())),
- )?;
- } else {
- Manifest::complete_from_path(&mut manifest, Path::new("Cargo.toml"))?;
- }
- let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
- manifest::check(&config, &mut manifest)?;
- let mut plugin_manifests = acl::get_plugin_manifests()?;
- for (name, plugin) in attributes.inlined_plugins {
- let plugin_out_dir = out_dir.join("plugins").join(name);
- let mut permission_files = if plugin.commands.is_empty() {
- Vec::new()
- } else {
- tauri_utils::acl::build::autogenerate_command_permissions(
- &plugin_out_dir,
- plugin.commands,
- "",
- );
- tauri_utils::acl::build::define_permissions(
- &plugin_out_dir.join("*").to_string_lossy(),
- name,
- &plugin_out_dir,
- )?
- };
- if let Some(pattern) = plugin.permissions_path_pattern {
- permission_files.extend(tauri_utils::acl::build::define_permissions(
- pattern,
- name,
- &plugin_out_dir,
- )?);
- } else {
- let default_permissions_path = Path::new("permissions").join(name);
- println!(
- "cargo:rerun-if-changed={}",
- default_permissions_path.display()
- );
- permission_files.extend(tauri_utils::acl::build::define_permissions(
- &default_permissions_path
- .join("**")
- .join("*")
- .to_string_lossy(),
- name,
- &plugin_out_dir,
- )?);
- }
- let manifest = tauri_utils::acl::plugin::Manifest::new(permission_files, None);
- plugin_manifests.insert(name.into(), manifest);
- }
- std::fs::write(
- out_dir.join(PLUGIN_MANIFESTS_FILE_NAME),
- serde_json::to_string(&plugin_manifests)?,
- )?;
- let capabilities = if let Some(pattern) = attributes.capabilities_path_pattern {
- parse_capabilities(pattern)?
- } else {
- println!("cargo:rerun-if-changed=capabilities");
- parse_capabilities("./capabilities/**/*")?
- };
- acl::generate_schema(&plugin_manifests, target)?;
- acl::validate_capabilities(&plugin_manifests, &capabilities)?;
- let capabilities_path = acl::save_capabilities(&capabilities)?;
- copy(capabilities_path, out_dir.join(CAPABILITIES_FILE_NAME))?;
- acl::save_plugin_manifests(&plugin_manifests)?;
- println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}");
- // TODO: far from ideal, but there's no other way to get the target dir, see <https://github.com/rust-lang/cargo/issues/5457>
- let target_dir = out_dir
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .parent()
- .unwrap();
- if let Some(paths) = &config.bundle.external_bin {
- copy_binaries(
- ResourcePaths::new(external_binaries(paths, &target_triple).as_slice(), true),
- &target_triple,
- target_dir,
- manifest.package.as_ref().map(|p| &p.name),
- )?;
- }
- #[allow(unused_mut, clippy::redundant_clone)]
- let mut resources = config
- .bundle
- .resources
- .clone()
- .unwrap_or_else(|| BundleResources::List(Vec::new()));
- if target_triple.contains("windows") {
- if let Some(fixed_webview2_runtime_path) =
- match &config.bundle.windows.webview_fixed_runtime_path {
- Some(path) => Some(path),
- None => match &config.bundle.windows.webview_install_mode {
- WebviewInstallMode::FixedRuntime { path } => Some(path),
- _ => None,
- },
- }
- {
- resources.push(fixed_webview2_runtime_path.display().to_string());
- }
- }
- match resources {
- BundleResources::List(res) => {
- copy_resources(ResourcePaths::new(res.as_slice(), true), target_dir)?
- }
- BundleResources::Map(map) => copy_resources(ResourcePaths::from_map(&map, true), target_dir)?,
- }
- if target_triple.contains("darwin") {
- if let Some(frameworks) = &config.bundle.macos.frameworks {
- if !frameworks.is_empty() {
- let frameworks_dir = target_dir.parent().unwrap().join("Frameworks");
- let _ = std::fs::remove_dir_all(&frameworks_dir);
- // copy frameworks to the root `target` folder (instead of `target/debug` for instance)
- // because the rpath is set to `@executable_path/../Frameworks`.
- copy_frameworks(&frameworks_dir, frameworks)?;
- // If we have frameworks, we need to set the @rpath
- // https://github.com/tauri-apps/tauri/issues/7710
- println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks");
- }
- }
- if let Some(version) = &config.bundle.macos.minimum_system_version {
- println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={version}");
- }
- }
- if target_triple.contains("windows") {
- use semver::Version;
- use tauri_winres::{VersionInfo, WindowsResource};
- fn find_icon<F: Fn(&&String) -> bool>(config: &Config, predicate: F, default: &str) -> PathBuf {
- let icon_path = config
- .bundle
- .icon
- .iter()
- .find(|i| predicate(i))
- .cloned()
- .unwrap_or_else(|| default.to_string());
- icon_path.into()
- }
- let window_icon_path = attributes
- .windows_attributes
- .window_icon_path
- .unwrap_or_else(|| find_icon(&config, |i| i.ends_with(".ico"), "icons/icon.ico"));
- let mut res = WindowsResource::new();
- if let Some(manifest) = attributes.windows_attributes.app_manifest {
- res.set_manifest(&manifest);
- } else {
- res.set_manifest(include_str!("window-app-manifest.xml"));
- }
- if let Some(version_str) = &config.version {
- if let Ok(v) = Version::parse(version_str) {
- let version = v.major << 48 | v.minor << 32 | v.patch << 16;
- res.set_version_info(VersionInfo::FILEVERSION, version);
- res.set_version_info(VersionInfo::PRODUCTVERSION, version);
- }
- }
- if let Some(product_name) = &config.product_name {
- res.set("ProductName", product_name);
- }
- if let Some(short_description) = &config.bundle.short_description {
- res.set("FileDescription", short_description);
- }
- if let Some(copyright) = &config.bundle.copyright {
- res.set("LegalCopyright", copyright);
- }
- if window_icon_path.exists() {
- res.set_icon_with_id(&window_icon_path.display().to_string(), "32512");
- } else {
- return Err(anyhow!(format!(
- "`{}` not found; required for generating a Windows Resource file during tauri-build",
- window_icon_path.display()
- )));
- }
- res.compile().with_context(|| {
- format!(
- "failed to compile `{}` into a Windows Resource file during tauri-build",
- window_icon_path.display()
- )
- })?;
- let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap();
- match target_env.as_str() {
- "gnu" => {
- let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
- "x86_64" => Some("x64"),
- "x86" => Some("x86"),
- "aarch64" => Some("arm64"),
- arch => None,
- };
- if let Some(target_arch) = target_arch {
- for entry in std::fs::read_dir(target_dir.join("build"))? {
- let path = entry?.path();
- let webview2_loader_path = path
- .join("out")
- .join(target_arch)
- .join("WebView2Loader.dll");
- if path.to_string_lossy().contains("webview2-com-sys") && webview2_loader_path.exists()
- {
- std::fs::copy(webview2_loader_path, target_dir.join("WebView2Loader.dll"))?;
- break;
- }
- }
- }
- }
- "msvc" => {
- if std::env::var("STATIC_VCRUNTIME").map_or(false, |v| v == "true") {
- static_vcruntime::build();
- }
- }
- _ => (),
- }
- }
- #[cfg(feature = "codegen")]
- if let Some(codegen) = attributes.codegen {
- codegen.try_build()?;
- }
- Ok(())
- }
- #[derive(serde::Deserialize)]
- struct CargoMetadata {
- workspace_root: PathBuf,
- }
- fn get_workspace_dir() -> Result<PathBuf> {
- let output = std::process::Command::new("cargo")
- .args(["metadata", "--no-deps", "--format-version", "1"])
- .output()?;
- if !output.status.success() {
- return Err(anyhow::anyhow!(
- "cargo metadata command exited with a non zero exit code: {}",
- String::from_utf8(output.stderr)?
- ));
- }
- Ok(serde_json::from_slice::<CargoMetadata>(&output.stdout)?.workspace_root)
- }
|