Merge remote-tracking branch 'origin/dev' into feat/truly-portable-appimage

This commit is contained in:
FabianLars
2026-01-28 20:29:08 +01:00
237 changed files with 5215 additions and 3617 deletions

View File

@@ -1,5 +0,0 @@
---
"@tauri-apps/api": minor:feat
---
Adds the `scrollBarStyle` option to the Webview and WebviewBuilder constructors.

View File

@@ -1,6 +0,0 @@
---
"tauri-cli": minor:feat
"tauri-utils": minor:feat
---
Adds the `scrollBarStyle` option to the window configuration.

View File

@@ -1,9 +0,0 @@
---
"tauri-runtime-wry": minor:feat
"tauri-runtime": minor:feat
"tauri": minor:feat
---
Adds the `scroll_bar_style` option to the Webview and WebviewWindow builders.
The possible values for this option are gated behind conditional compilation
flags, and will need to be applied using conditional compilation if customised.

View File

@@ -1,6 +0,0 @@
---
'tauri-cli': 'minor:feat'
'tauri-bundler': 'minor:feat'
---
Add a `--no-sign` flag to the `tauri build` and `tauri bundle` commands to skip the code signing step, improving the developer experience for local testing and development without requiring code signing keys.

View File

@@ -1,5 +0,0 @@
---
'tauri-cli': 'patch:enhance'
---
Add support for Android's adaptive and themed icons.

View File

@@ -0,0 +1,5 @@
---
"tauri": patch:bug
---
Fixed 500 error when accessing local video files in Android external storage directory via `convertFileSrc`. Added better error handling and logging for Android external storage access to help diagnose permission and accessibility issues.

View File

@@ -1,6 +0,0 @@
---
'tauri-cli': 'patch:bug'
'@tauri-apps/cli': patch:bug
---
Strip Windows-only extensions from the binary path so an Android project initialized on Windows can be used on UNIX systems.

View File

@@ -1,5 +0,0 @@
---
"tauri": patch:bug
---
Properly deserialize Android plugin args with key starting with `is` (previously treated as a getter instead of a field name).

View File

@@ -1,6 +1,6 @@
---
"tauri-cli": patch:enhance
"@tauri-apps/cli": patch:enhance
"tauri-cli": patch:enhance
---
Check mismatched versions in `tauri info`
Allow electron to run the CLI directly

View File

@@ -0,0 +1,7 @@
---
"tauri-utils": patch:enhance
"tauri-build": patch:enhance
"tauri-cli": patch:enhance
---
Small code refactors for improved code readability. No user facing changes.

View File

@@ -0,0 +1,5 @@
---
"tauri-macos-sign": patch:deps
---
Remove once-cell-regex from direct dependencies.

View File

@@ -0,0 +1,5 @@
---
"tauri": patch:bug
---
fix(specta): don't use `#[specta(rename = ...)]` with `tauri::ipc::Channel`

View File

@@ -0,0 +1,5 @@
---
'tauri-bundler': 'patch:enhance'
---
feat(nsis): add Norwegian language support for installer.

View File

@@ -27,12 +27,6 @@
"dryRunCommand": true,
"pipe": true
},
{
"command": "cargo generate-lockfile",
"dryRunCommand": true,
"runFromRoot": true,
"pipe": true
},
{
"command": "cargo audit ${ process.env.CARGO_AUDIT_OPTIONS || '' }",
"dryRunCommand": true,

View File

@@ -1,6 +0,0 @@
---
"tauri-utils": "minor:enhance"
"@tauri-apps/api": "minor:enhance"
---
Added a config to set a data_directory relative to the app-specific data dir in JavaScript and `tauri.conf.json`.

View File

@@ -1,6 +0,0 @@
---
"@tauri-apps/cli": patch:enhance
"tauri-cli": patch:enhance
---
Set a default log level filter when running `tauri add log`.

View File

@@ -0,0 +1,6 @@
---
"@tauri-apps/cli": patch:enhance
"tauri-cli": patch:enhance
---
Simplified internal representation of `features: Option<Vec<String>>` with `Vec<String>`, no user facing changes

View File

@@ -1,6 +0,0 @@
---
"tauri-cli": minor:feat
"@tauri-apps/cli": minor:feat
---
Try to detect ANDROID_HOME and NDK_HOME environment variables from default system locations and install them if needed using the Android Studio command line tools.

View File

@@ -1,6 +0,0 @@
---
"tauri-cli": minor:feat
"@tauri-apps/cli": minor:feat
---
Added support to defining the content type of the declared file association on macOS (maps to LSItemContentTypes property).

View File

@@ -1,6 +0,0 @@
---
"tauri-cli": minor:feat
"@tauri-apps/cli": minor:feat
---
Added support to defining the metadata for custom types declared in `tauri.conf.json > bundle > fileAssociations > exportedType` via the `UTExportedTypeDeclarations` Info.plist property.

View File

@@ -1,5 +0,0 @@
---
"tauri-utils": minor:feat
---
Added `FileAssociation::exported_type` and `FileAssociation::content_types` for better support to defining custom types on macOS.

View File

@@ -1,6 +0,0 @@
---
'tauri-cli': 'patch:bug'
'@tauri-apps/cli': patch:bug
---
Enhance Android build script usage on Windows by attempting to run cmd, bat and exe formats.

View File

@@ -0,0 +1,6 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
Fix `android build`'s `--aab` and `--apk` flags requiring a value to be provided.

View File

@@ -0,0 +1,8 @@
---
"tauri": minor:changes
"tauri-cli": minor:changes
"tauri-bundler": minor:changes
"@tauri-apps/cli": minor:changes
---
Change the way bundle type information is added to binary files. Instead of looking up the value of a variable we simply look for the default value.

View File

@@ -0,0 +1,6 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
Fix empty associated-domains entitlements when domains are not configured for deep links.

View File

@@ -1,6 +1,6 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
"tauri-cli": patch:bug
---
Fix iOS CLI usage after modifying the package name.
Fixed the command description for `tauri inspect`

View File

@@ -1,6 +0,0 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
Replaced the non-standard nerd font character with ` ⱼₛ` in `tarui info`

View File

@@ -1,6 +0,0 @@
---
"@tauri-apps/cli": patch:bug
"tauri-cli": patch:bug
---
Resolve local IP address when `tauri.conf.json > build > devUrl` host is `0.0.0.0`.

View File

@@ -1,8 +0,0 @@
---
"@tauri-apps/cli": minor:enhance
"tauri-cli": minor:enhance
"tauri-bundler": minor:enhance
---
Improve error messages with more context.

View File

@@ -1,8 +0,0 @@
---
"tauri-utils": minor:feat
"tauri-cli": minor:feat
"@tauri-apps/cli": minor:feat
---
Added `bundle > macOS > infoPlist` and `bundle > iOS > infoPlist` configurations to allow defining custom Info.plist extensions.

View File

@@ -1,6 +0,0 @@
---
"@tauri-apps/cli": minor:enhance
"tauri-cli": minor:enhance
---
Prompt to install the iOS platform if it isn't installed yet.

View File

@@ -1,5 +0,0 @@
---
"tauri": minor:feat
---
Support async Swift plugin methods (`completionHandler:`) in PluginManager

View File

@@ -0,0 +1,5 @@
---
tauri-runtime-wry: patch:bug
---
On Linux, keep the WebContext alive to prevent zombie WebKit processes after repeatedly closing all windows and re-opening them.

View File

@@ -1,5 +0,0 @@
---
tauri-bundler: patch:bug
---
Set `APPIMAGE_EXTRACT_AND_RUN` on top of using the `--appimage-extra-and-run` cli arg for linuxdeploy.

View File

@@ -1,6 +0,0 @@
---
"@tauri-apps/cli": patch:bug
"tauri-cli": patch:bug
---
Fixes mobile project initialization when using `pnpx` or `pnpm dlx`.

View File

@@ -1,6 +0,0 @@
---
"@tauri-apps/cli": minor:feat
"tauri-cli": minor:feat
---
Added `ios run` and `android run` commands to run the app in production mode.

View File

@@ -0,0 +1,9 @@
---
"tauri-bundler": patch:bug
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
Updated `nsis_tauri_utils` to 0.5.3:
- Use an alternative method `CreateProcessWithTokenW` to run programs as user, this fixed a problem that the program launched with the previous method can't query its own handle

View File

@@ -0,0 +1,6 @@
---
"@tauri-apps/cli": patch:bug
"tauri-cli": patch:bug
---
Only watch dependent workspace members when running `tauri dev` instead of watching on all members

View File

@@ -0,0 +1,6 @@
---
"@tauri-apps/cli": patch:changes
"tauri-cli": patch:changes
---
Refactored internal use of static on config and directory resolvings, no user facing changes, please report any regressions if you encounter any

View File

@@ -1,5 +0,0 @@
---
"tauri-bundler": minor:breaking
---
Changed `MacOsSettings::info_plist_path` to `MacOsSettings::info_plist`.

View File

@@ -1,5 +0,0 @@
---
"@tauri-apps/api": patch:bug
---
Fix `core > addPluginListener` failing on command permission check.

6
.changes/runtime-bsd.md Normal file
View File

@@ -0,0 +1,6 @@
---
tauri-runtime: patch:bug
tauri-runtime-wry: patch:bug
---
Fix compilation errors when targeting BSD.

View File

@@ -0,0 +1,14 @@
---
"tauri-cli": patch:enhance
"@tauri-apps/cli": patch:enhance
---
Added new environment variables for `tauri signer sign` command, to align with existing environment variables used in `tauri build`, `tauri bundle` and `tauri signer generate`
- `TAURI_SIGNING_PRIVATE_KEY`
- `TAURI_SIGNING_PRIVATE_KEY_PATH`
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`
The old environment variables are deprecated and will be removed in a future release.
- `TAURI_PRIVATE_KEY`
- `TAURI_PRIVATE_KEY_PATH`
- `TAURI_PRIVATE_KEY_PASSWORD`

View File

@@ -1,6 +0,0 @@
---
"tauri": "patch:bug"
"tauri-macros": "patch:bug"
---
Fix the stack overflow when having too many commands in a single invoke handler in release build

View File

@@ -1,5 +0,0 @@
---
"tauri-bundler": minor:feat
---
Support providing `plist::Value` as macOS entitlements.

View File

@@ -1,7 +0,0 @@
---
"tauri-cli": minor:feat
"@tauri-apps/cli": minor:feat
"tauri-utils": minor:feat
---
Added support to universal app links on macOS with the `plugins > deep-link > desktop > domains` configuration.

View File

@@ -0,0 +1,6 @@
---
tauri-cli: patch:bug
"@tauri-apps/cli": patch:bug
---
`tauri signer sign` doesn't work for files without an extension

View File

@@ -1,7 +0,0 @@
---
"tauri-cli": patch:enhance
"@tauri-apps/cli": patch:enhance
---
Warn if productName is empty when initializing mobile project.

7
.changes/webkitgtk202.md Normal file
View File

@@ -0,0 +1,7 @@
---
tauri-runtime-wry: minor:deps
tauri-runtime: minor:deps
tauri: minor:deps
---
**Breaking Change** for `with_webview` users: Updated webkit2gtk-rs crates to `v2.0.2`.

View File

@@ -0,0 +1,7 @@
---
'tauri': 'minor:feat'
---
Add `set_simple_fullscreen` method to `WebviewWindow`.
This method was already available on the `Window` type and is now also available on `WebviewWindow` for consistency. On macOS, it toggles fullscreen mode without creating a new macOS Space. On other platforms, it falls back to regular fullscreen.

View File

@@ -0,0 +1,5 @@
---
tauri: patch:bug
---
`WindowConfig::focus` is set to `false` in `WindowConfig::default()`

6
.changes/wry-054.md Normal file
View File

@@ -0,0 +1,6 @@
---
tauri-runtime-wry: patch:deps
tauri: patch:deps
---
Update wry to `v0.54`.

View File

@@ -33,11 +33,9 @@ Hi! We, the maintainers, are really excited that you are interested in contribut
- It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging.
- If adding new feature:
- Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it.
- If fixing a bug:
- If you are resolving a special issue, add `(fix: #xxxx[,#xxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `fix: update entities encoding/decoding (fix #3899)`.
- Provide detailed description of the bug in the PR, or link to an issue that does.
@@ -98,7 +96,7 @@ You can use `cargo install --path . --debug` to speed up test builds.
You can build the Rust documentation locally running the following script:
```bash
$ RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --open
$ cargo +nightly doc --all-features --open
```
### Developing the JS API

View File

@@ -78,7 +78,6 @@ jobs:
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- name: cargo login
run: cargo login ${{ secrets.ORG_CRATES_IO_TOKEN }}

View File

@@ -38,35 +38,62 @@ jobs:
rust_target: aarch64-pc-windows-msvc
ext: '.exe'
args: ''
- os: ubuntu-22.04
rust_target: riscv64gc-unknown-linux-gnu
ext: ''
args: ''
cross: true
steps:
- uses: actions/checkout@v4
- name: 'Setup Rust'
if: ${{ !matrix.config.cross }}
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.config.rust_target }}
- uses: Swatinem/rust-cache@v2
if: ${{ !matrix.config.cross }}
with:
key: ${{ matrix.config.rust_target }}
- name: install Linux dependencies
if: matrix.config.os == 'ubuntu-latest'
if: ${{ !matrix.config.cross && startsWith(matrix.config.os, 'ubuntu') }}
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev
- name: Install cross
if: ${{ matrix.config.cross }}
uses: taiki-e/install-action@v2
with:
tool: cross@0.2.5
- name: Build CLI
if: ${{ !matrix.config.cross }}
run: cargo build --manifest-path ./crates/tauri-cli/Cargo.toml --profile release-size-optimized ${{ matrix.config.args }}
- name: Build CLI (cross)
if: ${{ matrix.config.cross }}
run: cross build --manifest-path ./crates/tauri-cli/Cargo.toml --target ${{ matrix.config.rust_target }} --profile release-size-optimized ${{ matrix.config.args }}
- name: Upload CLI
if: ${{ !matrix.config.cross }}
uses: actions/upload-artifact@v4
with:
name: cargo-tauri-${{ matrix.config.rust_target }}${{ matrix.config.ext }}
path: target/release-size-optimized/cargo-tauri${{ matrix.config.ext }}
if-no-files-found: error
- name: Upload CLI (cross)
if: ${{ matrix.config.cross }}
uses: actions/upload-artifact@v4
with:
name: cargo-tauri-${{ matrix.config.rust_target }}${{ matrix.config.ext }}
path: target/${{ matrix.config.rust_target }}/release-size-optimized/cargo-tauri${{ matrix.config.ext }}
if-no-files-found: error
upload:
needs: build
runs-on: ubuntu-latest

View File

