feat(bundler): implement wix fragments, closes #1528 (#1601)

This commit is contained in:
Lucas Fernandes Nogueira 2021-04-23 15:30:44 -03:00 committed by GitHub
parent 7471e347d3
commit 69ea51ec93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 173 additions and 79 deletions

View File

@ -0,0 +1,5 @@
---
"tauri-bundler": patch
---
Adds support to `wix` fragments for custom .msi installer functionality.

View File

@ -28,7 +28,7 @@ pub use self::{
},
};
#[cfg(windows)]
pub use settings::WindowsSettings;
pub use settings::{WindowsSettings, WixSettings};
use common::print_finished;

View File

@ -165,6 +165,24 @@ pub struct MacOsSettings {
pub entitlements: Option<String>,
}
#[cfg(windows)]
#[derive(Clone, Debug, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct WixSettings {
#[serde(default)]
pub fragment_paths: Vec<PathBuf>,
#[serde(default)]
pub component_group_refs: Vec<String>,
#[serde(default)]
pub component_refs: Vec<String>,
#[serde(default)]
pub feature_group_refs: Vec<String>,
#[serde(default)]
pub feature_refs: Vec<String>,
#[serde(default)]
pub merge_refs: Vec<String>,
}
/// The Windows bundle settings.
#[cfg(windows)]
#[derive(Clone, Debug, Deserialize, Default)]
@ -172,6 +190,7 @@ 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.

View File

