Skip to content

Commit 60e6f6c

Browse files
feat(bundler): Add support for creating NSIS bundles on unix hosts (#5788)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent 35b587c commit 60e6f6c

File tree

23 files changed

+317
-152
lines changed

23 files changed

+317
-152
lines changed

.changes/nsis-linux.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"tauri-bundler": minor
3+
"tauri-utils": minor
4+
"cli.rs": minor
5+
"cli.js": minor
6+
"tauri-build": minor
7+
---
8+
9+
Add initial support for building `nsis` bundles on non-Windows platforms.

core/tauri-build/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ serde_json = "1"
2626
heck = "0.4"
2727
json-patch = "0.3"
2828

29-
[target."cfg(windows)".dependencies]
30-
winres = "0.1"
29+
# dependencies for Windows targets
30+
tauri-winres = "0.1"
3131
semver = "1"
3232

3333
[features]

core/tauri-build/src/lib.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::path::{Path, PathBuf};
1313

1414
#[cfg(feature = "codegen")]
1515
mod codegen;
16-
#[cfg(windows)]
1716
mod static_vcruntime;
1817

1918
#[cfg(feature = "codegen")]
@@ -344,25 +343,25 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
344343

345344
#[allow(unused_mut, clippy::redundant_clone)]
346345
let mut resources = config.tauri.bundle.resources.clone().unwrap_or_default();
347-
#[cfg(windows)]
348-
if let Some(fixed_webview2_runtime_path) = &config.tauri.bundle.windows.webview_fixed_runtime_path
349-
{
350-
resources.push(fixed_webview2_runtime_path.display().to_string());
346+
if target_triple.contains("windows") {
347+
if let Some(fixed_webview2_runtime_path) =
348+
&config.tauri.bundle.windows.webview_fixed_runtime_path
349+
{
350+
resources.push(fixed_webview2_runtime_path.display().to_string());
351+
}
351352
}
352353
copy_resources(ResourcePaths::new(resources.as_slice(), true), target_dir)?;
353354

354-
#[cfg(target_os = "macos")]
355-
{
356-
if let Some(version) = config.tauri.bundle.macos.minimum_system_version {
355+
if target_triple.contains("darwin") {
356+
if let Some(version) = &config.tauri.bundle.macos.minimum_system_version {
357357
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={}", version);
358358
}
359359
}
360360

361-
#[cfg(windows)]
362-
{
361+
if target_triple.contains("windows") {
363362
use anyhow::Context;
364363
use semver::Version;
365-
use winres::{VersionInfo, WindowsResource};
364+
use tauri_winres::{VersionInfo, WindowsResource};
366365

367366
fn find_icon<F: Fn(&&String) -> bool>(config: &Config, predicate: F, default: &str) -> PathBuf {
368367
let icon_path = config

core/tauri-build/src/static_vcruntime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn override_msvcrt_lib() {
4848
let f = fs::OpenOptions::new()
4949
.write(true)
5050
.create_new(true)
51-
.open(&path);
51+
.open(path);
5252
if let Ok(mut f) = f {
5353
f.write_all(machine).unwrap();
5454
f.write_all(bytes).unwrap();

examples/api/src-tauri/Cargo.lock

Lines changed: 11 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tooling/bundler/Cargo.toml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,23 @@ tempfile = "3.3.0"
3333
log = { version = "0.4.17", features = [ "kv_unstable" ] }
3434
dirs-next = "2.0"
3535
encoding_rs = "0.8"
36+
os_pipe = "1"
3637

37-
[target."cfg(target_os = \"windows\")".dependencies]
38+
# dependencies for Windows targets
3839
attohttpc = "0.24"
40+
hex = "0.4"
41+
semver = "1"
42+
sha1 = "0.10"
43+
sha2 = "0.10"
44+
zip = "0.6"
45+
46+
# dependencies for code signing on Windows hosts
47+
[target."cfg(target_os = \"windows\")".dependencies]
3948
uuid = { version = "1", features = [ "v4", "v5" ] }
4049
bitness = "0.4"
41-
winreg = "0.10"
42-
sha2 = "0.10"
43-
sha1 = "0.10"
44-
hex = "0.4"
50+
winreg = "0.10" # Can only be compiled for Windows hosts
4551
glob = "0.3"
46-
zip = "0.6"
47-
semver = "1"
52+
4853

4954
[target."cfg(target_os = \"macos\")".dependencies]
5055
icns = { package = "tauri-icns", version = "0.1" }

tooling/bundler/src/bundle.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ mod path_utils;
1313
mod platform;
1414
mod settings;
1515
mod updater_bundle;
16-
#[cfg(target_os = "windows")]
1716
mod windows;
1817

1918
pub use self::{
@@ -43,25 +42,38 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
4342
let mut bundles = Vec::new();
4443
let package_types = settings.package_types()?;
4544

45+
let target_os = settings
46+
.target()
47+
.split('-')
48+
.nth(2)
49+
.unwrap_or(std::env::consts::OS)
50+
.replace("darwin", "macos");
51+
52+
if target_os != std::env::consts::OS {
53+
warn!("Cross-platform compilation is experimental and does not support all features. Please use a matching host system for full compatibility.");
54+
}
55+
4656
for package_type in &package_types {
4757
let bundle_paths = match package_type {
4858
#[cfg(target_os = "macos")]
4959
PackageType::MacOsBundle => macos::app::bundle_project(&settings)?,
5060
#[cfg(target_os = "macos")]
5161
PackageType::IosBundle => macos::ios::bundle_project(&settings)?,
62+
// dmg is dependant of MacOsBundle, we send our bundles to prevent rebuilding
63+
#[cfg(target_os = "macos")]
64+
PackageType::Dmg => macos::dmg::bundle_project(&settings, &bundles)?,
65+
5266
#[cfg(target_os = "windows")]
5367
PackageType::WindowsMsi => windows::msi::bundle_project(&settings, false)?,
54-
#[cfg(target_os = "windows")]
5568
PackageType::Nsis => windows::nsis::bundle_project(&settings, false)?,
69+
5670
#[cfg(target_os = "linux")]
5771
PackageType::Deb => linux::debian::bundle_project(&settings)?,
5872
#[cfg(target_os = "linux")]
5973
PackageType::Rpm => linux::rpm::bundle_project(&settings)?,
6074
#[cfg(target_os = "linux")]
6175
PackageType::AppImage => linux::appimage::bundle_project(&settings)?,
62-
// dmg is dependant of MacOsBundle, we send our bundles to prevent rebuilding
63-
#[cfg(target_os = "macos")]
64-
PackageType::Dmg => macos::dmg::bundle_project(&settings, &bundles)?,
76+
6577
// updater is dependant of multiple bundle, we send our bundles to prevent rebuilding
6678
PackageType::Updater => updater_bundle::bundle_project(&settings, &bundles)?,
6779
_ => {

tooling/bundler/src/bundle/common.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{
1010
fs::{self, File},
1111
io::{self, BufReader, BufWriter},
1212
path::Path,
13-
process::{Command, Output, Stdio},
13+
process::{Command, ExitStatus, Output, Stdio},
1414
sync::{Arc, Mutex},
1515
};
1616

@@ -136,10 +136,22 @@ pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
136136
}
137137

138138
pub trait CommandExt {
139+
// The `pipe` function sets the stdout and stderr to properly
140+
// show the command output in the Node.js wrapper.
141+
fn piped(&mut self) -> std::io::Result<ExitStatus>;
139142
fn output_ok(&mut self) -> crate::Result<Output>;
140143
}
141144

142145
impl CommandExt for Command {
146+
fn piped(&mut self) -> std::io::Result<ExitStatus> {
147+
self.stdout(os_pipe::dup_stdout()?);
148+
self.stderr(os_pipe::dup_stderr()?);
149+
let program = self.get_program().to_string_lossy().into_owned();
150+
debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
151+
152+
self.status().map_err(Into::into)
153+
}
154+
143155
fn output_ok(&mut self) -> crate::Result<Output> {
144156
let program = self.get_program().to_string_lossy().into_owned();
145157
debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg)));

tooling/bundler/src/bundle/settings.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ impl BundleBinary {
434434
/// The Settings exposed by the module.
435435
#[derive(Clone, Debug)]
436436
pub struct Settings {
437+
/// The log level.
438+
log_level: log::Level,
437439
/// the package settings.
438440
package: PackageSettings,
439441
/// the package types we're bundling.
@@ -453,6 +455,7 @@ pub struct Settings {
453455
/// A builder for [`Settings`].
454456
#[derive(Default)]
455457
pub struct SettingsBuilder {
458+
log_level: Option<log::Level>,
456459
project_out_directory: Option<PathBuf>,
457460
package_types: Option<Vec<PackageType>>,
458461
package_settings: Option<PackageSettings>,
@@ -511,6 +514,13 @@ impl SettingsBuilder {
511514
self
512515
}
513516

517+
/// Sets the log level for spawned commands. Defaults to [`log::Level::Error`].
518+
#[must_use]
519+
pub fn log_level(mut self, level: log::Level) -> Self {
520+
self.log_level.replace(level);
521+
self
522+
}
523+
514524
/// Builds a Settings from the CLI args.
515525
///
516526
/// Package settings will be read from Cargo.toml.
@@ -524,6 +534,7 @@ impl SettingsBuilder {
524534
};
525535

526536
Ok(Settings {
537+
log_level: self.log_level.unwrap_or(log::Level::Error),
527538
package: self.package_settings.expect("package settings is required"),
528539
package_types: self.package_types,
529540
project_out_directory: self
@@ -544,6 +555,16 @@ impl SettingsBuilder {
544555
}
545556

546557
impl Settings {
558+
/// Sets the log level for spawned commands.
559+
pub fn set_log_level(&mut self, level: log::Level) {
560+
self.log_level = level;
561+
}
562+
563+
/// Returns the log level for spawned commands.
564+
pub fn log_level(&self) -> log::Level {
565+
self.log_level
566+
}
567+
547568
/// Returns the directory where the bundle should be placed.
548569
pub fn project_out_directory(&self) -> &Path {
549570
&self.project_out_directory
@@ -604,8 +625,14 @@ impl Settings {
604625
///
605626
/// Fails if the host/target's native package type is not supported.
606627
pub fn package_types(&self) -> crate::Result<Vec<PackageType>> {
607-
let target_os = std::env::consts::OS;
608-
let mut platform_types = match target_os {
628+
let target_os = self
629+
.target
630+
.split('-')
631+
.nth(2)
632+
.unwrap_or(std::env::consts::OS)
633+
.replace("darwin", "macos");
634+
635+
let mut platform_types = match target_os.as_str() {
609636
"macos" => vec![PackageType::MacOsBundle, PackageType::Dmg],
610637
"ios" => vec![PackageType::IosBundle],
611638
"linux" => vec![PackageType::Deb, PackageType::AppImage],

0 commit comments

Comments
 (0)