Explorar el Código

feat(tauri) bundle formats config on tauri.js, fix bundler appim… (#537)

* feat(tauri) bundle formats config on tauri.js, fix bundler appimage

* fix(bundler) dmg chmod correctly
Lucas Fernandes Nogueira hace 5 años
padre
commit
f24f7e18f3

+ 2 - 7
cli/tauri-bundler/Cargo.toml

@@ -32,14 +32,14 @@ term = "0.6.1"
 toml = "0.5.6"
 uuid = { version = "0.8", features = ["v5"] }
 walkdir = "2"
+lazy_static = { version = "1.4" }
+handlebars = { version = "3.0" }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 attohttpc = { version = "0.12.0" }
 regex = { version = "1" }
 
 [target.'cfg(not(target_os = "linux"))'.dependencies]
-handlebars = { version = "3.0" }
-lazy_static = { version = "1.4" }
 zip = { version = "0.5" }
 sha2 = { version = "0.8" }
 hex = { version = "0.4" }
@@ -50,8 +50,3 @@ tempfile = "3"
 [[bin]]
 name = "cargo-tauri-bundler"
 path = "src/main.rs"
-
-[features]
-appimage = []
-ios = []
-dmg = []

+ 0 - 10
cli/tauri-bundler/src/bundle.rs

@@ -1,11 +1,8 @@
-#[cfg(feature = "appimage")]
 mod appimage_bundle;
 mod category;
 mod common;
 mod deb_bundle;
-#[cfg(feature = "dmg")]
 mod dmg_bundle;
-#[cfg(feature = "ios")]
 mod ios_bundle;
 #[cfg(target_os = "windows")]
 mod msi_bundle;
@@ -28,19 +25,12 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<PathBuf>> {
   for package_type in settings.package_types()? {
     paths.append(&mut match package_type {
       PackageType::OsxBundle => osx_bundle::bundle_project(&settings)?,
-      #[cfg(feature = "ios")]
       PackageType::IosBundle => ios_bundle::bundle_project(&settings)?,
-      // use dmg bundler
-      // PackageType::OsxBundle => dmg_bundle::bundle_project(&settings)?,
       #[cfg(target_os = "windows")]
       PackageType::WindowsMsi => msi_bundle::bundle_project(&settings)?,
-      // force appimage on linux
-      // PackageType::Deb => appimage_bundle::bundle_project(&settings)?,
       PackageType::Deb => deb_bundle::bundle_project(&settings)?,
       PackageType::Rpm => rpm_bundle::bundle_project(&settings)?,
-      #[cfg(feature = "appimage")]
       PackageType::AppImage => appimage_bundle::bundle_project(&settings)?,
-      #[cfg(feature = "dmg")]
       PackageType::Dmg => dmg_bundle::bundle_project(&settings)?,
     });
   }

+ 13 - 21
cli/tauri-bundler/src/bundle/appimage_bundle.rs

@@ -40,20 +40,19 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
   );
   let base_dir = settings.project_out_directory().join("bundle/deb");
   let package_dir = base_dir.join(&package_base_name);
-  if package_dir.exists() {
-    remove_dir_all(&package_dir)
-      .chain_err(|| format!("Failed to remove old {}", package_base_name))?;
-  }
 
   // generate deb_folder structure
   deb_bundle::generate_folders(settings, &package_dir)?;
 
-  let _app_dir_path = path_utils::create(
-    settings
-      .project_out_directory()
-      .join(format!("{}.AppDir", settings.binary_name())),
-    true,
-  );
+  let output_path = settings.project_out_directory().join("bundle/appimage");
+  if output_path.exists() {
+    remove_dir_all(&output_path)
+      .chain_err(|| format!("Failed to remove old {}", package_base_name))?;
+  }
+  std::fs::create_dir_all(output_path.clone())?;
+  let app_dir_path = output_path.join(format!("{}.AppDir", settings.binary_name()));
+  let appimage_path = output_path.join(format!("{}.AppImage", settings.binary_name()));
+  path_utils::create(app_dir_path.clone(), true)?;
 
   let upcase = settings.binary_name().to_uppercase();
 
@@ -67,27 +66,20 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
   let temp = HANDLEBARS
     .render("appimage", &sh_map)
     .or_else(|e| Err(e.to_string()))?;
-  let output_path = settings.project_out_directory();
 
   // create the shell script file in the target/ folder.
   let sh_file = output_path.join("build_appimage");
-  common::print_bundling(
-    format!(
-      "{:?}",
-      &output_path.join(format!("{}.AppImage", settings.binary_name()))
-    )
-    .as_str(),
-  )?;
+  common::print_bundling(format!("{:?}", &appimage_path).as_str())?;
   write(&sh_file, temp).or_else(|e| Err(e.to_string()))?;
 
   // chmod script for execution
   Command::new("chmod")
     .arg("777")
     .arg(&sh_file)
-    .current_dir(output_path)
+    .current_dir(output_path.clone())
     .stdout(Stdio::piped())
     .stderr(Stdio::piped())
-    .spawn()
+    .output()
     .expect("Failed to chmod script");
 
   // execute the shell script to build the appimage.
@@ -98,5 +90,5 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
     .spawn()
     .expect("Failed to execute shell script");
 
-  Ok(vec![sh_file])
+  Ok(vec![appimage_path])
 }