@ -157,6 +157,24 @@
</Feature>
</Feature>
<Feature Id="External" AllowAdvertise="no" Absent="disallow">
{{#each component_group_refs as |id| ~}}
<ComponentGroupRef Id="{{ id }}"/>
{{/each~}}
{{#each component_refs as |id| ~}}
<ComponentRef Id="{{ id }}"/>
{{/each~}}
{{#each feature_group_refs as |id| ~}}
<FeatureGroupRef Id="{{ id }}"/>
{{/each~}}
{{#each feature_refs as |id| ~}}
<FeatureRef Id="{{ id }}"/>
{{/each~}}
{{#each merge_refs as |id| ~}}
<MergeRef Id="{{ id }}"/>
{{/each~}}
</Feature>
<!-- WebView2 -->
<Property Id="WVRTINSTALLED">
<RegistrySearch Id="WVRTInstalled" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no"/>

View File

@ -3,8 +3,6 @@
// SPDX-License-Identifier: MIT
use super::common;
use libflate::gzip;
use walkdir::WalkDir;
#[cfg(target_os = "macos")]
use super::macos_bundle;
@ -159,7 +157,7 @@ fn bundle_update(settings: &Settings, bundles: &[Bundle]) -> crate::Result<Vec<P
}
#[cfg(target_os = "windows")]
pub fn create_zip(src_file: &PathBuf, dst_file: &PathBuf) -> crate::Result<PathBuf> {
pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
let parent_dir = dst_file.parent().expect("No data in parent");
fs::create_dir_all(parent_dir)?;
let writer = common::create_file(dst_file)?;
@ -186,7 +184,7 @@ pub fn create_zip(src_file: &PathBuf, dst_file: &PathBuf) -> crate::Result<PathB
#[cfg(not(target_os = "windows"))]
fn create_tar(src_dir: &Path, dest_path: &Path) -> crate::Result<PathBuf> {
let dest_file = common::create_file(&dest_path)?;
let gzip_encoder = gzip::Encoder::new(dest_file)?;
let gzip_encoder = libflate::gzip::Encoder::new(dest_file)?;
let gzip_encoder = create_tar_from_src(src_dir, gzip_encoder)?;
let mut dest_file = gzip_encoder.finish().into_result()?;
@ -210,7 +208,7 @@ fn create_tar_from_src<P: AsRef<Path>, W: Write>(src_dir: P, dest_file: W) -> cr
tar_builder.append_file(file_name, &mut src_file)?;
} else {
for entry in WalkDir::new(&src_dir) {
for entry in walkdir::WalkDir::new(&src_dir) {
let entry = entry?;
let src_path = entry.path();
if src_path == src_dir {

View File

@ -62,7 +62,7 @@ lazy_static! {
handlebars
.register_template_string("main.wxs", include_str!("templates/main.wxs"))
.or_else(|e| Err(e.to_string()))
.map_err(|e| e.to_string())
.expect("Failed to setup handlebar template");
handlebars
};
@ -143,7 +143,7 @@ impl ResourceDirectory {
}
directories.push_str(wix_string.as_str());
}
let wix_string = if self.name == "" {
let wix_string = if self.name.is_empty() {
format!("{}{}", files, directories)
} else {
format!(
@ -278,64 +278,12 @@ pub fn get_and_extract_wix(path: &Path) -> crate::Result<()> {
extract_zip(&data, path)
}
// For if bundler needs DLL files.
// fn run_heat_exe(
// wix_toolset_path: &Path,
// build_path: &Path,
// harvest_dir: &Path,
// platform: &str,
// ) -> Result<(), String> {
// let mut args = vec!["dir"];
// let harvest_str = harvest_dir.display().to_string();
// args.push(&harvest_str);
// args.push("-platform");
// args.push(platform);
// args.push("-cg");
// args.push("AppFiles");
// args.push("-dr");
// args.push("APPLICATIONFOLDER");
// args.push("-gg");
// args.push("-srd");
// args.push("-out");
// args.push("appdir.wxs");
// args.push("-var");
// args.push("var.SourceDir");
// let heat_exe = wix_toolset_path.join("heat.exe");
// let mut cmd = Command::new(&heat_exe)
// .args(&args)
// .stdout(Stdio::piped())
// .current_dir(build_path)
// .spawn()
// .expect("error running heat.exe");
// {
// let stdout = cmd.stdout.as_mut().unwrap();
// let reader = BufReader::new(stdout);
// for line in reader.lines() {
// info!(logger, "{}", line.unwrap());
// }
// }
// let status = cmd.wait().unwrap();
// if status.success() {
// Ok(())
// } else {
// Err("error running heat.exe".to_string())
// }
// }
/// Runs the Candle.exe executable for Wix. Candle parses the wxs file and generates the code for building the installer.
fn run_candle(
settings: &Settings,
wix_toolset_path: &Path,
build_path: &Path,
wxs_file_name: &str,
cwd: &Path,
wxs_file_path: &Path,
) -> crate::Result<()> {
let arch = match settings.binary_arch() {
"x86_64" => "x64",
@ -357,7 +305,7 @@ fn run_candle(
let args = vec![
"-arch".to_string(),
arch.to_string(),
wxs_file_name.to_string(),
wxs_file_path.to_string_lossy().to_string(),
format!(
"-dSourceDir={}",
settings.binary_path(main_binary).display()
@ -365,13 +313,10 @@ fn run_candle(
];
let candle_exe = wix_toolset_path.join("candle.exe");
common::print_info(format!("running candle for {}", wxs_file_name).as_str())?;
common::print_info(format!("running candle for {:?}", wxs_file_path).as_str())?;
let mut cmd = Command::new(&candle_exe);
cmd
.args(&args)
.stdout(Stdio::piped())
.current_dir(build_path);
cmd.args(&args).stdout(Stdio::piped()).current_dir(cwd);
common::print_info("running candle.exe")?;
common::execute_with_verbosity(&mut cmd, &settings).map_err(|_| {
@ -532,6 +477,17 @@ pub fn build_wix_app_installer(
data.insert("icon_path", to_json(icon_path));
let mut fragment_paths = Vec::new();
if let Some(wix) = &settings.windows().wix {
data.insert("component_group_refs", to_json(&wix.component_group_refs));
data.insert("component_refs", to_json(&wix.component_refs));
data.insert("feature_group_refs", to_json(&wix.feature_group_refs));
data.insert("feature_refs", to_json(&wix.feature_refs));
data.insert("merge_refs", to_json(&wix.merge_refs));
fragment_paths = wix.fragment_paths.clone();
}
let temp = HANDLEBARS.render("main.wxs", &data)?;
if output_path.exists() {
@ -543,14 +499,18 @@ pub fn build_wix_app_installer(
let main_wxs_path = output_path.join("main.wxs");
write(&main_wxs_path, temp)?;
let input_basenames = vec!["main"];
let mut candle_inputs = vec!["main.wxs".into()];
for basename in &input_basenames {
let wxs = format!("{}.wxs", basename);
let current_dir = std::env::current_dir()?;
for fragment_path in fragment_paths {
candle_inputs.push(current_dir.join(fragment_path));
}
for wxs in &candle_inputs {
run_candle(settings, &wix_toolset_path, &output_path, &wxs)?;
}
let wixobjs = vec!["main.wixobj"];
let wixobjs = vec!["*.wixobj"];
let target = run_light(
&wix_toolset_path,
&output_path,
@ -600,12 +560,12 @@ fn locate_signtool() -> crate::Result<PathBuf> {
let mut kit_bin_paths: Vec<PathBuf> = installed_kits
.iter()
.rev()
.map(|kit| kits_root_10_bin_path.join(kit).to_path_buf())
.map(|kit| kits_root_10_bin_path.join(kit))
.collect();
/* Add kits root bin path.
For Windows SDK 10 versions earlier than v10.0.15063.468, signtool will be located there. */
kit_bin_paths.push(kits_root_10_bin_path.to_path_buf());
kit_bin_paths.push(kits_root_10_bin_path);
// Choose which version of SignTool to use based on OS bitness
let arch_dir = match bitness::os_bitness().expect("failed to get os bitness") {
@ -622,7 +582,7 @@ fn locate_signtool() -> crate::Result<PathBuf> {
/* Check if SignTool exists at this location. */
if signtool_path.exists() {
// SignTool found. Return it.
return Ok(signtool_path.to_path_buf());
return Ok(signtool_path);
}
}

View File

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use serde_with::skip_serializing_none;
use std::collections::HashMap;
use std::{collections::HashMap, path::PathBuf};
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(untagged)]
@ -41,12 +41,30 @@ pub struct MacConfig {
pub entitlements: Option<String>,
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WixConfig {
#[serde(default)]
pub fragment_paths: Vec<PathBuf>,
#[serde(default)]
pub component_group_refs: Vec<String>,
#[serde(default)]
pub component_refs: Vec<String>,
#[serde(default)]
pub feature_group_refs: Vec<String>,
#[serde(default)]
pub feature_refs: Vec<String>,
#[serde(default)]
pub merge_refs: Vec<String>,
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowsConfig {
pub digest_algorithm: Option<String>,
pub certificate_thumbprint: Option<String>,
pub timestamp_url: Option<String>,
pub wix: Option<WixConfig>,
}
#[skip_serializing_none]

View File

@ -90,7 +90,8 @@
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": null,
"timestampUrl": null
"timestampUrl": null,
"wix": null
}
},
"updater": {
@ -343,7 +344,8 @@
"default": {
"certificateThumbprint": null,
"digestAlgorithm": null,
"timestampUrl": null
"timestampUrl": null,
"wix": null
},
"allOf": [
{
@ -909,7 +911,8 @@
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": null,
"timestampUrl": null
"timestampUrl": null,
"wix": null
}
},
"allOf": [
@ -1159,6 +1162,64 @@
"string",
"null"
]
},
"wix": {
"anyOf": [
{
"$ref": "#/definitions/WixConfig"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"WixConfig": {
"type": "object",
"properties": {
"componentGroupRefs": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"componentRefs": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"featureGroupRefs": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"featureRefs": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"fragmentPaths": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"mergeRefs": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false

View File

@ -352,6 +352,7 @@ fn tauri_config_to_bundle_settings(
timestamp_url: config.windows.timestamp_url,
digest_algorithm: config.windows.digest_algorithm,
certificate_thumbprint: config.windows.certificate_thumbprint,
wix: config.windows.wix.map(|w| w.into()),
},
updater: Some(UpdaterSettings {
active: updater_config.active,

View File

@ -12,6 +12,20 @@ use serde_json::Value as JsonValue;
mod config_definition;
pub use config_definition::*;
#[cfg(windows)]
impl From<WixConfig> for tauri_bundler::WixSettings {
fn from(config: WixConfig) -> tauri_bundler::WixSettings {
tauri_bundler::WixSettings {
fragment_paths: config.fragment_paths,
component_group_refs: config.component_group_refs,
component_refs: config.component_refs,
feature_group_refs: config.feature_group_refs,
feature_refs: config.feature_refs,
merge_refs: config.merge_refs,
}
}
}
use std::{
env::set_var,
fs::File,