123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-License-Identifier: MIT
- use super::category::AppCategory;
- use crate::bundle::{common, platform::target_triple};
- use std::{
- collections::HashMap,
- path::{Path, PathBuf},
- };
- /// The type of the package we're bundling.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub enum PackageType {
- /// The macOS application bundle (.app).
- MacOsBundle,
- /// The iOS app bundle.
- IosBundle,
- /// The Windows bundle (.msi).
- #[cfg(target_os = "windows")]
- WindowsMsi,
- /// The Linux Debian package bundle (.deb).
- Deb,
- /// The Linux RPM bundle (.rpm).
- Rpm,
- /// The Linux AppImage bundle (.AppImage).
- AppImage,
- /// The macOS DMG bundle (.dmg).
- Dmg,
- /// The Updater bundle.
- Updater,
- }
- impl PackageType {
- /// Maps a short name to a PackageType.
- /// Possible values are "deb", "ios", "msi", "app", "rpm", "appimage", "dmg", "updater".
- pub fn from_short_name(name: &str) -> Option<PackageType> {
- // Other types we may eventually want to support: apk.
- match name {
- "deb" => Some(PackageType::Deb),
- "ios" => Some(PackageType::IosBundle),
- #[cfg(target_os = "windows")]
- "msi" => Some(PackageType::WindowsMsi),
- "app" => Some(PackageType::MacOsBundle),
- "rpm" => Some(PackageType::Rpm),
- "appimage" => Some(PackageType::AppImage),
- "dmg" => Some(PackageType::Dmg),
- "updater" => Some(PackageType::Updater),
- _ => None,
- }
- }
- /// Gets the short name of this PackageType.
- #[allow(clippy::trivially_copy_pass_by_ref)]
- pub fn short_name(&self) -> &'static str {
- match *self {
- PackageType::Deb => "deb",
- PackageType::IosBundle => "ios",
- #[cfg(target_os = "windows")]
- PackageType::WindowsMsi => "msi",
- PackageType::MacOsBundle => "app",
- PackageType::Rpm => "rpm",
- PackageType::AppImage => "appimage",
- PackageType::Dmg => "dmg",
- PackageType::Updater => "updater",
- }
- }
- /// Gets the list of the possible package types.
- pub fn all() -> &'static [PackageType] {
- ALL_PACKAGE_TYPES
- }
- }
- const ALL_PACKAGE_TYPES: &[PackageType] = &[
- PackageType::Deb,
- PackageType::IosBundle,
- #[cfg(target_os = "windows")]
- PackageType::WindowsMsi,
- PackageType::MacOsBundle,
- PackageType::Rpm,
- PackageType::Dmg,
- PackageType::AppImage,
- PackageType::Updater,
- ];
- /// The package settings.
- #[derive(Debug, Clone)]
- pub struct PackageSettings {
- /// the package's product name.
- pub product_name: String,
- /// the package's version.
- pub version: String,
- /// the package's description.
- pub description: String,
- /// the package's homepage.
- pub homepage: Option<String>,
- /// the package's authors.
- pub authors: Option<Vec<String>>,
- /// the default binary to run.
- pub default_run: Option<String>,
- }
- /// The updater settings.
- #[derive(Debug, Clone)]
- pub struct UpdaterSettings {
- /// Whether the updater is active or not.
- pub active: bool,
- /// The updater endpoints.
- pub endpoints: Option<Vec<String>>,
- /// Optional pubkey.
- pub pubkey: Option<String>,
- /// Display built-in dialog or use event system if disabled.
- pub dialog: bool,
- }
- /// The Linux debian bundle settings.
- #[derive(Clone, Debug, Default)]
- pub struct DebianSettings {
- // OS-specific settings:
- /// the list of debian dependencies.
- pub depends: Option<Vec<String>>,
- /// whether we should use the bootstrap script on debian or not.
- ///
- /// this script goal is to allow your app to access environment variables e.g $PATH.
- ///
- /// without it, you can't run some applications installed by the user.
- pub use_bootstrapper: Option<bool>,
- /// List of custom files to add to the deb package.
- /// Maps the path on the debian package to the path of the file to include (relative to the current working directory).
- pub files: HashMap<PathBuf, PathBuf>,
- }
- /// The macOS bundle settings.
- #[derive(Clone, Debug, Default)]
- pub struct MacOsSettings {
- /// MacOS frameworks that need to be bundled with the app.
- ///
- /// Each string can either be the name of a framework (without the `.framework` extension, e.g. `"SDL2"`),
- /// in which case we will search for that framework in the standard install locations (`~/Library/Frameworks/`, `/Library/Frameworks/`, and `/Network/Library/Frameworks/`),
- /// 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
- /// (under `Foobar.app/Contents/Frameworks/`); you are still responsible for:
- ///
- /// - 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)
- ///
- /// - embedding the correct rpath in your binary (e.g. by running `install_name_tool -add_rpath "@executable_path/../Frameworks" path/to/binary` after compiling)
- pub frameworks: Option<Vec<String>>,
- /// A version string indicating the minimum MacOS version that the bundled app supports (e.g. `"10.11"`).
- /// 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`.
- pub minimum_system_version: Option<String>,
- /// The path to the LICENSE file for macOS apps.
- /// Currently only used by the dmg bundle.
- pub license: Option<String>,
- /// whether we should use the bootstrap script on macOS .app or not.
- ///
- /// this script goal is to allow your app to access environment variables e.g $PATH.
- ///
- /// without it, you can't run some applications installed by the user.
- pub use_bootstrapper: Option<bool>,
- /// The exception domain to use on the macOS .app bundle.
- ///
- /// This allows communication to the outside world e.g. a web server you're shipping.
- pub exception_domain: Option<String>,
- pub signing_identity: Option<String>,
- pub entitlements: Option<String>,
- }
- #[cfg(windows)]
- #[derive(Clone, Debug, Default)]
- pub struct WixSettings {
- /// By default, the bundler uses an internal template.
- /// This option allows you to define your own wix file.
- pub template: Option<PathBuf>,
- pub fragment_paths: Vec<PathBuf>,
- pub component_group_refs: Vec<String>,
- pub component_refs: Vec<String>,
- pub feature_group_refs: Vec<String>,
- pub feature_refs: Vec<String>,
- pub merge_refs: Vec<String>,
- pub skip_webview_install: bool,
- }
- /// The Windows bundle settings.
- #[cfg(windows)]
- #[derive(Clone, Debug, Default)]
- pub struct WindowsSettings {
- pub digest_algorithm: Option<String>,
- pub certificate_thumbprint: Option<String>,
- pub timestamp_url: Option<String>,
- pub wix: Option<WixSettings>,
- }
- /// The bundle settings of the BuildArtifact we're bundling.
- #[derive(Clone, Debug, Default)]
- pub struct BundleSettings {
- /// the app's identifier.
- pub identifier: Option<String>,
- /// the app's icon list.
- pub icon: Option<Vec<String>>,
- /// the app's resources to bundle.
- ///
- /// each item can be a path to a file or a path to a folder.
- ///
- /// supports glob patterns.
- pub resources: Option<Vec<String>>,
- /// the app's copyright.
- pub copyright: Option<String>,
- /// the app's category.
- pub category: Option<AppCategory>,
- /// the app's short description.
- pub short_description: Option<String>,
- /// the app's long description.
- pub long_description: Option<String>,
- // Bundles for other binaries:
- /// Configuration map for the possible [bin] apps to bundle.
- pub bin: Option<HashMap<String, BundleSettings>>,
- /// External binaries to add to the bundle.
- ///
- /// Note that each binary name will have the target platform's target triple appended,
- /// so if you're bundling the `sqlite3` app, the bundler will look for e.g.
- /// `sqlite3-x86_64-unknown-linux-gnu` on linux,
- /// and `sqlite3-x86_64-pc-windows-gnu.exe` on windows.
- ///
- /// The possible target triples can be seen by running `$ rustup target list`.
- pub external_bin: Option<Vec<String>>,
- /// Debian-specific settings.
- pub deb: DebianSettings,
- /// MacOS-specific settings.
- pub macos: MacOsSettings,
- // Updater configuration
- pub updater: Option<UpdaterSettings>,
- /// Windows-specific settings.
- #[cfg(windows)]
- pub windows: WindowsSettings,
- }
- #[derive(Clone, Debug)]
- pub struct BundleBinary {
- name: String,
- src_path: Option<String>,
- main: bool,
- }
- impl BundleBinary {
- pub fn new(name: String, main: bool) -> Self {
- Self {
- name: if cfg!(windows) {
- format!("{}.exe", name)
- } else {
- name
- },
- src_path: None,
- main,
- }
- }
- pub fn set_src_path(mut self, src_path: Option<String>) -> Self {
- self.src_path = src_path;
- self
- }
- pub fn set_main(&mut self, main: bool) {
- self.main = main;
- }
- pub fn set_name(&mut self, name: String) {
- self.name = name;
- }
- pub fn name(&self) -> &str {
- &self.name
- }
- #[cfg(windows)]
- pub fn main(&self) -> bool {
- self.main
- }
- pub fn src_path(&self) -> &Option<String> {
- &self.src_path
- }
- }
- /// The Settings exposed by the module.
- #[derive(Clone, Debug)]
- pub struct Settings {
- /// the package settings.
- package: PackageSettings,
- /// the package types we're bundling.
- ///
- /// if not present, we'll use the PackageType list for the target OS.
- package_types: Option<Vec<PackageType>>,
- /// the directory where the bundles will be placed.
- project_out_directory: PathBuf,
- /// whether or not to enable verbose logging
- is_verbose: bool,
- /// the bundle settings.
- bundle_settings: BundleSettings,
- /// the binaries to bundle.
- binaries: Vec<BundleBinary>,
- }
- #[derive(Default)]
- pub struct SettingsBuilder {
- project_out_directory: Option<PathBuf>,
- verbose: bool,
- package_types: Option<Vec<PackageType>>,
- package_settings: Option<PackageSettings>,
- bundle_settings: BundleSettings,
- binaries: Vec<BundleBinary>,
- }
- impl SettingsBuilder {
- pub fn new() -> Self {
- Default::default()
- }
- pub fn project_out_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
- self
- .project_out_directory
- .replace(path.as_ref().to_path_buf());
- self
- }
- pub fn verbose(mut self) -> Self {
- self.verbose = true;
- self
- }
- pub fn package_types(mut self, package_types: Vec<PackageType>) -> Self {
- self.package_types = Some(package_types);
- self
- }
- pub fn package_settings(mut self, settings: PackageSettings) -> Self {
- self.package_settings.replace(settings);
- self
- }
- pub fn bundle_settings(mut self, settings: BundleSettings) -> Self {
- self.bundle_settings = settings;
- self
- }
- pub fn binaries(mut self, binaries: Vec<BundleBinary>) -> Self {
- self.binaries = binaries;
- self
- }
- /// Builds a Settings from the CLI args.
- ///
- /// Package settings will be read from Cargo.toml.
- ///
- /// Bundle settings will be read from from $TAURI_DIR/tauri.conf.json if it exists and fallback to Cargo.toml's [package.metadata.bundle].
- pub fn build(self) -> crate::Result<Settings> {
- let bundle_settings = parse_external_bin(self.bundle_settings)?;
- Ok(Settings {
- package: self.package_settings.expect("package settings is required"),
- package_types: self.package_types,
- is_verbose: self.verbose,
- project_out_directory: self
- .project_out_directory
- .expect("out directory is required"),
- binaries: self.binaries,
- bundle_settings,
- })
- }
- }
- impl Settings {
- /// Returns the directory where the bundle should be placed.
- pub fn project_out_directory(&self) -> &Path {
- &self.project_out_directory
- }
- /// Returns the architecture for the binary being bundled (e.g. "arm", "x86" or "x86_64").
- pub fn binary_arch(&self) -> &str {
- std::env::consts::ARCH
- }
- /// Returns the file name of the binary being bundled.
- pub fn main_binary_name(&self) -> &str {
- self
- .binaries
- .iter()
- .find(|bin| bin.main)
- .expect("failed to find main binary")
- .name
- .as_str()
- }
- /// Returns the path to the specified binary.
- pub fn binary_path(&self, binary: &BundleBinary) -> PathBuf {
- let mut path = self.project_out_directory.clone();
- path.push(binary.name());
- path
- }
- pub fn binaries(&self) -> &Vec<BundleBinary> {
- &self.binaries
- }
- /// If a list of package types was specified by the command-line, returns
- /// that list filtered by the current target OS available targets.
- ///
- /// If a target triple was specified by the
- /// command-line, returns the native package type(s) for that target.
- ///
- /// Otherwise returns the native package type(s) for the host platform.
- ///
- /// Fails if the host/target's native package type is not supported.
- pub fn package_types(&self) -> crate::Result<Vec<PackageType>> {
- let target_os = std::env::consts::OS;
- let mut platform_types = match target_os {
- "macos" => vec![PackageType::MacOsBundle, PackageType::Dmg],
- "ios" => vec![PackageType::IosBundle],
- "linux" => vec![PackageType::Deb, PackageType::AppImage],
- #[cfg(target_os = "windows")]
- "windows" => vec![PackageType::WindowsMsi],
- os => {
- return Err(crate::Error::GenericError(format!(
- "Native {} bundles not yet supported.",
- os
- )))
- }
- };
- // add updater if needed
- if self.is_update_enabled() {
- platform_types.push(PackageType::Updater)
- }
- if let Some(package_types) = &self.package_types {
- let mut types = vec![];
- for package_type in package_types {
- let package_type = *package_type;
- if platform_types
- .clone()
- .into_iter()
- .any(|t| t == package_type)
- {
- types.push(package_type);
- }
- }
- Ok(types)
- } else {
- Ok(platform_types)
- }
- }
- /// Returns true if verbose logging is enabled
- pub fn is_verbose(&self) -> bool {
- self.is_verbose
- }
- /// Returns the product name.
- pub fn product_name(&self) -> &str {
- &self.package.product_name
- }
- /// Returns the bundle's identifier
- pub fn bundle_identifier(&self) -> &str {
- self.bundle_settings.identifier.as_deref().unwrap_or("")
- }
- /// Returns an iterator over the icon files to be used for this bundle.
- pub fn icon_files(&self) -> ResourcePaths<'_> {
- match self.bundle_settings.icon {
- Some(ref paths) => ResourcePaths::new(paths.as_slice(), false),
- None => ResourcePaths::new(&[], false),
- }
- }
- /// Returns an iterator over the resource files to be included in this
- /// bundle.
- pub fn resource_files(&self) -> ResourcePaths<'_> {
- match self.bundle_settings.resources {
- Some(ref paths) => ResourcePaths::new(paths.as_slice(), true),
- None => ResourcePaths::new(&[], true),
- }
- }
- /// Returns an iterator over the external binaries to be included in this
- /// bundle.
- pub fn external_binaries(&self) -> ResourcePaths<'_> {
- match self.bundle_settings.external_bin {
- Some(ref paths) => ResourcePaths::new(paths.as_slice(), true),
- None => ResourcePaths::new(&[], true),
- }
- }
- /// Copies external binaries to a path.
- pub fn copy_binaries(&self, path: &Path) -> crate::Result<()> {
- for src in self.external_binaries() {
- let src = src?;
- let dest = path.join(
- src
- .file_name()
- .expect("failed to extract external binary filename"),
- );
- common::copy_file(&src, &dest)?;
- }
- Ok(())
- }
- /// Copies resources to a path.
- pub fn copy_resources(&self, path: &Path) -> crate::Result<()> {
- for src in self.resource_files() {
- let src = src?;
- let dest = path.join(common::resource_relpath(&src));
- common::copy_file(&src, &dest)?;
- }
- Ok(())
- }
- /// Returns the version string of the bundle.
- pub fn version_string(&self) -> &str {
- &self.package.version
- }
- /// Returns the copyright text.
- pub fn copyright_string(&self) -> Option<&str> {
- self.bundle_settings.copyright.as_deref()
- }
- /// Returns the list of authors name.
- pub fn author_names(&self) -> &[String] {
- match self.package.authors {
- Some(ref names) => names.as_slice(),
- None => &[],
- }
- }
- /// Returns the authors as a comma-separated string.
- pub fn authors_comma_separated(&self) -> Option<String> {
- let names = self.author_names();
- if names.is_empty() {
- None
- } else {
- Some(names.join(", "))
- }
- }
- /// Returns the package's homepage URL, defaulting to "" if not defined.
- pub fn homepage_url(&self) -> &str {
- &self.package.homepage.as_deref().unwrap_or("")
- }
- /// Returns the app's category.
- pub fn app_category(&self) -> Option<AppCategory> {
- self.bundle_settings.category
- }
- /// Returns the app's short description.
- pub fn short_description(&self) -> &str {
- self
- .bundle_settings
- .short_description
- .as_ref()
- .unwrap_or(&self.package.description)
- }
- /// Returns the app's long description.
- pub fn long_description(&self) -> Option<&str> {
- self.bundle_settings.long_description.as_deref()
- }
- /// Returns the debian settings.
- pub fn deb(&self) -> &DebianSettings {
- &self.bundle_settings.deb
- }
- /// Returns the MacOS settings.
- pub fn macos(&self) -> &MacOsSettings {
- &self.bundle_settings.macos
- }
- /// Returns the Windows settings.
- #[cfg(windows)]
- pub fn windows(&self) -> &WindowsSettings {
- &self.bundle_settings.windows
- }
- /// Is update enabled
- pub fn is_update_enabled(&self) -> bool {
- match &self.bundle_settings.updater {
- Some(val) => val.active,
- None => false,
- }
- }
- /// Is pubkey provided?
- pub fn is_updater_pubkey(&self) -> bool {
- match &self.bundle_settings.updater {
- Some(val) => val.pubkey.is_some(),
- None => false,
- }
- }
- /// Get pubkey (mainly for testing)
- #[cfg(test)]
- pub fn updater_pubkey(&self) -> Option<&str> {
- self
- .bundle_settings
- .updater
- .as_ref()
- .expect("Updater is not defined")
- .pubkey
- .as_deref()
- }
- }
- /// Parses the external binaries to bundle, adding the target triple suffix to each of them.
- fn parse_external_bin(bundle_settings: BundleSettings) -> crate::Result<BundleSettings> {
- let target_triple = target_triple()?;
- let mut win_paths = Vec::new();
- let external_bin = match bundle_settings.external_bin {
- Some(paths) => {
- for curr_path in paths.iter() {
- win_paths.push(format!(
- "{}-{}{}",
- curr_path,
- target_triple,
- if cfg!(windows) { ".exe" } else { "" }
- ));
- }
- Some(win_paths)
- }
- None => Some(vec![]),
- };
- Ok(BundleSettings {
- external_bin,
- ..bundle_settings
- })
- }
- /// A helper to iterate through resources.
- pub struct ResourcePaths<'a> {
- /// the patterns to iterate.
- pattern_iter: std::slice::Iter<'a, String>,
- /// the glob iterator if the path from the current iteration is a glob pattern.
- glob_iter: Option<glob::Paths>,
- /// the walkdir iterator if the path from the current iteration is a directory.
- walk_iter: Option<walkdir::IntoIter>,
- /// whether the resource paths allows directories or not.
- allow_walk: bool,
- /// the pattern of the current iteration.
- current_pattern: Option<String>,
- /// whether the current pattern is valid or not.
- current_pattern_is_valid: bool,
- }
- impl<'a> ResourcePaths<'a> {
- /// Creates a new ResourcePaths from a slice of patterns to iterate
- fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
- ResourcePaths {
- pattern_iter: patterns.iter(),
- glob_iter: None,
- walk_iter: None,
- allow_walk,
- current_pattern: None,
- current_pattern_is_valid: false,
- }
- }
- }
- impl<'a> Iterator for ResourcePaths<'a> {
- type Item = crate::Result<PathBuf>;
- fn next(&mut self) -> Option<crate::Result<PathBuf>> {
- loop {
- if let Some(ref mut walk_entries) = self.walk_iter {
- if let Some(entry) = walk_entries.next() {
- let entry = match entry {
- Ok(entry) => entry,
- Err(error) => return Some(Err(crate::Error::from(error))),
- };
- let path = entry.path();
- if path.is_dir() {
- continue;
- }
- self.current_pattern_is_valid = true;
- return Some(Ok(path.to_path_buf()));
- }
- }
- self.walk_iter = None;
- if let Some(ref mut glob_paths) = self.glob_iter {
- if let Some(glob_result) = glob_paths.next() {
- let path = match glob_result {
- Ok(path) => path,
- Err(error) => return Some(Err(crate::Error::from(error))),
- };
- if path.is_dir() {
- if self.allow_walk {
- let walk = walkdir::WalkDir::new(path);
- self.walk_iter = Some(walk.into_iter());
- continue;
- } else {
- let msg = format!("{:?} is a directory", path);
- return Some(Err(crate::Error::GenericError(msg)));
- }
- }
- self.current_pattern_is_valid = true;
- return Some(Ok(path));
- } else if let Some(current_path) = &self.current_pattern {
- if !self.current_pattern_is_valid {
- return Some(Err(crate::Error::GenericError(format!(
- "Path matching '{}' not found",
- current_path
- ))));
- }
- }
- }
- self.glob_iter = None;
- if let Some(pattern) = self.pattern_iter.next() {
- self.current_pattern = Some(pattern.to_string());
- self.current_pattern_is_valid = false;
- let glob = match glob::glob(pattern) {
- Ok(glob) => glob,
- Err(error) => return Some(Err(crate::Error::from(error))),
- };
- self.glob_iter = Some(glob);
- continue;
- }
- return None;
- }
- }
- }
|