+ 2 - 2
cli/tauri-bundler/src/bundle/deb_bundle.rs

@@ -23,7 +23,7 @@ use crate::{ResultExt, Settings};
 
 use ar;
 use icns;
-use image::png::{PngDecoder};
+use image::png::PngDecoder;
 use image::{self, GenericImageView, ImageDecoder};
 use libflate::gzip;
 use md5;
@@ -334,7 +334,7 @@ fn generate_icon_files(settings: &Settings, data_dir: &PathBuf) -> crate::Result
         let dest_path = get_dest_path(width, height, is_high_density);
         icon.write_to(
           &mut common::create_file(&dest_path)?,
-          image::ImageOutputFormat::Png
+          image::ImageOutputFormat::Png,
         )?;
       }
     }

+ 1 - 1
cli/tauri-bundler/src/bundle/dmg_bundle.rs

@@ -68,7 +68,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
     .current_dir(output_path)
     .stdout(Stdio::piped())
     .stderr(Stdio::piped())
-    .spawn()
+    .output()
     .expect("Failed to chmod script");
 
   // execute the bundle script

+ 128 - 96
cli/tauri-bundler/src/bundle/ios_bundle.rs

@@ -12,143 +12,175 @@ use super::common;
 use crate::{ResultExt, Settings};
 
 use icns;
-use image::png::{PNGDecoder, PNGEncoder};
+use image::png::PngDecoder;
 use image::{self, GenericImageView, ImageDecoder};
 
 use std::collections::BTreeSet;
-use std::convert::TryInto;
 use std::ffi::OsStr;
 use std::fs::{self, File};
 use std::io::Write;
 use std::path::{Path, PathBuf};
 