@@ -57,7 +57,7 @@ function checkChangeFiles(changeFiles) {
for (const [file, packages] of unknownTagsEntries) {
for (const { package, tag } of packages) {
console.error(
`Package \`${package}\` has an uknown change tag ${tag} in ${file} `
`Package \`${package}\` has an unknown change tag ${tag} in ${file} `
)
}
}

View File

@@ -29,7 +29,7 @@ const ignore = [
async function checkFile(file) {
if (
extensions.some((e) => file.endsWith(e))
&& !ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) == i)
&& !ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) === i)
) {
const fileStream = fs.createReadStream(file)
const rl = readline.createInterface({

View File

@@ -1,16 +0,0 @@
.changes
.devcontainer
.docker
.github
.scripts
.vscode
audits
bench
packages/api
packages/cli
crates/tauri-cli
crates/tauri-bundler
crates/tauri-driver
crates/tauri-macos-sign
crates/tauri-schema-generator
crates/tests

689
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,35 @@
# Changelog
## \[2.5.3]
### Dependencies
- Upgraded to `tauri-utils@2.8.1`
- Upgraded to `tauri-codegen@2.5.2`
## \[2.5.2]
### Dependencies
- Upgraded to `tauri-codegen@2.5.1`
## \[2.5.1]
### Bug Fixes
- [`4b6b8690a`](https://www.github.com/tauri-apps/tauri/commit/4b6b8690ab886ebdf1307951cffbe03e31280baa) ([#14347](https://www.github.com/tauri-apps/tauri/pull/14347) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.5.0]
### New Features
- [`3b4fac201`](https://www.github.com/tauri-apps/tauri/commit/3b4fac2017832d426dd07c5e24e26684eda57f7b) ([#14194](https://www.github.com/tauri-apps/tauri/pull/14194)) Add `tauri.conf.json > bundle > android > autoIncrementVersionCode` config option to automatically increment the Android version code.
### Dependencies
- Upgraded to `tauri-utils@2.8.0`
- Upgraded to `tauri-codegen@2.5.0`
## \[2.4.1]
### Enhancements

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-build"
version = "2.4.1"
version = "2.5.3"
description = "build time code to pair with https://crates.io/crates/tauri"
exclude = ["CHANGELOG.md", "/target"]
readme = "README.md"
@@ -22,14 +22,12 @@ targets = [
"x86_64-linux-android",
"x86_64-apple-ios",
]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
anyhow = "1"
quote = { version = "1", optional = true }
tauri-codegen = { version = "2.4.0", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.7.0", path = "../tauri-utils", features = [
tauri-codegen = { version = "2.5.2", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.8.1", path = "../tauri-utils", features = [
"build",
"resources",
] }

View File

@@ -157,7 +157,7 @@ fn read_plugins_manifests() -> Result<BTreeMap<String, Manifest>> {
Ok(manifests)
}
struct InlinedPuginsAcl {
struct InlinedPluginsAcl {
manifests: BTreeMap<String, Manifest>,
permission_files: BTreeMap<String, Vec<PermissionFile>>,
}
@@ -165,7 +165,7 @@ struct InlinedPuginsAcl {
fn inline_plugins(
out_dir: &Path,
inlined_plugins: HashMap<&'static str, InlinedPlugin>,
) -> Result<InlinedPuginsAcl> {
) -> Result<InlinedPluginsAcl> {
let mut acl_manifests = BTreeMap::new();
let mut permission_files_map = BTreeMap::new();
@@ -250,7 +250,7 @@ permissions = [{default_permissions}]
acl_manifests.insert(name.into(), manifest);
}
Ok(InlinedPuginsAcl {
Ok(InlinedPluginsAcl {
manifests: acl_manifests,
permission_files: permission_files_map,
})

View File

@@ -165,21 +165,21 @@ fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> {
.with_context(|| format!("Failed to create frameworks output directory at {dest_dir:?}"))?;
for framework in frameworks.iter() {
if framework.ends_with(".framework") {
let src_path = PathBuf::from(framework);
let src_path = Path::new(framework);
let src_name = src_path
.file_name()
.expect("Couldn't get framework filename");
let dest_path = dest_dir.join(src_name);
copy_dir(&src_path, &dest_path)?;
copy_dir(src_path, &dest_path)?;
continue;
} else if framework.ends_with(".dylib") {
let src_path = PathBuf::from(framework);
let src_path = Path::new(framework);
if !src_path.exists() {
return Err(anyhow::anyhow!("Library not found: {}", framework));
}
let src_name = src_path.file_name().expect("Couldn't get library filename");
let dest_path = dest_dir.join(src_name);
copy_file(&src_path, &dest_path)?;
copy_file(src_path, &dest_path)?;
continue;
} else if framework.contains('/') {
return Err(anyhow::anyhow!(
@@ -192,12 +192,8 @@ fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> {
continue;
}
}
if copy_framework_from(&PathBuf::from("/Library/Frameworks/"), framework, dest_dir)?
|| copy_framework_from(
&PathBuf::from("/Network/Library/Frameworks/"),
framework,
dest_dir,
)?
if copy_framework_from("/Library/Frameworks/".as_ref(), framework, dest_dir)?
|| copy_framework_from("/Network/Library/Frameworks/".as_ref(), framework, dest_dir)?
{
continue;
}
@@ -263,7 +259,7 @@ impl WindowsAttributes {
}
}
/// Creates the default attriute set wihtou the default app manifest.
/// Creates the default attribute set without the default app manifest.
#[must_use]
pub fn new_without_app_manifest() -> Self {
Self {
@@ -499,7 +495,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_PREFIX={android_package_prefix}");
if let Some(project_dir) = env::var_os("TAURI_ANDROID_PROJECT_PATH").map(PathBuf::from) {
mobile::generate_gradle_files(project_dir, &config)?;
mobile::generate_gradle_files(project_dir)?;
}
cfg_alias("dev", is_dev());

View File

@@ -2,18 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{fs::write, path::PathBuf};
use std::path::PathBuf;
use anyhow::{Context, Result};
use semver::Version;
use tauri_utils::{config::Config, write_if_changed};
use tauri_utils::write_if_changed;
use crate::is_dev;
pub fn generate_gradle_files(project_dir: PathBuf, config: &Config) -> Result<()> {
pub fn generate_gradle_files(project_dir: PathBuf) -> Result<()> {
let gradle_settings_path = project_dir.join("tauri.settings.gradle");
let app_build_gradle_path = project_dir.join("app").join("tauri.build.gradle.kts");
let app_tauri_properties_path = project_dir.join("app").join("tauri.properties");
let mut gradle_settings =
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n".to_string();
@@ -21,7 +17,6 @@ pub fn generate_gradle_files(project_dir: PathBuf, config: &Config) -> Result<()
val implementation by configurations
dependencies {"
.to_string();
let mut app_tauri_properties = Vec::new();
for (env, value) in std::env::vars_os() {
let env = env.to_string_lossy();
@@ -54,32 +49,6 @@ dependencies {"
app_build_gradle.push_str("\n}");
if let Some(version) = config.version.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionName={version}"));
if let Some(version_code) = config.bundle.android.version_code.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
} else if let Ok(version) = Version::parse(version) {
let mut version_code = version.major * 1000000 + version.minor * 1000 + version.patch;
if is_dev() {
version_code = version_code.clamp(1, 2100000000);
}
if version_code == 0 {
return Err(anyhow::anyhow!(
"You must change the `version` in `tauri.conf.json`. The default value `0.0.0` is not allowed for Android package and must be at least `0.0.1`."
));
} else if version_code > 2100000000 {
return Err(anyhow::anyhow!(
"Invalid version code {}. Version code must be between 1 and 2100000000. You must change the `version` in `tauri.conf.json`.",
version_code
));
}
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
}
}
// Overwrite only if changed to not trigger rebuilds
write_if_changed(&gradle_settings_path, gradle_settings)
.context("failed to write tauri.settings.gradle")?;
@@ -87,28 +56,8 @@ dependencies {"
write_if_changed(&app_build_gradle_path, app_build_gradle)
.context("failed to write tauri.build.gradle.kts")?;
if !app_tauri_properties.is_empty() {
let app_tauri_properties_content = format!(
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n{}",
app_tauri_properties.join("\n")
);
if std::fs::read_to_string(&app_tauri_properties_path)
.map(|o| o != app_tauri_properties_content)
.unwrap_or(true)
{
write(&app_tauri_properties_path, app_tauri_properties_content)
.context("failed to write tauri.properties")?;
}
}
println!("cargo:rerun-if-changed={}", gradle_settings_path.display());
println!("cargo:rerun-if-changed={}", app_build_gradle_path.display());
if !app_tauri_properties.is_empty() {
println!(
"cargo:rerun-if-changed={}",
app_tauri_properties_path.display()
);
}
Ok(())
}

View File

@@ -1,5 +1,83 @@
# Changelog
## \[2.7.5]
### Enhancements
- [`4176f93ae`](https://www.github.com/tauri-apps/tauri/commit/4176f93ae43ef66714c4934feb3df19df3a3e28a) ([#14570](https://www.github.com/tauri-apps/tauri/pull/14570) by [@chfaft](https://www.github.com/tauri-apps/tauri/../../chfaft)) Consider extensions that are defined in the wxs template.
### Bug Fixes
- [`018b4db22`](https://www.github.com/tauri-apps/tauri/commit/018b4db22e167fa67b37b0933e192a0f3556d3e5) ([#14625](https://www.github.com/tauri-apps/tauri/pull/14625) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Skip signing for NSIS uninstaller when using `--no-sign` flag
- [`91becd9e4`](https://www.github.com/tauri-apps/tauri/commit/91becd9e4fa2db089ddc6b21dadc06133e939e08) ([#14627](https://www.github.com/tauri-apps/tauri/pull/14627) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix NSIS plugins not being signed due to wrong path handlings
### Dependencies
- Upgraded to `tauri-macos-sign@2.3.2`
## \[2.7.4]
### Bug Fixes
- [`1496145f8`](https://www.github.com/tauri-apps/tauri/commit/1496145f8222649efeff22b819a96208670bbea1) ([#14585](https://www.github.com/tauri-apps/tauri/pull/14585) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the AppImage bundler to fail with 404 errors for 32-bit builds.
### Performance Improvements
- [`ce98d87ce`](https://www.github.com/tauri-apps/tauri/commit/ce98d87ce0aaa907285852eb80691197424e03c3) ([#14474](https://www.github.com/tauri-apps/tauri/pull/14474) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) refactor: remove needless collect. No user facing changes.
- [`ee3cc4a91`](https://www.github.com/tauri-apps/tauri/commit/ee3cc4a91bf1315ecaefe90f423ffd55ef6c40db) ([#14475](https://www.github.com/tauri-apps/tauri/pull/14475) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) perf: remove needless clones in various files for improved performance. No user facing changes.
### Dependencies
- Upgraded to `tauri-macos-sign@2.3.1`
- Upgraded to `tauri-utils@2.8.1`
- [`b5ef603d8`](https://www.github.com/tauri-apps/tauri/commit/b5ef603d84bd8044625e50dcfdabb099b2e9fdd9) ([#14478](https://www.github.com/tauri-apps/tauri/pull/14478) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Updated NSIS from 3.8 to 3.11
## \[2.7.3]
### Enhancements
- [`22edc65aa`](https://www.github.com/tauri-apps/tauri/commit/22edc65aad0b3e45515008e8e0866112da70c8a1) ([#14408](https://www.github.com/tauri-apps/tauri/pull/14408) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Set user-agent in bundler and cli http requests when fetching build tools.
### Bug Fixes
- [`9a1922636`](https://www.github.com/tauri-apps/tauri/commit/9a192263693d71123a9953e2a6ee60fad07500b4) ([#14410](https://www.github.com/tauri-apps/tauri/pull/14410) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix uninstall fails if you close the app manually during the 'Click Ok to kill it' dialog
## \[2.7.2]
### Enhancements
- [`7f710b8f3`](https://www.github.com/tauri-apps/tauri/commit/7f710b8f3b509ed327d76761926511cf56e66b2d) ([#14390](https://www.github.com/tauri-apps/tauri/pull/14390) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Inline linuxdeploy plugins which were previously downloaded from `https://raw.githubusercontent.com` which lately blocks many users with a 429 error.
- [`fc017ee25`](https://www.github.com/tauri-apps/tauri/commit/fc017ee2577f48615367ea519386d3f37837e2c1) ([#14368](https://www.github.com/tauri-apps/tauri/pull/14368) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Mention symbol stripping on Linux in binary patch failed warning message
## \[2.7.1]
### Dependencies
- Upgraded to `tauri-macos-sign@2.3.0`
## \[2.7.0]
### New Features
- [`2a06d1006`](https://www.github.com/tauri-apps/tauri/commit/2a06d10066a806e392efe8bfb16d943ee0b0b61d) ([#14052](https://www.github.com/tauri-apps/tauri/pull/14052)) Add a `--no-sign` flag to the `tauri build` and `tauri bundle` commands to skip the code signing step, improving the developer experience for local testing and development without requiring code signing keys.
- [`cc8c0b531`](https://www.github.com/tauri-apps/tauri/commit/cc8c0b53171173dbd1d01781a50de1a3ea159031) ([#14031](https://www.github.com/tauri-apps/tauri/pull/14031)) Support providing `plist::Value` as macOS entitlements.
### Enhancements
- [`b06b3bd09`](https://www.github.com/tauri-apps/tauri/commit/b06b3bd091b0fed26cdcfb23cacb0462a7a9cc2d) ([#14126](https://www.github.com/tauri-apps/tauri/pull/14126)) Improve error messages with more context.
### Bug Fixes
- [`06d4a4ed6`](https://www.github.com/tauri-apps/tauri/commit/06d4a4ed6c146d6c7782016cf90037b56b944445) ([#14241](https://www.github.com/tauri-apps/tauri/pull/14241)) Set `APPIMAGE_EXTRACT_AND_RUN` on top of using the `--appimage-extra-and-run` cli arg for linuxdeploy.
### Dependencies
- Upgraded to `tauri-utils@2.8.0`
### Breaking Changes
- [`ed7c9a410`](https://www.github.com/tauri-apps/tauri/commit/ed7c9a4100e08c002212265549d12130d021ad1e) ([#14108](https://www.github.com/tauri-apps/tauri/pull/14108)) Changed `MacOsSettings::info_plist_path` to `MacOsSettings::info_plist`.
## \[2.6.1]
### Bug Fixes

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-bundler"
version = "2.6.1"
version = "2.7.5"
authors = [
"George Burton <burtonageo@gmail.com>",
"Tauri Programme within The Commons Conservancy",
@@ -15,7 +15,7 @@ rust-version = "1.77.2"
exclude = ["CHANGELOG.md", "/target", "rustfmt.toml"]
[dependencies]
tauri-utils = { version = "2.7.0", path = "../tauri-utils", features = [
tauri-utils = { version = "2.8.1", path = "../tauri-utils", features = [
"resources",
] }
image = "0.25"
@@ -59,7 +59,7 @@ features = ["Win32_System_SystemInformation", "Win32_System_Diagnostics_Debug"]
[target."cfg(target_os = \"macos\")".dependencies]
icns = { package = "tauri-icns", version = "0.1" }
time = { version = "0.3", features = ["formatting"] }
tauri-macos-sign = { version = "2.2.0", path = "../tauri-macos-sign" }
tauri-macos-sign = { version = "2.3.2", path = "../tauri-macos-sign" }
[target."cfg(target_os = \"linux\")".dependencies]
heck = "0.5"

View File

@@ -4,6 +4,8 @@
// SPDX-License-Identifier: MIT
mod category;
#[cfg(any(target_os = "linux", target_os = "windows"))]
mod kmp;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "macos")]
@@ -15,29 +17,46 @@ mod windows;
use tauri_utils::{display_path, platform::Target as TargetPlatform};
#[cfg(any(target_os = "linux", target_os = "windows"))]
const BUNDLE_VAR_TOKEN: &[u8] = b"__TAURI_BUNDLE_TYPE_VAR_UNK";
/// Patch a binary with bundle type information
#[cfg(any(target_os = "linux", target_os = "windows"))]
fn patch_binary(binary: &PathBuf, package_type: &PackageType) -> crate::Result<()> {
match package_type {
#[cfg(target_os = "linux")]
PackageType::AppImage | PackageType::Deb | PackageType::Rpm => {
log::info!(
"Patching binary {:?} for type {}",
binary,
package_type.short_name()
);
linux::patch_binary(binary, package_type)?;
}
PackageType::Nsis | PackageType::WindowsMsi => {
log::info!(
"Patching binary {:?} for type {}",
binary,
package_type.short_name()
);
windows::patch_binary(binary, package_type)?;
}
_ => (),
}
let mut file_data = std::fs::read(binary).expect("Could not read binary file.");
if let Some(bundle_var_index) = kmp::index_of(BUNDLE_VAR_TOKEN, &file_data) {
#[cfg(target_os = "linux")]
let bundle_type = match package_type {
crate::PackageType::Deb => b"__TAURI_BUNDLE_TYPE_VAR_DEB",
crate::PackageType::Rpm => b"__TAURI_BUNDLE_TYPE_VAR_RPM",
crate::PackageType::AppImage => b"__TAURI_BUNDLE_TYPE_VAR_APP",
_ => {
return Err(crate::Error::InvalidPackageType(
package_type.short_name().to_owned(),
"Linux".to_owned(),
))
}
};
#[cfg(target_os = "windows")]
let bundle_type = match package_type {
crate::PackageType::Nsis => b"__TAURI_BUNDLE_TYPE_VAR_NSS",
crate::PackageType::WindowsMsi => b"__TAURI_BUNDLE_TYPE_VAR_MSI",
_ => {
return Err(crate::Error::InvalidPackageType(
package_type.short_name().to_owned(),
"Windows".to_owned(),
))
}
};
file_data[bundle_var_index..bundle_var_index + BUNDLE_VAR_TOKEN.len()]
.copy_from_slice(bundle_type);
std::fs::write(binary, &file_data)
.map_err(|e| crate::Error::BinaryWriteError(e.to_string()))?;
} else {
return Err(crate::Error::MissingBundleTypeVar);
}
Ok(())
}
@@ -92,22 +111,17 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
.expect("Main binary missing in settings");
let main_binary_path = settings.binary_path(main_binary);
// When packaging multiple binary types, we make a copy of the unsigned main_binary so that we can
// restore it after each package_type step. This avoids two issues:
// We make a copy of the unsigned main_binary so that we can restore it after each package_type step.
// This allows us to patch the binary correctly and avoids two issues:
// - modifying a signed binary without updating its PE checksum can break signature verification
// - codesigning tools should handle calculating+updating this, we just need to ensure
// (re)signing is performed after every `patch_binary()` operation
// - signing an already-signed binary can result in multiple signatures, causing verification errors
let main_binary_reset_required = matches!(target_os, TargetPlatform::Windows)
&& settings.windows().can_sign()
&& package_types.len() > 1;
let mut unsigned_main_binary_copy = tempfile::tempfile()?;
if main_binary_reset_required {
let mut unsigned_main_binary = std::fs::File::open(&main_binary_path)?;
std::io::copy(&mut unsigned_main_binary, &mut unsigned_main_binary_copy)?;
}
// TODO: change this to work on a copy while preserving the main binary unchanged
let mut main_binary_copy = tempfile::tempfile()?;
let mut main_binary_orignal = std::fs::File::open(&main_binary_path)?;
std::io::copy(&mut main_binary_orignal, &mut main_binary_copy)?;
let mut main_binary_signed = false;
let mut bundles = Vec::<Bundle>::new();
for package_type in &package_types {
// bundle was already built! e.g. DMG already built .app
@@ -115,22 +129,14 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
continue;
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
if let Err(e) = patch_binary(&main_binary_path, package_type) {
log::warn!("Failed to add bundler type to the binary: {e}. Updater plugin may not be able to update this package. This shouldn't normally happen, please report it to https://github.com/tauri-apps/tauri/issues");
}
// sign main binary for every package type after patch
if matches!(target_os, TargetPlatform::Windows) && settings.windows().can_sign() {
if main_binary_signed && main_binary_reset_required {
let mut signed_main_binary = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(&main_binary_path)?;
unsigned_main_binary_copy.seek(SeekFrom::Start(0))?;
std::io::copy(&mut unsigned_main_binary_copy, &mut signed_main_binary)?;
}
windows::sign::try_sign(&main_binary_path, settings)?;
main_binary_signed = true;
}
let bundle_paths = match package_type {
@@ -172,6 +178,14 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
package_type: package_type.to_owned(),
bundle_paths,
});
// Restore unsigned and unpatched binary
let mut modified_main_binary = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(&main_binary_path)?;
main_binary_copy.seek(SeekFrom::Start(0))?;
std::io::copy(&mut main_binary_copy, &mut modified_main_binary)?;
}
if let Some(updater) = settings.updater() {
@@ -241,11 +255,10 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
return Ok(bundles);
}
let bundles_wo_updater = bundles
let finished_bundles = bundles
.iter()
.filter(|b| b.package_type != PackageType::Updater)
.collect::<Vec<_>>();
let finished_bundles = bundles_wo_updater.len();
.count();
let pluralised = if finished_bundles == 1 {
"bundle"
} else {
@@ -274,7 +287,7 @@ fn sign_binaries_if_needed(settings: &Settings, target_os: &TargetPlatform) -> c
if matches!(target_os, TargetPlatform::Windows) {
if settings.windows().can_sign() {
if settings.no_sign() {
log::info!("Skipping binary signing due to --no-sign flag.");
log::warn!("Skipping binary signing due to --no-sign flag.");
return Ok(());
}

View File

@@ -0,0 +1,61 @@
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// KnuthMorrisPratt algorithm
// based on https://github.com/howeih/rust_kmp
#[cfg(any(target_os = "linux", target_os = "windows"))]
pub fn index_of(pattern: &[u8], target: &[u8]) -> Option<usize> {
let failure_function = find_failure_function(pattern);
let mut t_i: usize = 0;
let mut p_i: usize = 0;
let target_len = target.len();
let mut result_idx = None;
let pattern_len = pattern.len();
while (t_i < target_len) && (p_i < pattern_len) {
if target[t_i] == pattern[p_i] {
if result_idx.is_none() {
result_idx.replace(t_i);
}
t_i += 1;
p_i += 1;
if p_i >= pattern_len {
return result_idx;
}
} else {
if p_i == 0 {
p_i = 0;
t_i += 1;
} else {
p_i = failure_function[p_i - 1];
}
result_idx = None;
}
}
None
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
fn find_failure_function(pattern: &[u8]) -> Vec<usize> {
let mut i = 1;
let mut j = 0;
let pattern_length = pattern.len();
let end_i = pattern_length - 1;
let mut failure_function = vec![0usize; pattern_length];
while i <= end_i {
if pattern[i] == pattern[j] {
failure_function[i] = j + 1;
i += 1;
j += 1;
} else if j == 0 {
failure_function[i] = 0;
i += 1;
} else {
j = failure_function[j - 1];
}
}
failure_function
}

View File

@@ -0,0 +1,165 @@
#! /bin/bash
# abort on all errors
set -e
if [ "$DEBUG" != "" ]; then
set -x
fi
script=$(readlink -f "$0")
show_usage() {
echo "Usage: $script --appdir <path to AppDir>"
echo
echo "Bundles GStreamer plugins into an AppDir"
echo
echo "Required variables:"
echo " LINUXDEPLOY=\".../linuxdeploy\" path to linuxdeploy (e.g., AppImage); set automatically when plugin is run directly by linuxdeploy"
echo
echo "Optional variables:"
echo " GSTREAMER_INCLUDE_BAD_PLUGINS=\"1\" (default: disabled; set to empty string or unset to disable)"
echo " GSTREAMER_PLUGINS_DIR=\"...\" (directory containing GStreamer plugins; default: guessed based on main distro architecture)"
echo " GSTREAMER_HELPERS_DIR=\"...\" (directory containing GStreamer helper tools like gst-plugin-scanner; default: guessed based on main distro architecture)"
echo " GSTREAMER_VERSION=\"1.0\" (default: 1.0)"
}
while [ "$1" != "" ]; do
case "$1" in
--plugin-api-version)
echo "0"
exit 0
;;
--appdir)
APPDIR="$2"
shift
shift
;;
--help)
show_usage
exit 0
;;
*)
echo "Invalid argument: $1"
echo
show_usage
exit 1
;;
esac
done
if [ "$APPDIR" == "" ]; then
show_usage
exit 1
fi
if ! which patchelf &>/dev/null && ! type patchelf &>/dev/null; then
echo "Error: patchelf not found"
echo
show_usage
exit 2
fi
if [[ "$LINUXDEPLOY" == "" ]]; then
echo "Error: \$LINUXDEPLOY not set"
echo
show_usage
exit 3
fi
mkdir -p "$APPDIR"
export GSTREAMER_VERSION="${GSTREAMER_VERSION:-1.0}"
plugins_target_dir="$APPDIR"/usr/lib/gstreamer-"$GSTREAMER_VERSION"
helpers_target_dir="$APPDIR"/usr/lib/gstreamer"$GSTREAMER_VERSION"/gstreamer-"$GSTREAMER_VERSION"
if [ "$GSTREAMER_PLUGINS_DIR" != "" ]; then
plugins_dir="${GSTREAMER_PLUGINS_DIR}"
elif [ -d /usr/lib/"$(uname -m)"-linux-gnu/gstreamer-"$GSTREAMER_VERSION" ]; then
plugins_dir=/usr/lib/$(uname -m)-linux-gnu/gstreamer-"$GSTREAMER_VERSION"
else
plugins_dir=/usr/lib/gstreamer-"$GSTREAMER_VERSION"
fi
if [ "$GSTREAMER_HELPERS_DIR" != "" ]; then
helpers_dir="${GSTREAMER_HELPERS_DIR}"
else
helpers_dir=/usr/lib/$(uname -m)-linux-gnu/gstreamer"$GSTREAMER_VERSION"/gstreamer-"$GSTREAMER_VERSION"
fi
if [ ! -d "$plugins_dir" ]; then
echo "Error: could not find plugins directory: $plugins_dir"
exit 1
fi
mkdir -p "$plugins_target_dir"
echo "Copying plugins into $plugins_target_dir"
for i in "$plugins_dir"/*; do
[ -d "$i" ] && continue
[ ! -f "$i" ] && echo "File does not exist: $i" && continue
echo "Copying plugin: $i"
cp "$i" "$plugins_target_dir"
done
"$LINUXDEPLOY" --appdir "$APPDIR"
for i in "$plugins_target_dir"/*; do
[ -d "$i" ] && continue
[ ! -f "$i" ] && echo "File does not exist: $i" && continue
(file "$i" | grep -v ELF --silent) && echo "Ignoring non ELF file: $i" && continue
echo "Manually setting rpath for $i"
patchelf --set-rpath '$ORIGIN/..:$ORIGIN' "$i"
done
mkdir -p "$helpers_target_dir"
echo "Copying helpers in $helpers_target_dir"
for i in "$helpers_dir"/*; do
[ -d "$i" ] && continue
[ ! -f "$i" ] && echo "File does not exist: $i" && continue
echo "Copying helper: $i"
cp "$i" "$helpers_target_dir"
done
for i in "$helpers_target_dir"/*; do
[ -d "$i" ] && continue
[ ! -f "$i" ] && echo "File does not exist: $i" && continue
(file "$i" | grep -v ELF --silent) && echo "Ignoring non ELF file: $i" && continue
echo "Manually setting rpath for $i"
patchelf --set-rpath '$ORIGIN/../..' "$i"
done
echo "Installing AppRun hook"
mkdir -p "$APPDIR"/apprun-hooks
if [ "$GSTREAMER_VERSION" == "1.0" ]; then
cat > "$APPDIR"/apprun-hooks/linuxdeploy-plugin-gstreamer.sh <<\EOF
#! /bin/bash
export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no"
export GST_PLUGIN_SYSTEM_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0"
export GST_PLUGIN_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0"
export GST_PLUGIN_SCANNER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner"
export GST_PTP_HELPER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper"
EOF
elif [ "$GSTREAMER_VERSION" == "0.10" ]; then
cat > "$APPDIR"/apprun-hooks/linuxdeploy-plugin-gstreamer.sh <<\EOF
#! /bin/bash
export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no"
export GST_PLUGIN_SYSTEM_PATH_0_10="${APPDIR}/usr/lib/gstreamer-1.0"
export GST_PLUGIN_SCANNER_0_10="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner"
export GST_PTP_HELPER_0_10="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper"
EOF
else
echo "Warning: unknown GStreamer version: $GSTREAMER_VERSION, cannot install AppRun hook"
fi

View File

@@ -0,0 +1,327 @@
#! /usr/bin/env bash
# GTK3 environment variables: https://developer.gnome.org/gtk3/stable/gtk-running.html
# GTK4 environment variables: https://developer.gnome.org/gtk4/stable/gtk-running.html
# abort on all errors
set -e
if [ "$DEBUG" != "" ]; then
set -x
verbose="--verbose"
fi
script=$(readlink -f "$0")
show_usage() {
echo "Usage: $script --appdir <path to AppDir>"
echo
echo "Bundles resources for applications that use GTK into an AppDir"
echo
echo "Required variables:"
echo " LINUXDEPLOY=\".../linuxdeploy\" path to linuxdeploy (e.g., AppImage); set automatically when plugin is run directly by linuxdeploy"
#echo
#echo "Optional variables:"
#echo " DEPLOY_GTK_VERSION (major version of GTK to deploy, e.g. '2', '3' or '4'; auto-detect by default)"
}
variable_is_true() {
local var="$1"
if [ -n "$var" ] && { [ "$var" == "true" ] || [ "$var" -gt 0 ]; } 2> /dev/null; then
return 0 # true
else
return 1 # false
fi
}
get_pkgconf_variable() {
local variable="$1"
local library="$2"
local default_path="$3"
path="$("$PKG_CONFIG" --variable="$variable" "$library")"
if [ -n "$path" ]; then
echo "$path"
elif [ -n "$default_path" ]; then
echo "$default_path"
else
echo "$0: there is no '$variable' variable for '$library' library." > /dev/stderr
echo "Please check the '$library.pc' file is present in \$PKG_CONFIG_PATH (you may need to install the appropriate -dev/-devel package)." > /dev/stderr
exit 1
fi
}
copy_tree() {
local src=("${@:1:$#-1}")
local dst="${*:$#}"
for elem in "${src[@]}"; do
mkdir -p "${dst::-1}$elem"
cp "$elem" --archive --parents --target-directory="$dst" $verbose
done
}
search_tool() {
local tool="$1"
local directory="$2"
if command -v "$tool"; then
return 0
fi
PATH_ARRAY=(
"/usr/lib/$(uname -m)-linux-gnu/$directory/$tool"
"/usr/lib/$directory/$tool"
"/usr/bin/$tool"
"/usr/bin/$tool-64"
"/usr/bin/$tool-32"
)
for path in "${PATH_ARRAY[@]}"; do
if [ -x "$path" ]; then
echo "$path"
return 0
fi
done
}
#DEPLOY_GTK_VERSION="${DEPLOY_GTK_VERSION:-0}" # When not set by user, this variable use the integer '0' as a sentinel value
DEPLOY_GTK_VERSION=3 # Force GTK3 for tauri apps
APPDIR=
while [ "$1" != "" ]; do
case "$1" in
--plugin-api-version)
echo "0"
exit 0
;;
--appdir)
APPDIR="$2"
shift
shift
;;
--help)
show_usage
exit 0
;;
*)
echo "Invalid argument: $1"
echo
show_usage
exit 1
;;
esac
done
if [ "$APPDIR" == "" ]; then
show_usage
exit 1
fi
mkdir -p "$APPDIR"
# make lib64 writable again.
chmod +w "$APPDIR"/usr/lib64 || true
if command -v pkgconf > /dev/null; then
PKG_CONFIG="pkgconf"
elif command -v pkg-config > /dev/null; then
PKG_CONFIG="pkg-config"
else
echo "$0: pkg-config/pkgconf not found in PATH, aborting"
exit 1
fi
if ! command -v find &>/dev/null && ! type find &>/dev/null; then
echo -e "$0: find not found.\nInstall findutils then re-run the plugin."
exit 1
fi
if [ -z "$LINUXDEPLOY" ]; then
echo -e "$0: LINUXDEPLOY environment variable is not set.\nDownload a suitable linuxdeploy AppImage, set the environment variable and re-run the plugin."
exit 1
fi
gtk_versions=0 # Count major versions of GTK when auto-detect GTK version
if [ "$DEPLOY_GTK_VERSION" -eq 0 ]; then
echo "Determining which GTK version to deploy"
while IFS= read -r -d '' file; do
if [ "$DEPLOY_GTK_VERSION" -ne 2 ] && ldd "$file" | grep -q "libgtk-x11-2.0.so"; then
DEPLOY_GTK_VERSION=2
gtk_versions="$((gtk_versions+1))"
fi
if [ "$DEPLOY_GTK_VERSION" -ne 3 ] && ldd "$file" | grep -q "libgtk-3.so"; then
DEPLOY_GTK_VERSION=3
gtk_versions="$((gtk_versions+1))"
fi
if [ "$DEPLOY_GTK_VERSION" -ne 4 ] && ldd "$file" | grep -q "libgtk-4.so"; then
DEPLOY_GTK_VERSION=4
gtk_versions="$((gtk_versions+1))"
fi
done < <(find "$APPDIR/usr/bin" -executable -type f -print0)
fi
if [ "$gtk_versions" -gt 1 ]; then
echo "$0: can not deploy multiple GTK versions at the same time."
echo "Please set DEPLOY_GTK_VERSION to {2, 3, 4}."
exit 1
elif [ "$DEPLOY_GTK_VERSION" -eq 0 ]; then
echo "$0: failed to auto-detect GTK version."
echo "Please set DEPLOY_GTK_VERSION to {2, 3, 4}."
exit 1
fi
echo "Installing AppRun hook"
HOOKSDIR="$APPDIR/apprun-hooks"
HOOKFILE="$HOOKSDIR/linuxdeploy-plugin-gtk.sh"
mkdir -p "$HOOKSDIR"
cat > "$HOOKFILE" <<\EOF
#! /usr/bin/env bash
gsettings get org.gnome.desktop.interface gtk-theme 2> /dev/null | grep -qi "dark" && GTK_THEME_VARIANT="dark" || GTK_THEME_VARIANT="light"
APPIMAGE_GTK_THEME="${APPIMAGE_GTK_THEME:-"Adwaita:$GTK_THEME_VARIANT"}" # Allow user to override theme (discouraged)
export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage
export GTK_DATA_PREFIX="$APPDIR"
export GTK_THEME="$APPIMAGE_GTK_THEME" # Custom themes are broken
export GDK_BACKEND=x11 # Crash with Wayland backend on Wayland - We tested it without it and ended up with this: https://github.com/tauri-apps/tauri/issues/8541
export XDG_DATA_DIRS="$APPDIR/usr/share:/usr/share:$XDG_DATA_DIRS" # g_get_system_data_dirs() from GLib
EOF
echo "Installing GLib schemas"
# Note: schemasdir is undefined on Ubuntu 16.04
glib_schemasdir="$(get_pkgconf_variable "schemasdir" "gio-2.0" "/usr/share/glib-2.0/schemas")"
copy_tree "$glib_schemasdir" "$APPDIR/"
glib-compile-schemas "$APPDIR/$glib_schemasdir"
cat >> "$HOOKFILE" <<EOF
export GSETTINGS_SCHEMA_DIR="\$APPDIR/$glib_schemasdir"
EOF
case "$DEPLOY_GTK_VERSION" in
2)
# https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/pull/20#issuecomment-826354261
echo "WARNING: Gtk+2 applications are not fully supported by this plugin"
;;
3)
echo "Installing GTK 3.0 modules"
gtk3_exec_prefix="$(get_pkgconf_variable "exec_prefix" "gtk+-3.0")"
gtk3_libdir="$(get_pkgconf_variable "libdir" "gtk+-3.0")/gtk-3.0"
#gtk3_path="$gtk3_libdir/modules" export GTK_PATH="\$APPDIR/$gtk3_path"
gtk3_immodulesdir="$gtk3_libdir/$(get_pkgconf_variable "gtk_binary_version" "gtk+-3.0")/immodules"
gtk3_printbackendsdir="$gtk3_libdir/$(get_pkgconf_variable "gtk_binary_version" "gtk+-3.0")/printbackends"
gtk3_immodules_cache_file="$(dirname "$gtk3_immodulesdir")/immodules.cache"
gtk3_immodules_query="$(search_tool "gtk-query-immodules-3.0" "libgtk-3-0")"
copy_tree "$gtk3_libdir" "$APPDIR/"
cat >> "$HOOKFILE" <<EOF
export GTK_EXE_PREFIX="\$APPDIR/$gtk3_exec_prefix"
export GTK_PATH="\$APPDIR/$gtk3_libdir:/usr/lib64/gtk-3.0:/usr/lib/x86_64-linux-gnu/gtk-3.0"
export GTK_IM_MODULE_FILE="\$APPDIR/$gtk3_immodules_cache_file"
EOF
if [ -x "$gtk3_immodules_query" ]; then
echo "Updating immodules cache in $APPDIR/$gtk3_immodules_cache_file"
"$gtk3_immodules_query" > "$APPDIR/$gtk3_immodules_cache_file"
else
echo "WARNING: gtk-query-immodules-3.0 not found"
fi
if [ ! -f "$APPDIR/$gtk3_immodules_cache_file" ]; then
echo "WARNING: immodules.cache file is missing"
fi
sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/$gtk3_immodules_cache_file"
;;
4)
echo "Installing GTK 4.0 modules"
gtk4_exec_prefix="$(get_pkgconf_variable "exec_prefix" "gtk4" "/usr")"
gtk4_libdir="$(get_pkgconf_variable "libdir" "gtk4")/gtk-4.0"
gtk4_path="$gtk4_libdir/modules"
copy_tree "$gtk4_libdir" "$APPDIR/"
cat >> "$HOOKFILE" <<EOF
export GTK_EXE_PREFIX="\$APPDIR/$gtk4_exec_prefix"
export GTK_PATH="\$APPDIR/$gtk4_path"
EOF
;;
*)
echo "$0: '$DEPLOY_GTK_VERSION' is not a valid GTK major version."
echo "Please set DEPLOY_GTK_VERSION to {2, 3, 4}."
exit 1
esac
echo "Installing GDK PixBufs"
gdk_libdir="$(get_pkgconf_variable "libdir" "gdk-pixbuf-2.0")"
gdk_pixbuf_binarydir="$(get_pkgconf_variable "gdk_pixbuf_binarydir" "gdk-pixbuf-2.0")"
gdk_pixbuf_cache_file="$(get_pkgconf_variable "gdk_pixbuf_cache_file" "gdk-pixbuf-2.0")"
gdk_pixbuf_moduledir="$(get_pkgconf_variable "gdk_pixbuf_moduledir" "gdk-pixbuf-2.0")"
# Note: gdk_pixbuf_query_loaders variable is not defined on some systems
gdk_pixbuf_query="$(search_tool "gdk-pixbuf-query-loaders" "gdk-pixbuf-2.0")"
copy_tree "$gdk_pixbuf_binarydir" "$APPDIR/"
cat >> "$HOOKFILE" <<EOF
export GDK_PIXBUF_MODULE_FILE="\$APPDIR/$gdk_pixbuf_cache_file"
EOF
if [ -x "$gdk_pixbuf_query" ]; then
echo "Updating pixbuf cache in $APPDIR/$gdk_pixbuf_cache_file"
"$gdk_pixbuf_query" > "$APPDIR/$gdk_pixbuf_cache_file"
else
echo "WARNING: gdk-pixbuf-query-loaders not found"
fi
if [ ! -f "$APPDIR/$gdk_pixbuf_cache_file" ]; then
echo "WARNING: loaders.cache file is missing"
fi
sed -i "s|$gdk_pixbuf_moduledir/||g" "$APPDIR/$gdk_pixbuf_cache_file"
echo "Copying more libraries"
gobject_libdir="$(get_pkgconf_variable "libdir" "gobject-2.0")"
gio_libdir="$(get_pkgconf_variable "libdir" "gio-2.0")"
librsvg_libdir="$(get_pkgconf_variable "libdir" "librsvg-2.0")"
pango_libdir="$(get_pkgconf_variable "libdir" "pango")"
pangocairo_libdir="$(get_pkgconf_variable "libdir" "pangocairo")"
pangoft2_libdir="$(get_pkgconf_variable "libdir" "pangoft2")"
FIND_ARRAY=(
"$gdk_libdir" "libgdk_pixbuf-*.so*"
"$gobject_libdir" "libgobject-*.so*"
"$gio_libdir" "libgio-*.so*"
"$librsvg_libdir" "librsvg-*.so*"
"$pango_libdir" "libpango-*.so*"
"$pangocairo_libdir" "libpangocairo-*.so*"
"$pangoft2_libdir" "libpangoft2-*.so*"
)
LIBRARIES=()
for (( i=0; i<${#FIND_ARRAY[@]}; i+=2 )); do
directory=${FIND_ARRAY[i]}
library=${FIND_ARRAY[i+1]}
while IFS= read -r -d '' file; do
LIBRARIES+=( "--library=$file" )
done < <(find "$directory" \( -type l -o -type f \) -name "$library" -print0)
done
env LINUXDEPLOY_PLUGIN_MODE=1 "$LINUXDEPLOY" --appdir="$APPDIR" "${LIBRARIES[@]}"
# Create symbolic links as a workaround
# Details: https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/issues/24#issuecomment-1030026529
echo "Manually setting rpath for GTK modules"
PATCH_ARRAY=(
"$gtk3_immodulesdir"
"$gtk3_printbackendsdir"
"$gdk_pixbuf_moduledir"
)
for directory in "${PATCH_ARRAY[@]}"; do
while IFS= read -r -d '' file; do
ln $verbose -s "${file/\/usr\/lib\//}" "$APPDIR/usr/lib"
done < <(find "$directory" -name '*.so' -print0)
done
# set write permission on lib64 again to make it deletable.
chmod +w "$APPDIR"/usr/lib64 || true
# We have to copy the files first to not get permission errors when we assign gio_extras_dir
find /usr/lib* -name libgiognutls.so -exec mkdir -p "$APPDIR"/"$(dirname '{}')" \; -exec cp --parents '{}' "$APPDIR/" \; || true
# related files that we seemingly don't need:
# libgiolibproxy.so - libgiognomeproxy.so - glib-pacrunner
gio_extras_dir=$(find "$APPDIR"/usr/lib* -name libgiognutls.so -exec dirname '{}' \; 2>/dev/null)
cat >> "$HOOKFILE" <<EOF
export GIO_EXTRA_MODULES="\$APPDIR/${gio_extras_dir#"$APPDIR"/}"
EOF
#binary patch absolute paths in libwebkit files
find "$APPDIR"/usr/lib* -name 'libwebkit*' -exec sed -i -e "s|/usr|././|g" '{}' \;

View File

@@ -119,8 +119,9 @@ pub fn generate_data(
for bin in settings.binaries() {
let bin_path = settings.binary_path(bin);
fs_utils::copy_file(&bin_path, &bin_dir.join(bin.name()))
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
let trgt = bin_dir.join(bin.name());
fs_utils::copy_file(&bin_path, &trgt)
.with_context(|| format!("Failed to copy binary from {bin_path:?} to {trgt:?}"))?;
}
copy_resource_files(settings, &data_dir).with_context(|| "Failed to copy resource files")?;

View File

@@ -7,8 +7,3 @@ pub mod appimage;
pub mod debian;
pub mod freedesktop;
pub mod rpm;
mod util;
#[cfg(target_os = "linux")]
pub use util::patch_binary;

View File

@@ -1,59 +0,0 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/// Change value of __TAURI_BUNDLE_TYPE static variable to mark which package type it was bundled in
#[cfg(target_os = "linux")]
pub fn patch_binary(
binary_path: &std::path::PathBuf,
package_type: &crate::PackageType,
) -> crate::Result<()> {
let mut file_data = std::fs::read(binary_path).expect("Could not read binary file.");
let elf = match goblin::Object::parse(&file_data)? {
goblin::Object::Elf(elf) => elf,
_ => return Err(crate::Error::GenericError("Not an ELF file".to_owned())),
};
let offset = find_bundle_type_symbol(elf).ok_or(crate::Error::MissingBundleTypeVar)?;
let offset = offset as usize;
if offset + 3 <= file_data.len() {
let chars = &mut file_data[offset..offset + 3];
match package_type {
crate::PackageType::Deb => chars.copy_from_slice(b"DEB"),
crate::PackageType::Rpm => chars.copy_from_slice(b"RPM"),
crate::PackageType::AppImage => chars.copy_from_slice(b"APP"),
_ => {
return Err(crate::Error::InvalidPackageType(
package_type.short_name().to_owned(),
"linux".to_owned(),
))
}
}
std::fs::write(binary_path, &file_data)
.map_err(|error| crate::Error::BinaryWriteError(error.to_string()))?;
} else {
return Err(crate::Error::BinaryOffsetOutOfRange);
}
Ok(())
}
/// Find address of a symbol in relocations table
#[cfg(target_os = "linux")]
fn find_bundle_type_symbol(elf: goblin::elf::Elf<'_>) -> Option<i64> {
for sym in elf.syms.iter() {
if let Some(name) = elf.strtab.get_at(sym.st_name) {
if name == "__TAURI_BUNDLE_TYPE" {
for reloc in elf.dynrelas.iter() {
if reloc.r_offset == sym.st_value {
return Some(reloc.r_addend.unwrap());
}
}
}
}
}
None
}

View File

@@ -65,16 +65,12 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
log::info!(action = "Bundling"; "{} ({})", app_product_name, app_bundle_path.display());
if app_bundle_path.exists() {
fs::remove_dir_all(&app_bundle_path).fs_context(
"failed to remove old app bundle",
app_bundle_path.to_path_buf(),
)?;
fs::remove_dir_all(&app_bundle_path)
.fs_context("failed to remove old app bundle", &app_bundle_path)?;
}
let bundle_directory = app_bundle_path.join("Contents");
fs::create_dir_all(&bundle_directory).fs_context(
"failed to create bundle directory",
bundle_directory.to_path_buf(),
)?;
fs::create_dir_all(&bundle_directory)
.fs_context("failed to create bundle directory", &bundle_directory)?;
let resources_dir = bundle_directory.join("Resources");
let bin_dir = bundle_directory.join("MacOS");
@@ -459,20 +455,12 @@ fn copy_frameworks_to_bundle(
) -> crate::Result<Vec<SignTarget>> {
let mut paths = Vec::new();
let frameworks = settings
.macos()
.frameworks
.as_ref()
.cloned()
.unwrap_or_default();
let frameworks = settings.macos().frameworks.clone().unwrap_or_default();
if frameworks.is_empty() {
return Ok(paths);
}
let dest_dir = bundle_directory.join("Frameworks");
fs::create_dir_all(&dest_dir).fs_context(
"failed to create Frameworks directory",
dest_dir.to_path_buf(),
)?;
fs::create_dir_all(&dest_dir).fs_context("failed to create Frameworks directory", &dest_dir)?;
for framework in frameworks.iter() {
if framework.ends_with(".framework") {
let src_path = PathBuf::from(framework);

View File

@@ -44,15 +44,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
log::info!(action = "Bundling"; "{} ({})", app_product_name, app_bundle_path.display());
if app_bundle_path.exists() {
fs::remove_dir_all(&app_bundle_path).fs_context(
"failed to remove old app bundle",
app_bundle_path.to_path_buf(),
)?;
fs::remove_dir_all(&app_bundle_path)
.fs_context("failed to remove old app bundle", &app_bundle_path)?;
}
fs::create_dir_all(&app_bundle_path).fs_context(
"failed to create bundle directory",
app_bundle_path.to_path_buf(),
)?;
fs::create_dir_all(&app_bundle_path)
.fs_context("failed to create bundle directory", &app_bundle_path)?;
for src in settings.resource_files() {
let src = src?;

View File

@@ -21,7 +21,7 @@ pub fn keychain(identity: Option<&str>) -> crate::Result<Option<tauri_macos_sign
var_os("APPLE_CERTIFICATE"),
var_os("APPLE_CERTIFICATE_PASSWORD"),
) {
// import user certificate - useful for for CI build
// import user certificate - useful for CI build
let keychain =
tauri_macos_sign::Keychain::with_certificate(&certificate_encoded, &certificate_password)
.map_err(Box::new)?;

View File

@@ -257,7 +257,7 @@ pub struct AppImageSettings {
pub struct RpmSettings {
/// The list of RPM dependencies your application relies on.
pub depends: Option<Vec<String>>,
/// the list of of RPM dependencies your application recommends.
/// the list of RPM dependencies your application recommends.
pub recommends: Option<Vec<String>>,
/// The list of RPM dependencies your application provides.
pub provides: Option<Vec<String>>,

View File

@@ -14,5 +14,3 @@ pub use util::{
NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, WIX_OUTPUT_FOLDER_NAME,
WIX_UPDATER_OUTPUT_FOLDER_NAME,
};
pub use util::patch_binary;

View File

@@ -753,26 +753,28 @@ pub fn build_wix_app_installer(
}
let main_wxs_path = output_path.join("main.wxs");
fs::write(main_wxs_path, handlebars.render("main.wxs", &data)?)?;
fs::write(&main_wxs_path, handlebars.render("main.wxs", &data)?)?;
let mut candle_inputs = vec![("main.wxs".into(), Vec::new())];
let mut candle_inputs = vec![];
let current_dir = std::env::current_dir()?;
let extension_regex = Regex::new("\"http://schemas.microsoft.com/wix/(\\w+)\"")?;
for fragment_path in fragment_paths {
let fragment_path = current_dir.join(fragment_path);
let fragment_content = fs::read_to_string(&fragment_path)?;
let fragment_handlebars = Handlebars::new();
let fragment = fragment_handlebars.render_template(&fragment_content, &data)?;
let input_paths =
std::iter::once(main_wxs_path).chain(fragment_paths.iter().map(|p| current_dir.join(p)));
for input_path in input_paths {
let input_content = fs::read_to_string(&input_path)?;
let input_handlebars = Handlebars::new();
let input = input_handlebars.render_template(&input_content, &data)?;
let mut extensions = Vec::new();
for cap in extension_regex.captures_iter(&fragment) {
for cap in extension_regex.captures_iter(&input) {
let path = wix_toolset_path.join(format!("Wix{}.dll", &cap[1]));
if settings.windows().can_sign() {
try_sign(&path, settings)?;
}
extensions.push(path);
}
candle_inputs.push((fragment_path, extensions));
candle_inputs.push((input_path, extensions));
}
let mut fragment_extensions = HashSet::new();

View File

@@ -0,0 +1,27 @@
LangString addOrReinstall ${LANG_NORWEGIAN} "Legg til/reinstaller komponenter"
LangString alreadyInstalled ${LANG_NORWEGIAN} "Allerede installert"
LangString alreadyInstalledLong ${LANG_NORWEGIAN} "${PRODUCTNAME} ${VERSION} er allerede installert. Velg operasjonen du vil utføre og klikk Neste for å fortsette."
LangString appRunning ${LANG_NORWEGIAN} "{{product_name}} kjører! Lukk den først og prøv igjen."
LangString appRunningOkKill ${LANG_NORWEGIAN} "{{product_name}} kjører!$\nKlikk OK for å avslutte den"
LangString chooseMaintenanceOption ${LANG_NORWEGIAN} "Velg vedlikeholdsoperasjonen som skal utføres."
LangString choowHowToInstall ${LANG_NORWEGIAN} "Velg hvordan du vil installere ${PRODUCTNAME}."
LangString createDesktop ${LANG_NORWEGIAN} "Opprett skrivebordssnarvei"
LangString dontUninstall ${LANG_NORWEGIAN} "Ikke avinstaller"
LangString dontUninstallDowngrade ${LANG_NORWEGIAN} "Ikke avinstaller (nedgradering uten avinstallasjon er deaktivert for denne installasjonen)"
LangString failedToKillApp ${LANG_NORWEGIAN} "Kunne ikke avslutte {{product_name}}. Lukk den først og prøv igjen"
LangString installingWebview2 ${LANG_NORWEGIAN} "Installerer WebView2..."
LangString newerVersionInstalled ${LANG_NORWEGIAN} "En nyere versjon av ${PRODUCTNAME} er allerede installert! Det anbefales ikke at du installerer en eldre versjon. Hvis du virkelig vil installere denne eldre versjonen, er det bedre å avinstallere den nåværende versjonen først. Velg operasjonen du vil utføre og klikk Neste for å fortsette."
LangString older ${LANG_NORWEGIAN} "eldre"
LangString olderOrUnknownVersionInstalled ${LANG_NORWEGIAN} "En $R4-versjon av ${PRODUCTNAME} er installert på systemet ditt. Det anbefales at du avinstallerer den nåværende versjonen før installasjon. Velg operasjonen du vil utføre og klikk Neste for å fortsette."
LangString silentDowngrades ${LANG_NORWEGIAN} "Nedgraderinger er deaktivert for denne installasjonen. Kan ikke fortsette med stille installasjon; bruk den grafiske installasjonen i stedet.$\n"
LangString unableToUninstall ${LANG_NORWEGIAN} "Kunne ikke avinstallere!"
LangString uninstallApp ${LANG_NORWEGIAN} "Avinstaller ${PRODUCTNAME}"
LangString uninstallBeforeInstalling ${LANG_NORWEGIAN} "Avinstaller før installasjon"
LangString unknown ${LANG_NORWEGIAN} "ukjent"
LangString webview2AbortError ${LANG_NORWEGIAN} "Kunne ikke installere WebView2! Appen kan ikke kjøre uten den. Prøv å starte installasjonen på nytt."
LangString webview2DownloadError ${LANG_NORWEGIAN} "Feil: Nedlasting av WebView2 mislyktes - $0"
LangString webview2DownloadSuccess ${LANG_NORWEGIAN} "WebView2-bootstrapper lastet ned"
LangString webview2Downloading ${LANG_NORWEGIAN} "Laster ned WebView2-bootstrapper..."
LangString webview2InstallError ${LANG_NORWEGIAN} "Feil: Installering av WebView2 mislyktes med avslutningskode $1"
LangString webview2InstallSuccess ${LANG_NORWEGIAN} "WebView2 ble installert"
LangString deleteAppData ${LANG_NORWEGIAN} "Slett programdata"

View File

@@ -36,12 +36,12 @@ use std::{
// URLS for the NSIS toolchain.
#[cfg(target_os = "windows")]
const NSIS_URL: &str =
"https://github.com/tauri-apps/binary-releases/releases/download/nsis-3/nsis-3.zip";
"https://github.com/tauri-apps/binary-releases/releases/download/nsis-3.11/nsis-3.11.zip";
#[cfg(target_os = "windows")]
const NSIS_SHA1: &str = "057e83c7d82462ec394af76c87d06733605543d4";
const NSIS_SHA1: &str = "EF7FF767E5CBD9EDD22ADD3A32C9B8F4500BB10D";
const NSIS_TAURI_UTILS_URL: &str =
"https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.5.1/nsis_tauri_utils.dll";
const NSIS_TAURI_UTILS_SHA1: &str = "B053B2E5FDB97257954C8F935D80964F056520AE";
"https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.5.3/nsis_tauri_utils.dll";
const NSIS_TAURI_UTILS_SHA1: &str = "75197FEE3C6A814FE035788D1C34EAD39349B860";
#[cfg(target_os = "windows")]
const NSIS_REQUIRED_FILES: &[&str] = &[
@@ -55,6 +55,9 @@ const NSIS_REQUIRED_FILES: &[&str] = &[
"Include/x64.nsh",
"Include/nsDialogs.nsh",
"Include/WinMessages.nsh",
"Include/Win/COM.nsh",
"Include/Win/Propkey.nsh",
"Include/Win/RestartManager.nsh",
];
const NSIS_PLUGIN_FILES: &[&str] = &[
"NSISdl.dll",
@@ -125,7 +128,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
let data = download_and_verify(NSIS_URL, NSIS_SHA1, HashAlgorithm::Sha1)?;
log::info!("extracting NSIS");
crate::utils::http_utils::extract_zip(&data, _tauri_tools_path)?;
fs::rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?;
fs::rename(_tauri_tools_path.join("nsis-3.11"), nsis_toolset_path)?;
}
// download additional plugins
@@ -295,8 +298,12 @@ fn build_nsis_app_installer(
data.insert("copyright", to_json(settings.copyright_string()));
if settings.windows().can_sign() {
let sign_cmd = format!("{:?}", sign_command("%1", &settings.sign_params())?);
data.insert("uninstaller_sign_cmd", to_json(sign_cmd));
if settings.no_sign() {
log::warn!("Skipping signing for NSIS uninstaller due to --no-sign flag.");
} else {
let sign_cmd = format!("{:?}", sign_command("%1", &settings.sign_params())?);
data.insert("uninstaller_sign_cmd", to_json(sign_cmd));
}
}
let version = settings.version_string();
@@ -614,13 +621,16 @@ fn build_nsis_app_installer(
fs::create_dir_all(nsis_installer_path.parent().unwrap())?;
if settings.windows().can_sign() {
log::info!("Signing NSIS plugins");
for dll in NSIS_PLUGIN_FILES {
let path = additional_plugins_path.join(dll);
if path.exists() {
try_sign(&path, settings)?;
} else {
log::warn!("Could not find {}, skipping signing", path.display());
if let Some(plugin_copy_path) = &maybe_plugin_copy_path {
let plugin_copy_path = plugin_copy_path.join("x86-unicode");
log::info!("Signing NSIS plugins");
for dll in NSIS_PLUGIN_FILES {
let path = plugin_copy_path.join(dll);
if path.exists() {
try_sign(&path, settings)?;
} else {
log::warn!("Could not find {}, skipping signing", path.display());
}
}
}
}
@@ -857,6 +867,7 @@ fn get_lang_data(lang: &str) -> Option<(String, &[u8])> {
"swedish" => include_bytes!("./languages/Swedish.nsh"),
"portuguese" => include_bytes!("./languages/Portuguese.nsh"),
"ukrainian" => include_bytes!("./languages/Ukrainian.nsh"),
"norwegian" => include_bytes!("./languages/Norwegian.nsh"),
_ => return None,
};
Some((path, content))

View File

@@ -48,6 +48,7 @@
Pop $R0
Sleep 500
${If} $R0 = 0
${OrIf} $R0 = 2
Goto app_check_done_${UniqueID}
${Else}
IfSilent silent_${UniqueID} ui_${UniqueID}

View File

@@ -266,8 +266,7 @@ pub fn try_sign<P: AsRef<Path>>(file_path: P, settings: &Settings) -> crate::Res
pub fn should_sign(file_path: &Path) -> crate::Result<bool> {
let is_binary = file_path
.extension()
.and_then(|extension| extension.to_str())
.is_some_and(|extension| matches!(extension, "exe" | "dll"));
.is_some_and(|ext| ext == "exe" || ext == "dll");
if !is_binary {
return Ok(false);
}

View File

@@ -77,75 +77,3 @@ pub fn os_bitness<'a>() -> Option<&'a str> {
_ => None,
}
}
pub fn patch_binary(binary_path: &PathBuf, package_type: &crate::PackageType) -> crate::Result<()> {
let mut file_data = std::fs::read(binary_path)?;
let pe = match goblin::Object::parse(&file_data)? {
goblin::Object::PE(pe) => pe,
_ => {
return Err(crate::Error::BinaryParseError(
std::io::Error::new(std::io::ErrorKind::InvalidInput, "binary is not a PE file").into(),
));
}
};
let tauri_bundle_section = pe
.sections
.iter()
.find(|s| s.name().unwrap_or_default() == ".taubndl")
.ok_or(crate::Error::MissingBundleTypeVar)?;
let data_offset = tauri_bundle_section.pointer_to_raw_data as usize;
let pointer_size = if pe.is_64 { 8 } else { 4 };
let ptr_bytes = file_data
.get(data_offset..data_offset + pointer_size)
.ok_or(crate::Error::BinaryOffsetOutOfRange)?;
// `try_into` is safe to `unwrap` here because we have already checked the slice's size through `get`
let ptr_value = if pe.is_64 {
u64::from_le_bytes(ptr_bytes.try_into().unwrap())
} else {
u32::from_le_bytes(ptr_bytes.try_into().unwrap()).into()
};
let rdata_section = pe
.sections
.iter()
.find(|s| s.name().unwrap_or_default() == ".rdata")
.ok_or_else(|| {
crate::Error::BinaryParseError(
std::io::Error::new(std::io::ErrorKind::InvalidInput, ".rdata section not found").into(),
)
})?;
let rva = ptr_value.checked_sub(pe.image_base as u64).ok_or_else(|| {
crate::Error::BinaryParseError(
std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid RVA offset").into(),
)
})?;
// see "Relative virtual address (RVA)" for explanation of offset arithmetic here:
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#general-concepts
let file_offset = rdata_section.pointer_to_raw_data as usize
+ (rva as usize).saturating_sub(rdata_section.virtual_address as usize);
// Overwrite the string at that offset
let string_bytes = file_data
.get_mut(file_offset..file_offset + 3)
.ok_or(crate::Error::BinaryOffsetOutOfRange)?;
match package_type {
crate::PackageType::Nsis => string_bytes.copy_from_slice(b"NSS"),
crate::PackageType::WindowsMsi => string_bytes.copy_from_slice(b"MSI"),
_ => {
return Err(crate::Error::InvalidPackageType(
package_type.short_name().to_owned(),
"windows".to_owned(),
));
}
}
std::fs::write(binary_path, &file_data)
.map_err(|e| crate::Error::BinaryWriteError(e.to_string()))?;
Ok(())
}

View File

@@ -105,6 +105,7 @@ pub enum Error {
#[error("Failed to write binary file changes: `{0}`")]
BinaryWriteError(String),
/// Invalid offset while patching binary file
#[deprecated]
#[error("Invalid offset while patching binary file")]
BinaryOffsetOutOfRange,
/// Unsupported architecture.

View File

@@ -14,6 +14,8 @@ use sha2::Digest;
use url::Url;
use zip::ZipArchive;
const BUNDLER_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
fn generate_github_mirror_url_from_template(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE")
.ok()
@@ -47,7 +49,15 @@ fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> {
generate_github_mirror_url_from_template(url)
.or_else(|| generate_github_mirror_url_from_base(url))
.map(|alt_url| (ureq::agent(), alt_url))
.map(|alt_url| {
(
ureq::Agent::config_builder()
.user_agent(BUNDLER_USER_AGENT)
.build()
.into(),
alt_url,
)
})
}
fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
@@ -55,22 +65,21 @@ fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
}
pub(crate) fn base_ureq_agent() -> ureq::Agent {
#[allow(unused_mut)]
let mut config_builder = ureq::Agent::config_builder()
.user_agent(BUNDLER_USER_AGENT)
.proxy(ureq::Proxy::try_from_env());
#[cfg(feature = "platform-certs")]
let agent: ureq::Agent = ureq::Agent::config_builder()
.tls_config(
{
config_builder = config_builder.tls_config(
ureq::tls::TlsConfig::builder()
.root_certs(ureq::tls::RootCerts::PlatformVerifier)
.build(),
)
.proxy(ureq::Proxy::try_from_env())
.build()
.into();
#[cfg(not(feature = "platform-certs"))]
let agent: ureq::Agent = ureq::Agent::config_builder()
.proxy(ureq::Proxy::try_from_env())
.build()
.into();
agent
);
}
config_builder.build().into()
}
#[allow(dead_code)]

View File

@@ -1,5 +1,108 @@
# Changelog
## \[2.9.6]
### What's Changed
- [`7b1b3514d`](https://www.github.com/tauri-apps/tauri/commit/7b1b3514df771e6e9859b9f54fa4df332433948e) ([#14621](https://www.github.com/tauri-apps/tauri/pull/14621) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Errors like `Error Failed to parse version 2 for for NPM package tauri` when there was no `package-lock.json` file present yet or when using ones like `link:./tauri` are now only logged in `--verbose` mode.
### Dependencies
- Upgraded to `tauri-macos-sign@2.3.2`
- Upgraded to `tauri-bundler@2.7.5`
## \[2.9.5]
### Bug Fixes
- [`f022b2d1a`](https://www.github.com/tauri-apps/tauri/commit/f022b2d1ae57612e39c75782926f2f341d9034a8) ([#14582](https://www.github.com/tauri-apps/tauri/pull/14582) by [@hrzlgnm](https://www.github.com/tauri-apps/tauri/../../hrzlgnm)) Fixed an issue that caused the cli to error out with missing private key, in case the option `--no-sign` was requested and the `tauri.config` has signing key set and the plugin `tauri-plugin-updater` is used.
- [`f855caf8a`](https://www.github.com/tauri-apps/tauri/commit/f855caf8a3830aa5dd6d0b039312866a5d9c3606) ([#14481](https://www.github.com/tauri-apps/tauri/pull/14481) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fixed the mismatched tauri package versions check didn't work for pnpm
- [`79a7d9ec0`](https://www.github.com/tauri-apps/tauri/commit/79a7d9ec01be1a371b8e923848140fea75e9caed) ([#14468](https://www.github.com/tauri-apps/tauri/pull/14468) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the cli to print errors like `Error Failed to parse version 2 for crate tauri` when there was no `Cargo.lock` file present yet. This will still be logged in `--verbose` mode.
### Performance Improvements
- [`ce98d87ce`](https://www.github.com/tauri-apps/tauri/commit/ce98d87ce0aaa907285852eb80691197424e03c3) ([#14474](https://www.github.com/tauri-apps/tauri/pull/14474) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) refactor: remove needless collect. No user facing changes.
- [`ee3cc4a91`](https://www.github.com/tauri-apps/tauri/commit/ee3cc4a91bf1315ecaefe90f423ffd55ef6c40db) ([#14475](https://www.github.com/tauri-apps/tauri/pull/14475) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) perf: remove needless clones in various files for improved performance. No user facing changes.
### Dependencies
- Upgraded to `tauri-bundler@2.7.4`
- Upgraded to `tauri-macos-sign@2.3.1`
- Upgraded to `tauri-utils@2.8.1`
## \[2.9.4]
### Bug Fixes
- [`b586ecf1f`](https://www.github.com/tauri-apps/tauri/commit/b586ecf1f4b3b087f9aa6c4668c2c18b1b7925f4) ([#14416](https://www.github.com/tauri-apps/tauri/pull/14416) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Premultiply Alpha before Resizing which gets rid of the gray fringe around the icons for svg images.
## \[2.9.3]
### Enhancements
- [`22edc65aa`](https://www.github.com/tauri-apps/tauri/commit/22edc65aad0b3e45515008e8e0866112da70c8a1) ([#14408](https://www.github.com/tauri-apps/tauri/pull/14408) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Set user-agent in bundler and cli http requests when fetching build tools.
- [`779612ac8`](https://www.github.com/tauri-apps/tauri/commit/779612ac8425a787626da4cefdb9eaf7d63bea18) ([#14379](https://www.github.com/tauri-apps/tauri/pull/14379) by [@moubctez](https://www.github.com/tauri-apps/tauri/../../moubctez)) Properly read the `required-features` field of binaries in Cargo.toml to prevent bundling issues when the features weren't enabled.
### Bug Fixes
- [`fd8c30b4f`](https://www.github.com/tauri-apps/tauri/commit/fd8c30b4f1bca8dd7165c5c0ebe7fbfd17662153) ([#14353](https://www.github.com/tauri-apps/tauri/pull/14353) by [@ChaseKnowlden](https://www.github.com/tauri-apps/tauri/../../ChaseKnowlden)) Premultiply Alpha before Resizing which gets rid of the gray fringe around the icons.
### Dependencies
- Upgraded to `tauri-bundler@2.7.3`
## \[2.9.2]
### Dependencies
- Upgraded to `tauri-bundler@2.7.2`
## \[2.9.1]
### Dependencies
- Upgraded to `tauri-macos-sign@2.3.0`
- Upgraded to `tauri-bundler@2.7.1`
## \[2.9.0]
### New Features
- [`f5851ee00`](https://www.github.com/tauri-apps/tauri/commit/f5851ee00d6d1f4d560a220ca5a728fedd525092) ([#14089](https://www.github.com/tauri-apps/tauri/pull/14089)) Adds the `scrollBarStyle` option to the window configuration.
- [`2a06d1006`](https://www.github.com/tauri-apps/tauri/commit/2a06d10066a806e392efe8bfb16d943ee0b0b61d) ([#14052](https://www.github.com/tauri-apps/tauri/pull/14052)) Add a `--no-sign` flag to the `tauri build` and `tauri bundle` commands to skip the code signing step, improving the developer experience for local testing and development without requiring code signing keys.
- [`3b4fac201`](https://www.github.com/tauri-apps/tauri/commit/3b4fac2017832d426dd07c5e24e26684eda57f7b) ([#14194](https://www.github.com/tauri-apps/tauri/pull/14194)) Add `tauri.conf.json > bundle > android > autoIncrementVersionCode` config option to automatically increment the Android version code.
- [`673867aa0`](https://www.github.com/tauri-apps/tauri/commit/673867aa0e1ccd766ee879ffe96aba58c758613c) ([#14094](https://www.github.com/tauri-apps/tauri/pull/14094)) Try to detect ANDROID_HOME and NDK_HOME environment variables from default system locations and install them if needed using the Android Studio command line tools.
- [`3d6868d09`](https://www.github.com/tauri-apps/tauri/commit/3d6868d09c323d68a152f3c3f8c7256311bd020a) ([#14128](https://www.github.com/tauri-apps/tauri/pull/14128)) Added support to defining the content type of the declared file association on macOS (maps to LSItemContentTypes property).
- [`3d6868d09`](https://www.github.com/tauri-apps/tauri/commit/3d6868d09c323d68a152f3c3f8c7256311bd020a) ([#14128](https://www.github.com/tauri-apps/tauri/pull/14128)) Added support to defining the metadata for custom types declared in `tauri.conf.json > bundle > fileAssociations > exportedType` via the `UTExportedTypeDeclarations` Info.plist property.
- [`ed7c9a410`](https://www.github.com/tauri-apps/tauri/commit/ed7c9a4100e08c002212265549d12130d021ad1e) ([#14108](https://www.github.com/tauri-apps/tauri/pull/14108)) Added `bundle > macOS > infoPlist` and `bundle > iOS > infoPlist` configurations to allow defining custom Info.plist extensions.
- [`75082cc5b`](https://www.github.com/tauri-apps/tauri/commit/75082cc5b340e30e2c4b4cd4bd6a1fe5382164aa) ([#14120](https://www.github.com/tauri-apps/tauri/pull/14120)) Added `ios run` and `android run` commands to run the app in production mode.
- [`cc8c0b531`](https://www.github.com/tauri-apps/tauri/commit/cc8c0b53171173dbd1d01781a50de1a3ea159031) ([#14031](https://www.github.com/tauri-apps/tauri/pull/14031)) Added support to universal app links on macOS with the `plugins > deep-link > desktop > domains` configuration.
### Enhancements
- [`94cbd40fc`](https://www.github.com/tauri-apps/tauri/commit/94cbd40fc733e08c0bccd48149d22a0e9c2f1e5c) ([#14223](https://www.github.com/tauri-apps/tauri/pull/14223)) Add support for Android's adaptive and themed icons.
- [`b5aa01870`](https://www.github.com/tauri-apps/tauri/commit/b5aa018702bf45dc98297698f9b7d238705865a6) ([#14268](https://www.github.com/tauri-apps/tauri/pull/14268)) Update cargo-mobile2 to 0.21, enhancing error messages and opening Xcode when multiple apps are installed.
- [`55453e845`](https://www.github.com/tauri-apps/tauri/commit/55453e8453d927b8197f1ba9f26fd944482938f7) ([#14262](https://www.github.com/tauri-apps/tauri/pull/14262)) Check mismatched versions in `tauri info`
- [`1a6627ee7`](https://www.github.com/tauri-apps/tauri/commit/1a6627ee7d085a4e66784e2705254714d68c7244) ([#14122](https://www.github.com/tauri-apps/tauri/pull/14122)) Set a default log level filter when running `tauri add log`.
- [`b06b3bd09`](https://www.github.com/tauri-apps/tauri/commit/b06b3bd091b0fed26cdcfb23cacb0462a7a9cc2d) ([#14126](https://www.github.com/tauri-apps/tauri/pull/14126)) Improve error messages with more context.
- [`f6622a3e3`](https://www.github.com/tauri-apps/tauri/commit/f6622a3e342f5dd5fb3cf6e0f79fb309a10e9b3d) ([#14129](https://www.github.com/tauri-apps/tauri/pull/14129)) Prompt to install the iOS platform if it isn't installed yet.
- [`6bbb530fd`](https://www.github.com/tauri-apps/tauri/commit/6bbb530fd5edfc07b180a4f3782b8566872ca3b1) ([#14105](https://www.github.com/tauri-apps/tauri/pull/14105)) Warn if productName is empty when initializing mobile project.
### Bug Fixes
- [`19fb6f7cb`](https://www.github.com/tauri-apps/tauri/commit/19fb6f7cb0d702cb2f25f6f2d1e11014d9dada5d) ([#14146](https://www.github.com/tauri-apps/tauri/pull/14146)) Strip Windows-only extensions from the binary path so an Android project initialized on Windows can be used on UNIX systems.
- [`19fb6f7cb`](https://www.github.com/tauri-apps/tauri/commit/19fb6f7cb0d702cb2f25f6f2d1e11014d9dada5d) ([#14146](https://www.github.com/tauri-apps/tauri/pull/14146)) Enhance Android build script usage on Windows by attempting to run cmd, bat and exe formats.
- [`28a2f9bc5`](https://www.github.com/tauri-apps/tauri/commit/28a2f9bc55f658eb71ef1a970ff9f791346f7682) ([#14101](https://www.github.com/tauri-apps/tauri/pull/14101)) Fix iOS CLI usage after modifying the package name.
- [`d2938486e`](https://www.github.com/tauri-apps/tauri/commit/d2938486e9d974debd90c15d7160b8a17bf4d763) ([#14261](https://www.github.com/tauri-apps/tauri/pull/14261)) Replaced the non-standard nerd font character with ` ⱼₛ ` in `tarui info`
- [`25e920e16`](https://www.github.com/tauri-apps/tauri/commit/25e920e169db900ca4f07c2bb9eb290e9f9f2c7d) ([#14298](https://www.github.com/tauri-apps/tauri/pull/14298)) Wait for dev server to exit before exiting the CLI when the app is closed on `tauri dev --no-watch`.
- [`b0012424c`](https://www.github.com/tauri-apps/tauri/commit/b0012424c5f432debfa42ba145e2672966d5f6d5) ([#14115](https://www.github.com/tauri-apps/tauri/pull/14115)) Resolve local IP address when `tauri.conf.json > build > devUrl` host is `0.0.0.0`.
- [`abf7e8850`](https://www.github.com/tauri-apps/tauri/commit/abf7e8850ba41e7173e9e9a3fdd6dfb8f357d72d) ([#14118](https://www.github.com/tauri-apps/tauri/pull/14118)) Fixes mobile project initialization when using `pnpx` or `pnpm dlx`.
### Dependencies
- Upgraded to `tauri-utils@2.8.0`
- Upgraded to `tauri-bundler@2.7.0`
## \[2.8.4]
### Enhancements

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-cli"
version = "2.8.4"
version = "2.9.6"
authors = ["Tauri Programme within The Commons Conservancy"]
edition = "2021"
rust-version = "1.77.2"
@@ -36,7 +36,7 @@ name = "cargo-tauri"
path = "src/main.rs"
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
cargo-mobile2 = { version = "0.20.6", default-features = false }
cargo-mobile2 = { version = "0.21.1", default-features = false }
[dependencies]
jsonrpsee = { version = "0.24", features = ["server"] }
@@ -47,7 +47,7 @@ sublime_fuzzy = "0.7"
clap_complete = "4"
clap = { version = "4", features = ["derive", "env"] }
thiserror = "2"
tauri-bundler = { version = "2.6.1", default-features = false, path = "../tauri-bundler" }
tauri-bundler = { version = "2.7.5", default-features = false, path = "../tauri-bundler" }
colored = "2"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] }
@@ -56,9 +56,9 @@ notify = "8"
notify-debouncer-full = "0.6"
shared_child = "1"
duct = "1.0"
toml_edit = { version = "0.23", features = ["serde"] }
toml_edit = { version = "0.24", features = ["serde"] }
json-patch = "3"
tauri-utils = { version = "2.7.0", path = "../tauri-utils", features = [
tauri-utils = { version = "2.8.1", path = "../tauri-utils", features = [
"isolation",
"schema",
"config-json5",
@@ -66,11 +66,11 @@ tauri-utils = { version = "2.7.0", path = "../tauri-utils", features = [
"html-manipulation",
] }
toml = "0.9"
jsonschema = "0.33"
jsonschema = { version = "0.33", default-features = false }
handlebars = "6"
include_dir = "0.7"
dirs = "6"
minisign = "=0.7.3"
minisign = "0.8"
base64 = "0.22"
ureq = { version = "3", default-features = false, features = ["gzip"] }
os_info = "3"
@@ -113,6 +113,7 @@ uuid = { version = "1", features = ["v5"] }
rand = "0.9"
zip = { version = "4", default-features = false, features = ["deflate"] }
which = "8"
rayon = "1.10"
[dev-dependencies]
insta = "1"
@@ -132,7 +133,7 @@ libc = "0.2"
[target."cfg(target_os = \"macos\")".dependencies]
plist = "1"
tauri-macos-sign = { version = "2.2.0", path = "../tauri-macos-sign" }
tauri-macos-sign = { version = "2.3.2", path = "../tauri-macos-sign" }
object = { version = "0.36", default-features = false, features = [
"macho",
"read_core",

View File

@@ -33,7 +33,7 @@ These environment variables are inputs to the CLI which may have an equivalent C
- See [creating API keys](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) for more information.
- `API_PRIVATE_KEYS_DIR` — Specify the directory where your AuthKey file is located. See `APPLE_API_KEY`.
- `APPLE_API_ISSUER` — Issuer ID. Required if `APPLE_API_KEY` is specified.
- `APPLE_API_KEY_PATH` - path to the API key `.p8` file. If not specified, for macOS apps the bundler searches the following directories in sequence for a private key file with the name of 'AuthKey\_<api_key>.p8': './private_keys', '~/private_keys', '~/.private_keys', and '~/.appstoreconnect/private_keys'. **For iOS this variable is required**.
- `APPLE_API_KEY_PATH` - path to the API key `.p8` file. If not specified, for macOS apps the bundler searches the following directories in sequence for a private key file with the name of `AuthKey\_<api_key>.p8`: `./private_keys`, `~/private_keys`, `~/.private_keys`, and `~/.appstoreconnect/private_keys`. **For iOS this variable is required**.
- `APPLE_SIGNING_IDENTITY` — The identity used to code sign. Overwrites `tauri.conf.json > bundle > macOS > signingIdentity`. If neither are set, it is inferred from `APPLE_CERTIFICATE` when provided.
- `APPLE_PROVIDER_SHORT_NAME` — If your Apple ID is connected to multiple teams, you have to specify the provider short name of the team you want to use to notarize your app. Overwrites `tauri.conf.json > bundle > macOS > providerShortName`.
- `APPLE_DEVELOPMENT_TEAM` — The team ID used to code sign on iOS. Overwrites `tauri.conf.json > bundle > iOS > developmentTeam`. Can be found in https://developer.apple.com/account#MembershipDetailsCard.

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schema.tauri.app/config/2.8.5",
"$id": "https://schema.tauri.app/config/2.9.5",
"title": "Config",
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://v2.tauri.app/reference/cli/#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"http://localhost:3000\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
"type": "object",
@@ -84,6 +84,7 @@
"default": {
"active": false,
"android": {
"autoIncrementVersionCode": false,
"minSdkVersion": 24
},
"createUpdaterArtifacts": false,
@@ -167,7 +168,7 @@
"type": "object",
"properties": {
"windows": {
"description": "The app windows configuration.\n\n ## Example:\n\n To create a window at app startup\n\n ```json\n {\n \"app\": {\n \"windows\": [\n { \"width\": 800, \"height\": 600 }\n ]\n }\n }\n ```\n\n If not specified, the window's label (its identifier) defaults to \"main\",\n you can use this label to get the window through\n `app.get_webview_window` in Rust or `WebviewWindow.getByLabel` in JavaScript\n\n When working with multiple windows, each window will need an unique label\n\n ```json\n {\n \"app\": {\n \"windows\": [\n { \"label\": \"main\", \"width\": 800, \"height\": 600 },\n { \"label\": \"secondary\", \"width\": 800, \"height\": 600 }\n ]\n }\n }\n ```\n\n You can also set `create` to false and use this config through the Rust APIs\n\n ```json\n {\n \"app\": {\n \"windows\": [\n { \"create\": false, \"width\": 800, \"height\": 600 }\n ]\n }\n }\n ```\n\n and use it like this\n\n ```rust\n tauri::Builder::default()\n .setup(|app| {\n tauri::WebviewWindowBuilder::from_config(app.handle(), app.config().app.windows[0])?.build()?;\n Ok(())\n });\n ```",
"description": "The app windows configuration.\n\n ## Example:\n\n To create a window at app startup\n\n ```json\n {\n \"app\": {\n \"windows\": [\n { \"width\": 800, \"height\": 600 }\n ]\n }\n }\n ```\n\n If not specified, the window's label (its identifier) defaults to \"main\",\n you can use this label to get the window through\n `app.get_webview_window` in Rust or `WebviewWindow.getByLabel` in JavaScript\n\n When working with multiple windows, each window will need an unique label\n\n ```json\n {\n \"app\": {\n \"windows\": [\n { \"label\": \"main\", \"width\": 800, \"height\": 600 },\n { \"label\": \"secondary\", \"width\": 800, \"height\": 600 }\n ]\n }\n }\n ```\n\n You can also set `create` to false and use this config through the Rust APIs\n\n ```json\n {\n \"app\": {\n \"windows\": [\n { \"create\": false, \"width\": 800, \"height\": 600 }\n ]\n }\n }\n ```\n\n and use it like this\n\n ```rust\n tauri::Builder::default()\n .setup(|app| {\n tauri::WebviewWindowBuilder::from_config(app.handle(), &app.config().app.windows[0])?.build()?;\n Ok(())\n });\n ```",
"default": [],
"type": "array",
"items": {
@@ -233,7 +234,7 @@
"type": "string"
},
"create": {
"description": "Whether Tauri should create this window at app startup or not.\n\n When this is set to `false` you must manually grab the config object via `app.config().app.windows`\n and create it with [`WebviewWindowBuilder::from_config`](https://docs.rs/tauri/2/tauri/webview/struct.WebviewWindowBuilder.html#method.from_config).\n\n ## Example:\n\n ```rust\n tauri::Builder::default()\n .setup(|app| {\n tauri::WebviewWindowBuilder::from_config(app.handle(), app.config().app.windows[0])?.build()?;\n Ok(())\n });\n ```",
"description": "Whether Tauri should create this window at app startup or not.\n\n When this is set to `false` you must manually grab the config object via `app.config().app.windows`\n and create it with [`WebviewWindowBuilder::from_config`](https://docs.rs/tauri/2/tauri/webview/struct.WebviewWindowBuilder.html#method.from_config).\n\n ## Example:\n\n ```rust\n tauri::Builder::default()\n .setup(|app| {\n tauri::WebviewWindowBuilder::from_config(app.handle(), &app.config().app.windows[0])?.build()?;\n Ok(())\n });\n ```",
"default": true,
"type": "boolean"
},
@@ -264,7 +265,7 @@
"type": "boolean"
},
"x": {
"description": "The horizontal position of the window's top left corner",
"description": "The horizontal position of the window's top left corner in logical pixels",
"type": [
"number",
"null"
@@ -272,7 +273,7 @@
"format": "double"
},
"y": {
"description": "The vertical position of the window's top left corner",
"description": "The vertical position of the window's top left corner in logical pixels",
"type": [
"number",
"null"
@@ -280,19 +281,19 @@
"format": "double"
},
"width": {
"description": "The window width.",
"description": "The window width in logical pixels.",
"default": 800.0,
"type": "number",
"format": "double"
},
"height": {
"description": "The window height.",
"description": "The window height in logical pixels.",
"default": 600.0,
"type": "number",
"format": "double"
},
"minWidth": {
"description": "The min window width.",
"description": "The min window width in logical pixels.",
"type": [
"number",
"null"
@@ -300,7 +301,7 @@
"format": "double"
},
"minHeight": {
"description": "The min window height.",
"description": "The min window height in logical pixels.",
"type": [
"number",
"null"
@@ -308,7 +309,7 @@
"format": "double"
},
"maxWidth": {
"description": "The max window width.",
"description": "The max window width in logical pixels.",
"type": [
"number",
"null"
@@ -316,7 +317,7 @@
"format": "double"
},
"maxHeight": {
"description": "The max window height.",
"description": "The max window height in logical pixels.",
"type": [
"number",
"null"
@@ -654,13 +655,13 @@
],
"properties": {
"width": {
"description": "Horizontal margin in physical unit",
"description": "Horizontal margin in physical pixels",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"height": {
"description": "Vertical margin in physical unit",
"description": "Vertical margin in physical pixels",
"type": "integer",
"format": "uint32",
"minimum": 0.0
@@ -923,7 +924,7 @@
]
},
{
"description": "Mica effect that matches the system dark perefence **Windows 11 Only**",
"description": "Mica effect that matches the system dark preference **Windows 11 Only**",
"type": "string",
"enum": [
"mica"
@@ -944,7 +945,7 @@
]
},
{
"description": "Tabbed effect that matches the system dark perefence **Windows 11 Only**",
"description": "Tabbed effect that matches the system dark preference **Windows 11 Only**",
"type": "string",
"enum": [
"tabbed"
@@ -1298,7 +1299,7 @@
"additionalProperties": false
},
"FsScope": {
"description": "Protocol scope definition.\n It is a list of glob patterns that restrict the API access from the webview.\n\n Each pattern can start with a variable that resolves to a system base directory.\n The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,\n `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,\n `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,\n `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"description": "Protocol scope definition.\n It is a list of glob patterns that restrict the API access from the webview.\n\n Each pattern can start with a variable that resolves to a system base directory.\n The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,\n `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,\n `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$TEMP`,\n `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"anyOf": [
{
"description": "A list of paths that are allowed by this scope.",
@@ -1993,7 +1994,7 @@
"description": "Defines the URL or assets to embed in the application.",
"anyOf": [
{
"description": "An external URL that should be used as the default application URL.",
"description": "An external URL that should be used as the default application URL. No assets are embedded in the app in this case.",
"type": "string",
"format": "uri"
},
@@ -2002,7 +2003,7 @@
"type": "string"
},
{
"description": "An array of files to embed on the app.",
"description": "An array of files to embed in the app.",
"type": "array",
"items": {
"type": "string"
@@ -2288,6 +2289,7 @@
"android": {
"description": "Android configuration.",
"default": {
"autoIncrementVersionCode": false,
"minSdkVersion": 24
},
"allOf": [
@@ -2796,7 +2798,7 @@
"type": "object",
"properties": {
"version": {
"description": "MSI installer version in the format `major.minor.patch.build` (build is optional).\n\n Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set.\n\n The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.\n The third and foruth fields have a maximum value of 65,535.\n\n See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.",
"description": "MSI installer version in the format `major.minor.patch.build` (build is optional).\n\n Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set.\n\n The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.\n The third and fourth fields have a maximum value of 65,535.\n\n See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.",
"type": [
"string",
"null"
@@ -3100,7 +3102,7 @@
"description": "Custom Signing Command configuration.",
"anyOf": [
{
"description": "A string notation of the script to execute.\n\n \"%1\" will be replaced with the path to the binary to be signed.\n\n This is a simpler notation for the command.\n Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments.\n\n If you need to use whitespace in the command or arguments, use the object notation [`Self::ScriptWithOptions`].",
"description": "A string notation of the script to execute.\n\n \"%1\" will be replaced with the path to the binary to be signed.\n\n This is a simpler notation for the command.\n Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments.\n\n If you need to use whitespace in the command or arguments, use the object notation [`Self::CommandWithOptions`].",
"type": "string"
},
{
@@ -3853,6 +3855,11 @@
"format": "uint32",
"maximum": 2100000000.0,
"minimum": 1.0
},
"autoIncrementVersionCode": {
"description": "Whether to automatically increment the `versionCode` on each build.\n\n - If `true`, the generator will try to read the last `versionCode` from\n `tauri.properties` and increment it by 1 for every build.\n - If `false` or not set, it falls back to `version_code` or semver-derived logic.\n\n Note that to use this feature, you should remove `/tauri.properties` from `src-tauri/gen/android/app/.gitignore` so the current versionCode is committed to the repository.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false

View File

@@ -1,9 +1,9 @@
{
"cli.js": {
"version": "2.8.4",
"version": "2.9.6",
"node": ">= 10.0.0"
},
"tauri": "2.8.5",
"tauri-build": "2.4.1",
"tauri-plugin": "2.4.0"
"tauri": "2.9.5",
"tauri-build": "2.5.3",
"tauri-plugin": "2.5.2"
}

View File

@@ -7,12 +7,7 @@ use std::{collections::HashSet, path::PathBuf};
use clap::Parser;
use tauri_utils::acl::capability::{Capability, PermissionEntry};
use crate::{
acl::FileFormat,
error::ErrorExt,
helpers::{app_paths::tauri_dir, prompts},
Result,
};
use crate::{acl::FileFormat, error::ErrorExt, helpers::prompts, Result};
#[derive(Debug, Parser)]
#[clap(about = "Create a new permission file")]
@@ -37,7 +32,7 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let dirs = crate::helpers::app_paths::resolve_dirs();
let identifier = match options.identifier {
Some(i) => i,
@@ -111,8 +106,7 @@ pub fn command(options: Options) -> Result<()> {
.canonicalize()
.fs_context("failed to canonicalize capability file path", o.clone())?,
None => {
let dir = tauri_dir();
let capabilities_dir = dir.join("capabilities");
let capabilities_dir = dirs.tauri.join("capabilities");
capabilities_dir.join(format!(
"{}.{}",
capability.identifier,

View File

@@ -6,7 +6,6 @@ use clap::Parser;
use crate::{
error::{Context, ErrorExt},
helpers::app_paths::tauri_dir,
Result,
};
use colored::Colorize;
@@ -25,16 +24,17 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let dirs = crate::helpers::app_paths::resolve_dirs();
let acl_manifests_path = tauri_dir()
let acl_manifests_path = dirs
.tauri
.join("gen")
.join("schemas")
.join("acl-manifests.json");
if acl_manifests_path.exists() {
let plugin_manifest_json = read_to_string(&acl_manifests_path)
.fs_context("failed to read plugin manifest", acl_manifests_path.clone())?;
.fs_context("failed to read plugin manifest", acl_manifests_path)?;
let acl = serde_json::from_str::<BTreeMap<String, Manifest>>(&plugin_manifest_json)
.context("failed to parse plugin manifest as JSON")?;

View File

@@ -10,7 +10,7 @@ use crate::{
acl,
error::ErrorExt,
helpers::{
app_paths::{resolve_frontend_dir, tauri_dir},
app_paths::{resolve_frontend_dir, Dirs},
cargo,
npm::PackageManager,
},
@@ -39,11 +39,11 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
run(options)
let dirs = crate::helpers::app_paths::resolve_dirs();
run(options, &dirs)
}
pub fn run(options: Options) -> Result<()> {
pub fn run(options: Options, dirs: &Dirs) -> Result<()> {
let (plugin, version) = options
.plugin
.split_once('@')
@@ -71,7 +71,6 @@ pub fn run(options: Options) -> Result<()> {
}
let frontend_dir = resolve_frontend_dir();
let tauri_dir = tauri_dir();
let target_str = metadata
.desktop_only
@@ -90,7 +89,7 @@ pub fn run(options: Options) -> Result<()> {
branch: options.branch.as_deref(),
rev: options.rev.as_deref(),
tag: options.tag.as_deref(),
cwd: Some(tauri_dir),
cwd: Some(dirs.tauri),
target: target_str,
})?;
@@ -117,7 +116,7 @@ pub fn run(options: Options) -> Result<()> {
(None, None, None, None) => npm_name,
_ => crate::error::bail!("Only one of --tag, --rev and --branch can be specified"),
};
manager.install(&[npm_spec], tauri_dir)?;
manager.install(&[npm_spec], dirs.tauri)?;
}
let _ = acl::permission::add::command(acl::permission::add::Options {
@@ -143,7 +142,10 @@ pub fn run(options: Options) -> Result<()> {
let plugin_init = format!(".plugin(tauri_plugin_{plugin_snake_case}::{plugin_init_fn})");
let re = Regex::new(r"(tauri\s*::\s*Builder\s*::\s*default\(\))(\s*)").unwrap();
for file in [tauri_dir.join("src/main.rs"), tauri_dir.join("src/lib.rs")] {
for file in [
dirs.tauri.join("src/main.rs"),
dirs.tauri.join("src/lib.rs"),
] {
let contents =
std::fs::read_to_string(&file).fs_context("failed to read Rust entry point", file.clone())?;
@@ -166,7 +168,7 @@ pub fn run(options: Options) -> Result<()> {
log::info!("Running `cargo fmt`...");
let _ = Command::new("cargo")
.arg("fmt")
.current_dir(tauri_dir)
.current_dir(dirs.tauri)
.status();
}

View File

@@ -7,8 +7,8 @@ use crate::{
error::{Context, ErrorExt},
helpers::{
self,
app_paths::{frontend_dir, tauri_dir},
config::{get as get_config, ConfigHandle, FrontendDist},
app_paths::Dirs,
config::{get_config, ConfigMetadata, FrontendDist},
},
info::plugins::check_mismatched_packages,
interface::{rust::get_cargo_target_dir, AppInterface, Interface},
@@ -40,7 +40,7 @@ pub struct Options {
pub target: Option<String>,
/// Space or comma separated list of features to activate
#[clap(short, long, action = ArgAction::Append, num_args(0..))]
pub features: Option<Vec<String>>,
pub features: Vec<String>,
/// Space or comma separated list of bundles to package.
#[clap(short, long, action = ArgAction::Append, num_args(0..), value_delimiter = ',')]
pub bundles: Option<Vec<BundleFormat>>,
@@ -82,7 +82,7 @@ pub struct Options {
}
pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
crate::helpers::app_paths::resolve();
let dirs = crate::helpers::app_paths::resolve_dirs();
if options.no_sign {
log::warn!("--no-sign flag detected: Signing will be skipped.");
@@ -99,41 +99,37 @@ pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
let config = get_config(
target,
&options.config.iter().map(|c| &c.0).collect::<Vec<_>>(),
dirs.tauri,
)?;
let mut interface = AppInterface::new(
config.lock().unwrap().as_ref().unwrap(),
options.target.clone(),
)?;
let mut interface = AppInterface::new(&config, options.target.clone(), dirs.tauri)?;
setup(&interface, &mut options, config.clone(), false)?;
setup(&interface, &mut options, &config, &dirs, false)?;
let config_guard = config.lock().unwrap();
let config_ = config_guard.as_ref().unwrap();
if let Some(minimum_system_version) = &config_.bundle.macos.minimum_system_version {
if let Some(minimum_system_version) = &config.bundle.macos.minimum_system_version {
std::env::set_var("MACOSX_DEPLOYMENT_TARGET", minimum_system_version);
}
let app_settings = interface.app_settings();
let interface_options = options.clone().into();
let out_dir = app_settings.out_dir(&interface_options)?;
let out_dir = app_settings.out_dir(&interface_options, dirs.tauri)?;
let bin_path = interface.build(interface_options)?;
let bin_path = interface.build(interface_options, &dirs)?;
log::info!(action ="Built"; "application at: {}", tauri_utils::display_path(bin_path));
let app_settings = interface.app_settings();
if !options.no_bundle && (config_.bundle.active || options.bundles.is_some()) {
if !options.no_bundle && (config.bundle.active || options.bundles.is_some()) {
crate::bundle::bundle(
&options.into(),
verbosity,
ci,
&interface,
&app_settings,
config_,
&*app_settings,
&config,
&dirs,
&out_dir,
)?;
}
@@ -144,15 +140,14 @@ pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
pub fn setup(
interface: &AppInterface,
options: &mut Options,
config: ConfigHandle,
config: &ConfigMetadata,
dirs: &Dirs,
mobile: bool,
) -> Result<()> {
let tauri_path = tauri_dir();
// TODO: Maybe optimize this to run in parallel in the future
// see https://github.com/tauri-apps/tauri/pull/13993#discussion_r2280697117
log::info!("Looking up installed tauri packages to check mismatched versions...");
if let Err(error) = check_mismatched_packages(frontend_dir(), tauri_path) {
if let Err(error) = check_mismatched_packages(dirs.frontend, dirs.tauri) {
if options.ignore_version_mismatches {
log::error!("{error}");
} else {
@@ -160,46 +155,47 @@ pub fn setup(
}
}
set_current_dir(tauri_path).context("failed to set current directory")?;
set_current_dir(dirs.tauri).context("failed to set current directory")?;
let config_guard = config.lock().unwrap();
let config_ = config_guard.as_ref().unwrap();
let bundle_identifier_source = config_
let bundle_identifier_source = config
.find_bundle_identifier_overwriter()
.unwrap_or_else(|| "tauri.conf.json".into());
if config_.identifier == "com.tauri.dev" {
if config.identifier == "com.tauri.dev" {
crate::error::bail!(
"You must change the bundle identifier in `{bundle_identifier_source} identifier`. The default value `com.tauri.dev` is not allowed as it must be unique across applications.",
);
}
if config_
if config
.identifier
.chars()
.any(|ch| !(ch.is_alphanumeric() || ch == '-' || ch == '.'))
{
crate::error::bail!(
"The bundle identifier \"{}\" set in `{} identifier`. The bundle identifier string must contain only alphanumeric characters (A-Z, a-z, and 0-9), hyphens (-), and periods (.).",
config_.identifier,
bundle_identifier_source
"The bundle identifier \"{}\" set in `{bundle_identifier_source:?} identifier`. The bundle identifier string must contain only alphanumeric characters (A-Z, a-z, and 0-9), hyphens (-), and periods (.).",
config.identifier,
);
}
if config_.identifier.ends_with(".app") {
if config.identifier.ends_with(".app") {
log::warn!(
"The bundle identifier \"{}\" set in `{} identifier` ends with `.app`. This is not recommended because it conflicts with the application bundle extension on macOS.",
config_.identifier,
bundle_identifier_source
"The bundle identifier \"{}\" set in `{bundle_identifier_source:?} identifier` ends with `.app`. This is not recommended because it conflicts with the application bundle extension on macOS.",
config.identifier,
);
}
if let Some(before_build) = config_.build.before_build_command.clone() {
helpers::run_hook("beforeBuildCommand", before_build, interface, options.debug)?;
if let Some(before_build) = config.build.before_build_command.clone() {
helpers::run_hook(
"beforeBuildCommand",
before_build,
interface,
options.debug,
dirs.frontend,
)?;
}
if let Some(FrontendDist::Directory(web_asset_path)) = &config_.build.frontend_dist {
if let Some(FrontendDist::Directory(web_asset_path)) = &config.build.frontend_dist {
if !web_asset_path.exists() {
let absolute_path = web_asset_path
.parent()
@@ -224,7 +220,7 @@ pub fn setup(
// Issue #13287 - Allow the use of target dir inside frontendDist/distDir
// https://github.com/tauri-apps/tauri/issues/13287
let target_path = get_cargo_target_dir(&options.args)?;
let target_path = get_cargo_target_dir(&options.args, dirs.tauri)?;
let mut out_folders = Vec::new();
if let Ok(web_asset_canonical) = dunce::canonicalize(web_asset_path) {
if let Ok(relative_path) = target_path.strip_prefix(&web_asset_canonical) {
@@ -252,13 +248,12 @@ pub fn setup(
}
if options.runner.is_none() {
options.runner = config_.build.runner.clone();
options.runner = config.build.runner.clone();
}
options
.features
.get_or_insert(Vec::new())
.extend(config_.build.features.clone().unwrap_or_default());
.extend_from_slice(config.build.features.as_deref().unwrap_or_default());
interface.build_options(&mut options.args, &mut options.features, mobile);
Ok(())

View File

@@ -16,8 +16,8 @@ use crate::{
error::{Context, ErrorExt},
helpers::{
self,
app_paths::tauri_dir,
config::{get as get_config, ConfigMetadata},
app_paths::Dirs,
config::{get_config, ConfigMetadata},
updater_signature,
},
interface::{AppInterface, AppSettings, Interface},
@@ -71,7 +71,7 @@ pub struct Options {
pub config: Vec<ConfigValue>,
/// Space or comma separated list of features, should be the same features passed to `tauri build` if any.
#[clap(short, long, action = ArgAction::Append, num_args(0..))]
pub features: Option<Vec<String>>,
pub features: Vec<String>,
/// Target triple to build against.
///
/// It must be one of the values outputted by `$rustc --print target-list` or `universal-apple-darwin` for an universal macOS application.
@@ -118,7 +118,7 @@ impl From<crate::build::Options> for Options {
}
pub fn command(options: Options, verbosity: u8) -> crate::Result<()> {
crate::helpers::app_paths::resolve();
let dirs = crate::helpers::app_paths::resolve_dirs();
let ci = options.ci;
@@ -131,35 +131,30 @@ pub fn command(options: Options, verbosity: u8) -> crate::Result<()> {
let config = get_config(
target,
&options.config.iter().map(|c| &c.0).collect::<Vec<_>>(),
dirs.tauri,
)?;
let interface = AppInterface::new(
config.lock().unwrap().as_ref().unwrap(),
options.target.clone(),
)?;
let interface = AppInterface::new(&config, options.target.clone(), dirs.tauri)?;
let tauri_path = tauri_dir();
std::env::set_current_dir(tauri_path).context("failed to set current directory")?;
std::env::set_current_dir(dirs.tauri).context("failed to set current directory")?;
let config_guard = config.lock().unwrap();
let config_ = config_guard.as_ref().unwrap();
if let Some(minimum_system_version) = &config_.bundle.macos.minimum_system_version {
if let Some(minimum_system_version) = &config.bundle.macos.minimum_system_version {
std::env::set_var("MACOSX_DEPLOYMENT_TARGET", minimum_system_version);
}
let app_settings = interface.app_settings();
let interface_options = options.clone().into();
let out_dir = app_settings.out_dir(&interface_options)?;
let out_dir = app_settings.out_dir(&interface_options, dirs.tauri)?;
bundle(
&options,
verbosity,
ci,
&interface,
&app_settings,
config_,
&*app_settings,
&config,
&dirs,
&out_dir,
)
}
@@ -170,8 +165,9 @@ pub fn bundle<A: AppSettings>(
verbosity: u8,
ci: bool,
interface: &AppInterface,
app_settings: &std::sync::Arc<A>,
app_settings: &A,
config: &ConfigMetadata,
dirs: &Dirs,
out_dir: &Path,
) -> crate::Result<()> {
let package_types: Vec<PackageType> = if let Some(bundles) = &options.bundles {
@@ -198,12 +194,19 @@ pub fn bundle<A: AppSettings>(
before_bundle,
interface,
options.debug,
dirs.frontend,
)?;
}
}
let mut settings = app_settings
.get_bundler_settings(options.clone().into(), config, out_dir, package_types)
.get_bundler_settings(
options.clone().into(),
config,
out_dir,
package_types,
dirs.tauri,
)
.with_context(|| "failed to build bundler settings")?;
settings.set_no_sign(options.no_sign);
@@ -249,6 +252,11 @@ fn sign_updaters(
return Ok(());
}
if settings.no_sign() {
log::warn!("Updater signing is skipped due to --no-sign flag.");
return Ok(());
}
// get the public key
let pubkey = &update_settings.pubkey;
// check if pubkey points to a file...

View File

@@ -5,11 +5,9 @@
use crate::{
error::{Context, ErrorExt},
helpers::{
app_paths::{frontend_dir, tauri_dir},
app_paths::Dirs,
command_env,
config::{
get as get_config, reload as reload_config, BeforeDevCommand, ConfigHandle, FrontendDist,
},
config::{get_config, reload_config, BeforeDevCommand, ConfigMetadata, FrontendDist},
},
info::plugins::check_mismatched_packages,
interface::{AppInterface, ExitReason, Interface},
@@ -34,7 +32,7 @@ use std::{
mod builtin_dev_server;
static BEFORE_DEV: OnceLock<Mutex<Arc<SharedChild>>> = OnceLock::new();
static KILL_BEFORE_DEV_FLAG: OnceLock<AtomicBool> = OnceLock::new();
static KILL_BEFORE_DEV_FLAG: AtomicBool = AtomicBool::new(false);
#[cfg(unix)]
const KILL_CHILDREN_SCRIPT: &[u8] = include_bytes!("../scripts/kill-children.sh");
@@ -57,7 +55,7 @@ pub struct Options {
pub target: Option<String>,
/// List of cargo features to activate
#[clap(short, long, action = ArgAction::Append, num_args(0..))]
pub features: Option<Vec<String>>,
pub features: Vec<String>,
/// Exit on panic
#[clap(short, long)]
pub exit_on_panic: bool,
@@ -99,61 +97,57 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let dirs = crate::helpers::app_paths::resolve_dirs();
let r = command_internal(options);
let r = command_internal(options, dirs);
if r.is_err() {
kill_before_dev_process();
}
r
}
fn command_internal(mut options: Options) -> Result<()> {
fn command_internal(mut options: Options, dirs: Dirs) -> Result<()> {
let target = options
.target
.as_deref()
.map(Target::from_triple)
.unwrap_or_else(Target::current);
let config = get_config(
let mut config = get_config(
target,
&options.config.iter().map(|c| &c.0).collect::<Vec<_>>(),
dirs.tauri,
)?;
let mut interface = AppInterface::new(
config.lock().unwrap().as_ref().unwrap(),
options.target.clone(),
)?;
let mut interface = AppInterface::new(&config, options.target.clone(), dirs.tauri)?;
setup(&interface, &mut options, config)?;
setup(&interface, &mut options, &mut config, &dirs)?;
let exit_on_panic = options.exit_on_panic;
let no_watch = options.no_watch;
interface.dev(options.into(), move |status, reason| {
on_app_exit(status, reason, exit_on_panic, no_watch)
})
interface.dev(
&mut config,
options.into(),
move |status, reason| on_app_exit(status, reason, exit_on_panic, no_watch),
&dirs,
)
}
pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHandle) -> Result<()> {
let tauri_path = tauri_dir();
pub fn setup(
interface: &AppInterface,
options: &mut Options,
config: &mut ConfigMetadata,
dirs: &Dirs,
) -> Result<()> {
std::thread::spawn(|| {
if let Err(error) = check_mismatched_packages(frontend_dir(), tauri_path) {
if let Err(error) = check_mismatched_packages(dirs.frontend, dirs.tauri) {
log::error!("{error}");
}
});
set_current_dir(tauri_path).context("failed to set current directory")?;
set_current_dir(dirs.tauri).context("failed to set current directory")?;
if let Some(before_dev) = config
.lock()
.unwrap()
.as_ref()
.unwrap()
.build
.before_dev_command
.clone()
{
if let Some(before_dev) = config.build.before_dev_command.clone() {
let (script, script_cwd, wait) = match before_dev {
BeforeDevCommand::Script(s) if s.is_empty() => (None, None, false),
BeforeDevCommand::Script(s) => (Some(s), None, false),
@@ -161,7 +155,7 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
(Some(script), cwd.map(Into::into), wait)
}
};
let cwd = script_cwd.unwrap_or_else(|| frontend_dir().clone());
let cwd = script_cwd.unwrap_or_else(|| dirs.frontend.to_owned());
if let Some(before_dev) = script {
log::info!(action = "Running"; "BeforeDevCommand (`{}`)", before_dev);
let mut env = command_env(true);
@@ -218,14 +212,13 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
let status = child_
.wait()
.expect("failed to wait on \"beforeDevCommand\"");
if !(status.success() || KILL_BEFORE_DEV_FLAG.get().unwrap().load(Ordering::Relaxed)) {
if !(status.success() || KILL_BEFORE_DEV_FLAG.load(Ordering::Relaxed)) {
log::error!("The \"beforeDevCommand\" terminated with a non-zero status code.");
exit(status.code().unwrap_or(1));
}
});
BEFORE_DEV.set(Mutex::new(child)).unwrap();
KILL_BEFORE_DEV_FLAG.set(AtomicBool::default()).unwrap();
let _ = ctrlc::set_handler(move || {
kill_before_dev_process();
@@ -236,45 +229,14 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
}
if options.runner.is_none() {
options.runner = config
.lock()
.unwrap()
.as_ref()
.unwrap()
.build
.runner
.clone();
options.runner = config.build.runner.clone();
}
let mut cargo_features = config
.lock()
.unwrap()
.as_ref()
.unwrap()
.build
.features
.clone()
.unwrap_or_default();
if let Some(features) = &options.features {
cargo_features.extend(features.clone());
}
let mut cargo_features = config.build.features.clone().unwrap_or_default();
cargo_features.extend(options.features.clone());
let mut dev_url = config
.lock()
.unwrap()
.as_ref()
.unwrap()
.build
.dev_url
.clone();
let frontend_dist = config
.lock()
.unwrap()
.as_ref()
.unwrap()
.build
.frontend_dist
.clone();
let mut dev_url = config.build.dev_url.clone();
let frontend_dist = config.build.frontend_dist.clone();
if !options.no_dev_server && dev_url.is_none() {
if let Some(FrontendDist::Directory(path)) = &frontend_dist {
if path.exists() {
@@ -297,19 +259,21 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
}
})));
reload_config(&options.config.iter().map(|c| &c.0).collect::<Vec<_>>())?;
reload_config(
config,
&options.config.iter().map(|c| &c.0).collect::<Vec<_>>(),
dirs.tauri,
)?;
}
}
}
if !options.no_dev_server_wait {
if let Some(url) = dev_url {
let host = url
.host()
.unwrap_or_else(|| panic!("No host name in the URL"));
let host = url.host().expect("No host name in the URL");
let port = url
.port_or_known_default()
.unwrap_or_else(|| panic!("No port number in the URL"));
.expect("No port number in the URL");
let addrs;
let addr;
let addrs = match host {
@@ -352,16 +316,9 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
}
if options.additional_watch_folders.is_empty() {
options.additional_watch_folders.extend(
config
.lock()
.unwrap()
.as_ref()
.unwrap()
.build
.additional_watch_folders
.clone(),
);
options
.additional_watch_folders
.extend(config.build.additional_watch_folders.clone());
}
Ok(())
@@ -380,11 +337,10 @@ pub fn on_app_exit(code: Option<i32>, reason: ExitReason, exit_on_panic: bool, n
pub fn kill_before_dev_process() {
if let Some(child) = BEFORE_DEV.get() {
let child = child.lock().unwrap();
let kill_before_dev_flag = KILL_BEFORE_DEV_FLAG.get().unwrap();
if kill_before_dev_flag.load(Ordering::Relaxed) {
if KILL_BEFORE_DEV_FLAG.load(Ordering::Relaxed) {
return;
}
kill_before_dev_flag.store(true, Ordering::Relaxed);
KILL_BEFORE_DEV_FLAG.store(true, Ordering::Relaxed);
#[cfg(windows)]
{
let powershell_path = std::env::var("SYSTEMROOT").map_or_else(

View File

@@ -155,9 +155,9 @@ fn inject_address(html_bytes: Vec<u8>, address: &SocketAddr) -> Vec<u8> {
}
fn fs_read_scoped(path: PathBuf, scope: &Path) -> crate::Result<Vec<u8>> {
let path = dunce::canonicalize(&path).fs_context("failed to canonicalize path", path.clone())?;
let path = dunce::canonicalize(&path).fs_context("failed to canonicalize path", path)?;
if path.starts_with(scope) {
std::fs::read(&path).fs_context("failed to read file", path.clone())
std::fs::read(&path).fs_context("failed to read file", &path)
} else {
crate::error::bail!("forbidden path")
}

View File

@@ -23,6 +23,11 @@ const ENV_TAURI_APP_PATH: &str = "TAURI_APP_PATH";
// path to the frontend app directory, usually `<project>/`
const ENV_TAURI_FRONTEND_PATH: &str = "TAURI_FRONTEND_PATH";
pub struct Dirs {
pub tauri: &'static Path,
pub frontend: &'static Path,
}
static FRONTEND_DIR: OnceLock<PathBuf> = OnceLock::new();
static TAURI_DIR: OnceLock<PathBuf> = OnceLock::new();
@@ -75,21 +80,13 @@ fn lookup<F: Fn(&PathBuf) -> bool>(dir: &Path, checker: F) -> Option<PathBuf> {
}
fn env_tauri_app_path() -> Option<PathBuf> {
std::env::var(ENV_TAURI_APP_PATH)
.map(PathBuf::from)
.ok()?
.canonicalize()
.ok()
.map(|p| dunce::simplified(&p).to_path_buf())
let p = PathBuf::from(std::env::var_os(ENV_TAURI_APP_PATH)?);
dunce::canonicalize(p).ok()
}
fn env_tauri_frontend_path() -> Option<PathBuf> {
std::env::var(ENV_TAURI_FRONTEND_PATH)
.map(PathBuf::from)
.ok()?
.canonicalize()
.ok()
.map(|p| dunce::simplified(&p).to_path_buf())
let p = PathBuf::from(std::env::var_os(ENV_TAURI_FRONTEND_PATH)?);
dunce::canonicalize(p).ok()
}
pub fn resolve_tauri_dir() -> Option<PathBuf> {
@@ -130,8 +127,8 @@ pub fn resolve_tauri_dir() -> Option<PathBuf> {
})
}
pub fn resolve() {
TAURI_DIR.set(resolve_tauri_dir().unwrap_or_else(|| {
pub fn resolve_dirs() -> Dirs {
let tauri = TAURI_DIR.get_or_init(|| resolve_tauri_dir().unwrap_or_else(|| {
let env_var_name = env_tauri_app_path().is_some().then(|| format!("`{ENV_TAURI_APP_PATH}`"));
panic!("Couldn't recognize the {} folder as a Tauri project. It must contain a `{}`, `{}` or `{}` file in any subfolder.",
env_var_name.as_deref().unwrap_or("current"),
@@ -139,16 +136,11 @@ pub fn resolve() {
ConfigFormat::Json5.into_file_name(),
ConfigFormat::Toml.into_file_name()
)
})).expect("tauri dir already resolved");
FRONTEND_DIR
.set(resolve_frontend_dir().unwrap_or_else(|| tauri_dir().parent().unwrap().to_path_buf()))
.expect("app dir already resolved");
}
pub fn tauri_dir() -> &'static PathBuf {
TAURI_DIR
.get()
.expect("app paths not initialized, this is a Tauri CLI bug")
}));
let frontend = FRONTEND_DIR.get_or_init(|| {
resolve_frontend_dir().unwrap_or_else(|| tauri.parent().unwrap().to_path_buf())
});
Dirs { tauri, frontend }
}
pub fn resolve_frontend_dir() -> Option<PathBuf> {
@@ -173,9 +165,3 @@ pub fn resolve_frontend_dir() -> Option<PathBuf> {
})
.map(|p| p.parent().unwrap().to_path_buf())
}
pub fn frontend_dir() -> &'static PathBuf {
FRONTEND_DIR
.get()
.expect("app paths not initialized, this is a Tauri CLI bug")
}

View File

@@ -56,7 +56,7 @@ pub fn cargo_manifest_and_lock(tauri_dir: &Path) -> (Option<CargoManifest>, Opti
.ok()
.and_then(|manifest_contents| toml::from_str(&manifest_contents).ok());
let lock: Option<CargoLock> = get_workspace_dir()
let lock: Option<CargoLock> = get_workspace_dir(tauri_dir)
.ok()
.and_then(|p| fs::read_to_string(p.join("Cargo.lock")).ok())
.and_then(|s| toml::from_str(&s).ok());

Some files were not shown because too many files have changed in this diff Show More