Skip to content

Commit 69ea51e

Browse files
authored
feat(bundler): implement wix fragments, closes #1528 (#1601)
1 parent 7471e34 commit 69ea51e

File tree

10 files changed

+173
-79
lines changed

10 files changed

+173
-79
lines changed

.changes/wix-fragments.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-bundler": patch
3+
---
4+
5+
Adds support to `wix` fragments for custom .msi installer functionality.

tooling/bundler/src/bundle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub use self::{
2828
},
2929
};
3030
#[cfg(windows)]
31-
pub use settings::WindowsSettings;
31+
pub use settings::{WindowsSettings, WixSettings};
3232

3333
use common::print_finished;
3434

tooling/bundler/src/bundle/settings.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,32 @@ pub struct MacOsSettings {
165165
pub entitlements: Option<String>,
166166
}
167167

168+
#[cfg(windows)]
169+
#[derive(Clone, Debug, Deserialize, Default)]
170+
#[serde(rename_all = "camelCase")]
171+
pub struct WixSettings {
172+
#[serde(default)]
173+
pub fragment_paths: Vec<PathBuf>,
174+
#[serde(default)]
175+
pub component_group_refs: Vec<String>,
176+
#[serde(default)]
177+
pub component_refs: Vec<String>,
178+
#[serde(default)]
179+
pub feature_group_refs: Vec<String>,
180+
#[serde(default)]
181+
pub feature_refs: Vec<String>,
182+
#[serde(default)]
183+
pub merge_refs: Vec<String>,
184+
}
185+
168186
/// The Windows bundle settings.
169187
#[cfg(windows)]
170188
#[derive(Clone, Debug, Deserialize, Default)]
171189
pub struct WindowsSettings {
172190
pub digest_algorithm: Option<String>,
173191
pub certificate_thumbprint: Option<String>,
174192
pub timestamp_url: Option<String>,
193+
pub wix: Option<WixSettings>,
175194
}
176195

177196
/// The bundle settings of the BuildArtifact we're bundling.

tooling/bundler/src/bundle/templates/main.wxs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,24 @@
157157
</Feature>
158158
</Feature>
159159