-
-pub fn bundle_project(settings: &Settings) -> ::Result<Vec<PathBuf>> {
+pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
   common::print_warning("iOS bundle support is still experimental.")?;
 
   let app_bundle_name = format!("{}.app", settings.bundle_name());
   common::print_bundling(&app_bundle_name)?;
-  let bundle_dir = settings.project_out_directory().join("bundle/ios").join(&app_bundle_name);
+  let bundle_dir = settings
+    .project_out_directory()
+    .join("bundle/ios")
+    .join(&app_bundle_name);
   if bundle_dir.exists() {
-      fs::remove_dir_all(&bundle_dir).chain_err(|| {
-          format!("Failed to remove old {}", app_bundle_name)
-      })?;
+    fs::remove_dir_all(&bundle_dir)
+      .chain_err(|| format!("Failed to remove old {}", app_bundle_name))?;
   }
-  fs::create_dir_all(&bundle_dir).chain_err(|| {
-      format!("Failed to create bundle directory at {:?}", bundle_dir)
-  })?;
+  fs::create_dir_all(&bundle_dir)
+    .chain_err(|| format!("Failed to create bundle directory at {:?}", bundle_dir))?;
 
   for src in settings.resource_files() {
-      let src = src?;
-      let dest = bundle_dir.join(common::resource_relpath(&src));
-      common::copy_file(&src, &dest).chain_err(|| {
-          format!("Failed to copy resource file {:?}", src)
-      })?;
+    let src = src?;
+    let dest = bundle_dir.join(common::resource_relpath(&src));
+    common::copy_file(&src, &dest)
+      .chain_err(|| format!("Failed to copy resource file {:?}", src))?;
   }
 
-  let icon_filenames = generate_icon_files(&bundle_dir, settings).chain_err(|| {
-      "Failed to create app icons"
-  })?;
-  generate_info_plist(&bundle_dir, settings, &icon_filenames).chain_err(|| {
-      "Failed to create Info.plist"
-  })?;
+  let icon_filenames =
+    generate_icon_files(&bundle_dir, settings).chain_err(|| "Failed to create app icons")?;
+  generate_info_plist(&bundle_dir, settings, &icon_filenames)
+    .chain_err(|| "Failed to create Info.plist")?;
   let bin_path = bundle_dir.join(&settings.bundle_name());
-  common::copy_file(settings.binary_path(), &bin_path).chain_err(|| {
-      format!("Failed to copy binary from {:?}", settings.binary_path())
-  })?;
+  common::copy_file(settings.binary_path(), &bin_path)
+    .chain_err(|| format!("Failed to copy binary from {:?}", settings.binary_path()))?;
   Ok(vec![bundle_dir])
 }
 
 /// Generate the icon files and store them under the `bundle_dir`.
