tauri_bundler/bundle/
settings.rs

1// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
2// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
3// SPDX-License-Identifier: Apache-2.0
4// SPDX-License-Identifier: MIT
5
6use super::category::AppCategory;
7use crate::{bundle::platform::target_triple, error::Context, utils::fs_utils};
8pub use tauri_utils::config::WebviewInstallMode;
9use tauri_utils::{
10  config::{
11    BundleType, DeepLinkProtocol, FileAssociation, NSISInstallerMode, NsisCompression,
12    RpmCompression,
13  },
14  platform::Target as TargetPlatform,
15  resources::{external_binaries, ResourcePaths},
16};
17
18use std::{
19  collections::HashMap,
20  path::{Path, PathBuf},
21};
22
23/// The type of the package we're bundling.
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25#[non_exhaustive]
26pub enum PackageType {
27  /// The macOS application bundle (.app).
28  MacOsBundle,
29  /// The iOS app bundle.
30  IosBundle,
31  /// The Windows bundle (.msi).
32  WindowsMsi,
33  /// The NSIS bundle (.exe).
34  Nsis,
35  /// The Linux Debian package bundle (.deb).
36  Deb,
37  /// The Linux RPM bundle (.rpm).
38  Rpm,
39  /// The Linux AppImage bundle (.AppImage).
40  AppImage,
41  /// The macOS DMG bundle (.dmg).
42  Dmg,
43  /// The Updater bundle.
44  Updater,
45}
46
47impl From<BundleType> for PackageType {
48  fn from(bundle: BundleType) -> Self {
49    match bundle {
50      BundleType::Deb => Self::Deb,
51      BundleType::Rpm => Self::Rpm,
52      BundleType::AppImage => Self::AppImage,
53      BundleType::Msi => Self::WindowsMsi,
54      BundleType::Nsis => Self::Nsis,
55      BundleType::App => Self::MacOsBundle,
56      BundleType::Dmg => Self::Dmg,
57    }
58  }
59}
60
61impl PackageType {
62  /// Maps a short name to a PackageType.
63  /// Possible values are "deb", "ios", "msi", "app", "rpm", "appimage", "dmg", "updater".
64  pub fn from_short_name(name: &str) -> Option<PackageType> {
65    // Other types we may eventually want to support: apk.
66    match name {
67      "deb" => Some(PackageType::Deb),
68      "ios" => Some(PackageType::IosBundle),
69      "msi" => Some(PackageType::WindowsMsi),
70      "nsis" => Some(PackageType::Nsis),
71      "app" => Some(PackageType::MacOsBundle),
72      "rpm" => Some(PackageType::Rpm),
73      "appimage" => Some(PackageType::AppImage),
74      "dmg" => Some(PackageType::Dmg),
75      "updater" => Some(PackageType::Updater),
76      _ => None,
77    }
78  }
79
80  /// Gets the short name of this PackageType.
81  #[allow(clippy::trivially_copy_pass_by_ref)]
82  pub fn short_name(&self) -> &'static str {
83    match *self {
84      PackageType::Deb => "deb",
85      PackageType::IosBundle => "ios",
86      PackageType::WindowsMsi => "msi",
87      PackageType::Nsis => "nsis",
88      PackageType::MacOsBundle => "app",
89      PackageType::Rpm => "rpm",
90      PackageType::AppImage => "appimage",
91      PackageType::Dmg => "dmg",
92      PackageType::Updater => "updater",
93    }
94  }
95
96  /// Gets the list of the possible package types.
97  pub fn all() -> &'static [PackageType] {
98    ALL_PACKAGE_TYPES
99  }
100
101  /// Gets a number representing priority which used to sort package types
102  /// in an order that guarantees that if a certain package type
103  /// depends on another (like Dmg depending on MacOsBundle), the dependency
104  /// will be built first
105  ///
106  /// The lower the number, the higher the priority
107  pub fn priority(&self) -> u32 {
108    match self {
109      PackageType::MacOsBundle => 0,
110      PackageType::IosBundle => 0,
111      PackageType::WindowsMsi => 0,
112      PackageType::Nsis => 0,
113      PackageType::Deb => 0,
114      PackageType::Rpm => 0,
115      PackageType::AppImage => 0,
116      PackageType::Dmg => 1,
117      PackageType::Updater => 2,
118    }
119  }
120}
121
122const ALL_PACKAGE_TYPES: &[PackageType] = &[
123  #[cfg(target_os = "linux")]
124  PackageType::Deb,
125  #[cfg(target_os = "macos")]
126  PackageType::IosBundle,
127  #[cfg(target_os = "windows")]
128  PackageType::WindowsMsi,
129  #[cfg(target_os = "windows")]
130  PackageType::Nsis,
131  #[cfg(target_os = "macos")]
132  PackageType::MacOsBundle,
133  #[cfg(target_os = "linux")]
134  PackageType::Rpm,
135  #[cfg(target_os = "macos")]
136  PackageType::Dmg,
137  #[cfg(target_os = "linux")]
138  PackageType::AppImage,
139  PackageType::Updater,
140];
141
142/// The package settings.
143#[derive(Debug, Clone)]
144pub struct PackageSettings {
145  /// the package's product name.
146  pub product_name: String,
147  /// the package's version.
148  pub version: String,
149  /// the package's description.
150  pub description: String,
151  /// the package's homepage.
152  pub homepage: Option<String>,
153  /// the package's authors.
154  pub authors: Option<Vec<String>>,
155  /// the default binary to run.
156  pub default_run: Option<String>,
157}
158
159/// The updater settings.
160#[derive(Debug, Default, Clone)]
161pub struct UpdaterSettings {
162  /// Should generate v1 compatible zipped updater
163  pub v1_compatible: bool,
164  /// Signature public key.
165  pub pubkey: String,
166  /// Args to pass to `msiexec.exe` to run the updater on Windows.
167  pub msiexec_args: &'static [&'static str],
168}
169
170/// The Linux debian bundle settings.
171#[derive(Clone, Debug, Default)]
172pub struct DebianSettings {
173  // OS-specific settings:
174  /// the list of debian dependencies.
175  pub depends: Option<Vec<String>>,
176  /// the list of debian dependencies recommendations.
177  pub recommends: Option<Vec<String>>,
178  /// the list of dependencies the package provides.
179  pub provides: Option<Vec<String>>,
180  /// the list of package conflicts.
181  pub conflicts: Option<Vec<String>>,
182  /// the list of package replaces.
183  pub replaces: Option<Vec<String>>,
184  /// List of custom files to add to the deb package.
185  /// Maps the path on the debian package to the path of the file to include (relative to the current working directory).
186  pub files: HashMap<PathBuf, PathBuf>,
187  /// Path to a custom desktop file Handlebars template.
188  ///
189  /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.
190  ///
191  /// Default file contents:
192  /// ```text
193  #[doc = include_str!("./linux/freedesktop/main.desktop")]
194  /// ```
195  pub desktop_template: Option<PathBuf>,
196  /// Define the section in Debian Control file. See : <https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections>
197  pub section: Option<String>,
198  /// Change the priority of the Debian Package. By default, it is set to `optional`.
199  /// Recognized Priorities as of now are :  `required`, `important`, `standard`, `optional`, `extra`
200  pub priority: Option<String>,
201  /// Path of the uncompressed Changelog file, to be stored at /usr/share/doc/package-name/changelog.gz. See
202  /// <https://www.debian.org/doc/debian-policy/ch-docs.html#changelog-files-and-release-notes>
203  pub changelog: Option<PathBuf>,
204  /// Path to script that will be executed before the package is unpacked. See
205  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
206  pub pre_install_script: Option<PathBuf>,
207  /// Path to script that will be executed after the package is unpacked. See
208  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
209  pub post_install_script: Option<PathBuf>,
210  /// Path to script that will be executed before the package is removed. See
211  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
212  pub pre_remove_script: Option<PathBuf>,
213  /// Path to script that will be executed after the package is removed. See
214  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
215  pub post_remove_script: Option<PathBuf>,
216}
217
218/// The Linux AppImage bundle settings.
219#[derive(Clone, Debug, Default)]
220pub struct AppImageSettings {
221  /// The files to include in the Appimage Binary.
222  pub files: HashMap<PathBuf, PathBuf>,
223  /// Whether to include gstreamer plugins for audio/media support.
224  pub bundle_media_framework: bool,
225  /// Whether to include the `xdg-open` binary.
226  pub bundle_xdg_open: bool,
227}
228
229/// The RPM bundle settings.
230#[derive(Clone, Debug, Default)]
231pub struct RpmSettings {
232  /// The list of RPM dependencies your application relies on.
233  pub depends: Option<Vec<String>>,
234  /// the list of of RPM dependencies your application recommends.
235  pub recommends: Option<Vec<String>>,
236  /// The list of RPM dependencies your application provides.
237  pub provides: Option<Vec<String>>,
238  /// The list of RPM dependencies your application conflicts with. They must not be present
239  /// in order for the package to be installed.
240  pub conflicts: Option<Vec<String>>,
241  /// The list of RPM dependencies your application supersedes - if this package is installed,
242  /// packages listed as "obsoletes" will be automatically removed (if they are present).
243  pub obsoletes: Option<Vec<String>>,
244  /// The RPM release tag.
245  pub release: String,
246  /// The RPM epoch.
247  pub epoch: u32,
248  /// List of custom files to add to the RPM package.
249  /// Maps the path on the RPM package to the path of the file to include (relative to the current working directory).
250  pub files: HashMap<PathBuf, PathBuf>,
251  /// Path to a custom desktop file Handlebars template.
252  ///
253  /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.
254  ///
255  /// Default file contents:
256  /// ```text
257  #[doc = include_str!("./linux/freedesktop/main.desktop")]
258  /// ```
259  pub desktop_template: Option<PathBuf>,
260  /// Path to script that will be executed before the package is unpacked. See
261  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
262  pub pre_install_script: Option<PathBuf>,
263  /// Path to script that will be executed after the package is unpacked. See
264  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
265  pub post_install_script: Option<PathBuf>,
266  /// Path to script that will be executed before the package is removed. See
267  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
268  pub pre_remove_script: Option<PathBuf>,
269  /// Path to script that will be executed after the package is removed. See
270  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
271  pub post_remove_script: Option<PathBuf>,
272  /// Compression algorithm and level. Defaults to `Gzip` with level 6.
273  pub compression: Option<RpmCompression>,
274}
275
276/// Position coordinates struct.
277#[derive(Clone, Debug, Default)]
278pub struct Position {
279  /// X coordinate.
280  pub x: u32,
281  /// Y coordinate.
282  pub y: u32,
283}
284
285/// Size of the window.
286#[derive(Clone, Debug, Default)]
287pub struct Size {
288  /// Width of the window.
289  pub width: u32,
290  /// Height of the window.
291  pub height: u32,
292}
293
294/// The DMG bundle settings.
295#[derive(Clone, Debug, Default)]
296pub struct DmgSettings {
297  /// Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`.
298  pub background: Option<PathBuf>,
299  /// Position of volume window on screen.
300  pub window_position: Option<Position>,
301  /// Size of volume window.
302  pub window_size: Size,
303  /// Position of app file on window.
304  pub app_position: Position,
305  /// Position of application folder on window.
306  pub application_folder_position: Position,
307}
308
309/// The iOS bundle settings.
310#[derive(Clone, Debug, Default)]
311pub struct IosSettings {
312  /// The version of the build that identifies an iteration of the bundle.
313  pub bundle_version: Option<String>,
314}
315
316/// The macOS bundle settings.
317#[derive(Clone, Debug, Default)]
318pub struct MacOsSettings {
319  /// MacOS frameworks that need to be bundled with the app.
320  ///
321  /// Each string can either be the name of a framework (without the `.framework` extension, e.g. `"SDL2"`),
322  /// in which case we will search for that framework in the standard install locations (`~/Library/Frameworks/`, `/Library/Frameworks/`, and `/Network/Library/Frameworks/`),
323  /// or a path to a specific framework bundle (e.g. `./data/frameworks/SDL2.framework`).  Note that this setting just makes tauri-bundler copy the specified frameworks into the OS X app bundle
324  /// (under `Foobar.app/Contents/Frameworks/`); you are still responsible for:
325  ///
326  /// - arranging for the compiled binary to link against those frameworks (e.g. by emitting lines like `cargo:rustc-link-lib=framework=SDL2` from your `build.rs` script)
327  ///
328  /// - embedding the correct rpath in your binary (e.g. by running `install_name_tool -add_rpath "@executable_path/../Frameworks" path/to/binary` after compiling)
329  pub frameworks: Option<Vec<String>>,
330  /// List of custom files to add to the application bundle.
331  /// Maps the path in the Contents directory in the app to the path of the file to include (relative to the current working directory).
332  pub files: HashMap<PathBuf, PathBuf>,
333  /// The version of the build that identifies an iteration of the bundle.
334  pub bundle_version: Option<String>,
335  /// The name of the build that identifies a string of the bundle.
336  ///
337  /// If not set, defaults to the package's product name.
338  pub bundle_name: Option<String>,
339  /// A version string indicating the minimum MacOS version that the bundled app supports (e.g. `"10.11"`).
340  /// If you are using this config field, you may also want have your `build.rs` script emit `cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.11`.
341  pub minimum_system_version: Option<String>,
342  /// The exception domain to use on the macOS .app bundle.
343  ///
344  /// This allows communication to the outside world e.g. a web server you're shipping.
345  pub exception_domain: Option<String>,
346  /// Code signing identity.
347  pub signing_identity: Option<String>,
348  /// Whether to wait for notarization to finish and `staple` the ticket onto the app.
349  ///
350  /// Gatekeeper will look for stapled tickets to tell whether your app was notarized without
351  /// reaching out to Apple's servers which is helpful in offline environments.
352  ///
353  /// Enabling this option will also result in `tauri build` not waiting for notarization to finish
354  /// which is helpful for the very first time your app is notarized as this can take multiple hours.
355  /// On subsequent runs, it's recommended to disable this setting again.
356  pub skip_stapling: bool,
357  /// Preserve the hardened runtime version flag, see <https://developer.apple.com/documentation/security/hardened_runtime>
358  ///
359  /// Settings this to `false` is useful when using an ad-hoc signature, making it less strict.
360  pub hardened_runtime: bool,
361  /// Provider short name for notarization.
362  pub provider_short_name: Option<String>,
363  /// Path or contents of the entitlements.plist file.
364  pub entitlements: Option<Entitlements>,
365  /// Path to the Info.plist file or raw plist value to merge with the bundle Info.plist.
366  pub info_plist: Option<PlistKind>,
367}
368
369/// Entitlements for macOS code signing.
370#[derive(Debug, Clone)]
371pub enum Entitlements {
372  /// Path to the entitlements.plist file.
373  Path(PathBuf),
374  /// Raw plist::Value.
375  Plist(plist::Value),
376}
377
378/// Plist format.
379#[derive(Debug, Clone)]
380pub enum PlistKind {
381  /// Path to a .plist file.
382  Path(PathBuf),
383  /// Raw plist value.
384  Plist(plist::Value),
385}
386
387/// Configuration for a target language for the WiX build.
388#[derive(Debug, Clone, Default)]
389pub struct WixLanguageConfig {
390  /// The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.
391  pub locale_path: Option<PathBuf>,
392}
393
394/// The languages to build using WiX.
395#[derive(Debug, Clone)]
396pub struct WixLanguage(pub Vec<(String, WixLanguageConfig)>);
397
398impl Default for WixLanguage {
399  fn default() -> Self {
400    Self(vec![("en-US".into(), Default::default())])
401  }
402}
403
404/// Settings specific to the WiX implementation.
405#[derive(Clone, Debug, Default)]
406pub struct WixSettings {
407  /// MSI installer version in the format `major.minor.patch.build` (build is optional).
408  ///
409  /// Because a valid version is required for MSI installer, it will be derived from [`PackageSettings::version`] if this field is not set.
410  ///
411  /// The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.
412  /// The third and fourth fields have a maximum value of 65,535.
413  ///
414  /// See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.
415  pub version: Option<String>,
416  /// A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**,
417  /// otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app.
418  ///
419  /// By default, tauri generates this code by generating a Uuid v5 using the string `<productName>.exe.app.x64` in the DNS namespace.
420  /// You can use Tauri's CLI to generate and print this code for you by running `tauri inspect wix-upgrade-code`.
421  ///
422  /// It is recommended that you set this value in your tauri config file to avoid accidental changes in your upgrade code
423  /// whenever you want to change your product name.
424  pub upgrade_code: Option<uuid::Uuid>,
425  /// The app languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
426  pub language: WixLanguage,
427  /// By default, the bundler uses an internal template.
428  /// This option allows you to define your own wix file.
429  pub template: Option<PathBuf>,
430  /// A list of paths to .wxs files with WiX fragments to use.
431  pub fragment_paths: Vec<PathBuf>,
432  /// The ComponentGroup element ids you want to reference from the fragments.
433  pub component_group_refs: Vec<String>,
434  /// The Component element ids you want to reference from the fragments.
435  pub component_refs: Vec<String>,
436  /// The FeatureGroup element ids you want to reference from the fragments.
437  pub feature_group_refs: Vec<String>,
438  /// The Feature element ids you want to reference from the fragments.
439  pub feature_refs: Vec<String>,
440  /// The Merge element ids you want to reference from the fragments.
441  pub merge_refs: Vec<String>,
442  /// Create an elevated update task within Windows Task Scheduler.
443  pub enable_elevated_update_task: bool,
444  /// Path to a bitmap file to use as the installation user interface banner.
445  /// This bitmap will appear at the top of all but the first page of the installer.
446  ///
447  /// The required dimensions are 493px × 58px.
448  pub banner_path: Option<PathBuf>,
449  /// Path to a bitmap file to use on the installation user interface dialogs.
450  /// It is used on the welcome and completion dialogs.
451  ///
452  /// The required dimensions are 493px × 312px.
453  pub dialog_image_path: Option<PathBuf>,
454  /// Enables FIPS compliant algorithms.
455  pub fips_compliant: bool,
456}
457
458/// Settings specific to the NSIS implementation.
459#[derive(Clone, Debug, Default)]
460pub struct NsisSettings {
461  /// A custom .nsi template to use.
462  pub template: Option<PathBuf>,
463  /// The path to a bitmap file to display on the header of installers pages.
464  ///
465  /// The recommended dimensions are 150px x 57px.
466  pub header_image: Option<PathBuf>,
467  /// The path to a bitmap file for the Welcome page and the Finish page.
468  ///
469  /// The recommended dimensions are 164px x 314px.
470  pub sidebar_image: Option<PathBuf>,
471  /// The path to an icon file used as the installer icon.
472  pub installer_icon: Option<PathBuf>,
473  /// Whether the installation will be for all users or just the current user.
474  pub install_mode: NSISInstallerMode,
475  /// A list of installer languages.
476  /// By default the OS language is used. If the OS language is not in the list of languages, the first language will be used.
477  /// To allow the user to select the language, set `display_language_selector` to `true`.
478  ///
479  /// See <https://github.com/kichik/nsis/tree/9465c08046f00ccb6eda985abbdbf52c275c6c4d/Contrib/Language%20files> for the complete list of languages.
480  pub languages: Option<Vec<String>>,
481  /// An key-value pair where the key is the language and the
482  /// value is the path to a custom `.nsi` file that holds the translated text for tauri's custom messages.
483  ///
484  /// See <https://github.com/tauri-apps/tauri/blob/dev/crates/tauri-bundler/src/bundle/windows/nsis/languages/English.nsh> for an example `.nsi` file.
485  ///
486  /// **Note**: the key must be a valid NSIS language and it must be added to [`NsisConfig`]languages array,
487  pub custom_language_files: Option<HashMap<String, PathBuf>>,
488  /// Whether to display a language selector dialog before the installer and uninstaller windows are rendered or not.
489  /// By default the OS language is selected, with a fallback to the first language in the `languages` array.
490  pub display_language_selector: bool,
491  /// Set compression algorithm used to compress files in the installer.
492  pub compression: NsisCompression,
493  /// Set the folder name for the start menu shortcut.
494  ///
495  /// Use this option if you have multiple apps and wish to group their shortcuts under one folder
496  /// or if you generally prefer to set your shortcut inside a folder.
497  ///
498  /// Examples:
499  /// - `AwesomePublisher`, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\AwesomePublisher\<your-app>.lnk`
500  /// - If unset, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\<your-app>.lnk`
501  pub start_menu_folder: Option<String>,
502  /// A path to a `.nsh` file that contains special NSIS macros to be hooked into the
503  /// main installer.nsi script.
504  ///
505  /// Supported hooks are:
506  /// - `NSIS_HOOK_PREINSTALL`: This hook runs before copying files, setting registry key values and creating shortcuts.
507  /// - `NSIS_HOOK_POSTINSTALL`: This hook runs after the installer has finished copying all files, setting the registry keys and created shortcuts.
508  /// - `NSIS_HOOK_PREUNINSTALL`: This hook runs before removing any files, registry keys and shortcuts.
509  /// - `NSIS_HOOK_POSTUNINSTALL`: This hook runs after files, registry keys and shortcuts have been removed.
510  ///
511  ///
512  /// ### Example
513  ///
514  /// ```nsh
515  /// !macro NSIS_HOOK_PREINSTALL
516  ///   MessageBox MB_OK "PreInstall"
517  /// !macroend
518  ///
519  /// !macro NSIS_HOOK_POSTINSTALL
520  ///   MessageBox MB_OK "PostInstall"
521  /// !macroend
522  ///
523  /// !macro NSIS_HOOK_PREUNINSTALL
524  ///   MessageBox MB_OK "PreUnInstall"
525  /// !macroend
526  ///
527  /// !macro NSIS_HOOK_POSTUNINSTALL
528  ///   MessageBox MB_OK "PostUninstall"
529  /// !macroend
530  /// ```
531  pub installer_hooks: Option<PathBuf>,
532  /// Try to ensure that the WebView2 version is equal to or newer than this version,
533  /// if the user's WebView2 is older than this version,
534  /// the installer will try to trigger a WebView2 update.
535  pub minimum_webview2_version: Option<String>,
536}
537
538/// The Custom Signing Command Settings for Windows exe
539#[derive(Clone, Debug)]
540pub struct CustomSignCommandSettings {
541  /// The command to run to sign the binary.
542  pub cmd: String,
543  /// The arguments to pass to the command.
544  ///
545  /// "%1" will be replaced with the path to the binary to be signed.
546  pub args: Vec<String>,
547}
548
549/// The Windows bundle settings.
550#[derive(Clone, Debug)]
551pub struct WindowsSettings {
552  /// The file digest algorithm to use for creating file signatures. Required for code signing. SHA-256 is recommended.
553  pub digest_algorithm: Option<String>,
554  /// The SHA1 hash of the signing certificate.
555  pub certificate_thumbprint: Option<String>,
556  /// Server to use during timestamping.
557  pub timestamp_url: Option<String>,
558  /// Whether to use Time-Stamp Protocol (TSP, a.k.a. RFC 3161) for the timestamp server. Your code signing provider may
559  /// use a TSP timestamp server, like e.g. SSL.com does. If so, enable TSP by setting to true.
560  pub tsp: bool,
561  /// WiX configuration.
562  pub wix: Option<WixSettings>,
563  /// Nsis configuration.
564  pub nsis: Option<NsisSettings>,
565  /// The path to the application icon. Defaults to `./icons/icon.ico`.
566  #[deprecated = "This is used for the MSI installer and will be removed in 3.0.0, use `BundleSettings::icon` field and make sure a `.ico` icon exists instead."]
567  pub icon_path: PathBuf,
568  /// The installation mode for the Webview2 runtime.
569  pub webview_install_mode: WebviewInstallMode,
570  /// Validates a second app installation, blocking the user from installing an older version if set to `false`.
571  ///
572  /// For instance, if `1.2.1` is installed, the user won't be able to install app version `1.2.0` or `1.1.5`.
573  ///
574  /// /// The default value of this flag is `true`.
575  pub allow_downgrades: bool,
576
577  /// Specify a custom command to sign the binaries.
578  /// This command needs to have a `%1` in it which is just a placeholder for the binary path,
579  /// which we will detect and replace before calling the command.
580  ///
581  /// Example:
582  /// ```text
583  /// sign-cli --arg1 --arg2 %1
584  /// ```
585  ///
586  /// By Default we use `signtool.exe` which can be found only on Windows so
587  /// if you are on another platform and want to cross-compile and sign you will
588  /// need to use another tool like `osslsigncode`.
589  pub sign_command: Option<CustomSignCommandSettings>,
590}
591
592impl WindowsSettings {
593  pub(crate) fn can_sign(&self) -> bool {
594    self.sign_command.is_some() || self.certificate_thumbprint.is_some()
595  }
596}
597
598#[allow(deprecated)]
599mod _default {
600  use super::*;
601
602  impl Default for WindowsSettings {
603    fn default() -> Self {
604      Self {
605        digest_algorithm: None,
606        certificate_thumbprint: None,
607        timestamp_url: None,
608        tsp: false,
609        wix: None,
610        nsis: None,
611        icon_path: PathBuf::from("icons/icon.ico"),
612        webview_install_mode: Default::default(),
613        allow_downgrades: true,
614        sign_command: None,
615      }
616    }
617  }
618}
619
620/// The bundle settings of the BuildArtifact we're bundling.
621#[derive(Clone, Debug, Default)]
622pub struct BundleSettings {
623  /// the app's identifier.
624  pub identifier: Option<String>,
625  /// The app's publisher. Defaults to the second element in the identifier string.
626  ///
627  /// Currently maps to the Manufacturer property of the Windows Installer
628  /// and the Maintainer field of debian packages if the Cargo.toml does not have the authors field.
629  pub publisher: Option<String>,
630  /// A url to the home page of your application. If None, will
631  /// fallback to [PackageSettings::homepage].
632  ///
633  /// Supported bundle targets: `deb`, `rpm`, `nsis` and `msi`
634  pub homepage: Option<String>,
635  /// the app's icon list.
636  pub icon: Option<Vec<String>>,
637  /// the app's resources to bundle.
638  ///
639  /// each item can be a path to a file or a path to a folder.
640  ///
641  /// supports glob patterns.
642  pub resources: Option<Vec<String>>,
643  /// The app's resources to bundle. Takes precedence over `Self::resources` when specified.
644  ///
645  /// Maps each resource path to its target directory in the bundle resources directory.
646  ///
647  /// Supports glob patterns.
648  pub resources_map: Option<HashMap<String, String>>,
649  /// the app's copyright.
650  pub copyright: Option<String>,
651  /// The package's license identifier to be included in the appropriate bundles.
652  /// If not set, defaults to the license from the Cargo.toml file.
653  pub license: Option<String>,
654  /// The path to the license file to be included in the appropriate bundles.
655  pub license_file: Option<PathBuf>,
656  /// the app's category.
657  pub category: Option<AppCategory>,
658  /// the file associations
659  pub file_associations: Option<Vec<FileAssociation>>,
660  /// the app's short description.
661  pub short_description: Option<String>,
662  /// the app's long description.
663  pub long_description: Option<String>,
664  // Bundles for other binaries:
665  /// Configuration map for the apps to bundle.
666  pub bin: Option<HashMap<String, BundleSettings>>,
667  /// External binaries to add to the bundle.
668  ///
669  /// Note that each binary name should have the target platform's target triple appended,
670  /// as well as `.exe` for Windows.
671  /// For example, if you're bundling a sidecar called `sqlite3`, the bundler expects
672  /// a binary named `sqlite3-x86_64-unknown-linux-gnu` on linux,
673  /// and `sqlite3-x86_64-pc-windows-gnu.exe` on windows.
674  ///
675  /// Run `tauri build --help` for more info on targets.
676  ///
677  /// If you are building a universal binary for MacOS, the bundler expects
678  /// your external binary to also be universal, and named after the target triple,
679  /// e.g. `sqlite3-universal-apple-darwin`. See
680  /// <https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary>
681  pub external_bin: Option<Vec<String>>,
682  /// Deep-link protocols.
683  pub deep_link_protocols: Option<Vec<DeepLinkProtocol>>,
684  /// Debian-specific settings.
685  pub deb: DebianSettings,
686  /// AppImage-specific settings.
687  pub appimage: AppImageSettings,
688  /// Rpm-specific settings.
689  pub rpm: RpmSettings,
690  /// DMG-specific settings.
691  pub dmg: DmgSettings,
692  /// iOS-specific settings.
693  pub ios: IosSettings,
694  /// MacOS-specific settings.
695  pub macos: MacOsSettings,
696  /// Updater configuration.
697  pub updater: Option<UpdaterSettings>,
698  /// Windows-specific settings.
699  pub windows: WindowsSettings,
700}
701
702/// A binary to bundle.
703#[derive(Clone, Debug)]
704pub struct BundleBinary {
705  name: String,
706  main: bool,
707  src_path: Option<String>,
708}
709
710impl BundleBinary {
711  /// Creates a new bundle binary.
712  pub fn new(name: String, main: bool) -> Self {
713    Self {
714      name,
715      main,
716      src_path: None,
717    }
718  }
719
720  /// Creates a new bundle binary with path.
721  pub fn with_path(name: String, main: bool, src_path: Option<String>) -> Self {
722    Self {
723      name,
724      src_path,
725      main,
726    }
727  }
728
729  /// Mark the binary as the main executable.
730  pub fn set_main(&mut self, main: bool) {
731    self.main = main;
732  }
733
734  /// Sets the binary name.
735  pub fn set_name(&mut self, name: String) {
736    self.name = name;
737  }
738
739  /// Sets the src path of the binary.
740  #[must_use]
741  pub fn set_src_path(mut self, src_path: Option<String>) -> Self {
742    self.src_path = src_path;
743    self
744  }
745
746  /// Returns the binary `main` flag.
747  pub fn main(&self) -> bool {
748    self.main
749  }
750
751  /// Returns the binary name.
752  pub fn name(&self) -> &str {
753    &self.name
754  }
755
756  /// Returns the binary source path.
757  pub fn src_path(&self) -> Option<&String> {
758    self.src_path.as_ref()
759  }
760}
761
762#[derive(Clone, Copy, Debug, Eq, PartialEq)]
763pub enum Arch {
764  /// For the x86_64 / x64 / AMD64 instruction sets (64 bits).
765  X86_64,
766  /// For the x86 / i686 / i686 / 8086 instruction sets (32 bits).
767  X86,
768  /// For the AArch64 / ARM64 instruction sets (64 bits).
769  AArch64,
770  /// For the AArch32 / ARM32 instruction sets with hard-float (32 bits).
771  Armhf,
772  /// For the AArch32 / ARM32 instruction sets with soft-float (32 bits).
773  Armel,
774  /// For the RISC-V instruction sets (64 bits).
775  Riscv64,
776  /// For universal macOS applications.
777  Universal,
778}
779
780/// The Settings exposed by the module.
781#[derive(Clone, Debug)]
782pub struct Settings {
783  /// The log level.
784  log_level: log::Level,
785  /// the package settings.
786  package: PackageSettings,
787  /// the package types we're bundling.
788  ///
789  /// if not present, we'll use the PackageType list for the target OS.
790  package_types: Option<Vec<PackageType>>,
791  /// the directory where the bundles will be placed.
792  project_out_directory: PathBuf,
793  /// the directory to place tools used by the bundler,
794  /// if `None`, tools are placed in the current user's platform-specific cache directory.
795  local_tools_directory: Option<PathBuf>,
796  /// the bundle settings.
797  bundle_settings: BundleSettings,
798  /// the binaries to bundle.
799  binaries: Vec<BundleBinary>,
800  /// The target platform.
801  target_platform: TargetPlatform,
802  /// The target triple.
803  target: String,
804  /// Whether to disable code signing during the bundling process.
805  no_sign: bool,
806}
807
808/// A builder for [`Settings`].
809#[derive(Default)]
810pub struct SettingsBuilder {
811  log_level: Option<log::Level>,
812  project_out_directory: Option<PathBuf>,
813  package_types: Option<Vec<PackageType>>,
814  package_settings: Option<PackageSettings>,
815  bundle_settings: BundleSettings,
816  binaries: Vec<BundleBinary>,
817  target: Option<String>,
818  local_tools_directory: Option<PathBuf>,
819  no_sign: bool,
820}
821
822impl SettingsBuilder {
823  /// Creates the default settings builder.
824  pub fn new() -> Self {
825    Default::default()
826  }
827
828  /// Sets the project output directory. It's used as current working directory.
829  #[must_use]
830  pub fn project_out_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
831    self
832      .project_out_directory
833      .replace(path.as_ref().to_path_buf());
834    self
835  }
836
837  /// Sets the directory to place tools used by the bundler
838  /// when [`BundleSettings::use_local_tools_dir`] is true.
839  #[must_use]
840  pub fn local_tools_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
841    self
842      .local_tools_directory
843      .replace(path.as_ref().to_path_buf());
844    self
845  }
846
847  /// Sets the package types to create.
848  #[must_use]
849  pub fn package_types(mut self, package_types: Vec<PackageType>) -> Self {
850    self.package_types = Some(package_types);
851    self
852  }
853
854  /// Sets the package settings.
855  #[must_use]
856  pub fn package_settings(mut self, settings: PackageSettings) -> Self {
857    self.package_settings.replace(settings);
858    self
859  }
860
861  /// Sets the bundle settings.
862  #[must_use]
863  pub fn bundle_settings(mut self, settings: BundleSettings) -> Self {
864    self.bundle_settings = settings;
865    self
866  }
867
868  /// Sets the binaries to bundle.
869  #[must_use]
870  pub fn binaries(mut self, binaries: Vec<BundleBinary>) -> Self {
871    self.binaries = binaries;
872    self
873  }
874
875  /// Sets the target triple.
876  #[must_use]
877  pub fn target(mut self, target: String) -> Self {
878    self.target.replace(target);
879    self
880  }
881
882  /// Sets the log level for spawned commands. Defaults to [`log::Level::Error`].
883  #[must_use]
884  pub fn log_level(mut self, level: log::Level) -> Self {
885    self.log_level.replace(level);
886    self
887  }
888
889  /// Sets whether to skip code signing.
890  #[must_use]
891  pub fn no_sign(mut self, no_sign: bool) -> Self {
892    self.no_sign = no_sign;
893    self
894  }
895
896  /// Builds a Settings from the CLI args.
897  ///
898  /// Package settings will be read from Cargo.toml.
899  ///
900  /// Bundle settings will be read from $TAURI_DIR/tauri.conf.json if it exists and fallback to Cargo.toml's [package.metadata.bundle].
901  pub fn build(self) -> crate::Result<Settings> {
902    let target = if let Some(t) = self.target {
903      t
904    } else {
905      target_triple()?
906    };
907    let target_platform = TargetPlatform::from_triple(&target);
908
909    Ok(Settings {
910      log_level: self.log_level.unwrap_or(log::Level::Error),
911      package: self
912        .package_settings
913        .ok_or_else(|| crate::Error::GenericError("package settings is required".into()))?,
914      package_types: self.package_types,
915      project_out_directory: self
916        .project_out_directory
917        .ok_or_else(|| crate::Error::GenericError("out directory is required".into()))?,
918      local_tools_directory: self.local_tools_directory,
919      binaries: self.binaries,
920      bundle_settings: BundleSettings {
921        external_bin: self
922          .bundle_settings
923          .external_bin
924          .as_ref()
925          .map(|bins| external_binaries(bins, &target, &target_platform)),
926        ..self.bundle_settings
927      },
928      target_platform,
929      target,
930      no_sign: self.no_sign,
931    })
932  }
933}
934
935impl Settings {
936  /// Sets the log level for spawned commands.
937  pub fn set_log_level(&mut self, level: log::Level) {
938    self.log_level = level;
939  }
940
941  /// Returns the log level for spawned commands.
942  pub fn log_level(&self) -> log::Level {
943    self.log_level
944  }
945
946  /// Returns the directory where the bundle should be placed.
947  pub fn project_out_directory(&self) -> &Path {
948    &self.project_out_directory
949  }
950
951  /// Returns the target triple.
952  pub fn target(&self) -> &str {
953    &self.target
954  }
955
956  /// Returns the [`TargetPlatform`].
957  pub fn target_platform(&self) -> &TargetPlatform {
958    &self.target_platform
959  }
960
961  /// Returns the architecture for the binary being bundled (e.g. "arm", "x86" or "x86_64").
962  pub fn binary_arch(&self) -> Arch {
963    if self.target.starts_with("x86_64") {
964      Arch::X86_64
965    } else if self.target.starts_with('i') {
966      Arch::X86
967    } else if self.target.starts_with("arm") && self.target.ends_with("hf") {
968      Arch::Armhf
969    } else if self.target.starts_with("arm") {
970      Arch::Armel
971    } else if self.target.starts_with("aarch64") {
972      Arch::AArch64
973    } else if self.target.starts_with("riscv64") {
974      Arch::Riscv64
975    } else if self.target.starts_with("universal") {
976      Arch::Universal
977    } else {
978      panic!("Unexpected target triple {}", self.target)
979    }
980  }
981
982  /// Returns the file name of the binary being bundled.
983  pub fn main_binary(&self) -> crate::Result<&BundleBinary> {
984    self
985      .binaries
986      .iter()
987      .find(|bin| bin.main)
988      .context("failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file")
989  }
990
991  /// Returns the file name of the binary being bundled.
992  pub fn main_binary_mut(&mut self) -> crate::Result<&mut BundleBinary> {
993    self
994      .binaries
995      .iter_mut()
996      .find(|bin| bin.main)
997      .context("failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file")
998  }
999
1000  /// Returns the file name of the binary being bundled.
1001  pub fn main_binary_name(&self) -> crate::Result<&str> {
1002    self
1003      .binaries
1004      .iter()
1005      .find(|bin| bin.main)
1006      .context("failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file")
1007      .map(|b| b.name())
1008  }
1009
1010  /// Returns the path to the specified binary.
1011  pub fn binary_path(&self, binary: &BundleBinary) -> PathBuf {
1012    let target_os = self.target_platform();
1013
1014    let mut path = self.project_out_directory.join(binary.name());
1015
1016    if matches!(target_os, TargetPlatform::Windows) {
1017      // Append the `.exe` extension without overriding the existing extensions
1018      let extension = if let Some(extension) = path.extension() {
1019        let mut extension = extension.to_os_string();
1020        extension.push(".exe");
1021        extension
1022      } else {
1023        "exe".into()
1024      };
1025      path.set_extension(extension);
1026    };
1027
1028    path
1029  }
1030
1031  /// Returns the list of binaries to bundle.
1032  pub fn binaries(&self) -> &Vec<BundleBinary> {
1033    &self.binaries
1034  }
1035
1036  /// If a list of package types was specified by the command-line, returns
1037  /// that list filtered by the current target OS available targets.
1038  ///
1039  /// If a target triple was specified by the
1040  /// command-line, returns the native package type(s) for that target.
1041  ///
1042  /// Otherwise returns the native package type(s) for the host platform.
1043  ///
1044  /// Fails if the host/target's native package type is not supported.
1045  pub fn package_types(&self) -> crate::Result<Vec<PackageType>> {
1046    let target_os = self.target_platform();
1047
1048    let platform_types = match target_os {
1049      TargetPlatform::MacOS => vec![PackageType::MacOsBundle, PackageType::Dmg],
1050      TargetPlatform::Ios => vec![PackageType::IosBundle],
1051      TargetPlatform::Linux => vec![PackageType::Deb, PackageType::Rpm, PackageType::AppImage],
1052      TargetPlatform::Windows => vec![PackageType::WindowsMsi, PackageType::Nsis],
1053      os => {
1054        return Err(crate::Error::GenericError(format!(
1055          "Native {os} bundles not yet supported."
1056        )))
1057      }
1058    };
1059
1060    if let Some(package_types) = &self.package_types {
1061      let mut types = vec![];
1062      for package_type in package_types {
1063        let package_type = *package_type;
1064        if platform_types
1065          .clone()
1066          .into_iter()
1067          .any(|t| t == package_type)
1068        {
1069          types.push(package_type);
1070        }
1071      }
1072      Ok(types)
1073    } else {
1074      Ok(platform_types)
1075    }
1076  }
1077
1078  /// Returns the product name.
1079  pub fn product_name(&self) -> &str {
1080    &self.package.product_name
1081  }
1082
1083  /// Returns the bundle's identifier
1084  pub fn bundle_identifier(&self) -> &str {
1085    self.bundle_settings.identifier.as_deref().unwrap_or("")
1086  }
1087
1088  /// Returns the bundle's publisher
1089  pub fn publisher(&self) -> Option<&str> {
1090    self.bundle_settings.publisher.as_deref()
1091  }
1092
1093  /// Returns an iterator over the icon files to be used for this bundle.
1094  pub fn icon_files(&self) -> ResourcePaths<'_> {
1095    match self.bundle_settings.icon {
1096      Some(ref paths) => ResourcePaths::new(paths.as_slice(), false),
1097      None => ResourcePaths::new(&[], false),
1098    }
1099  }
1100
1101  /// Returns an iterator over the resource files to be included in this
1102  /// bundle.
1103  pub fn resource_files(&self) -> ResourcePaths<'_> {
1104    match (
1105      &self.bundle_settings.resources,
1106      &self.bundle_settings.resources_map,
1107    ) {
1108      (Some(paths), None) => ResourcePaths::new(paths.as_slice(), true),
1109      (None, Some(map)) => ResourcePaths::from_map(map, true),
1110      (Some(_), Some(_)) => panic!("cannot use both `resources` and `resources_map`"),
1111      (None, None) => ResourcePaths::new(&[], true),
1112    }
1113  }
1114
1115  /// Returns an iterator over the external binaries to be included in this
1116  /// bundle.
1117  pub fn external_binaries(&self) -> ResourcePaths<'_> {
1118    match self.bundle_settings.external_bin {
1119      Some(ref paths) => ResourcePaths::new(paths.as_slice(), true),
1120      None => ResourcePaths::new(&[], true),
1121    }
1122  }
1123
1124  /// Copies external binaries to a path.
1125  ///
1126  /// Returns the list of destination paths.
1127  pub fn copy_binaries(&self, path: &Path) -> crate::Result<Vec<PathBuf>> {
1128    let mut paths = Vec::new();
1129
1130    for src in self.external_binaries() {
1131      let src = src?;
1132      let dest = path.join(
1133        src
1134          .file_name()
1135          .expect("failed to extract external binary filename")
1136          .to_string_lossy()
1137          .replace(&format!("-{}", self.target), ""),
1138      );
1139      fs_utils::copy_file(&src, &dest)?;
1140      paths.push(dest);
1141    }
1142    Ok(paths)
1143  }
1144
1145  /// Copies resources to a path.
1146  pub fn copy_resources(&self, path: &Path) -> crate::Result<()> {
1147    for resource in self.resource_files().iter() {
1148      let resource = resource?;
1149      let dest = path.join(resource.target());
1150      fs_utils::copy_file(resource.path(), &dest)?;
1151    }
1152    Ok(())
1153  }
1154
1155  /// Returns the version string of the bundle.
1156  pub fn version_string(&self) -> &str {
1157    &self.package.version
1158  }
1159
1160  /// Returns the copyright text.
1161  pub fn copyright_string(&self) -> Option<&str> {
1162    self.bundle_settings.copyright.as_deref()
1163  }
1164
1165  /// Returns the list of authors name.
1166  pub fn author_names(&self) -> &[String] {
1167    match self.package.authors {
1168      Some(ref names) => names.as_slice(),
1169      None => &[],
1170    }
1171  }
1172
1173  /// Returns the authors as a comma-separated string.
1174  pub fn authors_comma_separated(&self) -> Option<String> {
1175    let names = self.author_names();
1176    if names.is_empty() {
1177      None
1178    } else {
1179      Some(names.join(", "))
1180    }
1181  }
1182
1183  /// Returns the bundle license.
1184  pub fn license(&self) -> Option<String> {
1185    self.bundle_settings.license.clone()
1186  }
1187
1188  /// Returns the bundle license file.
1189  pub fn license_file(&self) -> Option<PathBuf> {
1190    self.bundle_settings.license_file.clone()
1191  }
1192
1193  /// Returns the package's homepage URL, defaulting to "" if not defined.
1194  pub fn homepage_url(&self) -> Option<&str> {
1195    self
1196      .bundle_settings
1197      .homepage
1198      .as_deref()
1199      .or(self.package.homepage.as_deref())
1200  }
1201
1202  /// Returns the app's category.
1203  pub fn app_category(&self) -> Option<AppCategory> {
1204    self.bundle_settings.category
1205  }
1206
1207  /// Return file associations.
1208  pub fn file_associations(&self) -> Option<&Vec<FileAssociation>> {
1209    self.bundle_settings.file_associations.as_ref()
1210  }
1211
1212  /// Return the list of deep link protocols to be registered for
1213  /// this bundle.
1214  pub fn deep_link_protocols(&self) -> Option<&Vec<DeepLinkProtocol>> {
1215    self.bundle_settings.deep_link_protocols.as_ref()
1216  }
1217
1218  /// Returns the app's short description.
1219  pub fn short_description(&self) -> &str {
1220    self
1221      .bundle_settings
1222      .short_description
1223      .as_ref()
1224      .unwrap_or(&self.package.description)
1225  }
1226
1227  /// Returns the app's long description.
1228  pub fn long_description(&self) -> Option<&str> {
1229    self.bundle_settings.long_description.as_deref()
1230  }
1231
1232  /// Returns the directory for local tools path.
1233  pub fn local_tools_directory(&self) -> Option<&Path> {
1234    self.local_tools_directory.as_deref()
1235  }
1236
1237  /// Returns the debian settings.
1238  pub fn deb(&self) -> &DebianSettings {
1239    &self.bundle_settings.deb
1240  }
1241
1242  /// Returns the appimage settings.
1243  pub fn appimage(&self) -> &AppImageSettings {
1244    &self.bundle_settings.appimage
1245  }
1246
1247  /// Returns the RPM settings.
1248  pub fn rpm(&self) -> &RpmSettings {
1249    &self.bundle_settings.rpm
1250  }
1251
1252  /// Returns the DMG settings.
1253  pub fn dmg(&self) -> &DmgSettings {
1254    &self.bundle_settings.dmg
1255  }
1256
1257  /// Returns the iOS settings.
1258  pub fn ios(&self) -> &IosSettings {
1259    &self.bundle_settings.ios
1260  }
1261
1262  /// Returns the MacOS settings.
1263  pub fn macos(&self) -> &MacOsSettings {
1264    &self.bundle_settings.macos
1265  }
1266
1267  /// Returns the Windows settings.
1268  pub fn windows(&self) -> &WindowsSettings {
1269    &self.bundle_settings.windows
1270  }
1271
1272  /// Returns the Updater settings.
1273  pub fn updater(&self) -> Option<&UpdaterSettings> {
1274    self.bundle_settings.updater.as_ref()
1275  }
1276
1277  /// Whether to skip signing.
1278  pub fn no_sign(&self) -> bool {
1279    self.no_sign
1280  }
1281
1282  /// Set whether to skip signing.
1283  pub fn set_no_sign(&mut self, no_sign: bool) {
1284    self.no_sign = no_sign;
1285  }
1286}