160+
<Feature Id="External" AllowAdvertise="no" Absent="disallow">
161+
{{#each component_group_refs as |id| ~}}
162+
<ComponentGroupRef Id="{{ id }}"/>
163+
{{/each~}}
164+
{{#each component_refs as |id| ~}}
165+
<ComponentRef Id="{{ id }}"/>
166+
{{/each~}}
167+
{{#each feature_group_refs as |id| ~}}
168+
<FeatureGroupRef Id="{{ id }}"/>
169+
{{/each~}}
170+
{{#each feature_refs as |id| ~}}
171+
<FeatureRef Id="{{ id }}"/>
172+
{{/each~}}
173+
{{#each merge_refs as |id| ~}}
174+
<MergeRef Id="{{ id }}"/>
175+
{{/each~}}
176+
</Feature>
177+
160178
<!-- WebView2 -->
161179
<Property Id="WVRTINSTALLED">
162180
<RegistrySearch Id="WVRTInstalled" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no"/>

tooling/bundler/src/bundle/updater_bundle.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// SPDX-License-Identifier: MIT
44

55
use super::common;
6-
use libflate::gzip;
7-
use walkdir::WalkDir;
86

97
#[cfg(target_os = "macos")]
108
use super::macos_bundle;
@@ -159,7 +157,7 @@ fn bundle_update(settings: &Settings, bundles: &[Bundle]) -> crate::Result<Vec<P
159157
}
160158

161159
#[cfg(target_os = "windows")]
162-
pub fn create_zip(src_file: &PathBuf, dst_file: &PathBuf) -> crate::Result<PathBuf> {
160+
pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
163161
let parent_dir = dst_file.parent().expect("No data in parent");
164162
fs::create_dir_all(parent_dir)?;
165163
let writer = common::create_file(dst_file)?;
@@ -186,7 +184,7 @@ pub fn create_zip(src_file: &PathBuf, dst_file: &PathBuf) -> crate::Result<PathB
186184
#[cfg(not(target_os = "windows"))]
187185
fn create_tar(src_dir: &Path, dest_path: &Path) -> crate::Result<PathBuf> {
188186
let dest_file = common::create_file(&dest_path)?;
189-
let gzip_encoder = gzip::Encoder::new(dest_file)?;
187+
let gzip_encoder = libflate::gzip::Encoder::new(dest_file)?;
190188

191189
let gzip_encoder = create_tar_from_src(src_dir, gzip_encoder)?;
192190
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
210208

211209
tar_builder.append_file(file_name, &mut src_file)?;
212210
} else {
213-
for entry in WalkDir::new(&src_dir) {
211+
for entry in walkdir::WalkDir::new(&src_dir) {
214212
let entry = entry?;
215213
let src_path = entry.path();
216214
if src_path == src_dir {

tooling/bundler/src/bundle/wix.rs

Lines changed: 29 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ lazy_static! {
6262

6363
handlebars
6464
.register_template_string("main.wxs", include_str!("templates/main.wxs"))
65-
.or_else(|e| Err(e.to_string()))
65+
.map_err(|e| e.to_string())
6666
.expect("Failed to setup handlebar template");
6767
handlebars
6868
};
@@ -143,7 +143,7 @@ impl ResourceDirectory {
143143
}
144144
directories.push_str(wix_string.as_str());
145145
}
146-
let wix_string = if self.name == "" {
146+
let wix_string = if self.name.is_empty() {
147147
format!("{}{}", files, directories)
148148
} else {
149149
format!(
@@ -278,64 +278,12 @@ pub fn get_and_extract_wix(path: &Path) -> crate::Result<()> {
278278
extract_zip(&data, path)
279279
}
280280

281-
// For if bundler needs DLL files.
282-
283-
// fn run_heat_exe(
284-
// wix_toolset_path: &Path,
285-
// build_path: &Path,
286-
// harvest_dir: &Path,
287-
// platform: &str,
288-
// ) -> Result<(), String> {
289-
// let mut args = vec!["dir"];
290-
291-
// let harvest_str = harvest_dir.display().to_string();
292-
293-
// args.push(&harvest_str);
294-
// args.push("-platform");
295-
// args.push(platform);
296-
// args.push("-cg");
297-
// args.push("AppFiles");
298-
// args.push("-dr");
299-
// args.push("APPLICATIONFOLDER");
300-
// args.push("-gg");
301-
// args.push("-srd");
302-
// args.push("-out");
303-
// args.push("appdir.wxs");
304-
// args.push("-var");
305-
// args.push("var.SourceDir");
306-
307-
// let heat_exe = wix_toolset_path.join("heat.exe");
308-
309-
// let mut cmd = Command::new(&heat_exe)
310-
// .args(&args)
311-
// .stdout(Stdio::piped())
312-
// .current_dir(build_path)
313-
// .spawn()
314-
// .expect("error running heat.exe");
315-
316-
// {
317-
// let stdout = cmd.stdout.as_mut().unwrap();
318-
// let reader = BufReader::new(stdout);
319-
320-
// for line in reader.lines() {
321-
// info!(logger, "{}", line.unwrap());
322-
// }
323-
// }
324-
325-
// let status = cmd.wait().unwrap();
326-
// if status.success() {
327-
// Ok(())
328-
// } else {
329-
// Err("error running heat.exe".to_string())
330-
// }
331-
// }
332-
333281
/// Runs the Candle.exe executable for Wix. Candle parses the wxs file and generates the code for building the installer.
334282
fn run_candle(
335283
settings: &Settings,
336284
wix_toolset_path: &Path,
337-
build_path: &Path,
338-
wxs_file_name: &str,
285+
cwd: &Path,
286+
wxs_file_path: &Path,
339287
) -> crate::Result<()> {
340288
let arch = match settings.binary_arch() {
341289
"x86_64" => "x64",
@@ -357,21 +305,18 @@ fn run_candle(
357305
let args = vec![
358306
"-arch".to_string(),
359307
arch.to_string(),
360-
wxs_file_name.to_string(),
308+
wxs_file_path.to_string_lossy().to_string(),
361309
format!(
362310
"-dSourceDir={}",
363311
settings.binary_path(main_binary).display()
364312
),
365313
];
366314

367315
let candle_exe = wix_toolset_path.join("candle.exe");
368-
common::print_info(format!("running candle for {}", wxs_file_name).as_str())?;
316+
common::print_info(format!("running candle for {:?}", wxs_file_path).as_str())?;
369317

370318
let mut cmd = Command::new(&candle_exe);
371-
cmd
372-
.args(&args)
373-
.stdout(Stdio::piped())
374-
.current_dir(build_path);
319+
cmd.args(&args).stdout(Stdio::piped()).current_dir(cwd);
375320

376321
common::print_info("running candle.exe")?;
377322
common::execute_with_verbosity(&mut cmd, &settings).map_err(|_| {
@@ -532,6 +477,17 @@ pub fn build_wix_app_installer(
532477

533478
data.insert("icon_path", to_json(icon_path));
534479

480+
let mut fragment_paths = Vec::new();
481+
482+
if let Some(wix) = &settings.windows().wix {
483+
data.insert("component_group_refs", to_json(&wix.component_group_refs));
484+
data.insert("component_refs", to_json(&wix.component_refs));
485+
data.insert("feature_group_refs", to_json(&wix.feature_group_refs));
486+
data.insert("feature_refs", to_json(&wix.feature_refs));
487+
data.insert("merge_refs", to_json(&wix.merge_refs));
488+
fragment_paths = wix.fragment_paths.clone();
489+
}
490+
535491
let temp = HANDLEBARS.render("main.wxs", &data)?;
536492

537493
if output_path.exists() {
@@ -543,14 +499,18 @@ pub fn build_wix_app_installer(
543499
let main_wxs_path = output_path.join("main.wxs");
544500
write(&main_wxs_path, temp)?;
545501

546-
let input_basenames = vec!["main"];
502+
let mut candle_inputs = vec!["main.wxs".into()];
503+
504+
let current_dir = std::env::current_dir()?;
505+
for fragment_path in fragment_paths {
506+
candle_inputs.push(current_dir.join(fragment_path));
507+
}
547508

548-
for basename in &input_basenames {
549-
let wxs = format!("{}.wxs", basename);
509+
for wxs in &candle_inputs {
550510
run_candle(settings, &wix_toolset_path, &output_path, &wxs)?;
551511
}
552512

553-
let wixobjs = vec!["main.wixobj"];
513+
let wixobjs = vec!["*.wixobj"];
554514
let target = run_light(
555515
&wix_toolset_path,
556516
&output_path,
@@ -600,12 +560,12 @@ fn locate_signtool() -> crate::Result<PathBuf> {
600560
let mut kit_bin_paths: Vec<PathBuf> = installed_kits
601561
.iter()
602562
.rev()
603-
.map(|kit| kits_root_10_bin_path.join(kit).to_path_buf())
563+
.map(|kit| kits_root_10_bin_path.join(kit))
604564
.collect();
605565

606566
/* Add kits root bin path.
607567
For Windows SDK 10 versions earlier than v10.0.15063.468, signtool will be located there. */
608-
kit_bin_paths.push(kits_root_10_bin_path.to_path_buf());
568+
kit_bin_paths.push(kits_root_10_bin_path);
609569

610570
// Choose which version of SignTool to use based on OS bitness
611571
let arch_dir = match bitness::os_bitness().expect("failed to get os bitness") {
@@ -622,7 +582,7 @@ fn locate_signtool() -> crate::Result<PathBuf> {
622582
/* Check if SignTool exists at this location. */
623583
if signtool_path.exists() {
624584
// SignTool found. Return it.
625-
return Ok(signtool_path.to_path_buf());
585+
return Ok(signtool_path);
626586
}
627587
}
628588

tooling/cli.rs/config_definition.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
99
use serde_json::Value as JsonValue;
1010
use serde_with::skip_serializing_none;
1111

12-
use std::collections::HashMap;
12+
use std::{collections::HashMap, path::PathBuf};
1313

1414
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
1515
#[serde(untagged)]
@@ -41,12 +41,30 @@ pub struct MacConfig {
4141
pub entitlements: Option<String>,
4242
}
4343

44+
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
45+
#[serde(rename_all = "camelCase", deny_unknown_fields)]
46+
pub struct WixConfig {
47+
#[serde(default)]
48+
pub fragment_paths: Vec<PathBuf>,
49+
#[serde(default)]
50+
pub component_group_refs: Vec<String>,
51+
#[serde(default)]
52+
pub component_refs: Vec<String>,
53+
#[serde(default)]
54+
pub feature_group_refs: Vec<String>,
55+
#[serde(default)]
56+
pub feature_refs: Vec<String>,
57+
#[serde(default)]
58+
pub merge_refs: Vec<String>,
59+
}
60+
4461
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
4562
#[serde(rename_all = "camelCase", deny_unknown_fields)]
4663
pub struct WindowsConfig {
4764
pub digest_algorithm: Option<String>,
4865
pub certificate_thumbprint: Option<String>,
4966
pub timestamp_url: Option<String>,
67+
pub wix: Option<WixConfig>,
5068
}
5169

5270
#[skip_serializing_none]

0 commit comments

Comments
 (0)