-fn generate_icon_files(bundle_dir: &Path, settings: &Settings) -> ::Result<Vec<String>> {
+fn generate_icon_files(bundle_dir: &Path, settings: &Settings) -> crate::Result<Vec<String>> {
   let mut filenames = Vec::new();
   {
-      let mut get_dest_path = |width: u32, height: u32, is_retina: bool| {
-          let filename = format!("icon_{}x{}{}.png",
-                                 width,
-                                 height,
-                                 if is_retina { "@2x" } else { "" });
-          let path = bundle_dir.join(&filename);
-          filenames.push(filename);
-          path
-      };
-      let mut sizes = BTreeSet::new();
-      // Prefer PNG files.
-      for icon_path in settings.icon_files() {
-          let icon_path = icon_path?;
-          if icon_path.extension() != Some(OsStr::new("png")) {
-              continue;
-          }
-          let mut decoder = PNGDecoder::new(File::open(&icon_path)?);
-          let (width, height) = decoder.dimensions()?;
-          let is_retina = common::is_retina(&icon_path);
-          if !sizes.contains(&(width, height, is_retina)) {
-              sizes.insert((width, height, is_retina));
-              let dest_path = get_dest_path(width, height, is_retina);
-              common::copy_file(&icon_path, &dest_path)?;
-          }
+    let mut get_dest_path = |width: u32, height: u32, is_retina: bool| {
+      let filename = format!(
+        "icon_{}x{}{}.png",
+        width,
+        height,
+        if is_retina { "@2x" } else { "" }
+      );
+      let path = bundle_dir.join(&filename);
+      filenames.push(filename);
+      path
+    };
+    let mut sizes = BTreeSet::new();
+    // Prefer PNG files.
+    for icon_path in settings.icon_files() {
+      let icon_path = icon_path?;
+      if icon_path.extension() != Some(OsStr::new("png")) {
+        continue;
+      }
+      let decoder = PngDecoder::new(File::open(&icon_path)?)?;
+      let width = decoder.dimensions().0;
+      let height = decoder.dimensions().1;
+      let is_retina = common::is_retina(&icon_path);
+      if !sizes.contains(&(width, height, is_retina)) {
+        sizes.insert((width, height, is_retina));
+        let dest_path = get_dest_path(width, height, is_retina);
+        common::copy_file(&icon_path, &dest_path)?;
       }
-      // Fall back to non-PNG files for any missing sizes.
-      for icon_path in settings.icon_files() {
-          let icon_path = icon_path?;
-          if icon_path.extension() == Some(OsStr::new("png")) {
-              continue;
-          } else if icon_path.extension() == Some(OsStr::new("icns")) {
-              let icon_family = icns::IconFamily::read(File::open(&icon_path)?)?;
-              for icon_type in icon_family.available_icons() {
-                  let width = icon_type.screen_width();
-                  let height = icon_type.screen_height();
-                  let is_retina = icon_type.pixel_density() > 1;
-                  if !sizes.contains(&(width, height, is_retina)) {
-                      sizes.insert((width, height, is_retina));
-                      let dest_path = get_dest_path(width, height, is_retina);
-                      let icon = icon_family.get_icon_with_type(icon_type)?;
-                      icon.write_png(File::create(dest_path)?)?;
-                  }
-              }
-          } else {
-              let icon = try!(image::open(&icon_path));
-              let (width, height) = icon.dimensions();
-              let is_retina = common::is_retina(&icon_path);
-              if !sizes.contains(&(width, height, is_retina)) {
-                  sizes.insert((width, height, is_retina));
-                  let dest_path = get_dest_path(width, height, is_retina);
-                  let encoder = PNGEncoder::new(common::create_file(&dest_path)?);
-                  encoder.encode(&icon.raw_pixels(), width, height, icon.color())?;
-              }
+    }
+    // Fall back to non-PNG files for any missing sizes.
+    for icon_path in settings.icon_files() {
+      let icon_path = icon_path?;
+      if icon_path.extension() == Some(OsStr::new("png")) {
+        continue;
+      } else if icon_path.extension() == Some(OsStr::new("icns")) {
+        let icon_family = icns::IconFamily::read(File::open(&icon_path)?)?;
+        for icon_type in icon_family.available_icons() {
+          let width = icon_type.screen_width();
+          let height = icon_type.screen_height();
+          let is_retina = icon_type.pixel_density() > 1;
+          if !sizes.contains(&(width, height, is_retina)) {
+            sizes.insert((width, height, is_retina));
+            let dest_path = get_dest_path(width, height, is_retina);
+            let icon = icon_family.get_icon_with_type(icon_type)?;
+            icon.write_png(File::create(dest_path)?)?;
           }
+        }
+      } else {
+        let icon = image::open(&icon_path)?;
+        let (width, height) = icon.dimensions();
+        let is_retina = common::is_retina(&icon_path);
+        if !sizes.contains(&(width, height, is_retina)) {
+          sizes.insert((width, height, is_retina));
+          let dest_path = get_dest_path(width, height, is_retina);
+          icon.write_to(
+            &mut common::create_file(&dest_path)?,
+            image::ImageOutputFormat::Png,
+          )?;
+        }
       }
+    }
   }
   Ok(filenames)
 }
 
-fn generate_info_plist(bundle_dir: &Path, settings: &Settings, icon_filenames: &Vec<String>) -> ::Result<()> {
+fn generate_info_plist(
+  bundle_dir: &Path,
+  settings: &Settings,
+  icon_filenames: &Vec<String>,
+) -> crate::Result<()> {
   let file = &mut common::create_file(&bundle_dir.join("Info.plist"))?;
-  write!(file,
-         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
+  write!(
+    file,
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
           <!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \
           \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
           <plist version=\"1.0\">\n\
-          <dict>\n")?;
-
-  write!(file, "  <key>CFBundleIdentifier</key>\n  <string>{}</string>\n", settings.bundle_identifier())?;
-  write!(file, "  <key>CFBundleDisplayName</key>\n  <string>{}</string>\n", settings.bundle_name())?;
-  write!(file, "  <key>CFBundleName</key>\n  <string>{}</string>\n", settings.bundle_name())?;
-  write!(file, "  <key>CFBundleExecutable</key>\n  <string>{}</string>\n", settings.binary_name())?;
-  write!(file, "  <key>CFBundleVersion</key>\n  <string>{}</string>\n", settings.version_string())?;
-  write!(file, "  <key>CFBundleShortVersionString</key>\n  <string>{}</string>\n", settings.version_string())?;
-  write!(file, "  <key>CFBundleDevelopmentRegion</key>\n  <string>en_US</string>\n")?;
+          <dict>\n"
+  )?;
 
+  write!(
+    file,
+    "  <key>CFBundleIdentifier</key>\n  <string>{}</string>\n",
+    settings.bundle_identifier()
+  )?;
+  write!(
+    file,
+    "  <key>CFBundleDisplayName</key>\n  <string>{}</string>\n",
+    settings.bundle_name()
+  )?;
+  write!(
+    file,
+    "  <key>CFBundleName</key>\n  <string>{}</string>\n",
+    settings.bundle_name()
+  )?;
+  write!(
+    file,
+    "  <key>CFBundleExecutable</key>\n  <string>{}</string>\n",
+    settings.binary_name()
+  )?;
+  write!(
+    file,
+    "  <key>CFBundleVersion</key>\n  <string>{}</string>\n",
+    settings.version_string()
+  )?;
+  write!(
+    file,
+    "  <key>CFBundleShortVersionString</key>\n  <string>{}</string>\n",
+    settings.version_string()
+  )?;
+  write!(
+    file,
+    "  <key>CFBundleDevelopmentRegion</key>\n  <string>en_US</string>\n"
+  )?;
 
   if !icon_filenames.is_empty() {
-      write!(file, "  <key>CFBundleIconFiles</key>\n  <array>\n")?;
-      for filename in icon_filenames {
-          write!(file, "    <string>{}</string>\n", filename)?;
-      }
-      write!(file, "  </array>\n")?;
+    write!(file, "  <key>CFBundleIconFiles</key>\n  <array>\n")?;
+    for filename in icon_filenames {
+      write!(file, "    <string>{}</string>\n", filename)?;
+    }
+    write!(file, "  </array>\n")?;
   }
   write!(file, "  <key>LSRequiresIPhoneOS</key>\n  <true/>\n")?;
   write!(file, "</dict>\n</plist>\n")?;
   file.flush()?;
   Ok(())
-}
+}

+ 5 - 1
cli/tauri-bundler/src/bundle/osx_bundle.rs

@@ -375,7 +375,11 @@ fn create_icns_file(
   }
 
   for (icon, next_size_down, density) in images_to_resize {
-    let icon = icon.resize_exact(next_size_down, next_size_down, image::imageops::FilterType::Lanczos3);
+    let icon = icon.resize_exact(
+      next_size_down,
+      next_size_down,
+      image::imageops::FilterType::Lanczos3,
+    );
     add_icon_to_family(icon, density, &mut family)?;
   }
 

+ 45 - 35
cli/tauri-bundler/src/bundle/settings.rs

@@ -19,15 +19,12 @@ use std::path::{Path, PathBuf};
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum PackageType {
   OsxBundle,
-  #[cfg(feature = "ios")]
   IosBundle,
   #[cfg(target_os = "windows")]
   WindowsMsi,
   Deb,
   Rpm,
-  #[cfg(feature = "appimage")]
   AppImage,
-  #[cfg(feature = "dmg")]
   Dmg,
 }
 
@@ -36,15 +33,12 @@ impl PackageType {
     // Other types we may eventually want to support: apk
     match name {
       "deb" => Some(PackageType::Deb),
-      #[cfg(feature = "ios")]
       "ios" => Some(PackageType::IosBundle),
       #[cfg(target_os = "windows")]
       "msi" => Some(PackageType::WindowsMsi),
       "osx" => Some(PackageType::OsxBundle),
       "rpm" => Some(PackageType::Rpm),
-      #[cfg(feature = "appimage")]
       "appimage" => Some(PackageType::AppImage),
-      #[cfg(feature = "dmg")]
       "dmg" => Some(PackageType::Dmg),
       _ => None,
     }
@@ -53,15 +47,12 @@ impl PackageType {
   pub fn short_name(&self) -> &'static str {
     match *self {
       PackageType::Deb => "deb",
-      #[cfg(feature = "ios")]
       PackageType::IosBundle => "ios",
       #[cfg(target_os = "windows")]
       PackageType::WindowsMsi => "msi",
       PackageType::OsxBundle => "osx",
       PackageType::Rpm => "rpm",
-      #[cfg(feature = "appimage")]
       PackageType::AppImage => "appimage",
-      #[cfg(feature = "dmg")]
       PackageType::Dmg => "dmg",
     }
   }
@@ -73,14 +64,13 @@ impl PackageType {
 
 const ALL_PACKAGE_TYPES: &[PackageType] = &[
   PackageType::Deb,
-  #[cfg(feature = "ios")]
   PackageType::IosBundle,
   #[cfg(target_os = "windows")]
   PackageType::WindowsMsi,
   PackageType::OsxBundle,
   PackageType::Rpm,
-  #[cfg(feature = "dmg")]
   PackageType::Dmg,
+  PackageType::AppImage,
 ];
 
 #[derive(Clone, Debug)]
@@ -143,7 +133,7 @@ struct CargoSettings {
 #[derive(Clone, Debug)]
 pub struct Settings {
   package: PackageSettings,
-  package_type: Option<PackageType>, // If `None`, use the default package type for this os
+  package_types: Option<Vec<PackageType>>, // If `None`, use the default package type for this os
   target: Option<(String, TargetInfo)>,
   features: Option<Vec<String>>,
   project_out_directory: PathBuf,
@@ -169,11 +159,19 @@ impl CargoSettings {
 
 impl Settings {
   pub fn new(current_dir: PathBuf, matches: &ArgMatches<'_>) -> crate::Result<Self> {
-    let package_type = match matches.value_of("format") {
-      Some(name) => match PackageType::from_short_name(name) {
-        Some(package_type) => Some(package_type),
-        None => bail!("Unsupported bundle format: {}", name),
-      },
+    let package_types = match matches.values_of("format") {
+      Some(names) => {
+        let mut types = vec![];
+        for name in names {
+          match PackageType::from_short_name(name) {
+            Some(package_type) => {
+              types.push(package_type);
+            }
+            None => bail!("Unsupported bundle format: {}", name),
+          }
+        }
+        Some(types)
+      }
       None => None,
     };
     let build_artifact = if let Some(bin) = matches.value_of("bin") {
@@ -248,7 +246,7 @@ impl Settings {
 
     Ok(Settings {
       package,
-      package_type,
+      package_types,
       target,
       features,
       build_artifact,
@@ -350,29 +348,41 @@ impl Settings {
     &self.binary_path
   }
 
-  /// If a specific package type was specified by the command-line, returns
-  /// that package type; otherwise, if a target triple was specified by the
+  /// If a list of package types was specified by the command-line, returns
+  /// that list filtered by the current target's available targets;
+  /// otherwise, 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>> {
-    if let Some(package_type) = self.package_type {
-      Ok(vec![package_type])
+    let target_os = if let Some((_, ref info)) = self.target {
+      info.target_os()
     } else {
-      let target_os = if let Some((_, ref info)) = self.target {
-        info.target_os()
-      } else {
-        std::env::consts::OS
-      };
-      match target_os {
-        "macos" => Ok(vec![PackageType::OsxBundle]),
-        #[cfg(feature = "ios")]
-        "ios" => Ok(vec![PackageType::IosBundle]),
-        "linux" => Ok(vec![PackageType::Deb]), // TODO: Do Rpm too, once it's implemented.
-        #[cfg(target_os = "windows")]
-        "windows" => Ok(vec![PackageType::WindowsMsi]),
-        os => bail!("Native {} bundles not yet supported.", os),
+      std::env::consts::OS
+    };
+    let platform_types = match target_os {
+      "macos" => vec![PackageType::OsxBundle, PackageType::Dmg],
+      "ios" => vec![PackageType::IosBundle],
+      "linux" => vec![PackageType::Deb, PackageType::AppImage],
+      #[cfg(target_os = "windows")]
+      "windows" => vec![PackageType::WindowsMsi],
+      os => bail!("Native {} bundles not yet supported.", os),
+    };
+    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)
     }
   }
 

+ 2 - 2
cli/tauri-bundler/src/bundle/tauri_config.rs

@@ -1,5 +1,5 @@
-use serde::Deserialize;
 use super::category::AppCategory;
+use serde::Deserialize;
 use std::path::PathBuf;
 
 use std::fs;
@@ -67,4 +67,4 @@ pub fn get() -> crate::Result<Config> {
       None => Err(crate::Error::from("Couldn't get tauri config; please specify the TAURI_CONFIG or TAURI_DIR environment variables"))
     }
   }
-}
+}

+ 1 - 0
cli/tauri-bundler/src/main.rs

@@ -97,6 +97,7 @@ fn run() -> crate::Result<()> {
             .long("format")
             .value_name("FORMAT")
             .possible_values(&all_formats)
+            .multiple(true)
             .help("Which bundle format to produce"),
         )
         .arg(

+ 6 - 1
cli/tauri.js/src/runner.ts

@@ -164,7 +164,12 @@ class Runner {
         cargoArgs: [
           cfg.tauri.bundle.active ? 'tauri-bundler' : 'build',
           '--features',
-          ...features
+          ...features,
+          ...(
+            cfg.tauri.bundle.active && Array.isArray(cfg.tauri.bundle.targets) && cfg.tauri.bundle.targets.length
+              ? ['--format'].concat(cfg.tauri.bundle.targets)
+              : []
+            )
         ]
           .concat(cfg.ctx.debug ? [] : ['--release'])
           .concat(target ? ['--target', target] : [])

+ 1 - 0
cli/tauri.js/src/template/defaultConfig.ts

@@ -12,6 +12,7 @@ export default {
     },
     bundle: {
       active: true,
+      targets: 'all', // or an array of targets
       identifier: 'com.tauri.dev',
       icon: ['icons/32x32.png', 'icons/128x128.png', 'icons/128x128@2x.png', 'icons/icon.icns', 'icons/icon.ico'],
       resources: [],

+ 17 - 1
cli/tauri.js/src/types/config.ts

@@ -16,7 +16,6 @@ export interface TauriConfig {
     targetName: string
     exitOnPanic?: boolean
   }
-  bundle: {}
   tauri: {
     inlinedAssets: string[]
     devPath: string
@@ -25,6 +24,23 @@ export interface TauriConfig {
     }
     bundle: {
       active: boolean
+      targets?: string | string[]
+      identifier: string
+      icon: string[]
+      resources?: string[]
+      externalBin?: string[]
+      copyright?: string
+      category: string
+      shortDescription?: string
+      longDescription?: string
+      deb?: {
+        depends?: string[]
+      }
+      osx?: {
+        frameworks?: string[]
+        minimumSystemVersion?: string
+      }
+      exceptionDomain?: string
     }
     whitelist: {
       all: boolean