Compare commits

...

247 Commits

Author SHA1 Message Date
Lucas Nogueira
1259ca56b2 update example 2025-10-09 14:26:32 -03:00
Lucas Nogueira
2ece5d7adf added emit2 and emitTo2 instead 2025-10-09 14:21:33 -03:00
Lucas Nogueira
95f6c48dc0 feat(core): optimize raw payload in event system, closes #13405 2025-10-09 13:09:31 -03:00
Lucas Fernandes Nogueira
b5aa018702 feat(cli): update cargo-mobile2 to 0.21, closes #14238 (#14268) 2025-10-09 08:30:36 -03:00
Tony
55453e8453 enhance(cli): check mismatched packages in info (#14262) 2025-10-08 19:53:55 +08:00
Lucas Fernandes Nogueira
75082cc5b3 feat(cli): add mobile run commands, closes #13196 (#14120)
* feat(cli): add mobile run commands, closes #13196

* headers

* debug by default

* fix android env

* implement watcher

* clippy

* skip ipa build
2025-10-08 07:58:17 -03:00
Lucas Fernandes Nogueira
006d592837 fix(core): parse Android plugin args starting with is, closes #14254 (#14260)
by default Jackson treats the `isX` as a getter, so it looks for the `x` key in the JSON. To match behavior on other platforms we now configure Jackson to treat it as the field name itself.
2025-10-08 07:53:35 -03:00
Tony
d2938486e9 fix(cli): js icon in tauri info (#14261) 2025-10-08 17:58:47 +08:00
DomanskiFilip
19fb6f7cb0 fix(cli): improve Android BuildTask.kt Windows executable detection for nvm4w Fixes #13892 (#14146)
* fix(cli): improve Android BuildTask.kt Windows executable detection

- Fix Android build error on Windows when using nvm4w
- Add robust fallback logic for Windows executable detection
- Prevent 'node.exe.cmd' and 'Cannot find module' errors
- Graceful fallback to cargo when Node.js detection fails

Fixes #13892

* strip extension from project, try exe/cmd/bat

* revert args

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-10-07 15:25:49 -03:00
Lucas Fernandes Nogueira
3d6868d09c feat(cli): UTExportedTypeDeclarations support for file associations (#14128)
* feat(cli): UTExportedTypeDeclarations support for file associations

closes #13314

* update example

* update readme
2025-10-07 13:12:39 -03:00
Lucas Fernandes Nogueira
cc8c0b5317 feat(core): add support to universal app links on macOS (#14031)
* feat(core): add support to universal app links on macOS

follow-up for https://github.com/tauri-apps/tao/pull/1108

* fix ci

* clippy

* ignore empty schemes
2025-10-07 09:27:30 -03:00
renovate[bot]
20e53a4b95 chore(deps): update dependency cross-env to v10.1.0 (#14242)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 08:47:31 -03:00
Lucas Fernandes Nogueira
08bda64c25 fix(api): "command not found" error when running addPluginListener (#14132)
* fix(api): "command not found" error when running addPluginListener

the backend expects the command name to be in snake case

we've made this change already for check_permissions and request_permissions, but missed register_listener

* fix check instead

* update bundle.global.js

* code review suggestion

* add note

* adjust change file

* remove unused var

* fmt

* build
2025-10-06 14:55:20 -03:00
Lucas Fernandes Nogueira
28a2f9bc55 fix(cli): ensure Xcode project is up to date with Cargo project name (#14101)
* fix(cli): ensure Xcode project is up to date with Cargo project name

closes #13542

* clippy
2025-10-06 14:06:04 -03:00
Lucas Fernandes Nogueira
ed7c9a4100 feat(core): add config for Info.plist extensions, closes #13667 (#14108)
* feat(core): add config for Info.plist extensions, closes #13667

* add missing tag

* do not lie :)
2025-10-06 14:05:33 -03:00
Lucas Fernandes Nogueira
abf7e8850b fix(cli): mobile init when using pnpm dlx (#14118)
i noticed this when testing #13180 (though the original issue refers to npx, which I could not reproduce yet)
2025-10-06 13:12:00 -03:00
Lucas Fernandes Nogueira
b0012424c5 fix(cli): resolve IP when dev URL host is unspecified, closes #13356 (#14115)
currently the `use_network_address_for_dev_url` function already detects Ipv4Addr::UNSPECIFIED to resolve the local IP address for mobile development when the dev URL host is 0.0.0.0, but we only call it when `--host` is provided or running on a physical device. This change detects the unspecified host early and force the resolution to run even for simulator builds
2025-10-06 13:11:48 -03:00
Fabian-Lars
06d4a4ed6c fix(bundler): set APPIMAGE_EXTRACT_AND_RUN env var as well for linuxdeploy (#14241)
* fix(bundler): set APPIMAGE_EXTRACT_AND_RUN env var as well for linuxdeploy

* Aktualisieren von linuxdeploy-extract.md
2025-10-06 13:11:35 -03:00
renovate[bot]
a99601ee4b chore(deps): update dependency rollup to v4.52.4 (#14256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 19:10:02 +08:00
Vladimir Pankratov
2e089f6acb feat(ios): support async Swift plugin methods (completionHandler:) in PluginManager (#14148)
* Added selector with completionHandler handling

* Added .changes file

* fix change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-10-02 09:00:44 -03:00
Lucas Fernandes Nogueira
6bbb530fd5 chore(cli): warn when product name is missing, closes #14034 (#14105) 2025-10-02 08:28:30 -03:00
Lucas Fernandes Nogueira
b06b3bd091 refactor(cli): improve errors (#14126)
* refactor(cli): improve errors

* update change files

* license

* add errorext with fs_context helper

* update linux

* lint

* fmt

* windows

* revert bundler breaking change

* fix ios mod

* ref

* reduce amount of enum variants

* fix macos build

* Fix windows build

* Clippy

* capitalize cargo [skip ci]

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-10-02 06:58:26 -03:00
renovate[bot]
eb60b9966b chore(deps): update dependency rollup to v4.52.3 (#14230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-02 16:34:29 +08:00
kandrelczyk
94cbd40fc7 Add support for adaptive and themed icons on android (#14223)
* add support for adaptive icons

* fix

* small cleanup

* combine android_bg and android_fg when specified

* Update crates/tauri-cli/src/icon.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* add scale option

* properly generated rounded icons

* covector, clippy

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-10-01 09:41:54 -03:00
Lucas Fernandes Nogueira
673867aa0e feat(cli): detect Android env and install SDK and NDK if needed (#14094)
* feat(cli): detect Android env and install SDK and NDK if needed

changes the Android setup to be a bit more automated - looking up ANDROID_HOME and NDK_HOME from common system paths and installing the Android SDK and NDK if needed using the command line tools

* fix windows

* clippy

* lint

* add prmopts and ci check

* also check ANDROID_SDK_ROOT
2025-10-01 09:33:14 -03:00
Rasmus Mecklenburg
4188ffdafc chore(core): allow clippy::used_underscore_binding lint in command macro (#14225) 2025-09-28 13:21:58 +02:00
renovate[bot]
12a6787110 chore(deps): update dependency rollup to v4.52.2 (#14216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 21:54:42 +08:00
renovate[bot]
6cb73194c4 chore(deps): update dependency rollup to v4.52.0 (#14188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 15:41:58 +02:00
FabianLars
d1892b97ce ci(renovate): group windows-rs and webview2 crates 2025-09-25 14:57:50 +02:00
Fabian-Lars
e446926a6a chore: set minimumReleaseAge to 3 days in renovate and pnpm (#14206) 2025-09-25 11:40:25 +02:00
Kirill Gribunin
b0c493a4ea FIX: Fixed GDI object leak when a resizable window is created and then closed on Windows platform (#14209)
Co-authored-by: Kirill Gribunin <kgribunin@contoso.local>
2025-09-24 21:57:05 +03:00
Ryan Seys
d340b8c8b1 fix(macos): check if path is file or dir or neither (#13793) 2025-09-22 10:25:25 +02:00
Shane Cavanaugh
830146d0be chore: remove spam link in readme (#14197) 2025-09-18 18:52:47 +02:00
dependabot[bot]
fa3771b7bc chore(deps-dev): bump vite from 7.0.4 to 7.0.7 (#14172)
* chore(deps-dev): bump vite from 7.0.4 to 7.0.7

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.4 to 7.0.7.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.0.7/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.0.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.7
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deduplicate

* pnpm dedupe

* Update vite to 7.1.5

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-09-11 11:14:50 +08:00
renovate[bot]
9efe474e06 chore(deps): update dependency rollup to v4.50.1 (#14169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 11:12:36 +08:00
Tony
69476d8e23 fix(macros): stack overflow in invoke handler (#14170) 2025-09-08 17:38:15 +08:00
Jamie Ridding
f5851ee00d feat: Expose ScrollBarStyle webview option to tauri. (#14089)
* Expose `ScrollBarStyle` webview option to tauri.

This commit exposes the scroll_bar_style option from wry via the tauri
WebviewWindowBuilder API. By itself, the commit does not include changes
to the configuration file or JavaScript APIs: These will be added in a
later commit.

* Fix a compile error on macOS and Linux.

* Add `scroll_bar_style` to WindowConfig.

This commit exposes the `scroll_bar_style` option in tauri.conf.json/
.json5/.toml as `scrollBarStyle` and `scroll-bar-style`.

* Expose `scroll_bar_style` to JavaScript API.

This commit exposes the `scroll_bar_style` in the options object passed
to the JavaScript API `Webview` and `WebviewWindow` constructors.

While testing this, I discovered that on Windows, attempting to create
a webview from the JavaScript API will cause the hosting window to
immediately hang if it attempts to use the same data directory as
another webview without sharing the same environment options. This
commit includes no mitigation for this behaviour, as I will be opening
a separate issue about it at some point in the near future.

* Document WebView2 environment requirements.

This commit adds a message to the documentation for all components of
the `scroll_bar_style` configuration option, telling users that all
webviews that use the same data directory must use the same value for
this option.

* Fix formatting.

* Add change files to .changes directory.

* Remove `tauri-schema-generator` from change file.

* Remove quotes from change tags.

* Add tags to change files.

I did not realise that these were needed, as the pull request that I
used as my reference when building this feature did not have them.

* update conf

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-09-02 07:14:59 -03:00
FabianLars
66cb1dbbef chore(bench): clippy fixes 2025-09-01 20:28:21 +02:00
Kushal Meghani
a58d461eb0 refactor(bench): improve code style (#14062)
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2025-09-01 20:18:20 +02:00
SHIGRAF SALIK
2a06d10066 feat(bundle): add --no-sign flag to skip code signing in bundling pro… (#14052)
* feat(bundle): add --no-sign flag to skip code signing in bundling process

- Introduce a
o_sign option in bundle settings to allow skipping code signing
- Update macOS and Windows bundler implementations to respect the flag
- Wire up CLI option --no-sign to control signing behavior during bundling
- Add necessary config and type changes to propagate the flag throughout bundler

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* ci: added yml for github action testing

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* fix: fixed field 'digest_algorithm' is already declared error

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* ci: updated to test the new features as well

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* ci: fixed yml issue

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* fix: fixed missing parameter issue in android sign.rs

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* chore: apply linting

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* chore: remove redundant files

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* chore: revert indentations

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* fix: added parameters to ios mobile build.rs

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* docs: updated documentation for settigs.rs

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* docs(cli): add documentation for
o_sign flag in build options

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* chore: apply cargo fmt

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* docs: added CHANGES.md

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* refactor(bundler): make
o_sign private and add getter

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* fix: minor error

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* refactor: revert build_benchmark_jsons.rs

Signed-off-by: ShigrafS <shigrafsalik@proton.me>

* impl for macos too

* fix ci

* fix windows build

---------

Signed-off-by: ShigrafS <shigrafsalik@proton.me>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-09-01 13:59:55 -03:00
Fabian-Lars
59089723fc feat(api): add dataDirectory setting config (#14091)
* feat(api): ad dataDirectory setting config

* changefile fmt

* chain, log if dirs::data_local_dir fails

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-09-01 13:56:26 -03:00
Lucas Fernandes Nogueira
1a6627ee7d feat(cli): set default log level when adding the log plugin (#14122)
* feat(cli): set default log level when adding the log plugin

needs https://github.com/tauri-apps/plugins-workspace/pull/2965

ref #14075

* Update crates/tauri-cli/src/add.rs
2025-09-01 13:55:59 -03:00
Lucas Fernandes Nogueira
f6622a3e34 feat(cli): prompt to install iOS runtime if needed, closes #9186 (#14129)
* feat(cli): prompt to install iOS runtime if needed, closes #9186

* ensure runtime is installed

* only when running directly

* use starts_with
2025-09-01 13:55:51 -03:00
github-actions[bot]
80eadb7387 apply version updates (#14100)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-01 12:44:16 -03:00
Tony
346a420812 docs: improve resources docs (#14136)
* docs: improve resources docs

* Clippy
2025-09-01 12:15:43 -03:00
renovate[bot]
5239d39149 chore(deps): update dependency rollup to v4.50.0 (#14127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-31 17:51:26 +08:00
Lucas Fernandes Nogueira
0b1da30d28 chore(tauri): update documentation for home_dir on iOS (#14121)
* chore(tauri): update documentation for home_dir on iOS

ref #12497

* update
2025-08-30 08:09:38 -03:00
Lucas Fernandes Nogueira
7db7142f9f fix(cli): empty Android emulator name (#14119)
applies https://github.com/tauri-apps/cargo-mobile2/pull/481
2025-08-29 11:45:04 -03:00
Lucas Fernandes Nogueira
a9b342125d fix(cli): iOS simulator dev/build on Apple Intel, closes #13456 (#14114)
applies https://github.com/tauri-apps/cargo-mobile2/pull/479
2025-08-28 18:16:42 -03:00
Lucas Fernandes Nogueira
bcf000c0a8 fix(cli): ios command failing when running with deno, closes #13547 (#14110)
Deno doesn't set an environment variable to help us, so I had to use the exe path to determine whether we're running under deno or not
2025-08-28 18:02:46 -03:00
Lucas Fernandes Nogueira
61b9b681e8 feat(cli): retain all RUST_* env vars on mobile commands (#14111)
useful to propagate RUST_BACKTRACE to the IDE commands
2025-08-28 18:02:22 -03:00
Lucas Fernandes Nogueira
c37a298331 fix(cli): set package type for Deno (#14112)
without the type Deno assumes that the package is a ESM so it cannot use `require`
we should probably update our minimum Node.js version and use ESM instead, but I want to ship this fix first
2025-08-28 18:02:09 -03:00
Lucas Nogueira
b8b866fcc7 fix(examples): update tauri-plugin-log
fixes iOS deadlock
2025-08-28 15:09:36 -03:00
Lucas Fernandes Nogueira
956b4fd6ff fix(cli): export method on Xcode < 15.4, closes #13818 (#14106)
see https://github.com/fastlane/fastlane/issues/22028 for additional context
2025-08-28 12:37:26 -03:00
Lucas Fernandes Nogueira
07e134f70e feat(core): enhance error message for dev server request, closes #13816 (#14107) 2025-08-28 12:36:49 -03:00
Lucas Fernandes Nogueira
f70b28529d feat(cli): ensure mobile Rust targets are installed (#14093)
currently we only install Rust targets for mobile when initializing the projects.

Users can commit the Android/iOS projects, so running the init command is not a requirement to develop mobile apps. This change ensures the Rust targets are installed before trying to compile them.
2025-08-27 18:05:24 -03:00
Fabian-Lars
c23bec62d6 fix: don't set macos deployment target in dev (#14083)
* fix: don't set macos deployment target in dev.

* doc comment

* Update .changes/skip-deployment-target-in-dev.md

* Apply suggestions from code review

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2025-08-27 17:07:07 -03:00
renovate[bot]
9a35a616f5 chore(deps): update dependency rollup to v4.49.0 (#14098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-27 23:27:02 +08:00
Fabian-Lars
755eb33d1c docs: use get_webview_window in example (#14082) 2025-08-25 16:19:44 +02:00
Lucas Nogueira
df61fac2b5 fix(ci): bump tauri-cli to 2.8.3 to match @tauri-apps/cli 2025-08-25 10:32:46 -03:00
github-actions[bot]
16348ac2bd apply version updates (#14081)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-25 10:30:08 -03:00
Lucas Fernandes Nogueira
03e7c11932 fix(tauri-runtime-wry): ignore about:blank initial URL (#14080)
* fix(tauri-runtime-wry): ignore about:blank initial URL

fixes a macOS warning when a navigation handler is registered and you choose to create a new window on the on_new_window hook - in this case we shouldn't perform the initial navigation since the URL is provided by the webview configuration from the hook

* change tag

* bump min wry
2025-08-25 10:03:35 -03:00
github-actions[bot]
e81635aa3d apply version updates (#14079)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-25 14:25:58 +02:00
Fabian-Lars
0ac89d3b6c chore(deps): Update cargo-mobile2 for ios 18.6 sim support (#14078) 2025-08-25 14:04:43 +02:00
renovate[bot]
4791d09a0a chore(deps): update dependency rollup to v4.48.1 (#14077)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 12:32:49 +02:00
renovate[bot]
bc829ee24d chore(deps): update dependency rollup to v4.48.0 (#14053)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 12:55:18 +08:00
renovate[bot]
11800a0071 chore(deps): update rust crate jsonschema to 0.33 (#14074)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 09:25:53 +08:00
github-actions[bot]
662b39adb3 apply version updates (#14070)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-24 11:38:07 -03:00
Tony
2aaa801c35 Improve documentation of app > windows (#14058) 2025-08-24 10:55:44 -03:00
Fabian-Lars
5349984064 fix: set webview2 path before initializing runtime (#14054)
* fix: set webview2 path before initializing runtime

* wrong current_exe function
2025-08-24 08:18:14 -03:00
Lucas Nogueira
5f535b4150 fix(bench): lint warnings 2025-08-24 08:17:25 -03:00
Tony
f3df96fb38 fix(windows): binary patching 32 bit updater type (#14065)
* fix(windows): binary patching 32 bit updater type

* Use `get` instead of size check and then assert
2025-08-24 08:16:12 -03:00
Lucas Nogueira
c0d3f9d47e chore(bench): fix build and clippy 2025-08-24 07:14:53 -03:00
Kushal Meghani
d54f3b95a6 refactor(bench): improve utils.rs safety, error handling, and docs (#14057)
- Replace `unwrap` and `expect` with `anyhow::Result` for safer error handling
- Add contextual error messages using `anyhow::Context`
- Fix `home_path` on Windows by using `USERPROFILE` instead of `HOMEPATH`
- Ensure process helpers (`run_collect`, `run`) return `Result` instead of panicking
- Improve parsing logic in `parse_max_mem` and `parse_strace_output`
- Add documentation comments for all public functions
- Add best-effort cleanup and resilience against malformed input
2025-08-22 08:04:57 -03:00
renovate[bot]
1e7aac355f chore(deps): update dependency rollup to v4.46.4 (#14049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 12:01:01 +08:00
github-actions[bot]
8d869717da apply version updates (#14041)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-19 09:08:54 -03:00
Kushal Meghani
f0172a454a fix(app): correct removeDataStore return type, add docs for getBundle… (#14038)
* fix(app): correct removeDataStore return type, add docs for getBundleType

* add change file

* fmt

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-19 08:02:21 -03:00
Lucas Fernandes Nogueira
5075b67d36 fix(tauri): build without the wry feature flag (#14039)
* fix(tauri): build without the wry feature flag

* change file

* fix windows
2025-08-19 07:42:41 -03:00
Lucas Nogueira
c3252f72f6 fix(tauri): on_related_view should be behind the wry feature flag 2025-08-18 17:23:06 -03:00
github-actions[bot]
b4abb6cae8 Apply Version Updates From Current Changes (#13887)
* apply version updates

* chore: minor bump codegen, build, macros

* fix audit

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-18 15:50:13 -03:00
Will
1a3d1a024e fix(ios): Tauri iOS build with binary XCFramework dependencies (#13995)
* Fix Tauri iOS build not having a PATH variable to access unzip to extract binaryTargets and also not including Frameworks when linking

* Add covector change

* fmt

* Update crates/tauri-utils/src/build.rs

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2025-08-18 07:35:49 -03:00
renovate[bot]
37154ebdcd chore(deps): update dependency rollup to v4.46.3 (#14027)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 14:34:25 +08:00
Tony
380656874e Remove AsRef<Window> on WebviewWindow (#14026) 2025-08-18 13:45:43 +08:00
Lucas Fernandes Nogueira
bc4afe7dd4 feat(cli): check plugin versions for incompatibilities (#13993)
* feat(cli): check plugin versions for incompatibilities

check core plugin versions for incompatibilities between Cargo and NPM releases

a plugin NPM/cargo version is considered "incompatible" if their major or minor versions are not equal

on dev we show an warning
on build we error out (with a `--ignore-incompatible-plugins` flag to prevent that)

this is an idea from @oscartbeaumont
we've seen several plugin changes that require updates for both the cargo and the NPM releases of a plugin, and if they are not in sync, the functionality does not work
e.g. https://github.com/tauri-apps/plugins-workspace/pull/2573 where the change actually breaks the app updater if you miss the NPM update

* Use list to get multiple package versions at once

* Fix for older rust versions

* Clippy

* Support yarn classic

* Support yarn berry

* Use `.cmd` only for `npm`, `yarn`, `pnpm`

* Use yarn list without --pattern

* rename

* Extract function `check_incompatible_packages`

* Check `tauri` <-> `@tauri-apps/api`

* incompatible -> mismatched

* run build check in parallel

* rename struct

* Switch back to use sync check and add todo

* Extract to function `cargo_manifest_and_lock`

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-08-17 12:24:40 -03:00
Akshanabha Chakraborty
7c2eb31c83 feat: add PluginHandle::run_mobile_plugin_async (#13895)
* add async

* chore: fmt

* feat: add run_mobile_plugin_async

* changes

* chore: fix misplaced `}`

* chore: fix minor pattern matching error

* fix: copy the response handling directly from run_mobile_plugin

* fix android build

* Fix clippy lint

* lint

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-17 12:14:16 -03:00
Tony
737364b8d3 fix: a few regressions from previous PRs (#14020)
* fix: a few regressions from previous PRs

* rename with_window_features to window_features

* Clippy

* clippy

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-17 12:03:31 -03:00
Robert
68874c68c5 feat(core): webview window focusable property, closes #11130 (#13564)
* Adds the ability to set the focused property from tauri.conf.json -- windows

* add set_focusable, pin tao

* fmt

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-17 11:50:17 -03:00
Sean Wang
dfadcb764b feat: add WebView::set_cookie and WebView::delete_cookie (#13661)
* chore: patch wry

* feat: added `Webview::set_cookie` and `Webview::delete_cookie`

* chore: changes-files

* fmt

* owned cookie, re-export crate

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-16 23:41:16 -03:00
Sean Wang
22d6bcacbb feat(tauri): impl App::set_device_event_filter for AppHandle also (#14008)
* feat(tauri): impl `App::set_device_event_filter` for `AppHandle` also

* Update .changes/impl-set_device_event_filter-for-apphandle.md

* Update .changes/impl-set_device_event_filter-for-apphandle.md

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2025-08-16 15:30:46 -03:00
Tony
b21d86a8a3 fix(cli): permission add could add duplicated (#13981) 2025-08-16 14:51:45 -03:00
THELOSTSOUL
33d0b3f0c1 feat: add WebviewBuilder::on_new_window and WebviewBuilder::on_document_title_changed (#13876)
* add "new window" and "document title changed" webview handler

* take document title changed handler

* update example, add missing api, change files

* allow creating tauri window for the window.open call

* set size and position, fix linux, example

* enhance document title change

* fix windows deadlock

* wry 0.53

* update wry

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-16 14:49:01 -03:00
Petr
f1232671ab feat: expose internal TrayIcon (#13959) 2025-08-17 00:23:23 +08:00
Lucas Fernandes Nogueira
0c402bfb6b feat(cli): increase iOS deployment target to 14.0 (#13997)
* feat(cli): increase iOS deployment target to 14.0

closes https://github.com/tauri-apps/plugins-workspace/issues/1876

ref https://github.com/tauri-apps/tauri-docs/pull/3455

* fix tests
2025-08-16 10:32:26 -03:00
Lucas Fernandes Nogueira
d6d5f37077 feat: add --root-certificate-path option for mobile dev (#13358)
* feat: add `--root-certificate-path` option for mobile dev

lets you use a HTTPS development server

example usage:
```
cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/mobile-dev-cert
cargo tauri android dev --open --root-certificate-path "/Users/lucas/Library/Application Support/mkcert/rootCA.pem" --features tauri/rustls-tls
```

* Apply suggestions from code review

Co-authored-by: Fabian-Lars <github@fabianlars.de>

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-08-16 09:13:10 -03:00
Sean Wang
7261a14368 feat: impl AsRef<Window> and on_webview_event for WebviewWindow (#14012) 2025-08-16 11:01:53 +08:00
Sean Wang
0e6b5cbe5f feat(tauri): re-export PixelUnit, PhysicalUnit, LogicalUnit (#14009)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-08-15 15:24:34 +02:00
Sean Wang
a3dc42477a feat(tauri): export TitleBarStyle for all platforms (#14013) 2025-08-15 15:13:05 +02:00
Sean Wang
21ebc6e820 feat(tauri): remove 'static lifetime bound from AppHandle::remove_plugin (#14007) 2025-08-15 15:28:39 +08:00
Lucas Fernandes Nogueira
2d5f5a9230 chore: update tests (#13998) 2025-08-13 13:34:01 -03:00
Fabian-Lars
4475e93e13 feat(bundler/cli): Add feature flag to use system certificates (#13824) 2025-08-12 13:30:23 +02:00
Naman Khandelwal
5110a762e9 feat(window): add macOS window::set_simple_fullscreen (closes #13670) (#13830)
* add implementation of set_simple_fullscreen

* add simple fullscreen API for macos

* register desktop command

* format

* fix errors

* chore: format

* change implementation

* add api

* fix tests

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-12 08:03:29 -03:00
Fabian-Lars
a9ec12843a feat: add option to not wait on notarization to finish (#13521)
* feat: add option to not wait on notarization to finish

* cli arg istead of config

* changefile

* fix serde

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-11 14:29:41 -03:00
Tony
f0dcf9637c fix(cli): add single-instance autostart init code (#13980) 2025-08-11 19:40:18 +08:00
Tony
196ace3c04 fix: return error on window creation failed (#13970)
* fix: return error on window creation failed

* Add todo about adding error inside `CreateWindow`
2025-08-09 22:43:18 +08:00
Aly Cerruti
82e264552e fix(windows): fix double free (STATUS_HEAP_CORRUPTION) of resizing handler's userdata (#13968)
* fix: double free of resizing handler's userdata on Windows

Using WM_NCDESTROY instead of WM_DESTROY is more correct for freeing userdata, as windows can receive multiple WM_DESTROY events if they're parented.

* chore: add change entry for resizing handler double-free fix
2025-08-09 12:03:21 +08:00
Tony
c134a769ea chore: fix some warnings on new rust version (#13965)
* chore: fix some warnings on new rust version

* No main

* allow dead code on specta Channel
2025-08-09 08:19:03 +08:00
Fabian-Lars
390cb9c36a fix(cli): reduce log level for goblin and handlebars (#13953) 2025-08-07 14:40:41 +02:00
Jaken Herman
9300b59f65 feat: Added fips_compliant field to WixConfig (#13787)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-08-05 20:35:25 +02:00
Jadon Jesse
e1d7be8e57 fix(example): runtime crash when counter less than 0 (#13955) 2025-08-05 18:45:49 +02:00
renovate[bot]
90c1c327ac chore(deps): update dependency cross-env to v10 (#13894)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-05 17:15:22 +02:00
renovate[bot]
83032e273b chore(deps): update rust crate which to v8 (#13711)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-05 14:59:02 +02:00
Tony
a8f1569b04 fix(windows): bundler should not sign non-binaries (#13921)
* fix(windows): bundler should not sign non-binaries

* Fix non Windows
2025-08-05 11:06:43 +08:00
Tony
0ea08e901e fix(example): unminimize window on tray icon click (#13949) 2025-08-05 10:07:16 +08:00
Fabian-Lars
887b8da684 fix(bundler): improve log format of sign command stdout (#13947) 2025-08-04 15:38:56 +02:00
Tony
7d21e3b2fa docs: how security > capabilities works (#13946)
* docs: how `security > capabilities` works

* Add how to use it

* Apply suggestions

* Relative to `Cargo.toml`

* Remove the relative base wording
2025-08-04 18:13:02 +08:00
Sam Lidder
4d270a96a8 fix(windows): patch_binary causing codesigning verification failure (#13943)
* fix(windows): `patch_binary` causing codesigning verification failure

* `cargo fmt`

* add change file

* Update .changes/fix-binary-patching-codesign-verification-failure.md

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2025-08-04 16:58:04 +08:00
renovate[bot]
bcc7a82a3a chore(deps): update rust crate notify-debouncer-full to 0.6 (#13945)
* chore(deps): update rust crate notify-debouncer-full to 0.6

* Bump jsonschema
2025-08-04 10:39:34 +08:00
Fabian-Lars
8b465a12ba fix(bundler/linux): pull latest appimage plugin (#13913) 2025-07-31 20:06:57 +02:00
Tony
ee68c918a1 chore(deps): unpin serialize-to-javascript (#13932) 2025-07-31 17:52:51 +08:00
renovate[bot]
d7075b66bd chore(deps): update rust crate toml to 0.9 (#13784)
* chore(deps): update rust crate toml to 0.9

* Bump toml_edit and cargo_toml

* Update tauri-winres

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-31 17:14:09 +08:00
xeodus
bbcea1f5e8 fix(cli): improve error messages regarding cargo metadata command (#13918) 2025-07-30 11:23:54 +02:00
Pavel Kuzmin
5ba1c3faa4 feat(menu): add icon support for Submenu in Rust and JS/TS APIs (#13722)
* feat(menu): add icon and nativeIcon support for Submenu in tauri and @tauri-apps/api

* Merge branch 'dev' into dev

* Update muda

* feat(menu): add set_icon and set_native_icon methods to set submenu icons

* feat(menu): unify icon handling by introducing MenuIcon type

* chore: sync bundle.global.js

* Make setIcon actually work

* Regenerate `bundle.global.js`

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-30 13:48:55 +08:00
renovate[bot]
e27427f795 chore(deps): update dependency rollup to v4.46.2 (#13914)
* chore(deps): update dependency rollup to v4.46.2

* Bump @eslint/plugin-kit

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-30 09:39:22 +08:00
renovate[bot]
a32a4ce3be chore(deps): update rust crate jsonschema to 0.32 (#13915)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 09:26:04 +08:00
Andrew Voynov
bc6b125b24 fix(bundler): replace empty RPM release value with 1 (#13909)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-07-29 22:20:25 +02:00
Takeaki Kobayashi
9c938be452 fix(cli): properly migrate svelte to v5 in the plugin example template (#13912)
* fix: update vite.config.ts to support Svelte 4 in example app

* Migrate the code to svelte 5 instead

* Add change file

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-29 17:47:17 +08:00
renovate[bot]
5c8182860c chore(deps): update rust crate jsonschema to 0.31 (#13903)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 23:29:31 +08:00
renovate[bot]
1d31e4647c chore(deps): update dependency rollup to v4.46.1 (#13904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 15:19:30 +08:00
renovate[bot]
517e7b60e1 chore(deps): update dependency rollup to v4.46.0 (#13897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-27 20:38:49 +08:00
Robin van Boven
72b4226ee9 feat: reduce Debug format size for binary buffers (#13809)
* feat: reduce Image debug output

For example now:
`Image { rgba: Cow::Borrowed([u8; 4096]), width: 32, height: 32 }) }`

* feat: reduce EmbeddedAssets debug size

For example now:
```
EmbeddedAssets {
    assets: {
        "/index.html": [u8; 1835],
        "/index.js": [u8; 212],
    },
    global_hashes: [
        Script(
            "'sha256-EOd6N98xxmK5s7VvxV7W2w7YG+dmP52MqNiZUq1NLeE='",
        ),
        Style(
            "'sha256-YEercZJImS+vUX2bz7vkQ0aA4rtBIPLuCEWz+yraQ/g='",
        ),
    ],
    html_hashes: {
        "/index.html": [
            Script(
                "'sha256-3g8CfFrjFLGpwD2o+hwMt+lh/hsHbQ3XY+EPJ35fFKk='",
            ),
            Script(
                "'sha256-EOd6N98xxmK5s7VvxV7W2w7YG+dmP52MqNiZUq1NLeE='",
            ),
        ],
    },
}
```

* feat: reduce `app_icon` debug size

* chore: changelog

* chore: include tauri-utils in changelog

* doc: comment had extra closing brackets [skip ci]
2025-07-25 07:55:00 -03:00
Lucas Fernandes Nogueira
d6d941c3a7 chore(cli): update plugin template (#13882) 2025-07-25 07:50:41 -03:00
Lucas Fernandes Nogueira
a0113a8c64 feat(tauri-plugin): add build::mobile::update_info_plist (#13888)
* feat(tauri-plugin): add build::mobile::update_info_plist

needed for https://github.com/tauri-apps/plugins-workspace/pull/2870

* Update .changes/update-info-plist.md
2025-07-25 07:50:24 -03:00
Jack Lavigne
91508c0b8d feat: add config option for custom watch folders (#13881)
* feat: add config option for custom watch folders

* fix: cargo clippy changes

* chore: remove file

* fix: ios dev

* docs: clarify absolute allowed

* refactor: rename variable

* fix: review suggestions

* fix: resolve paths

* fixL use canonicalize

* chore: add changefile

* chore: add error if cant canonicalize

* reformat changelog

* Update .changes/additional-watch-folders.md

* Update crates/tauri-cli/src/interface/rust.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* Revert "Update .changes/additional-watch-folders.md"

This reverts commit 98186b1a89.

* Also bump `@tauri-apps/cli`

* Apparently I'm so used to a higher rust version

* Revert "Apparently I'm so used to a higher rust version"

This reverts commit ea1d89e2d3.

* Need to check for existence for abs paths as well

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-24 22:21:00 +08:00
github-actions[bot]
fd63f229d5 apply version updates (#13871)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-22 12:32:14 +08:00
Krishna Chaitanya
af95fb6014 fix: sign main binary after patching with bundle info (fix #13868) (#13870)
* fixed #13868

* add main binary singing only on windows

* updated readme message
2025-07-22 11:32:49 +08:00
FabianLars
65bb24b9ae fix(cli): fix metadata version 2025-07-21 10:10:12 +02:00
FabianLars
332ec355a1 fix(cli): add default triplets to napi targets config 2025-07-21 09:59:32 +02:00
renovate[bot]
2c46b1873e chore(deps): update dependency eslint-config-prettier to v10.1.8 (#13855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 10:23:19 +08:00
FabianLars
96439c2c42 chore(deps): Update @eslint/plugin-kit to fix pnpm audit 2025-07-21 00:37:41 +02:00
github-actions[bot]
ab97f36b64 apply version updates (#13751)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-21 00:09:52 +02:00
Tony
6a4451bcd9 fix(windows): isolation pattern create iframe loop (#13849) 2025-07-20 23:33:14 +02:00
Tony
56277e4722 chore(deps): update napi to v3 (#13852) 2025-07-20 23:03:51 +02:00
Fabian-Lars
7a6fd5b75d fix(bundler): Move AppRun to mirror (#13863) 2025-07-20 23:00:19 +02:00
Sean Wang
7f3c989111 feat(tauri): add plugin_boxed methods (#13837)
* feat(tauri): add `plugin_dyn` methods

* refactor: rename `plugin_dyn` to `plugin_boxed`
2025-07-18 11:48:10 +08:00
Tony
bda8304107 fix(cli): error out when migrating from v2 alpha (#13833) 2025-07-16 22:06:17 +08:00
Fabian-Lars
fb9d9c7fd1 chore(readme): Update discord invite (#13836) 2025-07-16 11:02:23 +02:00
renovate[bot]
8263b412c6 chore(deps): update dependency rollup to v4.45.1 (#13831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 23:10:12 +08:00
Siddharth
3025d90951 feat: expose some window props from runtime-wry (#13822)
* test: make some of window id + stores public

* test: make window wrapper label pub

* feat: make label accessible for windowwrapper

* chore: adds wry runtime changefile

* chore: avoid forced clone on label

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2025-07-15 14:32:19 +08:00
Joseph Brooksbank
96391467e9 feat(mock): add mock for listen and emit (#13783)
* feat(mock): add mock for listen and emit
* feat(mock): add mock for listen and emit

* feat(mock): add mock for listen and emit

* Add change file

* correctly clear unregisterListener

* format with prettier

* build project

* opt-in to mocking events

* Use a minor bump
2025-07-15 09:30:01 +08:00
kandrelczyk
c0a654b863 failed binary patch warning (#13825)
* failed binary patch warning

* Update crates/tauri-bundler/src/bundle.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2025-07-14 22:24:11 +08:00
Lucas Raposeiras
b821796add docs: add missing trafficLightPosition to WindowOptions (fix #13790) (#13810)
* docs: add missing `trafficLightPosition` to `WindowOptions`

* Change file and tweak docs

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-13 20:58:39 +08:00
Mohammad Hossein Bagheri
33d079392a feat(cli): allow runner configuration to be an object with cwd and args (#13811)
* Update config.schema.json

* Add RunnerConfig for customizable build runner

Replaces runner String with RunnerConfig in CLI and config, allowing advanced runner configuration via string or object with cmd, cwd, and args. Updates schema and usage to support new format, and adds tests for serialization, deserialization, and API. Enables more flexible build and run command customization.

* Create runner-object-config.md

* Remove unused RunnerConfig import in tests

Cleaned up the test module in config.rs by removing the unused RunnerConfig import from two test functions.

* Fix tests failing

Updates related tests in tauri-utils to improve readability and maintain consistency. Minor import reordering in tauri-cli for clarity.

* Move RunnerConfig enum and impls above BuildConfig

Relocated the RunnerConfig enum and its associated implementations to appear before the BuildConfig definition. This improves code organization and logical grouping of configuration-related types.
2025-07-13 19:28:09 +08:00
Tony
7bc77a038a feat: allow all frame init scripts in plugin (#13609)
* feat: allow all frame init scripts in plugin

* Add change files

* Update crates/tauri/src/plugin.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* Default impl initialization_script_2 from 1

* Update crates/tauri/src/plugin.rs

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2025-07-13 14:12:39 +08:00
kandrelczyk
371ee34383 make static bundle type var mutable (#13812)
* make static bundle type var mutable

* remove unsafe from no_mangle and link_section
2025-07-12 18:48:13 +08:00
renovate[bot]
22cd1e2846 chore(deps): update dependency rollup to v4.45.0 (#13813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-12 17:55:16 +08:00
M
1c5df96fe8 fix(protocol): proxy body in mobile dev (#13773)
* proxy body in mobile dev

* add change file, use std::mem::take

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-07-11 07:22:46 -03:00
kandrelczyk
4f96ed41ca add #[used] to __TAURI_BUNDLE_TYPE so that it's not stripped in release builds (#13808) 2025-07-11 15:52:37 +08:00
renovate[bot]
24eb2b1cd3 chore(deps): update dependency @sveltejs/vite-plugin-svelte to v6 (#13803)
* chore(deps): update dependency @sveltejs/vite-plugin-svelte to v6

* Bump vite

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-11 13:22:46 +08:00
Lucas Fernandes Nogueira
0f248b111f fix(cli): install iOS dependencies when needed (#13799)
currently deps are only installed on init, which might not be executed on someone's machine if the xcode project is commited to the repo. we need to ensure dependencies are installed before running them

applies https://github.com/tauri-apps/cargo-mobile2/pull/468
2025-07-10 10:17:23 -03:00
Lucas Fernandes Nogueira
4ba871c5d2 fix(core): loading remote URLs on mobile, closes #13461 (#13782) 2025-07-09 17:08:38 -03:00
Catalin Andrei Cacuci
f94af90359 fix(nsis): per-machine installer not requesting elevation when run by non-admin users (#13786) 2025-07-09 13:42:07 +02:00
Tony
cfc5bb8196 feat(android): edge to edge in init template (#13780)
* feat(android): edge to edge in init template

* Add androidx.activity:activity-ktx

* androidx.webkit:webkit:1.14.0
2025-07-09 10:05:27 +08:00
Lucas Fernandes Nogueira
916aeaa486 fix(cli): android commands reading iOS config closes #13765 (#13781) 2025-07-08 08:49:47 -03:00
Jeong Min Oh
12e3590613 Fix(package.json) build script, Fix(android) build template (#13759)
* Update compileSdk targetSdk, Fix build cmd

* Add changelog

* Update AGP version

* Update package.json

* Update AGP

* Update SDK

* Update

* Update .changes/update-android-sdk.md

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* Use changes tag in change file

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2025-07-08 17:00:26 +08:00
kandrelczyk
232265c70e feat: bundle type detection at runtime via binary patching (#13209)
* patch binary with bundle type info

* only patch if the updater is included

* fix linux warnings

* patch binary when updaer is configured

* patch binary with bundle type info

only patch if the updater is included

fix linux warnings

patch binary when updaer is configured

* fix formatting

* fix license header

* fix taplo error

* move __TAURI_BUNDLE_TYPE to utils

* export get_current_bundle_type

* macos fix

* cleanup, add api

* update change file

* fix windows

* fmt, fix rust version support

* fix macos

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-07-07 12:08:00 -03:00
renovate[bot]
02440b875c chore(deps): update dependency rollup to v4.44.2 (#13766)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-06 20:39:00 +08:00
Catalin Andrei Cacuci
f2dbe73097 fix(nsis): wrong required files path for nsis_tauri_utils.dll (#13772)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-07-05 23:42:25 +02:00
Tony
152d971bcd fix(mock): expose callback functions (#13744)
* fix(mock): expose callback functions

* Add change file

* Bumped the wrong package

* Fix end quote in comment
2025-07-02 22:23:23 +08:00
Jeong Min Oh
acd7574284 fix(cli/ios): fix CFBundleVersion type (#13743)
Co-authored-by: devfive <devfive@devfiveui-MacStudio.local>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-07-02 16:01:59 +02:00
renovate[bot]
e296e4bc38 chore(deps): update dependency @rollup/plugin-typescript to v12.1.4 (#13729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 14:07:17 +02:00
AprilNEA
11b4a03881 fix(cli): remove unnecessary files in npm package, closes #12139 (#13735)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-06-30 14:02:03 +02:00
github-actions[bot]
0277596341 apply version updates (#13731)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-28 17:48:13 -03:00
Lucas Fernandes Nogueira
cbd9629729 fix(cli): load --config values on xcode and android-studio scripts (#13730) 2025-06-28 17:16:36 -03:00
FabianLars
0079d08ba9 chore(worker): Enable observability 2025-06-27 17:20:01 +02:00
FabianLars
effd106adf fix(worker): Fix route syntax 2025-06-27 17:14:34 +02:00
github-actions[bot]
4053ad1b58 apply version updates (#13724)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-27 16:22:27 +02:00
Fabian-Lars
f010ca5e91 fix(core): compile error without common-controls-v6 (#13719) 2025-06-27 15:54:10 +02:00
Fabian-Lars
6b2b9d6cbf chore: clippy 1.88 (#13720) 2025-06-27 15:33:36 +02:00
renovate[bot]
b6de1c89c2 chore(deps): update rust crate md5 to 0.8 (#13712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-06-26 16:29:19 +02:00
renovate[bot]
a3ae2cebbf chore(deps): update rust crate rand to 0.9 (#13700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-06-26 15:47:42 +02:00
github-actions[bot]
a3f11b4f3b apply version updates (#13693)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-26 20:47:38 +08:00
Fabian-Lars
4b7370e9e0 fix(cli): fix rollup-plugin-typescript v12 compatiblity (#13710)
* fix(cli): fix rollup-plugin-typescript v12 compatiblity

* always the same mistake
2025-06-26 20:13:05 +08:00
Tony
5bbcaaec89 fix: can't set tray menus and icons in js (#13707) 2025-06-26 19:42:01 +08:00
renovate[bot]
3eb3162404 chore(deps): update rust crate muda to 0.17 - tray-icon to 0.21 (#13695)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-06-26 12:31:00 +02:00
renovate[bot]
0cbfd8923c chore(deps): update dependency rollup to v4.44.1 (#13696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 15:33:56 +08:00
montyc1999
0a552a868c fix(macros): cache rustc -V output (#13690) 2025-06-26 10:48:52 +08:00
renovate[bot]
560067cd7e chore(deps): update rust crate getrandom to 0.3 (#13685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-06-25 23:05:29 +02:00
Fabian-Lars
6a4ea10274 fix(worker): add workaround to fix build on rust 1.87 2025-06-25 22:19:47 +02:00
Fabian-Lars
349bbfc5c7 chore(deps): update axum to 0.8 and worker to 0.6 (#13677) 2025-06-25 20:57:52 +02:00
Fabian-Lars
32a84650c0 ci: disable renovate for 1.x branch (#13673) 2025-06-25 17:36:07 +02:00
Tony
b19f16aba1 ci: enable create-pull-request sign commits (#13668) 2025-06-25 15:50:45 +02:00
github-actions[bot]
594822aa55 Apply Version Updates From Current Changes (#13282)
* apply version updates

* minor bump for build, plugin, macros and runtime

---------

Co-authored-by: lucasfernog <20051258+lucasfernog@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-06-24 15:50:44 -03:00
Lucas Nogueira
e4aa35e083 fix(cli): init tests 2025-06-24 15:02:22 -03:00
sftse
9c16eefa31 Update kuchikiki, html5ever, tao, wry, webview2-com (#13629)
* fix: dont depend on private schemars api

* tauri-cli/deps: update kuchikiki and html5ever
tauri-utils/deps: update kuchikiki and html5ever
tauri-runtime-wry/deps: update wry to match kuchikiki and html5ever versions

* fix: specify exact patch version of schemars

Without this, cargo resolves the patch version of schemars to one that
does not include the _private module on which tauri-utils v1 depends,
which is a dependency of tauri-cli. As a result of this, the build breaks.

* tauri-utils/fix: inline tauri-utils v1 config module

* deps: upgrade and pin schemars 0.8.21 to pick up crate patch in Cargo.toml

* update tao, wry

* lint, license

* lint

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-06-24 14:39:20 -03:00
Lucas Fernandes Nogueira
3242e1c946 feat(cli): allow passing Cargo commands to mobile dev/build commands (#13659)
* feat(cli): allow passing Cargo commands to mobile dev/build commands

* fmt
2025-06-23 14:47:21 -03:00
Lucas Fernandes Nogueira
4a880ca697 feat(cli): synchronize productName changes with iOS Xcode project (#13658)
* feat(cli): synchronize productName changes with iOS Xcode project

* fmt
2025-06-23 14:47:10 -03:00
Lucas Fernandes Nogueira
d1ce9af628 feat(cli): add --config arg to the mobile init cmds, closes #13284 (#13660) 2025-06-23 12:55:07 -03:00
Lucas Fernandes Nogueira
ec6065fa4a fix(cli): use original identifier to fix mobile options reading (#13625)
* fix(cli): read original identifier to fix mobile options reading

the iOS and Android CLI commands leverage an android_studio_script/xcode_script that is executed by the native IDE or build tool. This script reads the Tauri configuration to find the app identifier used to communicate with the parent Tauri CLI process to read CLI options.

The communication is broken when the `--config` arg is used, since the IDE script does not have access to that value before reaching the parent process, which is impossible without knowing the actual identifier used.

To bypass this we'll agree on using the original identifier. This obviously won't work if the original tauri.conf.json do not have an identifier, so we error out in this case

* fix build, lint
2025-06-23 09:29:46 -03:00
Tony
18b5299952 docs: where does resource dir resolve to (#13640)
* docs: where does resource dir resolve to

* Add note about potential change
2025-06-21 10:22:24 +08:00
Tony
eb3f0248c2 fix: write default permission words if not empty (#13646)
* fix: write default permission words if not empty

* Remove left over format!
2025-06-21 09:52:36 +08:00
Tony
c03cc586e3 chore: check generated file on package lock change (#13641)
* chore: check generated file on package lock change

* Build bundle.global.js

looks like it's already outdated
2025-06-20 20:13:16 +08:00
renovate[bot]
488bcea970 chore(deps): update dependency rollup to v4.44.0 (#13635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 19:20:17 +08:00
kandrelczyk
d48e7a39a7 chore: improve contributing guide (#13594) 2025-06-18 11:34:19 +02:00
renovate[bot]
221254738a chore(deps): update dependency @rollup/plugin-typescript to v12.1.3 (#13628)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 23:43:38 +08:00
Naman Khandelwal
9fb0586909 fix(cli) : final changes for macOS identifier issue (fix #12674) (#13627)
* fix(cli) : final changes for macOS identifier issue

* chore:formatted the code
2025-06-17 12:18:50 -03:00
Naman Khandelwal
8ee14a8648 fix(cli): prevent .app identifier (fix #12674) (#13618)
* fix(cli): prevent .app identifier

* chore(cli): changed warn log of non macOS

* Add change file

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-06-16 23:48:54 +08:00
renovate[bot]
0f0d6a4e02 chore(deps): update rust crate windows-sys to 0.60 (dev) (#13616)
* chore(deps): update rust crate windows-sys to 0.60

* Fix compile and update lock file

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-06-16 17:58:29 +08:00
Tony
f9bdb9b230 fix: failing scope test on Windows (#13596) 2025-06-11 15:10:25 +02:00
renovate[bot]
f34acf161d chore(deps): update dependency rollup to v4.43.0 (dev) (#13604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-06-11 14:52:46 +02:00
Emmanuel Ferdman
87b3cdce48 fix: resolve typos in schema (#13591)
* fix: update `English.nsh` reference

Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>

* fix: resolve typos in schema

Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>

* Generate schema

---------

Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-06-08 00:50:10 +08:00
renovate[bot]
aa1131a047 chore(deps): update dependency rollup to v4.42.0 (#13583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-06 23:10:34 +02:00
Pierre de la Martinière
bd8a7cf39d fix(bundler/linux): fix app icon not showing on Gnome+Wayland (#5258) (#13581) 2025-06-06 22:46:24 +02:00
Tony
f1891540bf refactor: simplify future in command IPC structs (#13529) 2025-06-04 20:32:51 +08:00
renovate[bot]
d15da3daae chore(deps): update rust crate duct to v1 (dev) (#13397)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-06-03 16:54:27 +02:00
Tunglies
414619c36e feat: add bundleName field to schema and MacConfig (#13110) (#13536) 2025-06-03 15:44:49 +02:00
Fabian-Lars
25757fece4 chore(cli): Remove outdated licensing errata in readme (#13548) 2025-06-03 13:58:40 +02:00
Tony
78d15e892d fix(windows): allow web fullscreen APIs to work (#13558)
* fix(windows): allow web fullscreen APIs to work

* tauri-runtime-wry not tauri-wry

* Remove last clone

* Change file on windows
2025-06-03 19:04:14 +08:00
Tony
6a39f49991 refactor: dynamic dispatch async commands in debug (#13464)
* Dynamic dispatch async commands

* format

* Preserve `'static`

* Use a inner function instead

* Only do it for dev for now

* Add change file

* Tag respond_async_serialized_dyn with debug
2025-05-29 22:02:56 +08:00
Tony
a35600cbd7 docs: fix setAutoResize typo (#13526) 2025-05-29 17:54:22 +08:00
Tunglies
1c53640ac3 fix(schema): update hardened_runtime description to include link (#13512) 2025-05-27 17:38:18 +02:00
Amr Bashir
e7f2d8cba4 chore: remove leftover in utils.nsh (#13507) 2025-05-26 06:20:31 +03:00
Amr Bashir
7322f05792 feat(nsis): allow using CheckIfAppIsRunning macro inside NSIS hooks (#13502) 2025-05-25 23:03:10 +03:00
renovate[bot]
06c75fd98b chore(deps): update dependency rollup to v4.41.1 (#13499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-24 21:37:42 +08:00
Lucas Fernandes Nogueira
c8a30a61d2 fix(core): capability filtering crashing allowed command generation (#13476)
* fix(core): capability filtering crashing allowed command generation

tauri-plugin and tauri build scripts cannot have access to the capabilities file (generated by tauri-build) and can only infer capabilities from the config path

* cleanup

* unused import

* followup

* fix reassign

* mut

* Update crates/tauri-utils/src/acl/build.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2025-05-22 07:45:32 -03:00
renovate[bot]
650c91c114 chore(deps): update rust crate zip to v4 (#13487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 09:11:50 +08:00
Lucas Fernandes Nogueira
5a5291d66c fix(cli): use platform name to detect iOS architecture to build (#13483) 2025-05-21 14:12:44 -03:00
renovate[bot]
dbcfaa18d7 chore(deps): update dependency rollup to v4.41.0 (#13467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 09:10:35 +08:00
WofWca
923b7c7bc6 docs: fix build instructions somewhat (#13463) 2025-05-18 20:43:49 +02:00
Tony
626165eeb4 fix: mock context without dynamic-acl feature (#13455) 2025-05-17 18:08:53 +08:00
Kingsword
638804e9c4 fix: ensure set_window_effects runs on main thread in WindowBuilder (#13422) (#13423)
Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2025-05-16 23:38:12 +02:00
Tony
1686296463 refactor: put dynamic acl to a feature (#13418)
* refactor: put dynamic acl to a feature

* Add change file

* Tweak remove_unused_commands's docs

* License header

* Document the feature

* Merge remote-tracking branch 'upstream/dev' into dynamic-acl-feature

* Use a inner non generic fn for add_capability

* Clippy and macro stability notice

* Merge remote-tracking branch 'upstream/dev' into dynamic-acl-feature

* Format
2025-05-16 22:54:23 +08:00
dependabot[bot]
85baacd18b chore(deps): bump undici from 5.28.5 to 5.29.0 (#13450)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-05-15 18:41:01 +02:00
Tony
c31c75fffc docs: fix missing docs for a few methods (#13449) 2025-05-16 00:17:28 +08:00
renovate[bot]
9687a9b4fb chore(deps): update rust crate zip to v3 (#13445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-15 15:25:25 +08:00
Tony
4acae1ec02 fix(macos): run app.set_theme on main thread (#13443)
* fix(macos): run `app.set_theme` on main thread

* Change file
2025-05-15 15:13:23 +08:00
Tony
d38d90b8d9 refactor: reduce the code in macro pass (#13441) 2025-05-15 09:26:49 +08:00
Tony
b52da29d5d fix: main binary name can't contain dots (#13429)
* fix: main binary name can't contain dots

* Revert the change in tauri.conf.json

* Change file

* Use target platform when matching extension
2025-05-14 16:50:12 +08:00
Tony
574a4d4d36 fix(cli): slow dev startup (#13426)
* fix(cli): slow dev startup

* Use `recv_timeout` instead

* Update crates/tauri-cli/src/interface/rust.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-05-14 16:48:05 +08:00
Tony
1777406a16 chore: apply some clippy suggestions (#13424) 2025-05-13 14:18:24 +08:00
MidnightCrowing
d3586a2afa docs(api): fix incorrect example and clarify description for dirname in path API docs (#13417) 2025-05-12 16:35:35 +08:00
Tony
b7cdb3b39e chore: feature gate html manipulation code (#13410)
* Feature gate html manipulation code

* Change file
2025-05-11 00:13:19 +08:00
Amr Bashir
96ecfca428 feat: check if webview runtime is accessible when creating a webview (#13406) 2025-05-10 06:00:09 +03:00
dependabot[bot]
0e616dbbcb chore(deps): bump ring from 0.17.8 to 0.17.14 (#13409)
Bumps [ring](https://github.com/briansmith/ring) from 0.17.8 to 0.17.14.
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: ring
  dependency-version: 0.17.14
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-10 09:58:21 +08:00
Oscar Beaumont
bc2f0e48ac fix(macOS): caculation for work area (#13401)
* remove y offset

* Create change-pr-13401.md
2025-05-09 08:36:12 -03:00
renovate[bot]
efcc840ff0 chore(deps): update dependency eslint-config-prettier to v10.1.5 (#13403)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-09 15:27:18 +08:00
Fabian-Lars
7897ed257d fix(cli): don't canonicalize cargo's target dir in frontendDist verification (#13392) 2025-05-07 23:00:24 +02:00
renovate[bot]
1a018878ab chore(deps): update dependency eslint-config-prettier to v10.1.3 (#13387)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-07 21:17:19 +08:00
377 changed files with 18795 additions and 5318 deletions

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
---
"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.

6
.changes/CHANGES.md Normal file
View File

@@ -0,0 +1,6 @@
---
'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

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

View File

@@ -0,0 +1,6 @@
---
'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

@@ -0,0 +1,5 @@
---
"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,5 +0,0 @@
---
tauri-bundler: "patch:bug"
---
The bundler now sets the `ARCH` env var to the current build target to prevent potential issues with `appimagetool`'s auto-detection.

View File

@@ -0,0 +1,6 @@
---
'tauri-cli': 'minor:enhance'
'@tauri-apps/cli': minor:enhance
---
Update cargo-mobile2 to 0.21, enhancing error messages and opening Xcode when multiple apps are installed.

View File

@@ -1,5 +0,0 @@
---
"tauri": 'patch:bug'
---
Prevent the JavaScript runtime crashing when channel events fire in a webview that no longer has callbacks for the channel.

View File

@@ -0,0 +1,6 @@
---
"tauri-cli": patch:enhance
"@tauri-apps/cli": patch:enhance
---
Check mismatched versions in `tauri info`

6
.changes/data-dir-js.md Normal file
View File

@@ -0,0 +1,6 @@
---
"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

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

5
.changes/emit-raw-api.md Normal file
View File

@@ -0,0 +1,5 @@
---
"@tauri-apps/api": minor:feat
---
Optimize raw Uint8Array/ArrayBuffer/number[] payloads on the event system via the emit2 and emitTo2 functions.

View File

@@ -0,0 +1,6 @@
---
"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

@@ -0,0 +1,5 @@
---
"tauri": minor:feat
---
Allow sending raw binary payloads using the event system through `Emitter::emit_raw` and `Emitter::emit_raw_to`.

View File

@@ -1,7 +0,0 @@
---
"tauri": "minor:feat"
"@tauri-apps/api": "minor:feat"
---
Expose the `setAutoResize` API for webviews in `@tauri-apps/api`.

View File

@@ -0,0 +1,6 @@
---
"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

@@ -0,0 +1,6 @@
---
"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

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

View File

@@ -0,0 +1,6 @@
---
'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

@@ -1,5 +0,0 @@
---
"tauri-bundler": patch:bug
---
Fix custom Windows sign command failing to sign app uninstaller if it references relative paths.

View File

@@ -1,6 +0,0 @@
---
"tauri-cli": "patch:bug"
"@tauri-apps/cli": "patch:bug"
---
fix: allow the target directory to be inside frontendDir as long as it is not the Rust target directory inside frontendDir.

View File

@@ -0,0 +1,6 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
Fix iOS CLI usage after modifying the package name.

View File

@@ -1,5 +0,0 @@
---
"tauri": patch:bug
---
Fixes multiple event listeners registration for iOS plugins.

View File

@@ -0,0 +1,6 @@
---
"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": "minor:bug"
"@tauri-apps/api": "minor:bug"
---
Fixed path joining behavior where `path.join('', 'a')` incorrectly returns "/a" instead of "a".

View File

@@ -1,5 +0,0 @@
---
"tauri-runtime-wry": patch:bug
---
Fix monitor check on the window prevent overflow implementation.

View File

@@ -1,5 +0,0 @@
---
"tauri": patch:bug
---
Fix `TrayIcon.getById` returning a new resource ID instead of reusing a previously created id from `TrayIcon.new`.

View File

@@ -0,0 +1,6 @@
---
"@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,5 +0,0 @@
---
'tauri': 'patch:bug'
---
Fix JavaScript API `Webview.proxyUrl` had no effect when used in the `Webview` constructor

View File

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

View File

@@ -0,0 +1,8 @@
---
"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

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

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
---
"tauri": minor:feat
---
Allow reading raw binary payloads using the event system through [`Event::payload_raw`].

View File

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

6
.changes/mobile-run.md Normal file
View File

@@ -0,0 +1,6 @@
---
"@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

@@ -1,5 +0,0 @@
---
"@tauri-apps/api": "minor:feat"
---
Add `Monitor.workArea` field.

View File

@@ -1,6 +0,0 @@
---
"tauri": "minor:feat"
---
Add `Monitor::work_area` getter

View File

@@ -1,6 +0,0 @@
---
"tauri": "minor:feat"
---
Added `tauri::PhysicalRect` and `tauri::LogicalRect` types.

View File

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

View File

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

View File

@@ -1,5 +0,0 @@
---
"tauri-utils": 'minor:feat'
"tauri": 'minor:feat'
---
Adds the option to configure the HTTP `Service-Worker-Allowed` response header in `app > security > headers`

View File

@@ -0,0 +1,6 @@
---
"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

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

View File

@@ -1,6 +0,0 @@
---
"@tauri-apps/api": minor:changes
"tauri": minor:changes
---
`transformCallback` now registers the callbacks inside `window.__TAURI_INTERNALS__.callbacks` instead of directly on `window['_{id}']`

View File

@@ -0,0 +1,7 @@
---
"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

@@ -1,6 +0,0 @@
---
"@tauri-apps/api": minor:bug
"tauri": minor:bug
---
Immediately unregister event listener when the unlisten function is called.

View File

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

View File

@@ -51,7 +51,10 @@ Hi! We, the maintainers, are really excited that you are interested in contribut
First, [join our Discord server](https://discord.gg/SpmNs4S) and let us know that you want to contribute. This way we can point you in the right direction and help ensure your contribution will be as helpful as possible.
To set up your machine for development, follow the [Tauri setup guide](https://v2.tauri.app/start/prerequisites/) to get all the tools you need to develop Tauri apps. The only additional tool you may need is [PNPM](https://pnpm.io/), it is only required if you are developing the Node CLI or API packages (`packages/cli` and `packages/api`). Next, fork and clone this repo. It is structured as a monorepo, which means that all the various Tauri packages are under the same repository. The development process varies depending on what part of Tauri you are contributing to, see the guides below for per-package instructions.
To set up your machine for development, follow the [Tauri setup guide](https://v2.tauri.app/start/prerequisites/) to get all the tools you need to develop Tauri apps. The only additional tool you may need is [PNPM](https://pnpm.io/), it is only required if you are developing the Node CLI or API packages (`packages/cli` and `packages/api`).
Next, [fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and clone [this repository](https://github.com/tauri-apps).
The development process varies depending on what part of Tauri you are contributing to, see the guides below for per-package instructions.
Some Tauri packages will be automatically built when running one of the examples. Others, however, will need to be built beforehand. To initialize, execute these commands in the repository root:
@@ -64,17 +67,31 @@ pnpm build
See [Architecture](../ARCHITECTURE.md#major-components) for an overview of the packages in this repository.
### Developing Tauri Core and Related Components (Rust API, Macros, Codegen, and Utils)
The code for the Rust crates, including the Core, Macros, Utils, WRY runtime, and a few more are located in the [main Tauri repository](https://github.com/tauri-apps/tauri/tree/dev/crates).
The easiest way to test your changes is to use the [helloworld](https://github.com/tauri-apps/tauri/tree/dev/examples/helloworld) example app. It automatically rebuilds and uses your local copy of the Tauri core packages. Just run `cargo run --example helloworld` after making changes to test them out.
To test local changes against your own application simply point the Tauri create to your local repository. In `src-tauri/Cargo.toml` file change:
`tauri = { version = "2.1.1" }`
to:
`tauri = { path = "path/to/local/tauri/crates/tauri" }`
If any other crates depend on Tauri you will have to point them to the local repo as well.
### Developing Tauri Bundler and Rust CLI
The code for the bundler is located in `[Tauri repo root]/crates/tauri-bundler`, and the code for the Rust CLI is located in `[Tauri repo root]/crates/tauri-cli`. If you are using your local copy of `@tauri-apps/cli` (see above), any changes you make to the bundler and CLI will be automatically built and applied when running the build or dev command. Otherwise, running `cargo install --path .` in the Rust CLI directory will allow you to run `cargo tauri build` and `cargo tauri dev` anywhere, using the updated copy of the bundler and cli. You will have to run this command each time you make a change in either package.
The code for the bundler is located in [crates/tauri-bundler](https://github.com/tauri-apps/tauri/tree/dev/crates/tauri-bundler), and the code for the Rust CLI is located in [tauri-cli](https://github.com/tauri-apps/tauri/tree/dev/crates/tauri-cli).
Running `cargo install --path .` in the Rust CLI directory will allow you to run `cargo tauri build` and `cargo tauri dev` anywhere, using the updated copy of the bundler and cli. You will have to run this command each time you make a change in either package.
You can use `cargo install --path . --debug` to speed up test builds.
### Developing The Node.js CLI (`@tauri-apps/cli`)
`@tauri-apps/cli` is a wrapper to `tauri-cli` so most changes should be written on the Rust CLI. The `[Tauri repo root]/crates/tauri-cli` folder contains only packaging scripts to properly publish the Rust CLI binaries to NPM.
### Developing Tauri Core and Related Components (Rust API, Macros, Codegen, and Utils)
The code for the Rust crates, including the Core, Macros, Utils, WRY runtime, and a few more are located in `[Tauri repo root]/crates/tauri-(macros/utils)`. The easiest way to test your changes is to use the `[Tauri repo root]/examples/helloworld` app. It automatically rebuilds and uses your local copy of the Tauri core packages. Just run `cargo run --example helloworld` after making changes to test them out.
[`@tauri-apps/cli`](https://github.com/tauri-apps/tauri/tree/dev/packages/cli) is a small wrapper around `tauri-cli` so most changes should be happen in the Rust CLI (see above).
#### Building the documentation locally
@@ -86,7 +103,8 @@ $ RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --open
### Developing the JS API
The JS API provides bindings between the developer's JS in the Webview and the builtin Tauri APIs, written in Rust. Its code is located in `[Tauri repo root]/packages/api`. After making changes to the code, run `pnpm build` to build it. To test your changes, we recommend using the API example app, located in `[Tauri repo root]/examples/api`. It will automatically use your local copy of the JS API and provides a helpful UI to test the various commands.
The JS API provides bindings between the developer's JS in the Webview and the built-in Tauri APIs, written in Rust. Its code is located in [/packages/api](https://github.com/tauri-apps/tauri/tree/dev/packages/api).
After making changes to the code, run `pnpm build` to build it. To test your changes, we recommend using the API example app, located in [/examples/api](https://github.com/tauri-apps/tauri/tree/dev/examples/api). It will automatically use your local copy of the JS API and provides a helpful UI to test the various commands.
## Financial Contribution

View File

@@ -33,11 +33,6 @@ Releasing can be as easy as merging the version pull request but here is a check
- [ ] Double check that every package is bumped correctly and there are no accidental major or minor being released unless that is indeed the intention.
- [ ] Make sure that there are no pending or unfinished [covector-version-or-publish.yml](./workflows/covector-version-or-publish.yml) workflow runs.
- [ ] Sign the Version PR before merging as we require signed commits
- [ ] `git fetch --all`
- [ ] `git checkout release/version-updates`
- [ ] `git commit --amend -S`
- [ ] `git push --force`
- [ ] Approve and merge the version pull request
## Publishing failed, what to do?

View File

@@ -8,6 +8,7 @@ on:
pull_request:
paths:
- '.github/workflows/check-generated-files.yml'
- 'pnpm-lock.yaml'
- 'packages/api/src/**'
- 'crates/tauri/scripts/bundle.global.js'
- 'crates/tauri-utils/src/config.rs'
@@ -31,6 +32,7 @@ jobs:
with:
filters: |
api:
- 'pnpm-lock.yaml'
- 'packages/api/src/**'
- 'crates/tauri/scripts/bundle.global.js'
schema:

View File

@@ -119,6 +119,7 @@ jobs:
commit-message: 'apply version updates'
labels: 'version updates'
body: ${{ steps.covector.outputs.change }}
sign-commits: true
- name: Trigger doc update
if: |

View File

@@ -90,15 +90,15 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: v2
prefix-key: v3
save-if: ${{ matrix.features.key == 'all' }}
- name: test
if: ${{ !matrix.platform.cross }}
run: cargo ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml
run: cargo ${{ matrix.features.key == 'no-default' && 'check' || matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml
- name: test (using cross)
if: ${{ matrix.platform.cross }}
run: |
cargo install cross --git https://github.com/cross-rs/cross --rev ac4c11cedc97cd7c27faed36e55377a90e6ed618 --locked
cross ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml
cargo install cross --git https://github.com/cross-rs/cross --rev 51f46f296253d8122c927c5bb933e3c4f27cc317 --locked
cross ${{ matrix.features.key == 'no-default' && 'check' || matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml

109
.gitignore vendored
View File

@@ -1,54 +1,55 @@
# dependency directories
node_modules/
# Optional npm and yarn cache directory
.npm/
.yarn/
# Output of 'npm pack'
*.tgz
# dotenv environment variables file
.env
# .vscode workspace settings file
.vscode/settings.json
.vscode/launch.json
.vscode/tasks.json
# npm, yarn and bun lock files
package-lock.json
yarn.lock
bun.lockb
# rust compiled folders
target/
# test video for streaming example
streaming_example_test_video.mp4
# examples /gen directory
/examples/**/src-tauri/gen/
/bench/**/src-tauri/gen/
# logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# runtime data
pids
*.pid
*.seed
*.pid.lock
# miscellaneous
/.vs
.DS_Store
.Thumbs.db
*.sublime*
.idea
debug.log
TODO.md
# dependency directories
node_modules/
# Optional npm and yarn cache directory
.npm/
.yarn/
# Output of 'npm pack'
*.tgz
# dotenv environment variables file
.env
# .vscode workspace settings file
.vscode/settings.json
.vscode/launch.json
.vscode/tasks.json
# npm, yarn and bun lock files
package-lock.json
yarn.lock
bun.lockb
# rust compiled folders
target/
# test video for streaming example
streaming_example_test_video.mp4
# examples /gen directory
/examples/**/src-tauri/gen/
/bench/**/src-tauri/gen/
# logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# runtime data
pids
*.pid
*.seed
*.pid.lock
# miscellaneous
/.vs
.DS_Store
.Thumbs.db
*.sublime*
.idea
debug.log
TODO.md
.aider*

1135
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -71,3 +71,4 @@ opt-level = "s"
schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = 'feat/preserve-description-newlines' }
tauri = { path = "./crates/tauri" }
tauri-plugin = { path = "./crates/tauri-plugin" }
tauri-utils = { path = "./crates/tauri-utils" }

View File

@@ -4,7 +4,7 @@
[![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri)
[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield)
[![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S)
[![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.com/invite/tauri)
[![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app)
[![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
[![support](https://img.shields.io/badge/sponsor-Open%20Collective-blue.svg)](https://opencollective.com/tauri)
@@ -81,7 +81,7 @@ For the complete list of sponsors please visit our [website](https://tauri.app#s
## Organization
Tauri aims to be a sustainable collective based on principles that guide [sustainable free and open software communities](https://sfosc.org). To this end it has become a Programme within the [Commons Conservancy](https://commonsconservancy.org/), and you can contribute financially via [Open Collective](https://opencollective.com/tauri).
Tauri aims to be a sustainable collective based on principles that guide sustainable free and open software communities. To this end it has become a Programme within the [Commons Conservancy](https://commonsconservancy.org/), and you can contribute financially via [Open Collective](https://opencollective.com/tauri).
## Licenses

View File

@@ -10,6 +10,8 @@
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
)]
// file is used by multiple binaries
#![allow(dead_code)]
use std::{fs::File, io::BufReader};
mod utils;
@@ -52,7 +54,7 @@ fn main() {
.expect("Something wrong with tauri_data"),
&serde_json::to_value(all_data).expect("Unable to build final json (all)"),
)
.unwrap_or_else(|_| panic!("Unable to write {:?}", tauri_data));
.unwrap_or_else(|_| panic!("Unable to write {tauri_data:?}"));
utils::write_json(
tauri_recent
@@ -60,5 +62,5 @@ fn main() {
.expect("Something wrong with tauri_recent"),
&serde_json::to_value(recent).expect("Unable to build final json (recent)"),
)
.unwrap_or_else(|_| panic!("Unable to write {:?}", tauri_recent));
.unwrap_or_else(|_| panic!("Unable to write {tauri_recent:?}"));
}

View File

@@ -2,16 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! This Rust binary runs on CI and provides internal metrics results of Tauri. To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
//! This Rust binary runs on CI and provides internal metrics results of Tauri.
//! To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
//!
//! ***_Internal use only_**
//! ***_Internal use only_***
#![doc(
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
)]
use anyhow::Result;
use anyhow::{Context, Result};
use std::{
collections::{HashMap, HashSet},
env,
@@ -21,62 +22,66 @@ use std::{
mod utils;
/// The list of the examples of the benchmark name and binary relative path
fn get_all_benchmarks() -> Vec<(String, String)> {
/// The list of examples for benchmarks
fn get_all_benchmarks(target: &str) -> Vec<(String, String)> {
vec![
(
"tauri_hello_world".into(),
format!("../target/{}/release/bench_helloworld", utils::get_target()),
format!("../target/{target}/release/bench_helloworld"),
),
(
"tauri_cpu_intensive".into(),
format!(
"../target/{}/release/bench_cpu_intensive",
utils::get_target()
),
format!("../target/{target}/release/bench_cpu_intensive"),
),
(
"tauri_3mb_transfer".into(),
format!(
"../target/{}/release/bench_files_transfer",
utils::get_target()
),
format!("../target/{target}/release/bench_files_transfer"),
),
]
}
fn run_strace_benchmarks(new_data: &mut utils::BenchResult) -> Result<()> {
fn run_strace_benchmarks(new_data: &mut utils::BenchResult, target: &str) -> Result<()> {
use std::io::Read;
let mut thread_count = HashMap::<String, u64>::new();
let mut syscall_count = HashMap::<String, u64>::new();
for (name, example_exe) in get_all_benchmarks() {
let mut file = tempfile::NamedTempFile::new()?;
for (name, example_exe) in get_all_benchmarks(target) {
let mut file = tempfile::NamedTempFile::new()
.context("failed to create temporary file for strace output")?;
let exe_path = utils::bench_root_path().join(&example_exe);
let exe_path_str = exe_path
.to_str()
.context("executable path contains invalid UTF-8")?;
let temp_path_str = file
.path()
.to_str()
.context("temporary file path contains invalid UTF-8")?;
Command::new("strace")
.args([
"-c",
"-f",
"-o",
file.path().to_str().unwrap(),
utils::bench_root_path().join(example_exe).to_str().unwrap(),
])
.args(["-c", "-f", "-o", temp_path_str, exe_path_str])
.stdout(Stdio::inherit())
.spawn()?
.wait()?;
.spawn()
.context("failed to spawn strace process")?
.wait()
.context("failed to wait for strace process")?;
let mut output = String::new();
file.as_file_mut().read_to_string(&mut output)?;
file
.as_file_mut()
.read_to_string(&mut output)
.context("failed to read strace output")?;
let strace_result = utils::parse_strace_output(&output);
// Note, we always have 1 thread. Use cloneX calls as counter for additional threads created.
let clone = 1
+ strace_result.get("clone").map(|d| d.calls).unwrap_or(0)
// Count clone/clone3 syscalls as thread creation indicators
let clone_calls = strace_result.get("clone").map(|d| d.calls).unwrap_or(0)
+ strace_result.get("clone3").map(|d| d.calls).unwrap_or(0);
let total = strace_result.get("total").unwrap().calls;
thread_count.insert(name.to_string(), clone);
syscall_count.insert(name.to_string(), total);
if let Some(total) = strace_result.get("total") {
thread_count.insert(name.clone(), clone_calls);
syscall_count.insert(name, total.calls);
}
}
new_data.thread_count = thread_count;
@@ -85,70 +90,100 @@ fn run_strace_benchmarks(new_data: &mut utils::BenchResult) -> Result<()> {
Ok(())
}
fn run_max_mem_benchmark() -> Result<HashMap<String, u64>> {
fn run_max_mem_benchmark(target: &str) -> Result<HashMap<String, u64>> {
let mut results = HashMap::<String, u64>::new();
for (name, example_exe) in get_all_benchmarks() {
let benchmark_file = utils::target_dir().join(format!("mprof{}_.dat", name));
let benchmark_file = benchmark_file.to_str().unwrap();
for (name, example_exe) in get_all_benchmarks(target) {
let benchmark_file = utils::target_dir().join(format!("mprof{name}_.dat"));
let benchmark_file_str = benchmark_file
.to_str()
.context("benchmark file path contains invalid UTF-8")?;
let exe_path = utils::bench_root_path().join(&example_exe);
let exe_path_str = exe_path
.to_str()
.context("executable path contains invalid UTF-8")?;
let proc = Command::new("mprof")
.args([
"run",
"-C",
"-o",
benchmark_file,
utils::bench_root_path().join(example_exe).to_str().unwrap(),
])
.args(["run", "-C", "-o", benchmark_file_str, exe_path_str])
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()?;
.spawn()
.with_context(|| format!("failed to spawn mprof for benchmark {name}"))?;
let proc_result = proc.wait_with_output()?;
println!("{:?}", proc_result);
results.insert(
name.to_string(),
utils::parse_max_mem(benchmark_file).unwrap(),
);
let proc_result = proc
.wait_with_output()
.with_context(|| format!("failed to wait for mprof {name}"))?;
if !proc_result.status.success() {
eprintln!(
"mprof failed for {name}: {}",
String::from_utf8_lossy(&proc_result.stderr)
);
}
if let Some(mem) = utils::parse_max_mem(benchmark_file_str)
.with_context(|| format!("failed to parse mprof data for {name}"))?
{
results.insert(name, mem);
}
// Clean up the temporary file
if let Err(e) = std::fs::remove_file(&benchmark_file) {
eprintln!("Warning: failed to remove temporary file {benchmark_file_str}: {e}");
}
}
Ok(results)
}
fn rlib_size(target_dir: &std::path::Path, prefix: &str) -> u64 {
fn rlib_size(target_dir: &Path, prefix: &str) -> Result<u64> {
let mut size = 0;
let mut seen = std::collections::HashSet::new();
let mut seen = HashSet::new();
let deps_dir = target_dir.join("deps");
for entry in std::fs::read_dir(&deps_dir).with_context(|| {
format!(
"failed to read target deps directory: {}",
deps_dir.display()
)
})? {
let entry = entry.context("failed to read directory entry")?;
let name = entry.file_name().to_string_lossy().to_string();
for entry in std::fs::read_dir(target_dir.join("deps")).unwrap() {
let entry = entry.unwrap();
let os_str = entry.file_name();
let name = os_str.to_str().unwrap();
if name.starts_with(prefix) && name.ends_with(".rlib") {
let start = name.split('-').next().unwrap().to_string();
if seen.contains(&start) {
println!("skip {}", name);
} else {
seen.insert(start);
size += entry.metadata().unwrap().len();
println!("check size {} {}", name, size);
if let Some(start) = name.split('-').next() {
if seen.insert(start.to_string()) {
size += entry
.metadata()
.context("failed to read file metadata")?
.len();
}
}
}
}
assert!(size > 0);
size
if size == 0 {
anyhow::bail!(
"no rlib files found for prefix {prefix} in {}",
deps_dir.display()
);
}
Ok(size)
}
fn get_binary_sizes(target_dir: &Path) -> Result<HashMap<String, u64>> {
fn get_binary_sizes(target_dir: &Path, target: &str) -> Result<HashMap<String, u64>> {
let mut sizes = HashMap::<String, u64>::new();
let wry_size = rlib_size(target_dir, "libwry");
println!("wry {} bytes", wry_size);
let wry_size = rlib_size(target_dir, "libwry")?;
sizes.insert("wry_rlib".to_string(), wry_size);
// add size for all EXEC_TIME_BENCHMARKS
for (name, example_exe) in get_all_benchmarks() {
let meta = std::fs::metadata(example_exe).unwrap();
sizes.insert(name.to_string(), meta.len());
for (name, example_exe) in get_all_benchmarks(target) {
let exe_path = utils::bench_root_path().join(&example_exe);
let meta = std::fs::metadata(&exe_path)
.with_context(|| format!("failed to read metadata for {}", exe_path.display()))?;
sizes.insert(name, meta.len());
}
Ok(sizes)
@@ -188,14 +223,33 @@ fn cargo_deps() -> HashMap<String, usize> {
cmd.args(["--target", target]);
cmd.current_dir(utils::tauri_root_path());
let full_deps = cmd.output().expect("failed to run cargo tree").stdout;
let full_deps = String::from_utf8(full_deps).expect("cargo tree output not utf-8");
let count = full_deps.lines().collect::<HashSet<_>>().len() - 1; // output includes wry itself
match cmd.output() {
Ok(output) if output.status.success() => {
let full_deps = String::from_utf8_lossy(&output.stdout);
let count = full_deps
.lines()
.collect::<HashSet<_>>()
.len()
.saturating_sub(1); // output includes wry itself
// set the count to the highest count seen for this OS
let existing = results.entry(os.to_string()).or_default();
*existing = count.max(*existing);
assert!(count > 10); // sanity check
// set the count to the highest count seen for this OS
let existing = results.entry(os.to_string()).or_default();
*existing = count.max(*existing);
if count <= 10 {
eprintln!("Warning: dependency count for {target} seems low: {count}");
}
}
Ok(output) => {
eprintln!(
"cargo tree failed for {target}: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Err(e) => {
eprintln!("Failed to run cargo tree for {target}: {e}");
}
}
}
}
results
@@ -203,104 +257,127 @@ fn cargo_deps() -> HashMap<String, usize> {
const RESULT_KEYS: &[&str] = &["mean", "stddev", "user", "system", "min", "max"];
fn run_exec_time(target_dir: &Path) -> Result<HashMap<String, HashMap<String, f64>>> {
fn run_exec_time(target: &str) -> Result<HashMap<String, HashMap<String, f64>>> {
let target_dir = utils::target_dir();
let benchmark_file = target_dir.join("hyperfine_results.json");
let benchmark_file = benchmark_file.to_str().unwrap();
let benchmark_file_str = benchmark_file
.to_str()
.context("benchmark file path contains invalid UTF-8")?;
let mut command = [
let mut command = vec![
"hyperfine",
"--export-json",
benchmark_file,
benchmark_file_str,
"--show-output",
"--warmup",
"3",
]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
];
for (_, example_exe) in get_all_benchmarks() {
command.push(
utils::bench_root_path()
.join(example_exe)
.to_str()
.unwrap()
.to_string(),
);
let benchmarks = get_all_benchmarks(target);
let mut benchmark_paths = Vec::new();
for (_, example_exe) in &benchmarks {
let exe_path = utils::bench_root_path().join(example_exe);
let exe_path_str = exe_path
.to_str()
.context("executable path contains invalid UTF-8")?;
benchmark_paths.push(exe_path_str.to_string());
}
utils::run(&command.iter().map(|s| s.as_ref()).collect::<Vec<_>>());
for path in &benchmark_paths {
command.push(path.as_str());
}
utils::run(&command)?;
let mut results = HashMap::<String, HashMap<String, f64>>::new();
let hyperfine_results = utils::read_json(benchmark_file)?;
for ((name, _), data) in get_all_benchmarks().iter().zip(
hyperfine_results
.as_object()
.unwrap()
.get("results")
.unwrap()
.as_array()
.unwrap(),
) {
let data = data.as_object().unwrap().clone();
results.insert(
name.to_string(),
data
.into_iter()
.filter(|(key, _)| RESULT_KEYS.contains(&key.as_str()))
.map(|(key, val)| (key, val.as_f64().unwrap()))
.collect(),
);
let hyperfine_results = utils::read_json(benchmark_file_str)?;
if let Some(results_array) = hyperfine_results
.as_object()
.and_then(|obj| obj.get("results"))
.and_then(|val| val.as_array())
{
for ((name, _), data) in benchmarks.iter().zip(results_array.iter()) {
if let Some(data_obj) = data.as_object() {
let filtered_data: HashMap<String, f64> = data_obj
.iter()
.filter(|(key, _)| RESULT_KEYS.contains(&key.as_str()))
.filter_map(|(key, val)| val.as_f64().map(|v| (key.clone(), v)))
.collect();
results.insert(name.clone(), filtered_data);
}
}
}
Ok(results)
}
fn main() -> Result<()> {
// download big files if not present
let json_3mb = utils::home_path().join(".tauri_3mb.json");
if !json_3mb.exists() {
println!("Downloading test data...");
utils::download_file(
"https://github.com/lemarier/tauri-test/releases/download/v2.0.0/json_3mb.json",
json_3mb,
);
)
.context("failed to download test data")?;
}
println!("Starting tauri benchmark");
let target_dir = utils::target_dir();
let target = utils::get_target();
env::set_current_dir(utils::bench_root_path())?;
env::set_current_dir(utils::bench_root_path())
.context("failed to set working directory to bench root")?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.context("failed to get current time")?;
let timestamp = format!("{}", now.as_secs());
println!("Running execution time benchmarks...");
let exec_time = run_exec_time(target)?;
println!("Getting binary sizes...");
let binary_size = get_binary_sizes(&target_dir, target)?;
println!("Analyzing cargo dependencies...");
let cargo_deps = cargo_deps();
let format =
time::format_description::parse("[year]-[month]-[day]T[hour]:[minute]:[second]Z").unwrap();
let now = time::OffsetDateTime::now_utc();
let mut new_data = utils::BenchResult {
created_at: now.format(&format).unwrap(),
sha1: utils::run_collect(&["git", "rev-parse", "HEAD"])
.0
.trim()
.to_string(),
exec_time: run_exec_time(&target_dir)?,
binary_size: get_binary_sizes(&target_dir)?,
cargo_deps: cargo_deps(),
created_at: timestamp,
sha1: {
let output = utils::run_collect(&["git", "rev-parse", "HEAD"])?;
output.0.trim().to_string()
},
exec_time,
binary_size,
cargo_deps,
..Default::default()
};
if cfg!(target_os = "linux") {
run_strace_benchmarks(&mut new_data)?;
new_data.max_memory = run_max_mem_benchmark()?;
println!("Running Linux-specific benchmarks...");
run_strace_benchmarks(&mut new_data, target)?;
new_data.max_memory = run_max_mem_benchmark(target)?;
}
println!("===== <BENCHMARK RESULTS>");
serde_json::to_writer_pretty(std::io::stdout(), &new_data)?;
serde_json::to_writer_pretty(std::io::stdout(), &new_data)
.context("failed to serialize benchmark results")?;
println!("\n===== </BENCHMARK RESULTS>");
if let Some(filename) = target_dir.join("bench.json").to_str() {
utils::write_json(filename, &serde_json::to_value(&new_data)?)?;
let bench_file = target_dir.join("bench.json");
if let Some(filename) = bench_file.to_str() {
utils::write_json(filename, &serde_json::to_value(&new_data)?)
.context("failed to write benchmark results to file")?;
println!("Results written to: {filename}");
} else {
eprintln!("Cannot write bench.json, path is invalid");
eprintln!("Cannot write bench.json, path contains invalid UTF-8");
}
Ok(())

View File

@@ -2,7 +2,16 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use anyhow::Result;
//! Utility functions for benchmarking tasks in the Tauri project.
//!
//! This module provides helpers for:
//! - Paths to project directories and targets
//! - Running and collecting process outputs
//! - Parsing memory profiler (`mprof`) and syscall profiler (`strace`) outputs
//! - JSON read/write utilities
//! - File download utilities (via `curl` or file copy)
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
@@ -13,6 +22,7 @@ use std::{
process::{Command, Output, Stdio},
};
/// Holds the results of a benchmark run.
#[derive(Default, Clone, Serialize, Deserialize, Debug)]
pub struct BenchResult {
pub created_at: String,
@@ -25,7 +35,7 @@ pub struct BenchResult {
pub cargo_deps: HashMap<String, usize>,
}
#[allow(dead_code)]
/// Represents a single line of parsed `strace` output.
#[derive(Debug, Clone, Serialize)]
pub struct StraceOutput {
pub percent_time: f64,
@@ -35,6 +45,7 @@ pub struct StraceOutput {
pub errors: u64,
}
/// Get the compilation target triple for the current platform.
pub fn get_target() -> &'static str {
#[cfg(target_os = "macos")]
return if cfg!(target_arch = "aarch64") {
@@ -42,18 +53,22 @@ pub fn get_target() -> &'static str {
} else {
"x86_64-apple-darwin"
};
#[cfg(target_os = "ios")]
return if cfg!(target_arch = "aarch64") {
"aarch64-apple-ios"
} else {
"x86_64-apple-ios"
};
#[cfg(target_os = "linux")]
return "x86_64-unknown-linux-gnu";
#[cfg(target_os = "windows")]
unimplemented!();
unimplemented!("Windows target not implemented yet");
}
/// Get the `target/release` directory path for benchmarks.
pub fn target_dir() -> PathBuf {
bench_root_path()
.join("..")
@@ -62,83 +77,90 @@ pub fn target_dir() -> PathBuf {
.join("release")
}
/// Get the root path of the current benchmark crate.
pub fn bench_root_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
#[allow(dead_code)]
/// Get the home directory of the current user.
pub fn home_path() -> PathBuf {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux"))]
return PathBuf::from(env!("HOME"));
{
PathBuf::from(std::env::var("HOME").unwrap_or_default())
}
#[cfg(target_os = "windows")]
return PathBuf::from(env!("HOMEPATH"));
{
PathBuf::from(std::env::var("USERPROFILE").unwrap_or_default())
}
}
#[allow(dead_code)]
/// Get the root path of the Tauri repository.
pub fn tauri_root_path() -> PathBuf {
bench_root_path().parent().unwrap().to_path_buf()
bench_root_path().parent().map(|p| p.to_path_buf()).unwrap()
}
#[allow(dead_code)]
pub fn run_collect(cmd: &[&str]) -> (String, String) {
let mut process_builder = Command::new(cmd[0]);
process_builder
/// Run a command and collect its stdout and stderr as strings.
/// Returns an error if the command fails or exits with a non-zero status.
pub fn run_collect(cmd: &[&str]) -> Result<(String, String)> {
let output: Output = Command::new(cmd[0])
.args(&cmd[1..])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let prog = process_builder.spawn().expect("failed to spawn script");
let Output {
stdout,
stderr,
status,
} = prog.wait_with_output().expect("failed to wait on child");
let stdout = String::from_utf8_lossy(&stdout).to_string();
let stderr = String::from_utf8_lossy(&stderr).to_string();
if !status.success() {
eprintln!("stdout: <<<{}>>>", stdout);
eprintln!("stderr: <<<{}>>>", stderr);
panic!("Unexpected exit code: {:?}", status.code());
.stderr(Stdio::piped())
.output()
.with_context(|| format!("failed to execute command: {cmd:?}"))?;
if !output.status.success() {
bail!(
"Command {:?} exited with {:?}\nstdout:\n{}\nstderr:\n{}",
cmd,
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
(stdout, stderr)
Ok((
String::from_utf8_lossy(&output.stdout).to_string(),
String::from_utf8_lossy(&output.stderr).to_string(),
))
}
#[allow(dead_code)]
pub fn parse_max_mem(file_path: &str) -> Option<u64> {
let file = fs::File::open(file_path).unwrap();
/// Parse a memory profiler (`mprof`) output file and return the maximum
/// memory usage in bytes. Returns `None` if no values are found.
pub fn parse_max_mem(file_path: &str) -> Result<Option<u64>> {
let file = fs::File::open(file_path)
.with_context(|| format!("failed to open mprof output file {file_path}"))?;
let output = BufReader::new(file);
let mut highest: u64 = 0;
// MEM 203.437500 1621617192.4123
for line in output.lines().map_while(Result::ok) {
// split line by space
let split = line.split(' ').collect::<Vec<_>>();
let split: Vec<&str> = line.split(' ').collect();
if split.len() == 3 {
// mprof generate result in MB
let current_bytes = str::parse::<f64>(split[1]).unwrap() as u64 * 1024 * 1024;
if current_bytes > highest {
highest = current_bytes;
if let Ok(mb) = split[1].parse::<f64>() {
let current_bytes = (mb * 1024.0 * 1024.0) as u64;
highest = highest.max(current_bytes);
}
}
}
fs::remove_file(file_path).unwrap();
// Best-effort cleanup
let _ = fs::remove_file(file_path);
if highest > 0 {
return Some(highest);
}
None
Ok(if highest > 0 { Some(highest) } else { None })
}
#[allow(dead_code)]
/// Parse the output of `strace -c` and return a summary of syscalls.
pub fn parse_strace_output(output: &str) -> HashMap<String, StraceOutput> {
let mut summary = HashMap::new();
let mut lines = output
.lines()
.filter(|line| !line.is_empty() && !line.contains("detached ..."));
let count = lines.clone().count();
let count = lines.clone().count();
if count < 4 {
return summary;
}
@@ -148,89 +170,91 @@ pub fn parse_strace_output(output: &str) -> HashMap<String, StraceOutput> {
let data_lines = lines.skip(2);
for line in data_lines {
let syscall_fields = line.split_whitespace().collect::<Vec<_>>();
let syscall_fields: Vec<&str> = line.split_whitespace().collect();
let len = syscall_fields.len();
let syscall_name = syscall_fields.last().unwrap();
if (5..=6).contains(&len) {
summary.insert(
syscall_name.to_string(),
StraceOutput {
percent_time: str::parse::<f64>(syscall_fields[0]).unwrap(),
seconds: str::parse::<f64>(syscall_fields[1]).unwrap(),
usecs_per_call: Some(str::parse::<u64>(syscall_fields[2]).unwrap()),
calls: str::parse::<u64>(syscall_fields[3]).unwrap(),
errors: if syscall_fields.len() < 6 {
if let Some(&syscall_name) = syscall_fields.last() {
if (5..=6).contains(&len) {
let output = StraceOutput {
percent_time: syscall_fields[0].parse().unwrap_or(0.0),
seconds: syscall_fields[1].parse().unwrap_or(0.0),
usecs_per_call: syscall_fields[2].parse().ok(),
calls: syscall_fields[3].parse().unwrap_or(0),
errors: if len < 6 {
0
} else {
str::parse::<u64>(syscall_fields[4]).unwrap()
syscall_fields[4].parse().unwrap_or(0)
},
},
);
};
summary.insert(syscall_name.to_string(), output);
}
}
}
let total_fields = total_line.split_whitespace().collect::<Vec<_>>();
summary.insert(
"total".to_string(),
match total_fields.len() {
// Old format, has no usecs/call
5 => StraceOutput {
percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
seconds: str::parse::<f64>(total_fields[1]).unwrap(),
usecs_per_call: None,
calls: str::parse::<u64>(total_fields[2]).unwrap(),
errors: str::parse::<u64>(total_fields[3]).unwrap(),
},
6 => StraceOutput {
percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
seconds: str::parse::<f64>(total_fields[1]).unwrap(),
usecs_per_call: Some(str::parse::<u64>(total_fields[2]).unwrap()),
calls: str::parse::<u64>(total_fields[3]).unwrap(),
errors: str::parse::<u64>(total_fields[4]).unwrap(),
},
_ => panic!("Unexpected total field count: {}", total_fields.len()),
let total_fields: Vec<&str> = total_line.split_whitespace().collect();
let total = match total_fields.len() {
5 => StraceOutput {
percent_time: total_fields[0].parse().unwrap_or(0.0),
seconds: total_fields[1].parse().unwrap_or(0.0),
usecs_per_call: None,
calls: total_fields[2].parse().unwrap_or(0),
errors: total_fields[3].parse().unwrap_or(0),
},
);
6 => StraceOutput {
percent_time: total_fields[0].parse().unwrap_or(0.0),
seconds: total_fields[1].parse().unwrap_or(0.0),
usecs_per_call: total_fields[2].parse().ok(),
calls: total_fields[3].parse().unwrap_or(0),
errors: total_fields[4].parse().unwrap_or(0),
},
_ => {
panic!("Unexpected total field count: {}", total_fields.len());
}
};
summary.insert("total".to_string(), total);
summary
}
#[allow(dead_code)]
pub fn run(cmd: &[&str]) {
let mut process_builder = Command::new(cmd[0]);
process_builder.args(&cmd[1..]).stdin(Stdio::piped());
let mut prog = process_builder.spawn().expect("failed to spawn script");
let status = prog.wait().expect("failed to wait on child");
/// Run a command and wait for completion.
/// Returns an error if the command fails.
pub fn run(cmd: &[&str]) -> Result<()> {
let status = Command::new(cmd[0])
.args(&cmd[1..])
.stdin(Stdio::piped())
.status()
.with_context(|| format!("failed to execute command: {cmd:?}"))?;
if !status.success() {
panic!("Unexpected exit code: {:?}", status.code());
bail!("Command {:?} exited with {:?}", cmd, status.code());
}
Ok(())
}
#[allow(dead_code)]
/// Read a JSON file into a [`serde_json::Value`].
pub fn read_json(filename: &str) -> Result<Value> {
let f = fs::File::open(filename)?;
let f =
fs::File::open(filename).with_context(|| format!("failed to open JSON file {filename}"))?;
Ok(serde_json::from_reader(f)?)
}
#[allow(dead_code)]
/// Write a [`serde_json::Value`] into a JSON file.
pub fn write_json(filename: &str, value: &Value) -> Result<()> {
let f = fs::File::create(filename)?;
let f =
fs::File::create(filename).with_context(|| format!("failed to create JSON file {filename}"))?;
serde_json::to_writer(f, value)?;
Ok(())
}
#[allow(dead_code)]
pub fn download_file(url: &str, filename: PathBuf) {
/// Download a file from either a local path or an HTTP/HTTPS URL.
/// Falls back to copying the file if the URL does not start with http/https.
pub fn download_file(url: &str, filename: PathBuf) -> Result<()> {
if !url.starts_with("http:") && !url.starts_with("https:") {
fs::copy(url, filename).unwrap();
return;
fs::copy(url, &filename).with_context(|| format!("failed to copy from {url}"))?;
return Ok(());
}
// Downloading with curl this saves us from adding
// a Rust HTTP client dependency.
println!("Downloading {}", url);
println!("Downloading {url}");
let status = Command::new("curl")
.arg("-L")
.arg("-s")
@@ -238,8 +262,14 @@ pub fn download_file(url: &str, filename: PathBuf) {
.arg(&filename)
.arg(url)
.status()
.unwrap();
.with_context(|| format!("failed to execute curl for {url}"))?;
assert!(status.success());
assert!(filename.exists());
if !status.success() {
bail!("curl failed with exit code {:?}", status.code());
}
if !filename.exists() {
bail!("expected file {:?} to exist after download", filename);
}
Ok(())
}

View File

@@ -1,5 +1,32 @@
# Changelog
## \[2.4.1]
### Enhancements
- [`c23bec62d`](https://www.github.com/tauri-apps/tauri/commit/c23bec62d6d5724798869681aa1534423aae28e2) ([#14083](https://www.github.com/tauri-apps/tauri/pull/14083) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Tauri now ignores `macOS.minimumSystemVersion` in `tauri dev` to prevent forced rebuilds of macOS specific dependencies when using something like `rust-analyzer` at the same time as `tauri dev`.
## \[2.4.0]
### Dependencies
- Upgraded to `tauri-utils@2.7.0`
- Upgraded to `tauri-codegen@2.4.0`
## \[2.3.1]
### Dependencies
- Upgraded to `tauri-utils@2.6.0`
- Upgraded to `tauri-codegen@2.3.1`
## \[2.2.1]
### Dependencies
- Upgraded to `tauri-codegen@2.3.0`
- Upgraded to `tauri-utils@2.5.0`
## \[2.2.0]
### Dependencies

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-build"
version = "2.2.0"
version = "2.4.1"
description = "build time code to pair with https://crates.io/crates/tauri"
exclude = ["CHANGELOG.md", "/target"]
readme = "README.md"
@@ -28,8 +28,8 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
anyhow = "1"
quote = { version = "1", optional = true }
tauri-codegen = { version = "2.2.0", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.4.0", path = "../tauri-utils", features = [
tauri-codegen = { version = "2.4.0", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.7.0", path = "../tauri-utils", features = [
"build",
"resources",
] }
@@ -43,9 +43,9 @@ tauri-winres = "0.3"
semver = "1"
dirs = "6"
glob = "0.3"
toml = "0.8"
# Our code requires at least 0.8.18 so don't simplify this to 0.8
schemars = { version = "0.8.18", features = ["preserve_order"] }
toml = "0.9"
# Our code requires at least 0.8.21 so don't simplify this to 0.8
schemars = { version = "0.8.21", features = ["preserve_order"] }
[features]
default = ["config-json"]

View File

@@ -437,7 +437,7 @@ pub fn build(out_dir: &Path, target: Target, attributes: &Attributes) -> super::
permissions_map.insert(APP_ACL_KEY.to_string(), app_acl.permission_files);
}
tauri_utils::acl::build::generate_allowed_commands(out_dir, permissions_map)?;
tauri_utils::acl::build::generate_allowed_commands(out_dir, Some(capabilities), permissions_map)?;
Ok(())
}

View File

@@ -120,6 +120,13 @@ impl CodegenContext {
if info_plist_path.exists() {
println!("cargo:rerun-if-changed={}", info_plist_path.display());
}
if let Some(plist_path) = &config.bundle.macos.info_plist {
let info_plist_path = config_parent.join(plist_path);
if info_plist_path.exists() {
println!("cargo:rerun-if-changed={}", info_plist_path.display());
}
}
}
let code = context_codegen(ContextData {

View File

@@ -367,6 +367,8 @@ impl Attributes {
/// Set the glob pattern to be used to find the capabilities.
///
/// **WARNING:** The `removeUnusedCommands` option does not work with a custom capabilities path.
///
/// **Note:** You must emit [rerun-if-changed] instructions for your capabilities directory.
///
/// [rerun-if-changed]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed
@@ -528,7 +530,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
if let Some(paths) = &config.bundle.external_bin {
copy_binaries(
ResourcePaths::new(external_binaries(paths, &target_triple).as_slice(), true),
ResourcePaths::new(&external_binaries(paths, &target_triple, &target), true),
&target_triple,
target_dir,
manifest.package.as_ref().map(|p| &p.name),
@@ -571,8 +573,10 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
}
}
if let Some(version) = &config.bundle.macos.minimum_system_version {
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={version}");
if !is_dev() {
if let Some(version) = &config.bundle.macos.minimum_system_version {
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={version}");
}
}
}

View File

@@ -1,5 +1,68 @@
# Changelog
## \[2.6.1]
### Bug Fixes
- [`f3df96fb3`](https://www.github.com/tauri-apps/tauri/commit/f3df96fb38e2f27ce6bf232fe87f35bcfec50ce4) ([#14065](https://www.github.com/tauri-apps/tauri/pull/14065) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix binary patching updater type fails on 32 bit Windows builds
## \[2.6.0]
### New Features
- [`a9ec12843`](https://www.github.com/tauri-apps/tauri/commit/a9ec12843aa7d0eb774bd3a53e2e63da12cfa77b) ([#13521](https://www.github.com/tauri-apps/tauri/pull/13521) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Added a `--skip-stapling` option to make `tauri build|bundle` *not* wait for notarization to finish on macOS.
### Enhancements
- [`8b465a12b`](https://www.github.com/tauri-apps/tauri/commit/8b465a12ba73e94d7a3995defd9cc362d15eeebe) ([#13913](https://www.github.com/tauri-apps/tauri/pull/13913) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler now pulls the latest AppImage linuxdeploy plugin instead of using the built-in one. This should remove the libfuse requirement.
- [`4475e93e1`](https://www.github.com/tauri-apps/tauri/commit/4475e93e136e9e2bd5f3c7817fa2040924f630f6) ([#13824](https://www.github.com/tauri-apps/tauri/pull/13824) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler and cli will now read TLS Certificates installed on the system when downloading tools and checking versions.
### Bug Fixes
- [`a8f1569b0`](https://www.github.com/tauri-apps/tauri/commit/a8f1569b04edf7b54a19e19ad37b421b0808f512) ([#13921](https://www.github.com/tauri-apps/tauri/pull/13921) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) The bundler will no longer try to sign non-binary and already signed binary files on Windows
- [`bc6b125b2`](https://www.github.com/tauri-apps/tauri/commit/bc6b125b24589ffc412a4f17d899a387a0fc0bb2) ([#13909](https://www.github.com/tauri-apps/tauri/pull/13909) by [@Andrew15-5](https://www.github.com/tauri-apps/tauri/../../Andrew15-5)) The bundler now falls back to `1` for the release in case an empty string was provided instead of using `-.` in the file name.
### Dependencies
- Upgraded to `tauri-utils@2.7.0`
- Upgraded to `tauri-macos-sign@2.2.0`
## \[2.5.2]
### Bug Fixes
- [`af95fb601`](https://www.github.com/tauri-apps/tauri/commit/af95fb6014ea54a2636bfd299095608f6cd93221) ([#13870](https://www.github.com/tauri-apps/tauri/pull/13870) by [@kittuov](https://www.github.com/tauri-apps/tauri/../../kittuov)) The bundler now signs the main binary after patching it for every package type on windows
## \[2.5.1]
### Bug Fixes
- [`f94af9035`](https://www.github.com/tauri-apps/tauri/commit/f94af90359ec8b01138ae542391caa704ec18ca8) ([#13786](https://www.github.com/tauri-apps/tauri/pull/13786) by [@catalinsh](https://www.github.com/tauri-apps/tauri/../../catalinsh)) Fix NSIS per-machine installer not requesting elevation when run by non-admin users.
- [`f2dbe7309`](https://www.github.com/tauri-apps/tauri/commit/f2dbe730979d570be3ee3ecac9621204c4ceb788) ([#13772](https://www.github.com/tauri-apps/tauri/pull/13772) by [@catalinsh](https://www.github.com/tauri-apps/tauri/../../catalinsh)) Fix incorrect expected file path for `nsis_tauri_utils.dll` resulting in tauri-cli re-downloading the file on every build.
- [`7a6fd5b75`](https://www.github.com/tauri-apps/tauri/commit/7a6fd5b75d61071e2771f6277c0376ec206d302a) ([#13863](https://www.github.com/tauri-apps/tauri/pull/13863) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The AppImage bundler now pulls the AppRun binaries from our GitHub mirror, fixing 404 errors.
### Dependencies
- Upgraded to `tauri-utils@2.6.0`
## \[2.5.0]
### New Features
- [`414619c36`](https://www.github.com/tauri-apps/tauri/commit/414619c36e94e21939534dd72c0438b93da75546) ([#13536](https://www.github.com/tauri-apps/tauri/pull/13536) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) Added support for the `bundleName` property in the macOS bundler configuration. This allows specifying the `CFBundleName` value for generated macOS bundles.
- [`7322f0579`](https://www.github.com/tauri-apps/tauri/commit/7322f057923aaec88960ad5556776774b745762f) ([#13502](https://www.github.com/tauri-apps/tauri/pull/13502) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Allow using `CheckIfAppIsRunning` macro inside NSIS hooks, for example `!insertmacro CheckIfAppIsRunning "another-executable.exe" "Another Executable"`.
### Bug Fixes
- [`479cee3d3`](https://www.github.com/tauri-apps/tauri/commit/479cee3d3680f9020005bdfb380d3a9482e286a1) ([#13260](https://www.github.com/tauri-apps/tauri/pull/13260) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler now sets the `ARCH` env var to the current build target to prevent potential issues with `appimagetool`'s auto-detection.
- [`e045fe32c`](https://www.github.com/tauri-apps/tauri/commit/e045fe32c9b0bed954916dc42528e28ee19f75b8) ([#13334](https://www.github.com/tauri-apps/tauri/pull/13334) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix custom Windows sign command failing to sign app uninstaller if it references relative paths.
- [`bd8a7cf39`](https://www.github.com/tauri-apps/tauri/commit/bd8a7cf39df316bf27c73a303d5e650301af0104) ([#13581](https://www.github.com/tauri-apps/tauri/pull/13581) by [@martpie](https://www.github.com/tauri-apps/tauri/../../martpie)) Fixes app icon not being displayed on Gnome dock and grid view when using Wayland.
- [`b52da29d5`](https://www.github.com/tauri-apps/tauri/commit/b52da29d5dbdb675ddba438a335e6a59f620e536) ([#13429](https://www.github.com/tauri-apps/tauri/pull/13429) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `mainBinaryName` doesn't work when there's `.` in it
### Dependencies
- Upgraded to `tauri-utils@2.5.0`
## \[2.4.0]
### New Features

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-bundler"
version = "2.4.0"
version = "2.6.1"
authors = [
"George Burton <burtonageo@gmail.com>",
"Tauri Programme within The Commons Conservancy",
@@ -15,13 +15,13 @@ rust-version = "1.77.2"
exclude = ["CHANGELOG.md", "/target", "rustfmt.toml"]
[dependencies]
tauri-utils = { version = "2.4.0", path = "../tauri-utils", features = [
tauri-utils = { version = "2.7.0", path = "../tauri-utils", features = [
"resources",
] }
image = "0.25"
flate2 = "1"
anyhow = "1"
thiserror = "2"
anyhow = "1"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
strsim = "0.11"
@@ -38,11 +38,14 @@ hex = "0.4"
semver = "1"
sha1 = "0.10"
sha2 = "0.10"
zip = { version = "2", default-features = false, features = ["deflate"] }
zip = { version = "4", default-features = false, features = ["deflate"] }
dunce = "1"
url = "2"
uuid = { version = "1", features = ["v4", "v5"] }
regex = "1"
goblin = "0.9"
plist = "1"
[target."cfg(target_os = \"windows\")".dependencies]
bitness = "0.4"
@@ -50,30 +53,30 @@ windows-registry = "0.5"
glob = "0.3"
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
version = "0.59"
version = "0.60"
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"] }
plist = "1"
tauri-macos-sign = { version = "2.1.0", path = "../tauri-macos-sign" }
tauri-macos-sign = { version = "2.2.0", path = "../tauri-macos-sign" }
[target."cfg(target_os = \"linux\")".dependencies]
heck = "0.5"
ar = "0.9"
md5 = "0.7"
md5 = "0.8"
rpm = { version = "0.16", features = ["bzip2-compression"] }
[target."cfg(unix)".dependencies]
which = "7"
which = "8"
[lib]
name = "tauri_bundler"
path = "src/lib.rs"
[features]
default = ["rustls"]
default = ["rustls", "platform-certs"]
native-tls = ["ureq/native-tls"]
native-tls-vendored = ["native-tls", "native-tls/vendored"]
rustls = ["ureq/rustls"]
platform-certs = ["ureq/platform-verifier"]

View File

@@ -13,21 +13,49 @@ mod settings;
mod updater_bundle;
mod windows;
use tauri_utils::display_path;
use tauri_utils::{display_path, platform::Target as TargetPlatform};
/// Patch a binary with bundle type information
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)?;
}
_ => (),
}
Ok(())
}
pub use self::{
category::AppCategory,
settings::{
AppImageSettings, BundleBinary, BundleSettings, CustomSignCommandSettings, DebianSettings,
DmgSettings, IosSettings, MacOsSettings, PackageSettings, PackageType, Position, RpmSettings,
Settings, SettingsBuilder, Size, UpdaterSettings,
DmgSettings, Entitlements, IosSettings, MacOsSettings, PackageSettings, PackageType, PlistKind,
Position, RpmSettings, Settings, SettingsBuilder, Size, UpdaterSettings,
},
};
#[cfg(target_os = "macos")]
use anyhow::Context;
pub use settings::{NsisSettings, WindowsSettings, WixLanguage, WixLanguageConfig, WixSettings};
use std::{fmt::Write, path::PathBuf};
use std::{
fmt::Write,
io::{Seek, SeekFrom},
path::PathBuf,
};
/// Generated bundle metadata.
#[derive(Debug)]
@@ -48,50 +76,38 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
package_types.sort_by_key(|a| a.priority());
let target_os = settings
.target()
.split('-')
.nth(2)
.unwrap_or(std::env::consts::OS)
.replace("darwin", "macos");
let target_os = settings.target_platform();
if target_os != std::env::consts::OS {
if *target_os != TargetPlatform::current() {
log::warn!("Cross-platform compilation is experimental and does not support all features. Please use a matching host system for full compatibility.");
}
// Sign windows binaries before the bundling step in case neither wix and nsis bundles are enabled
if target_os == "windows" {
if settings.can_sign() {
for bin in settings.binaries() {
let bin_path = settings.binary_path(bin);
windows::sign::try_sign(&bin_path, settings)?;
}
sign_binaries_if_needed(settings, target_os)?;
// Sign the sidecar binaries
for bin in settings.external_binaries() {
let path = bin?;
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
if skip {
continue;
}
let main_binary = settings
.binaries()
.iter()
.find(|b| b.main())
.expect("Main binary missing in settings");
let main_binary_path = settings.binary_path(main_binary);
#[cfg(windows)]
if windows::sign::verify(&path)? {
log::info!(
"sidecar at \"{}\" already signed. Skipping...",
path.display()
);
continue;
}
windows::sign::try_sign(&path, settings)?;
}
} else {
#[cfg(not(target_os = "windows"))]
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
}
// 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:
// - 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)?;
}
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
@@ -99,6 +115,24 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
continue;
}
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 {
#[cfg(target_os = "macos")]
PackageType::MacOsBundle => macos::app::bundle_project(settings)?,
@@ -119,6 +153,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
#[cfg(target_os = "windows")]
PackageType::WindowsMsi => windows::msi::bundle_project(settings, false)?,
// note: don't restrict to windows as NSIS installers can be built in linux using cargo-xwin
PackageType::Nsis => windows::nsis::bundle_project(settings, false)?,
#[cfg(target_os = "linux")]
@@ -186,24 +221,24 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
.map(|b| b.bundle_paths)
{
for app_bundle_path in &app_bundle_paths {
use crate::error::ErrorExt;
log::info!(action = "Cleaning"; "{}", app_bundle_path.display());
match app_bundle_path.is_dir() {
true => std::fs::remove_dir_all(app_bundle_path),
false => std::fs::remove_file(app_bundle_path),
}
.with_context(|| {
format!(
"Failed to clean the app bundle at {}",
app_bundle_path.display()
)
})?
.fs_context(
"failed to clean the app bundle",
app_bundle_path.to_path_buf(),
)?;
}
}
}
}
if bundles.is_empty() {
return Err(anyhow::anyhow!("No bundles were built").into());
return Ok(bundles);
}
let bundles_wo_updater = bundles
@@ -235,6 +270,51 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
Ok(bundles)
}
fn sign_binaries_if_needed(settings: &Settings, target_os: &TargetPlatform) -> crate::Result<()> {
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.");
return Ok(());
}
for bin in settings.binaries() {
if bin.main() {
// we will sign the main binary after patching per "package type"
continue;
}
let bin_path = settings.binary_path(bin);
windows::sign::try_sign(&bin_path, settings)?;
}
// Sign the sidecar binaries
for bin in settings.external_binaries() {
let path = bin?;
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
if skip {
continue;
}
#[cfg(windows)]
if windows::sign::verify(&path)? {
log::info!(
"sidecar at \"{}\" already signed. Skipping...",
path.display()
);
continue;
}
windows::sign::try_sign(&path, settings)?;
}
} else {
#[cfg(not(target_os = "windows"))]
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
}
}
Ok(())
}
/// Check to see if there are icons in the settings struct
pub fn check_icons(settings: &Settings) -> crate::Result<bool> {
// make a peekable iterator of the icon_files

View File

@@ -6,10 +6,10 @@
use super::debian;
use crate::{
bundle::settings::Arch,
error::{Context, ErrorExt},
utils::{fs_utils, http_utils::download, CommandExt},
Settings,
};
use anyhow::Context;
use std::{
fs,
path::{Path, PathBuf},
@@ -27,8 +27,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
Arch::Armhf => "armhf",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
"Unsupported architecture: {target:?}"
)));
}
};
@@ -53,7 +52,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
fs::create_dir_all(&tools_path)?;
let linuxdeploy_path = prepare_tools(&tools_path, tools_arch)?;
let linuxdeploy_path = prepare_tools(
&tools_path,
tools_arch,
settings.log_level() != log::Level::Error,
)?;
let package_dir = settings.project_out_directory().join("bundle/appimage_deb");
@@ -121,13 +124,13 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
// xdg-open will be handled by the `files` config instead
if settings.deep_link_protocols().is_some() && !app_dir_usr_bin.join("xdg-open").exists() {
fs::copy("/usr/bin/xdg-mime", app_dir_usr_bin.join("xdg-mime"))
.context("xdg-mime binary not found")?;
.fs_context("xdg-mime binary not found", "/usr/bin/xdg-mime".to_string())?;
}
// we also check if the user may have provided their own copy already
if settings.appimage().bundle_xdg_open && !app_dir_usr_bin.join("xdg-open").exists() {
fs::copy("/usr/bin/xdg-open", app_dir_usr_bin.join("xdg-open"))
.context("xdg-open binary not found")?;
.fs_context("xdg-open binary not found", "/usr/bin/xdg-open".to_string())?;
}
let search_dirs = [
@@ -187,6 +190,8 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let mut cmd = Command::new(linuxdeploy_path);
cmd.env("OUTPUT", &appimage_path);
cmd.env("ARCH", tools_arch);
// Looks like the cli arg isn't enough for the updated AppImage output-plugin.
cmd.env("APPIMAGE_EXTRACT_AND_RUN", "1");
cmd.args([
"--appimage-extract-and-run",
"--verbosity",
@@ -218,11 +223,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
}
// returns the linuxdeploy path to keep linuxdeploy_arch contained
fn prepare_tools(tools_path: &Path, arch: &str) -> crate::Result<PathBuf> {
fn prepare_tools(tools_path: &Path, arch: &str, verbose: bool) -> crate::Result<PathBuf> {
let apprun = tools_path.join(format!("AppRun-{arch}"));
if !apprun.exists() {
let data = download(&format!(
"https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-{arch}"
"https://github.com/tauri-apps/binary-releases/releases/download/apprun-old/AppRun-{arch}"
))?;
write_and_make_executable(&apprun, data)?;
}
@@ -246,6 +251,21 @@ fn prepare_tools(tools_path: &Path, arch: &str) -> crate::Result<PathBuf> {
write_and_make_executable(&gstreamer, data)?;
}
let appimage = tools_path.join("linuxdeploy-plugin-appimage.AppImage");
if !appimage.exists() {
// This is optional, linuxdeploy will fall back to its built-in version if the download failed.
let data = download(&format!("https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-{arch}.AppImage"));
match data {
Ok(data) => write_and_make_executable(&appimage, data)?,
Err(err) => {
log::error!("Download of AppImage plugin failed. Using older built-in version instead.");
if verbose {
log::debug!("{err:?}");
}
}
}
}
// This should prevent linuxdeploy to be detected by appimage integration tools
let _ = Command::new("dd")
.args([

View File

@@ -24,8 +24,12 @@
// generate postinst or prerm files.
use super::freedesktop;
use crate::{bundle::settings::Arch, utils::fs_utils, Settings};
use anyhow::Context;
use crate::{
bundle::settings::Arch,
error::{Context, ErrorExt},
utils::fs_utils,
Settings,
};
use flate2::{write::GzEncoder, Compression};
use tar::HeaderMode;
use walkdir::WalkDir;
@@ -49,8 +53,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
Arch::Riscv64 => "riscv64",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
"Unsupported architecture: {target:?}"
)));
}
};
@@ -65,30 +68,32 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let base_dir = settings.project_out_directory().join("bundle/deb");
let package_dir = base_dir.join(&package_base_name);
if package_dir.exists() {
fs::remove_dir_all(&package_dir)
.with_context(|| format!("Failed to remove old {package_base_name}"))?;
fs::remove_dir_all(&package_dir).fs_context(
"Failed to Remove old package directory",
package_dir.clone(),
)?;
}
let package_path = base_dir.join(&package_name);
log::info!(action = "Bundling"; "{} ({})", package_name, package_path.display());
let (data_dir, _) = generate_data(settings, &package_dir)
.with_context(|| "Failed to build data folders and files")?;
let (data_dir, _) =
generate_data(settings, &package_dir).context("Failed to build data folders and files")?;
fs_utils::copy_custom_files(&settings.deb().files, &data_dir)
.with_context(|| "Failed to copy custom files")?;
.context("Failed to copy custom files")?;
// Generate control files.
let control_dir = package_dir.join("control");
generate_control_file(settings, arch, &control_dir, &data_dir)
.with_context(|| "Failed to create control file")?;
generate_scripts(settings, &control_dir).with_context(|| "Failed to create control scripts")?;
generate_md5sums(&control_dir, &data_dir).with_context(|| "Failed to create md5sums file")?;
.context("Failed to create control file")?;
generate_scripts(settings, &control_dir).context("Failed to create control scripts")?;
generate_md5sums(&control_dir, &data_dir).context("Failed to create md5sums file")?;
// Generate `debian-binary` file; see
// http://www.tldp.org/HOWTO/Debian-Binary-Package-Building-HOWTO/x60.html#AEN66
let debian_binary_path = package_dir.join("debian-binary");
create_file_with_data(&debian_binary_path, "2.0\n")
.with_context(|| "Failed to create debian-binary file")?;
.context("Failed to create debian-binary file")?;
// Apply tar/gzip/ar to create the final package file.
let control_tar_gz_path =
@@ -164,7 +169,7 @@ fn generate_control_file(
let dest_path = control_dir.join("control");
let mut file = fs_utils::create_file(&dest_path)?;
let package = heck::AsKebabCase(settings.product_name());
writeln!(file, "Package: {}", package)?;
writeln!(file, "Package: {package}")?;
writeln!(file, "Version: {}", settings.version_string())?;
writeln!(file, "Architecture: {arch}")?;
// Installed-Size must be divided by 1024, see https://www.debian.org/doc/debian-policy/ch-controlfields.html#installed-size
@@ -183,16 +188,16 @@ fn generate_control_file(
writeln!(file, "Maintainer: {authors}")?;
if let Some(section) = &settings.deb().section {
writeln!(file, "Section: {}", section)?;
writeln!(file, "Section: {section}")?;
}
if let Some(priority) = &settings.deb().priority {
writeln!(file, "Priority: {}", priority)?;
writeln!(file, "Priority: {priority}")?;
} else {
writeln!(file, "Priority: optional")?;
}
if let Some(homepage) = settings.homepage_url() {
writeln!(file, "Homepage: {}", homepage)?;
writeln!(file, "Homepage: {homepage}")?;
}
let dependencies = settings.deb().depends.as_ref().cloned().unwrap_or_default();
@@ -305,7 +310,7 @@ fn generate_md5sums(control_dir: &Path, data_dir: &Path) -> crate::Result<()> {
let mut file = File::open(path)?;
let mut hash = md5::Context::new();
io::copy(&mut file, &mut hash)?;
for byte in hash.compute().iter() {
for byte in hash.finalize().iter() {
write!(md5sums_file, "{byte:02x}")?;
}
let rel_path = path.strip_prefix(data_dir)?;

View File

@@ -4,6 +4,7 @@ Categories={{categories}}
Comment={{comment}}
{{/if}}
Exec={{exec}}
StartupWMClass={{exec}}
Icon={{icon}}
Name={{name}}
Terminal=false

View File

@@ -21,12 +21,12 @@ use std::fs::{read_to_string, File};
use std::io::BufReader;
use std::path::{Path, PathBuf};
use anyhow::Context;
use handlebars::Handlebars;
use image::{self, codecs::png::PngDecoder, ImageDecoder};
use serde::Serialize;
use crate::{
error::Context,
utils::{self, fs_utils},
Settings,
};
@@ -114,11 +114,13 @@ pub fn generate_desktop_file(
if let Some(template) = custom_template_path {
handlebars
.register_template_string("main.desktop", read_to_string(template)?)
.with_context(|| "Failed to setup custom handlebar template")?;
.map_err(Into::into)
.context("Failed to setup custom handlebar template")?;
} else {
handlebars
.register_template_string("main.desktop", include_str!("./main.desktop"))
.with_context(|| "Failed to setup default handlebar template")?;
.map_err(Into::into)
.context("Failed to setup default handlebar template")?;
}
#[derive(Serialize)]

View File

@@ -7,3 +7,8 @@ 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

@@ -3,9 +3,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{bundle::settings::Arch, Settings};
use crate::{bundle::settings::Arch, error::ErrorExt, Settings};
use anyhow::Context;
use rpm::{self, signature::pgp, Dependency, FileMode, FileOptions};
use std::{
env,
@@ -21,7 +20,10 @@ use super::freedesktop;
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let product_name = settings.product_name();
let version = settings.version_string();
let release = settings.rpm().release.as_str();
let release = match settings.rpm().release.as_str() {
"" => "1", // Considered the default. If left empty, you get file with "-.".
v => v,
};
let epoch = settings.rpm().epoch;
let arch = match settings.binary_arch() {
Arch::X86_64 => "x86_64",
@@ -32,8 +34,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
Arch::Riscv64 => "riscv64",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
"Unsupported architecture: {target:?}"
)));
}
};
@@ -46,10 +47,13 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let base_dir = settings.project_out_directory().join("bundle/rpm");
let package_dir = base_dir.join(&package_base_name);
if package_dir.exists() {
fs::remove_dir_all(&package_dir)
.with_context(|| format!("Failed to remove old {package_base_name}"))?;
fs::remove_dir_all(&package_dir).fs_context(
"Failed to remove old package directory",
package_dir.clone(),
)?;
}
fs::create_dir_all(&package_dir)?;
fs::create_dir_all(&package_dir)
.fs_context("Failed to create package directory", package_dir.clone())?;
let package_path = base_dir.join(&package_name);
log::info!(action = "Bundling"; "{} ({})", package_name, package_path.display());
@@ -235,6 +239,5 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let mut f = fs::File::create(&package_path)?;
pkg.write(&mut f)?;
Ok(vec![package_path])
}

View File

@@ -0,0 +1,59 @@
// 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

@@ -24,15 +24,16 @@
use super::{
icon::create_icns_file,
sign::{notarize, notarize_auth, sign, NotarizeAuthError, SignTarget},
sign::{notarize, notarize_auth, notarize_without_stapling, sign, SignTarget},
};
use crate::{
bundle::settings::PlistKind,
error::{Context, ErrorExt, NotarizeAuthError},
utils::{fs_utils, CommandExt},
Error::GenericError,
Settings,
};
use anyhow::Context;
use std::{
ffi::OsStr,
fs,
@@ -64,16 +65,16 @@ 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)
.with_context(|| format!("Failed to remove old {}", app_product_name))?;
fs::remove_dir_all(&app_bundle_path).fs_context(
"failed to remove old app bundle",
app_bundle_path.to_path_buf(),
)?;
}
let bundle_directory = app_bundle_path.join("Contents");
fs::create_dir_all(&bundle_directory).with_context(|| {
format!(
"Failed to create bundle directory at {:?}",
bundle_directory
)
})?;
fs::create_dir_all(&bundle_directory).fs_context(
"failed to create bundle directory",
bundle_directory.to_path_buf(),
)?;
let resources_dir = bundle_directory.join("Resources");
let bin_dir = bundle_directory.join("MacOS");
@@ -107,7 +108,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
copy_custom_files_to_bundle(&bundle_directory, settings)?;
if let Some(keychain) = super::sign::keychain(settings.macos().signing_identity.as_deref())? {
if settings.no_sign() {
log::warn!("Skipping signing due to --no-sign flag.",);
} else if let Some(keychain) =
super::sign::keychain(settings.macos().signing_identity.as_deref())?
{
// Sign frameworks and sidecar binaries first, per apple, signing must be done inside out
// https://developer.apple.com/forums/thread/701514
sign_paths.push(SignTarget {
@@ -125,11 +130,15 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
// notarization is required for distribution
match notarize_auth() {
Ok(auth) => {
notarize(&keychain, app_bundle_path.clone(), &auth)?;
if settings.macos().skip_stapling {
notarize_without_stapling(&keychain, app_bundle_path.clone(), &auth)?;
} else {
notarize(&keychain, app_bundle_path.clone(), &auth)?;
}
}
Err(e) => {
if matches!(e, NotarizeAuthError::MissingTeamId) {
return Err(anyhow::anyhow!("{e}").into());
return Err(e.into());
} else {
log::warn!("skipping app notarization, {}", e.to_string());
}
@@ -160,7 +169,7 @@ fn copy_binaries_to_bundle(
let bin_path = settings.binary_path(bin);
let dest_path = dest_dir.join(bin.name());
fs_utils::copy_file(&bin_path, &dest_path)
.with_context(|| format!("Failed to copy binary from {:?}", bin_path))?;
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
paths.push(dest_path);
}
Ok(paths)
@@ -169,6 +178,12 @@ fn copy_binaries_to_bundle(
/// Copies user-defined files to the app under Contents.
fn copy_custom_files_to_bundle(bundle_directory: &Path, settings: &Settings) -> crate::Result<()> {
for (contents_path, path) in settings.macos().files.iter() {
if !path.try_exists()? {
return Err(GenericError(format!(
"Failed to copy {path:?} to {contents_path:?}. {path:?} does not exist."
)));
}
let contents_path = if contents_path.is_absolute() {
contents_path.strip_prefix("/").unwrap()
} else {
@@ -176,10 +191,14 @@ fn copy_custom_files_to_bundle(bundle_directory: &Path, settings: &Settings) ->
};
if path.is_file() {
fs_utils::copy_file(path, &bundle_directory.join(contents_path))
.with_context(|| format!("Failed to copy file {:?} to {:?}", path, contents_path))?;
} else {
.with_context(|| format!("Failed to copy file {path:?} to {contents_path:?}"))?;
} else if path.is_dir() {
fs_utils::copy_dir(path, &bundle_directory.join(contents_path))
.with_context(|| format!("Failed to copy directory {:?} to {:?}", path, contents_path))?;
.with_context(|| format!("Failed to copy directory {path:?} to {contents_path:?}"))?;
} else {
return Err(GenericError(format!(
"{path:?} is not a file or directory."
)));
}
}
Ok(())
@@ -214,7 +233,15 @@ fn create_info_plist(
settings.bundle_identifier().into(),
);
plist.insert("CFBundleInfoDictionaryVersion".into(), "6.0".into());
plist.insert("CFBundleName".into(), settings.product_name().into());
if let Some(bundle_name) = settings
.macos()
.bundle_name
.as_deref()
.unwrap_or_else(|| settings.product_name())
.into()
{
plist.insert("CFBundleName".into(), bundle_name.into());
}
plist.insert("CFBundlePackageType".into(), "APPL".into());
plist.insert(
"CFBundleShortVersionString".into(),
@@ -241,6 +268,55 @@ fn create_info_plist(
}
if let Some(associations) = settings.file_associations() {
let exported_associations = associations
.iter()
.filter_map(|association| {
association.exported_type.as_ref().map(|exported_type| {
let mut dict = plist::Dictionary::new();
dict.insert(
"UTTypeIdentifier".into(),
exported_type.identifier.clone().into(),
);
if let Some(description) = &association.description {
dict.insert("UTTypeDescription".into(), description.clone().into());
}
if let Some(conforms_to) = &exported_type.conforms_to {
dict.insert(
"UTTypeConformsTo".into(),
plist::Value::Array(conforms_to.iter().map(|s| s.clone().into()).collect()),
);
}
let mut specification = plist::Dictionary::new();
specification.insert(
"public.filename-extension".into(),
plist::Value::Array(
association
.ext
.iter()
.map(|s| s.to_string().into())
.collect(),
),
);
if let Some(mime_type) = &association.mime_type {
specification.insert("public.mime-type".into(), mime_type.clone().into());
}
dict.insert("UTTypeTagSpecification".into(), specification.into());
plist::Value::Dictionary(dict)
})
})
.collect::<Vec<_>>();
if !exported_associations.is_empty() {
plist.insert(
"UTExportedTypeDeclarations".into(),
plist::Value::Array(exported_associations),
);
}
plist.insert(
"CFBundleDocumentTypes".into(),
plist::Value::Array(
@@ -248,16 +324,27 @@ fn create_info_plist(
.iter()
.map(|association| {
let mut dict = plist::Dictionary::new();
dict.insert(
"CFBundleTypeExtensions".into(),
plist::Value::Array(
association
.ext
.iter()
.map(|ext| ext.to_string().into())
.collect(),
),
);
if !association.ext.is_empty() {
dict.insert(
"CFBundleTypeExtensions".into(),
plist::Value::Array(
association
.ext
.iter()
.map(|ext| ext.to_string().into())
.collect(),
),
);
}
if let Some(content_types) = &association.content_types {
dict.insert(
"LSItemContentTypes".into(),
plist::Value::Array(content_types.iter().map(|s| s.to_string().into()).collect()),
);
}
dict.insert(
"CFBundleTypeName".into(),
association
@@ -285,6 +372,7 @@ fn create_info_plist(
plist::Value::Array(
protocols
.iter()
.filter(|p| !p.schemes.is_empty())
.map(|protocol| {
let mut dict = plist::Dictionary::new();
dict.insert(
@@ -335,8 +423,11 @@ fn create_info_plist(
plist.insert("NSAppTransportSecurity".into(), security.into());
}
if let Some(user_plist_path) = &settings.macos().info_plist_path {
let user_plist = plist::Value::from_file(user_plist_path)?;
if let Some(user_plist) = &settings.macos().info_plist {
let user_plist = match user_plist {
PlistKind::Path(path) => plist::Value::from_file(path)?,
PlistKind::Plist(value) => value.clone(),
};
if let Some(dict) = user_plist.into_dictionary() {
for (key, value) in dict {
plist.insert(key, value);
@@ -351,7 +442,7 @@ fn create_info_plist(
// Copies the framework under `{src_dir}/{framework}.framework` to `{dest_dir}/{framework}.framework`.
fn copy_framework_from(dest_dir: &Path, framework: &str, src_dir: &Path) -> crate::Result<bool> {
let src_name = format!("{}.framework", framework);
let src_name = format!("{framework}.framework");
let src_path = src_dir.join(&src_name);
if src_path.exists() {
fs_utils::copy_dir(&src_path, &dest_dir.join(&src_name))?;
@@ -378,8 +469,10 @@ fn copy_frameworks_to_bundle(
return Ok(paths);
}
let dest_dir = bundle_directory.join("Frameworks");
fs::create_dir_all(bundle_directory)
.with_context(|| format!("Failed to create Frameworks directory at {:?}", dest_dir))?;
fs::create_dir_all(&dest_dir).fs_context(
"failed to create Frameworks directory",
dest_dir.to_path_buf(),
)?;
for framework in frameworks.iter() {
if framework.ends_with(".framework") {
let src_path = PathBuf::from(framework);
@@ -393,10 +486,7 @@ fn copy_frameworks_to_bundle(
} else if framework.ends_with(".dylib") {
let src_path = PathBuf::from(framework);
if !src_path.exists() {
return Err(crate::Error::GenericError(format!(
"Library not found: {}",
framework
)));
return Err(GenericError(format!("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);
@@ -407,9 +497,8 @@ fn copy_frameworks_to_bundle(
});
continue;
} else if framework.contains('/') {
return Err(crate::Error::GenericError(format!(
"Framework path should have .framework extension: {}",
framework
return Err(GenericError(format!(
"Framework path should have .framework extension: {framework}"
)));
}
if let Some(home_dir) = dirs::home_dir() {
@@ -426,9 +515,8 @@ fn copy_frameworks_to_bundle(
{
continue;
}
return Err(crate::Error::GenericError(format!(
"Could not locate framework: {}",
framework
return Err(GenericError(format!(
"Could not locate framework: {framework}"
)));
}
Ok(paths)
@@ -520,3 +608,153 @@ fn add_nested_code_sign_path(src_path: &Path, dest_path: &Path, sign_paths: &mut
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bundle::{BundleSettings, MacOsSettings, PackageSettings, SettingsBuilder};
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
};
/// Helper that builds a `Settings` instance and bundle directory for tests.
/// It receives a mapping of bundle-relative paths to source paths and
/// returns the generated bundle directory and settings.
fn create_test_bundle(
project_dir: &Path,
files: HashMap<PathBuf, PathBuf>,
) -> (PathBuf, crate::bundle::Settings) {
let macos_settings = MacOsSettings {
files,
..Default::default()
};
let settings = SettingsBuilder::new()
.project_out_directory(project_dir)
.package_settings(PackageSettings {
product_name: "TestApp".into(),
version: "0.1.0".into(),
description: "test".into(),
homepage: None,
authors: None,
default_run: None,
})
.bundle_settings(BundleSettings {
macos: macos_settings,
..Default::default()
})
.target("x86_64-apple-darwin".into())
.build()
.expect("failed to build settings");
let bundle_dir = project_dir.join("TestApp.app/Contents");
fs::create_dir_all(&bundle_dir).expect("failed to create bundle dir");
(bundle_dir, settings)
}
#[test]
fn test_copy_custom_file_to_bundle_file() {
let tmp_dir = tempfile::tempdir().expect("failed to create temp dir");
// Prepare a single file to copy.
let src_file = tmp_dir.path().join("sample.txt");
fs::write(&src_file, b"hello tauri").expect("failed to write sample file");
let files_map = HashMap::from([(PathBuf::from("Resources/sample.txt"), src_file.clone())]);
let (bundle_dir, settings) = create_test_bundle(tmp_dir.path(), files_map);
copy_custom_files_to_bundle(&bundle_dir, &settings)
.expect("copy_custom_files_to_bundle failed");
let dest_file = bundle_dir.join("Resources/sample.txt");
assert!(dest_file.exists() && dest_file.is_file());
assert_eq!(fs::read_to_string(dest_file).unwrap(), "hello tauri");
}
#[test]
fn test_copy_custom_file_to_bundle_dir() {
let tmp_dir = tempfile::tempdir().expect("failed to create temp dir");
// Create a source directory with a nested file.
let src_dir = tmp_dir.path().join("assets");
fs::create_dir_all(&src_dir).expect("failed to create assets directory");
let nested_file = src_dir.join("nested.txt");
fs::write(&nested_file, b"nested").expect("failed to write nested file");
let files_map = HashMap::from([(PathBuf::from("MyAssets"), src_dir.clone())]);
let (bundle_dir, settings) = create_test_bundle(tmp_dir.path(), files_map);
copy_custom_files_to_bundle(&bundle_dir, &settings)
.expect("copy_custom_files_to_bundle failed");
let dest_nested_file = bundle_dir.join("MyAssets/nested.txt");
assert!(
dest_nested_file.exists(),
"{dest_nested_file:?} does not exist"
);
assert!(
dest_nested_file.is_file(),
"{dest_nested_file:?} is not a file"
);
assert_eq!(
fs::read_to_string(dest_nested_file).unwrap().trim(),
"nested"
);
}
#[test]
fn test_copy_custom_files_to_bundle_missing_source() {
let tmp_dir = tempfile::tempdir().expect("failed to create temp dir");
// Intentionally reference a non-existent path.
let missing_path = tmp_dir.path().join("does_not_exist.txt");
let files_map = HashMap::from([(PathBuf::from("Missing.txt"), missing_path)]);
let (bundle_dir, settings) = create_test_bundle(tmp_dir.path(), files_map);
let result = copy_custom_files_to_bundle(&bundle_dir, &settings);
assert!(result.is_err());
assert!(result.err().unwrap().to_string().contains("does not exist"));
}
#[test]
fn test_copy_custom_files_to_bundle_invalid_source() {
let tmp_dir = tempfile::tempdir().expect("failed to create temp dir");
let files_map = HashMap::from([(PathBuf::from("Invalid.txt"), PathBuf::from("///"))]);
let (bundle_dir, settings) = create_test_bundle(tmp_dir.path(), files_map);
let result = copy_custom_files_to_bundle(&bundle_dir, &settings);
assert!(result.is_err());
assert!(result
.err()
.unwrap()
.to_string()
.contains("Failed to copy directory"));
}
#[test]
fn test_copy_custom_files_to_bundle_dev_null() {
let tmp_dir = tempfile::tempdir().expect("failed to create temp dir");
let files_map = HashMap::from([(PathBuf::from("Invalid.txt"), PathBuf::from("/dev/null"))]);
let (bundle_dir, settings) = create_test_bundle(tmp_dir.path(), files_map);
let result = copy_custom_files_to_bundle(&bundle_dir, &settings);
assert!(result.is_err());
assert!(result
.err()
.unwrap()
.to_string()
.contains("is not a file or directory."));
}
}

View File

@@ -6,12 +6,11 @@
use super::{app, icon::create_icns_file};
use crate::{
bundle::{settings::Arch, Bundle},
error::{Context, ErrorExt},
utils::CommandExt,
PackageType, Settings,
};
use anyhow::Context;
use std::{
env,
fs::{self, write},
@@ -49,8 +48,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
Arch::Universal => "universal",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
"Unsupported architecture: {target:?}"
)));
}
}
@@ -59,7 +57,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
let dmg_path = output_path.join(&dmg_name);
let product_name = settings.product_name();
let bundle_file_name = format!("{}.app", product_name);
let bundle_file_name = format!("{product_name}.app");
let bundle_dir = settings.project_out_directory().join("bundle/macos");
let support_directory_path = output_path
@@ -69,10 +67,9 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
for path in &[&support_directory_path, &output_path] {
if path.exists() {
fs::remove_dir_all(path).with_context(|| format!("Failed to remove old {}", dmg_name))?;
fs::remove_dir_all(path).fs_context("failed to remove old dmg", path.to_path_buf())?;
}
fs::create_dir_all(path)
.with_context(|| format!("Failed to create output directory at {:?}", path))?;
fs::create_dir_all(path).fs_context("failed to create output directory", path.to_path_buf())?;
}
// create paths for script
@@ -196,7 +193,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
// Sign DMG if needed
// skipping self-signing DMGs https://github.com/tauri-apps/tauri/issues/12288
let identity = settings.macos().signing_identity.as_deref();
if identity != Some("-") {
if !settings.no_sign() && identity != Some("-") {
if let Some(keychain) = super::sign::keychain(identity)? {
super::sign::sign(
&keychain,

View File

@@ -14,11 +14,11 @@
// explanation.
use crate::{
error::{Context, ErrorExt},
utils::{self, fs_utils},
Settings,
};
use anyhow::Context;
use image::{codecs::png::PngDecoder, GenericImageView, ImageDecoder};
use std::{
@@ -44,17 +44,21 @@ 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)
.with_context(|| format!("Failed to remove old {}", app_product_name))?;
fs::remove_dir_all(&app_bundle_path).fs_context(
"failed to remove old app bundle",
app_bundle_path.to_path_buf(),
)?;
}
fs::create_dir_all(&app_bundle_path)
.with_context(|| format!("Failed to create bundle directory at {:?}", app_bundle_path))?;
fs::create_dir_all(&app_bundle_path).fs_context(
"failed to create bundle directory",
app_bundle_path.to_path_buf(),
)?;
for src in settings.resource_files() {
let src = src?;
let dest = app_bundle_path.join(tauri_utils::resources::resource_relpath(&src));
fs_utils::copy_file(&src, &dest)
.with_context(|| format!("Failed to copy resource file {:?}", src))?;
.with_context(|| format!("Failed to copy resource file {src:?}"))?;
}
let icon_filenames = generate_icon_files(&app_bundle_path, settings)
@@ -65,7 +69,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
for bin in settings.binaries() {
let bin_path = settings.binary_path(bin);
fs_utils::copy_file(&bin_path, &app_bundle_path.join(bin.name()))
.with_context(|| format!("Failed to copy binary from {:?}", bin_path))?;
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
}
Ok(vec![app_bundle_path])
@@ -197,7 +201,7 @@ fn generate_info_plist(
if !icon_filenames.is_empty() {
writeln!(file, " <key>CFBundleIconFiles</key>\n <array>")?;
for filename in icon_filenames {
writeln!(file, " <string>{}</string>", filename)?;
writeln!(file, " <string>{filename}</string>")?;
}
writeln!(file, " </array>")?;
}

View File

@@ -6,10 +6,10 @@
use std::{
env::{var, var_os},
ffi::OsString,
path::{Path, PathBuf},
path::PathBuf,
};
use crate::Settings;
use crate::{error::NotarizeAuthError, Entitlements, Settings};
pub struct SignTarget {
pub path: PathBuf,
@@ -23,11 +23,14 @@ pub fn keychain(identity: Option<&str>) -> crate::Result<Option<tauri_macos_sign
) {
// import user certificate - useful for for CI build
let keychain =
tauri_macos_sign::Keychain::with_certificate(&certificate_encoded, &certificate_password)?;
tauri_macos_sign::Keychain::with_certificate(&certificate_encoded, &certificate_password)
.map_err(Box::new)?;
if let Some(identity) = identity {
let certificate_identity = keychain.signing_identity();
if !certificate_identity.contains(identity) {
return Err(anyhow::anyhow!("certificate from APPLE_CERTIFICATE \"{certificate_identity}\" environment variable does not match provided identity \"{identity}\"").into());
return Err(crate::Error::GenericError(format!(
"certificate from APPLE_CERTIFICATE \"{certificate_identity}\" environment variable does not match provided identity \"{identity}\""
)));
}
}
Ok(Some(keychain))
@@ -48,16 +51,23 @@ pub fn sign(
log::info!(action = "Signing"; "with identity \"{}\"", keychain.signing_identity());
for target in targets {
let entitlements_path = if target.is_an_executable {
settings.macos().entitlements.as_ref().map(Path::new)
} else {
None
let (entitlements_path, _temp_file) = match settings.macos().entitlements.as_ref() {
Some(Entitlements::Path(path)) => (Some(path.to_owned()), None),
Some(Entitlements::Plist(plist)) => {
let mut temp_file = tempfile::NamedTempFile::new()?;
plist::to_writer_xml(temp_file.as_file_mut(), &plist)?;
(Some(temp_file.path().to_path_buf()), Some(temp_file))
}
None => (None, None),
};
keychain.sign(
&target.path,
entitlements_path,
target.is_an_executable && settings.macos().hardened_runtime,
)?;
keychain
.sign(
&target.path,
entitlements_path.as_deref(),
target.is_an_executable && settings.macos().hardened_runtime,
)
.map_err(Box::new)?;
}
Ok(())
@@ -68,17 +78,19 @@ pub fn notarize(
app_bundle_path: PathBuf,
credentials: &tauri_macos_sign::AppleNotarizationCredentials,
) -> crate::Result<()> {
tauri_macos_sign::notarize(keychain, &app_bundle_path, credentials).map_err(Into::into)
tauri_macos_sign::notarize(keychain, &app_bundle_path, credentials)
.map_err(Box::new)
.map_err(Into::into)
}
#[derive(Debug, thiserror::Error)]
pub enum NotarizeAuthError {
#[error(
"The team ID is now required for notarization with app-specific password as authentication. Please set the `APPLE_TEAM_ID` environment variable. You can find the team ID in https://developer.apple.com/account#MembershipDetailsCard."
)]
MissingTeamId,
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
pub fn notarize_without_stapling(
keychain: &tauri_macos_sign::Keychain,
app_bundle_path: PathBuf,
credentials: &tauri_macos_sign::AppleNotarizationCredentials,
) -> crate::Result<()> {
tauri_macos_sign::notarize_without_stapling(keychain, &app_bundle_path, credentials)
.map_err(Box::new)
.map_err(Into::into)
}
pub fn notarize_auth() -> Result<tauri_macos_sign::AppleNotarizationCredentials, NotarizeAuthError>
@@ -97,10 +109,18 @@ pub fn notarize_auth() -> Result<tauri_macos_sign::AppleNotarizationCredentials,
}
(Some(_apple_id), Some(_password), None) => Err(NotarizeAuthError::MissingTeamId),
_ => {
match (var_os("APPLE_API_KEY"), var_os("APPLE_API_ISSUER"), var("APPLE_API_KEY_PATH")) {
match (
var_os("APPLE_API_KEY"),
var_os("APPLE_API_ISSUER"),
var("APPLE_API_KEY_PATH"),
) {
(Some(key_id), Some(issuer), Ok(key_path)) => {
Ok(tauri_macos_sign::AppleNotarizationCredentials::ApiKey { key_id, key: tauri_macos_sign::ApiKey::Path( key_path.into()), issuer })
},
Ok(tauri_macos_sign::AppleNotarizationCredentials::ApiKey {
key_id,
key: tauri_macos_sign::ApiKey::Path(key_path.into()),
issuer,
})
}
(Some(key_id), Some(issuer), Err(_)) => {
let mut api_key_file_name = OsString::from("AuthKey_");
api_key_file_name.push(&key_id);
@@ -122,12 +142,18 @@ pub fn notarize_auth() -> Result<tauri_macos_sign::AppleNotarizationCredentials,
}
if let Some(key_path) = key_path {
Ok(tauri_macos_sign::AppleNotarizationCredentials::ApiKey { key_id, key: tauri_macos_sign::ApiKey::Path(key_path), issuer })
Ok(tauri_macos_sign::AppleNotarizationCredentials::ApiKey {
key_id,
key: tauri_macos_sign::ApiKey::Path(key_path),
issuer,
})
} else {
Err(anyhow::anyhow!("could not find API key file. Please set the APPLE_API_KEY_PATH environment variables to the path to the {api_key_file_name:?} file").into())
Err(NotarizeAuthError::MissingApiKey {
file_name: api_key_file_name.to_string_lossy().into_owned(),
})
}
}
_ => Err(anyhow::anyhow!("no APPLE_ID & APPLE_PASSWORD & APPLE_TEAM_ID or APPLE_API_KEY & APPLE_API_ISSUER & APPLE_API_KEY_PATH environment variables found").into())
_ => Err(NotarizeAuthError::MissingCredentials),
}
}
}

View File

@@ -4,14 +4,14 @@
// SPDX-License-Identifier: MIT
use super::category::AppCategory;
use crate::{bundle::platform::target_triple, utils::fs_utils};
use anyhow::Context;
use crate::{bundle::platform::target_triple, error::Context, utils::fs_utils};
pub use tauri_utils::config::WebviewInstallMode;
use tauri_utils::{
config::{
BundleType, DeepLinkProtocol, FileAssociation, NSISInstallerMode, NsisCompression,
RpmCompression,
},
platform::Target as TargetPlatform,
resources::{external_binaries, ResourcePaths},
};
@@ -332,6 +332,10 @@ pub struct MacOsSettings {
pub files: HashMap<PathBuf, PathBuf>,
/// The version of the build that identifies an iteration of the bundle.
pub bundle_version: Option<String>,
/// The name of the build that identifies a string of the bundle.
///
/// If not set, defaults to the package's product name.
pub bundle_name: Option<String>,
/// A version string indicating the minimum MacOS version that the bundled app supports (e.g. `"10.11"`).
/// If you are using this config field, you may also want have your `build.rs` script emit `cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.11`.
pub minimum_system_version: Option<String>,
@@ -341,16 +345,43 @@ pub struct MacOsSettings {
pub exception_domain: Option<String>,
/// Code signing identity.
pub signing_identity: Option<String>,
/// Whether to wait for notarization to finish and `staple` the ticket onto the app.
///
/// Gatekeeper will look for stapled tickets to tell whether your app was notarized without
/// reaching out to Apple's servers which is helpful in offline environments.
///
/// Enabling this option will also result in `tauri build` not waiting for notarization to finish
/// which is helpful for the very first time your app is notarized as this can take multiple hours.
/// On subsequent runs, it's recommended to disable this setting again.
pub skip_stapling: bool,
/// Preserve the hardened runtime version flag, see <https://developer.apple.com/documentation/security/hardened_runtime>
///
/// Settings this to `false` is useful when using an ad-hoc signature, making it less strict.
pub hardened_runtime: bool,
/// Provider short name for notarization.
pub provider_short_name: Option<String>,
/// Path or contents of the entitlements.plist file.
pub entitlements: Option<Entitlements>,
/// Path to the Info.plist file or raw plist value to merge with the bundle Info.plist.
pub info_plist: Option<PlistKind>,
}
/// Entitlements for macOS code signing.
#[derive(Debug, Clone)]
pub enum Entitlements {
/// Path to the entitlements.plist file.
pub entitlements: Option<String>,
/// Path to the Info.plist file for the bundle.
pub info_plist_path: Option<PathBuf>,
Path(PathBuf),
/// Raw plist::Value.
Plist(plist::Value),
}
/// Plist format.
#[derive(Debug, Clone)]
pub enum PlistKind {
/// Path to a .plist file.
Path(PathBuf),
/// Raw plist value.
Plist(plist::Value),
}
/// Configuration for a target language for the WiX build.
@@ -558,6 +589,12 @@ pub struct WindowsSettings {
pub sign_command: Option<CustomSignCommandSettings>,
}
impl WindowsSettings {
pub(crate) fn can_sign(&self) -> bool {
self.sign_command.is_some() || self.certificate_thumbprint.is_some()
}
}
#[allow(deprecated)]
mod _default {
use super::*;
@@ -760,8 +797,12 @@ pub struct Settings {
bundle_settings: BundleSettings,
/// the binaries to bundle.
binaries: Vec<BundleBinary>,
/// The target platform.
target_platform: TargetPlatform,
/// The target triple.
target: String,
/// Whether to disable code signing during the bundling process.
no_sign: bool,
}
/// A builder for [`Settings`].
@@ -775,6 +816,7 @@ pub struct SettingsBuilder {
binaries: Vec<BundleBinary>,
target: Option<String>,
local_tools_directory: Option<PathBuf>,
no_sign: bool,
}
impl SettingsBuilder {
@@ -844,6 +886,13 @@ impl SettingsBuilder {
self
}
/// Sets whether to skip code signing.
#[must_use]
pub fn no_sign(mut self, no_sign: bool) -> Self {
self.no_sign = no_sign;
self
}
/// Builds a Settings from the CLI args.
///
/// Package settings will be read from Cargo.toml.
@@ -855,6 +904,7 @@ impl SettingsBuilder {
} else {
target_triple()?
};
let target_platform = TargetPlatform::from_triple(&target);
Ok(Settings {
log_level: self.log_level.unwrap_or(log::Level::Error),
@@ -872,10 +922,12 @@ impl SettingsBuilder {
.bundle_settings
.external_bin
.as_ref()
.map(|bins| external_binaries(bins, &target)),
.map(|bins| external_binaries(bins, &target, &target_platform)),
..self.bundle_settings
},
target_platform,
target,
no_sign: self.no_sign,
})
}
}
@@ -901,6 +953,11 @@ impl Settings {
&self.target
}
/// Returns the [`TargetPlatform`].
pub fn target_platform(&self) -> &TargetPlatform {
&self.target_platform
}
/// Returns the architecture for the binary being bundled (e.g. "arm", "x86" or "x86_64").
pub fn binary_arch(&self) -> Arch {
if self.target.starts_with("x86_64") {
@@ -929,7 +986,6 @@ impl Settings {
.iter()
.find(|bin| bin.main)
.context("failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file")
.map_err(Into::into)
}
/// Returns the file name of the binary being bundled.
@@ -939,7 +995,6 @@ impl Settings {
.iter_mut()
.find(|bin| bin.main)
.context("failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file")
.map_err(Into::into)
}
/// Returns the file name of the binary being bundled.
@@ -950,24 +1005,27 @@ impl Settings {
.find(|bin| bin.main)
.context("failed to find main binary, make sure you have a `package > default-run` in the Cargo.toml file")
.map(|b| b.name())
.map_err(Into::into)
}
/// Returns the path to the specified binary.
pub fn binary_path(&self, binary: &BundleBinary) -> PathBuf {
let target_os = self
.target()
.split('-')
.nth(2)
.unwrap_or(std::env::consts::OS);
let target_os = self.target_platform();
let path = self.project_out_directory.join(binary.name());
let mut path = self.project_out_directory.join(binary.name());
if target_os == "windows" {
path.with_extension("exe")
} else {
path
}
if matches!(target_os, TargetPlatform::Windows) {
// Append the `.exe` extension without overriding the existing extensions
let extension = if let Some(extension) = path.extension() {
let mut extension = extension.to_os_string();
extension.push(".exe");
extension
} else {
"exe".into()
};
path.set_extension(extension);
};
path
}
/// Returns the list of binaries to bundle.
@@ -985,18 +1043,13 @@ impl Settings {
///
/// Fails if the host/target's native package type is not supported.
pub fn package_types(&self) -> crate::Result<Vec<PackageType>> {
let target_os = self
.target
.split('-')
.nth(2)
.unwrap_or(std::env::consts::OS)
.replace("darwin", "macos");
let target_os = self.target_platform();
let platform_types = match target_os.as_str() {
"macos" => vec![PackageType::MacOsBundle, PackageType::Dmg],
"ios" => vec![PackageType::IosBundle],
"linux" => vec![PackageType::Deb, PackageType::Rpm, PackageType::AppImage],
"windows" => vec![PackageType::WindowsMsi, PackageType::Nsis],
let platform_types = match target_os {
TargetPlatform::MacOS => vec![PackageType::MacOsBundle, PackageType::Dmg],
TargetPlatform::Ios => vec![PackageType::IosBundle],
TargetPlatform::Linux => vec![PackageType::Deb, PackageType::Rpm, PackageType::AppImage],
TargetPlatform::Windows => vec![PackageType::WindowsMsi, PackageType::Nsis],
os => {
return Err(crate::Error::GenericError(format!(
"Native {os} bundles not yet supported."
@@ -1220,4 +1273,14 @@ impl Settings {
pub fn updater(&self) -> Option<&UpdaterSettings> {
self.bundle_settings.updater.as_ref()
}
/// Whether to skip signing.
pub fn no_sign(&self) -> bool {
self.no_sign
}
/// Set whether to skip signing.
pub fn set_no_sign(&mut self, no_sign: bool) {
self.no_sign = no_sign;
}
}

View File

@@ -11,10 +11,11 @@ use crate::{
},
Bundle,
},
error::{Context, ErrorExt},
utils::fs_utils,
Settings,
};
use tauri_utils::display_path;
use tauri_utils::{display_path, platform::Target as TargetPlatform};
use std::{
fs::{self, File},
@@ -22,19 +23,13 @@ use std::{
path::{Path, PathBuf},
};
use anyhow::Context;
use zip::write::SimpleFileOptions;
// Build update
pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<Vec<PathBuf>> {
let target_os = settings
.target()
.split('-')
.nth(2)
.unwrap_or(std::env::consts::OS)
.replace("darwin", "macos");
let target_os = settings.target_platform();
if target_os == "windows" {
if matches!(target_os, TargetPlatform::Windows) {
return bundle_update_windows(settings, bundles);
}
@@ -193,7 +188,7 @@ fn bundle_update_windows(settings: &Settings, bundles: &[Bundle]) -> crate::Resu
p.push(c);
(p, b)
});
let archived_path = archived_path.with_extension(format!("{}.zip", bundle_name));
let archived_path = archived_path.with_extension(format!("{bundle_name}.zip"));
log::info!(action = "Bundling"; "{}", display_path(&archived_path));
@@ -221,7 +216,9 @@ pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
.unix_permissions(0o755);
zip.start_file(file_name.to_string_lossy(), options)?;
let mut f = File::open(src_file)?;
let mut f =
File::open(src_file).fs_context("failed to open updater ZIP file", src_file.to_path_buf())?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
zip.write_all(&buffer)?;

View File

@@ -5,6 +5,7 @@
#[cfg(target_os = "windows")]
pub mod msi;
pub mod nsis;
pub mod sign;
@@ -13,3 +14,5 @@ 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

@@ -7,20 +7,20 @@ use crate::{
bundle::{
settings::{Arch, Settings},
windows::{
sign::try_sign,
sign::{should_sign, try_sign},
util::{
download_webview2_bootstrapper, download_webview2_offline_installer,
WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
},
},
},
error::Context,
utils::{
fs_utils::copy_file,
http_utils::{download_and_verify, extract_zip, HashAlgorithm},
CommandExt,
},
};
use anyhow::{bail, Context};
use handlebars::{html_escape, to_json, Handlebars};
use regex::Regex;
use serde::{Deserialize, Serialize};
@@ -177,7 +177,7 @@ impl ResourceDirectory {
directories.push_str(wix_string.as_str());
}
let wix_string = if self.name.is_empty() {
format!("{}{}", files, directories)
format!("{files}{directories}")
} else {
format!(
r#"<Directory Id="I{id}" Name="{name}">{files}{directories}</Directory>"#,
@@ -221,8 +221,7 @@ fn app_installer_output_path(
Arch::AArch64 => "arm64",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
"Unsupported architecture: {target:?}"
)))
}
};
@@ -280,37 +279,40 @@ fn clear_env_for_wix(cmd: &mut Command) {
}
}
fn validate_wix_version(version_str: &str) -> anyhow::Result<()> {
fn validate_wix_version(version_str: &str) -> crate::Result<()> {
let components = version_str
.split('.')
.flat_map(|c| c.parse::<u64>().ok())
.collect::<Vec<_>>();
anyhow::ensure!(
components.len() >= 3,
"app wix version should be in the format major.minor.patch.build (build is optional)"
);
if components.len() < 3 {
crate::error::bail!(
"app wix version should be in the format major.minor.patch.build (build is optional)"
);
}
if components[0] > 255 {
bail!("app version major number cannot be greater than 255");
crate::error::bail!("app version major number cannot be greater than 255");
}
if components[1] > 255 {
bail!("app version minor number cannot be greater than 255");
crate::error::bail!("app version minor number cannot be greater than 255");
}
if components[2] > 65535 {
bail!("app version patch number cannot be greater than 65535");
crate::error::bail!("app version patch number cannot be greater than 65535");
}
if components.len() == 4 && components[3] > 65535 {
bail!("app version build number cannot be greater than 65535");
crate::error::bail!("app version build number cannot be greater than 65535");
}
Ok(())
}
// WiX requires versions to be numeric only in a `major.minor.patch.build` format
fn convert_version(version_str: &str) -> anyhow::Result<String> {
let version = semver::Version::parse(version_str).context("invalid app version")?;
fn convert_version(version_str: &str) -> crate::Result<String> {
let version = semver::Version::parse(version_str)
.map_err(Into::into)
.context("invalid app version")?;
if !version.build.is_empty() {
let build = version.build.parse::<u64>();
if build.map(|b| b <= 65535).unwrap_or_default() {
@@ -319,7 +321,7 @@ fn convert_version(version_str: &str) -> anyhow::Result<String> {
version.major, version.minor, version.patch, version.build
));
} else {
bail!("optional build metadata in app version must be numeric-only and cannot be greater than 65535 for msi target");
crate::error::bail!("optional build metadata in app version must be numeric-only and cannot be greater than 65535 for msi target");
}
}
@@ -331,7 +333,7 @@ fn convert_version(version_str: &str) -> anyhow::Result<String> {
version.major, version.minor, version.patch, version.pre
));
} else {
bail!("optional pre-release identifier in app version must be numeric-only and cannot be greater than 65535 for msi target");
crate::error::bail!("optional pre-release identifier in app version must be numeric-only and cannot be greater than 65535 for msi target");
}
}
@@ -352,8 +354,7 @@ fn run_candle(
Arch::AArch64 => "arm64",
target => {
return Err(crate::Error::ArchError(format!(
"unsupported architecture: {:?}",
target
"unsupported architecture: {target:?}"
)))
}
};
@@ -389,11 +390,7 @@ fn run_candle(
cmd.arg(ext);
}
clear_env_for_wix(&mut cmd);
cmd
.args(&args)
.current_dir(cwd)
.output_ok()
.context("error running candle.exe")?;
cmd.args(&args).current_dir(cwd).output_ok()?;
Ok(())
}
@@ -418,11 +415,7 @@ fn run_light(
cmd.arg(ext);
}
clear_env_for_wix(&mut cmd);
cmd
.args(&args)
.current_dir(build_path)
.output_ok()
.context("error running light.exe")?;
cmd.args(&args).current_dir(build_path).output_ok()?;
Ok(())
}
@@ -443,8 +436,7 @@ pub fn build_wix_app_installer(
Arch::AArch64 => "arm64",
target => {
return Err(crate::Error::ArchError(format!(
"unsupported architecture: {:?}",
target
"unsupported architecture: {target:?}"
)))
}
};
@@ -473,10 +465,9 @@ pub fn build_wix_app_installer(
fs::create_dir_all(&output_path)?;
// when we're performing code signing, we'll sign some WiX DLLs, so we make a local copy
let wix_toolset_path = if settings.can_sign() {
let wix_toolset_path = if settings.windows().can_sign() {
let wix_path = output_path.join("wix");
crate::utils::fs_utils::copy_dir(wix_toolset_path, &wix_path)
.context("failed to copy wix directory")?;
crate::utils::fs_utils::copy_dir(wix_toolset_path, &wix_path)?;
wix_path
} else {
wix_toolset_path.to_path_buf()
@@ -706,7 +697,9 @@ pub fn build_wix_app_installer(
.iter()
.flat_map(|p| &p.schemes)
.collect::<Vec<_>>();
data.insert("deep_link_protocols", to_json(schemes));
if !schemes.is_empty() {
data.insert("deep_link_protocols", to_json(schemes));
}
}
if let Some(path) = custom_template_path {
@@ -774,7 +767,7 @@ pub fn build_wix_app_installer(
let mut extensions = Vec::new();
for cap in extension_regex.captures_iter(&fragment) {
let path = wix_toolset_path.join(format!("Wix{}.dll", &cap[1]));
if settings.can_sign() {
if settings.windows().can_sign() {
try_sign(&path, settings)?;
}
extensions.push(path);
@@ -788,7 +781,7 @@ pub fn build_wix_app_installer(
fragment_extensions.insert(wix_toolset_path.join("WixUtilExtension.dll"));
// sign default extensions
if settings.can_sign() {
if settings.windows().can_sign() {
for path in &fragment_extensions {
try_sign(path, settings)?;
}
@@ -845,7 +838,7 @@ pub fn build_wix_app_installer(
let locale_contents = locale_contents.replace(
"</WixLocalization>",
&format!("{}</WixLocalization>", unset_locale_strings),
&format!("{unset_locale_strings}</WixLocalization>"),
);
let locale_path = output_path.join("locale.wxl");
{
@@ -882,7 +875,7 @@ pub fn build_wix_app_installer(
)?;
fs::rename(&msi_output_path, &msi_path)?;
if settings.can_sign() {
if settings.windows().can_sign() {
try_sign(&msi_path, settings)?;
}
@@ -991,7 +984,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
}
added_resources.push(resource_path.clone());
if settings.can_sign() {
if settings.windows().can_sign() && should_sign(&resource_path)? {
try_sign(&resource_path, settings)?;
}
@@ -1079,7 +1072,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
.to_string_lossy()
.into_owned();
if !added_resources.iter().any(|r| r.ends_with(&relative_path)) {
if settings.can_sign() {
if settings.windows().can_sign() {
try_sign(resource_path, settings)?;
}

View File

@@ -95,7 +95,7 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
; Handle install mode, `perUser`, `perMachine` or `both`
!if "${INSTALLMODE}" == "perMachine"
RequestExecutionLevel highest
RequestExecutionLevel admin
!endif
!if "${INSTALLMODE}" == "currentUser"
@@ -618,7 +618,7 @@ Section Install
!insertmacro NSIS_HOOK_PREINSTALL
!endif
!insertmacro CheckIfAppIsRunning
!insertmacro CheckIfAppIsRunning "${MAINBINARYNAME}.exe" "${PRODUCTNAME}"
; Copy main executable
File "${MAINBINARYSRCPATH}"
@@ -755,7 +755,7 @@ Section Uninstall
!insertmacro NSIS_HOOK_PREUNINSTALL
!endif
!insertmacro CheckIfAppIsRunning
!insertmacro CheckIfAppIsRunning "${MAINBINARYNAME}.exe" "${PRODUCTNAME}"
; Delete the app directory and its content from disk
; Copy main executable

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_ARABIC} "إضافة أو إزالة المكونات"
LangString alreadyInstalled ${LANG_ARABIC} "التطبيق مثبت بالفعل"
LangString alreadyInstalledLong ${LANG_ARABIC} "${PRODUCTNAME} ${VERSION} مثبت بالفعل. قم باختيار العملية التى تريدها ثم اضغط على التالى."
LangString appRunning ${LANG_ARABIC} "${PRODUCTNAME} مازال يعمل! من فضلك، قم بإغلاق التطبيق أولاً ثم حاول مرة أخرى."
LangString appRunningOkKill ${LANG_ARABIC} "${PRODUCTNAME} مازال يعمل!$\nاضغط OK لإغلاقه"
LangString appRunning ${LANG_ARABIC} "{{product_name}} مازال يعمل! من فضلك، قم بإغلاق التطبيق أولاً ثم حاول مرة أخرى."
LangString appRunningOkKill ${LANG_ARABIC} "{{product_name}} مازال يعمل!$\nاضغط OK لإغلاقه"
LangString chooseMaintenanceOption ${LANG_ARABIC} "قم باختيار نوع الصيانة التى تريدها."
LangString choowHowToInstall ${LANG_ARABIC} "قم باختيار طريقة تنصيب ${PRODUCTNAME}."
LangString createDesktop ${LANG_ARABIC} "اضف اختصار على سطح المكتب"
LangString dontUninstall ${LANG_ARABIC} "عدم إزالة"
LangString dontUninstallDowngrade ${LANG_ARABIC} "عدم إزالة (التخفيض بدون إزالة غير مسموح لهذا المثبت)"
LangString failedToKillApp ${LANG_ARABIC} "فشل فى غلف ${PRODUCTNAME}. من فضلك، قم بإغلاق التطبيق أولاً ثم حاول مرة أخرى."
LangString failedToKillApp ${LANG_ARABIC} "فشل فى غلف {{product_name}}. من فضلك، قم بإغلاق التطبيق أولاً ثم حاول مرة أخرى."
LangString installingWebview2 ${LANG_ARABIC} "تنصيب WebView2..."
LangString newerVersionInstalled ${LANG_ARABIC} "يوجد نسخة جديدة من ${PRODUCTNAME} مثبتة بالغعل! لا ينصح بتنصيب نسخة اقدم من النسخة الحالية. اذا مازلت ترغب فى تنصيب النسخة الأقدم، فينصح بإزالة النسخة الحالية أولاً. قم باختيار العملية التى تريدها ثم اضغط على التالى للاستمرار."
LangString older ${LANG_ARABIC} "أقدم"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_BULGARIAN} "Добавяне/Преинсталиране на компоненти"
LangString alreadyInstalled ${LANG_BULGARIAN} "Вече инсталиран"
LangString alreadyInstalledLong ${LANG_BULGARIAN} "${PRODUCTNAME} ${VERSION} е вече е инсталиран. Изберете операцията, която искате да извършите и натиснете Напред, за да продължите."
LangString appRunning ${LANG_BULGARIAN} "${PRODUCTNAME} е отворен! Моля, затворете го първо и опитайте отново."
LangString appRunningOkKill ${LANG_BULGARIAN} "${PRODUCTNAME} е отворен!$\nНатиснете ОК, за да го затворите."
LangString appRunning ${LANG_BULGARIAN} "{{product_name}} е отворен! Моля, затворете го първо и опитайте отново."
LangString appRunningOkKill ${LANG_BULGARIAN} "{{product_name}} е отворен!$\nНатиснете ОК, за да го затворите."
LangString chooseMaintenanceOption ${LANG_BULGARIAN} "Изберете опция за поддръжка."
LangString choowHowToInstall ${LANG_BULGARIAN} "Изберете как искате да инсталирате ${PRODUCTNAME}."
LangString createDesktop ${LANG_BULGARIAN} "Създайте пряк път на работния плот"
LangString dontUninstall ${LANG_BULGARIAN} "Не деинсталирайте"
LangString dontUninstallDowngrade ${LANG_BULGARIAN} "Не деинсталирайте (Понижаването без деинсталация е забранено за този инсталатор)"
LangString failedToKillApp ${LANG_BULGARIAN} "Неуспешно прекратяване на ${PRODUCTNAME}. Моля, затворете го първо и опитайте отново."
LangString failedToKillApp ${LANG_BULGARIAN} "Неуспешно прекратяване на {{product_name}}. Моля, затворете го първо и опитайте отново."
LangString installingWebview2 ${LANG_BULGARIAN} "Инсталиране на WebView2..."
LangString newerVersionInstalled ${LANG_BULGARIAN} "Вече е инсталирана по-нова версия на ${PRODUCTNAME}! Не се препоръчва да инсталирате по-стара версия. Ако наистина желаете да инсталирате тази по-стара версия, по-добре е да деинсталирате текущата версия първо. Изберете операцията, която искате да извършите и натиснете Напред, за да продължите."
LangString older ${LANG_BULGARIAN} "по-стара"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_DUTCH} "(Her)installeer componenten"
LangString alreadyInstalled ${LANG_DUTCH} "Al geïnstalleerd"
LangString alreadyInstalledLong ${LANG_DUTCH} "${PRODUCTNAME} ${VERSION} is al geïnstalleerd. Kies een van de volgende opties en klik op Volgende om door te gaan."
LangString appRunning ${LANG_DUTCH} "${PRODUCTNAME} is geopend! Sluit het programma eerst en probeer het dan opnieuw."
LangString appRunningOkKill ${LANG_DUTCH} "${PRODUCTNAME} is geopend!$\nKlik op OK om het te stoppen."
LangString appRunning ${LANG_DUTCH} "{{product_name}} is geopend! Sluit het programma eerst en probeer het dan opnieuw."
LangString appRunningOkKill ${LANG_DUTCH} "{{product_name}} is geopend!$\nKlik op OK om het te stoppen."
LangString chooseMaintenanceOption ${LANG_DUTCH} "Kies de onderhoudsoptie die u wilt uitvoeren."
LangString choowHowToInstall ${LANG_DUTCH} "Kies hoe u ${PRODUCTNAME} wilt installeren."
LangString createDesktop ${LANG_DUTCH} "Maak een snelkoppeling aan op het bureaublad"
LangString dontUninstall ${LANG_DUTCH} "Deïnstalleer niet"
LangString dontUninstallDowngrade ${LANG_DUTCH} "Deïnstalleer niet (Downgraden zonder deïnstalleren is uitgeschakeld voor deze installer)"
LangString failedToKillApp ${LANG_DUTCH} "Het is niet gelukt ${PRODUCTNAME} te stoppen. Sluit het eerst zelf en probeer het dan nog een keer"
LangString failedToKillApp ${LANG_DUTCH} "Het is niet gelukt {{product_name}} te stoppen. Sluit het eerst zelf en probeer het dan nog een keer"
LangString installingWebview2 ${LANG_DUTCH} "WebView2 wordt geïnstalleerd..."
LangString newerVersionInstalled ${LANG_DUTCH} "Een nieuwere versie van ${PRODUCTNAME} is al geïnstalleerd! Het word niet aangeraden om een oudere versie te installeren. Als u echt deze oudere versie wilt installeren, kunt u beter de huidige versie eerst deïnstalleren. Kies een van de volgende opties en klik op Volgende om door te gaan."
LangString older ${LANG_DUTCH} "oudere"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_ENGLISH} "Add/Reinstall components"
LangString alreadyInstalled ${LANG_ENGLISH} "Already Installed"
LangString alreadyInstalledLong ${LANG_ENGLISH} "${PRODUCTNAME} ${VERSION} is already installed. Select the operation you want to perform and click Next to continue."
LangString appRunning ${LANG_ENGLISH} "${PRODUCTNAME} is running! Please close it first then try again."
LangString appRunningOkKill ${LANG_ENGLISH} "${PRODUCTNAME} is running!$\nClick OK to kill it"
LangString appRunning ${LANG_ENGLISH} "{{product_name}} is running! Please close it first then try again."
LangString appRunningOkKill ${LANG_ENGLISH} "{{product_name}} is running!$\nClick OK to kill it"
LangString chooseMaintenanceOption ${LANG_ENGLISH} "Choose the maintenance option to perform."
LangString choowHowToInstall ${LANG_ENGLISH} "Choose how you want to install ${PRODUCTNAME}."
LangString createDesktop ${LANG_ENGLISH} "Create desktop shortcut"
LangString dontUninstall ${LANG_ENGLISH} "Do not uninstall"
LangString dontUninstallDowngrade ${LANG_ENGLISH} "Do not uninstall (Downgrading without uninstall is disabled for this installer)"
LangString failedToKillApp ${LANG_ENGLISH} "Failed to kill ${PRODUCTNAME}. Please close it first then try again"
LangString failedToKillApp ${LANG_ENGLISH} "Failed to kill {{product_name}}. Please close it first then try again"
LangString installingWebview2 ${LANG_ENGLISH} "Installing WebView2..."
LangString newerVersionInstalled ${LANG_ENGLISH} "A newer version of ${PRODUCTNAME} is already installed! It is not recommended that you install an older version. If you really want to install this older version, it's better to uninstall the current version first. Select the operation you want to perform and click Next to continue."
LangString older ${LANG_ENGLISH} "older"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_FRENCH} "Ajouter/Réinstaller un composant."
LangString alreadyInstalled ${LANG_FRENCH} "Déja installé."
LangString alreadyInstalledLong ${LANG_FRENCH} "${PRODUCTNAME} ${VERSION} est déja installé. Sélectionnez l'opération que vous souhaitez effectuer, puis cliquez sur Suivant pour continuer."
LangString appRunning ${LANG_FRENCH} "${PRODUCTNAME} est en cours d'exécution. Veuillez fermer l'application avant de réessayer."
LangString appRunningOkKill ${LANG_FRENCH} "${PRODUCTNAME} est en cours d'exécution.$\nCliquez sur OK pour fermer l'application."
LangString appRunning ${LANG_FRENCH} "{{product_name}} est en cours d'exécution. Veuillez fermer l'application avant de réessayer."
LangString appRunningOkKill ${LANG_FRENCH} "{{product_name}} est en cours d'exécution.$\nCliquez sur OK pour fermer l'application."
LangString chooseMaintenanceOption ${LANG_FRENCH} "Veuillez choisir l'option de maintenance à effectuer."
LangString choowHowToInstall ${LANG_FRENCH} "Veuillez choisir l'emplacement d'installation de ${PRODUCTNAME}."
LangString createDesktop ${LANG_FRENCH} "Créer un raccourci sur le bureau."
LangString dontUninstall ${LANG_FRENCH} "Ne pas désinstaller"
LangString dontUninstallDowngrade ${LANG_FRENCH} "Ne pas désinstaller (revenir à une ancienne version sans désinstallation est désactivé pour cet installateur)"
LangString failedToKillApp ${LANG_FRENCH} "La fermeture de ${PRODUCTNAME} a échoué. Veuillez fermer l'application et réessayer."
LangString failedToKillApp ${LANG_FRENCH} "La fermeture de {{product_name}} a échoué. Veuillez fermer l'application et réessayer."
LangString installingWebview2 ${LANG_FRENCH} "Installation de WebView2..."
LangString newerVersionInstalled ${LANG_FRENCH} "Une version plus récente de ${PRODUCTNAME} est déja installée. Il n'est pas recommandé d'installer une ancienne version. Si vous souhaitez installer cette ancienne version, il est conseillé de désinstaller la version courante en premier. Veuillez sélectionner l'opération que vous souhaitez effectuer, puis cliquez sur Suivant pour continer."
LangString older ${LANG_FRENCH} "ancien"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_GERMAN} "Komponenten hinzufügen/neu installieren"
LangString alreadyInstalled ${LANG_GERMAN} "Bereits installiert"
LangString alreadyInstalledLong ${LANG_GERMAN} "${PRODUCTNAME} ${VERSION} ist bereits installiert. Wählen Sie den gewünschten Vorgang aus und klicken Sie auf Weiter, um fortzufahren."
LangString appRunning ${LANG_GERMAN} "${PRODUCTNAME} wird ausgeführt! Bitte schließen Sie es zuerst und versuchen Sie es dann erneut."
LangString appRunningOkKill ${LANG_GERMAN} "${PRODUCTNAME} läuft! $\nKlicken Sie auf OK, um es zu beenden"
LangString appRunning ${LANG_GERMAN} "{{product_name}} wird ausgeführt! Bitte schließen Sie es zuerst und versuchen Sie es dann erneut."
LangString appRunningOkKill ${LANG_GERMAN} "{{product_name}} läuft! $\nKlicken Sie auf OK, um es zu beenden"
LangString chooseMaintenanceOption ${LANG_GERMAN} "Wählen Sie die auszuführende Wartungsoption."
LangString choowHowToInstall ${LANG_GERMAN} "Wählen Sie, wie Sie ${PRODUCTNAME} installieren möchten."
LangString createDesktop ${LANG_GERMAN} "Desktop-Verknüpfung erstellen"
LangString dontUninstall ${LANG_GERMAN} "Nicht deinstallieren"
LangString dontUninstallDowngrade ${LANG_GERMAN} "Nicht deinstallieren (Downgrading ohne Deinstallation ist für dieses Installationsprogramm deaktiviert)"
LangString failedToKillApp ${LANG_GERMAN} "Failed to kill ${PRODUCTNAME}. Bitte schließen Sie es zuerst und versuchen Sie es dann erneut"
LangString failedToKillApp ${LANG_GERMAN} "Failed to kill {{product_name}}. Bitte schließen Sie es zuerst und versuchen Sie es dann erneut"
LangString installingWebview2 ${LANG_GERMAN} "Installiere WebView2..."
LangString newerVersionInstalled ${LANG_GERMAN} "Eine neuere Version von ${PRODUCTNAME} ist bereits installiert! Es wird nicht empfohlen, eine ältere Version zu installieren. Wenn Sie diese ältere Version wirklich installieren wollen, ist es besser, die aktuelle Version zuerst zu deinstallieren. Wählen Sie den gewünschten Vorgang aus und klicken Sie auf Weiter, um fortzufahren."
LangString älter ${LANG_GERMAN} "älter"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_HEBREW} "הוסף או התקן מחדש"
LangString alreadyInstalled ${LANG_HEBREW} "כבר מותקן"
LangString alreadyInstalledLong ${LANG_HEBREW} "${PRODUCTNAME} ${VERSION} כבר מותקן. בחר את הפעולה שברצונך לבצע ולחץ על הבא כדי להמשיך."
LangString appRunning ${LANG_HEBREW} "${PRODUCTNAME} פועל! נא לסגור אותו ולנסות שוב."
LangString appRunningOkKill ${LANG_HEBREW} "${PRODUCTNAME} פועל!$\nלחץ אישור כדי לסגור אותו."
LangString appRunning ${LANG_HEBREW} "{{product_name}} פועל! נא לסגור אותו ולנסות שוב."
LangString appRunningOkKill ${LANG_HEBREW} "{{product_name}} פועל!$\nלחץ אישור כדי לסגור אותו."
LangString chooseMaintenanceOption ${LANG_HEBREW} "בחר את פעולת התחזוקה לביצוע"
LangString choowHowToInstall ${LANG_HEBREW} "בחר איך תרצה להתקין את ${PRODUCTNAME}."
LangString createDesktop ${LANG_HEBREW} "צור קיצור דרך בשולחן העבודה"
LangString dontUninstall ${LANG_HEBREW} "אל תסיר"
LangString dontUninstallDowngrade ${LANG_HEBREW} "אל תסיר (התקנת גרסה ישנה ללא הסרת הגרסה הנוכחית מושעית עבור התקנה זו)"
LangString failedToKillApp ${LANG_HEBREW} "עצירת ${PRODUCTNAME} נכשלה. נא לסגור את היישום ולנסות שוב."
LangString failedToKillApp ${LANG_HEBREW} "עצירת {{product_name}} נכשלה. נא לסגור את היישום ולנסות שוב."
LangString installingWebview2 ${LANG_HEBREW} "מתקין את WebView2..."
LangString newerVersionInstalled ${LANG_HEBREW} "גרסה חדשה יותר של ${PRODUCTNAME} כבר מותקנת! לא מומלץ להתקין גרסה ישנה. אם בכל זאת תרצה להתקין את הגרסה הזו, מומלץ קודם להסיר את הגרסה הנוכחית. בחר את הפעולה שברצונך לבצע ולחץ הבא להמשך."
LangString older ${LANG_HEBREW} "ישנה"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_ITALIAN} "Aggiungi/Reinstalla componenti"
LangString alreadyInstalled ${LANG_ITALIAN} "Già installato"
LangString alreadyInstalledLong ${LANG_ITALIAN} "${PRODUCTNAME} ${VERSION} è già installato. Seleziona l'operazione che vuoi eseguire e clicca Avanti per continuare."
LangString appRunning ${LANG_ITALIAN} "${PRODUCTNAME} è in esecuzione! Chiudi e poi riprova."
LangString appRunningOkKill ${LANG_ITALIAN} "${PRODUCTNAME} è in esecuzione!$\nSeleziona OK per chiuderlo"
LangString appRunning ${LANG_ITALIAN} "{{product_name}} è in esecuzione! Chiudi e poi riprova."
LangString appRunningOkKill ${LANG_ITALIAN} "{{product_name}} è in esecuzione!$\nSeleziona OK per chiuderlo"
LangString chooseMaintenanceOption ${LANG_ITALIAN} "Seleziona l'operazione di manutenzione da eseguire."
LangString choowHowToInstall ${LANG_ITALIAN} "Seleziona come vuoi installare ${PRODUCTNAME}."
LangString createDesktop ${LANG_ITALIAN} "Crea scorciatoia sul Desktop"
LangString dontUninstall ${LANG_ITALIAN} "Non disinstallare"
LangString dontUninstallDowngrade ${LANG_ITALIAN} "Non disinstallare (Il downgrade senza la disinstallazione è disabilitato per questo installer)"
LangString failedToKillApp ${LANG_ITALIAN} "Impossibile chiudere ${PRODUCTNAME}. Chiudi e poi riprova"
LangString failedToKillApp ${LANG_ITALIAN} "Impossibile chiudere {{product_name}}. Chiudi e poi riprova"
LangString installingWebview2 ${LANG_ITALIAN} "Installando WebView2..."
LangString newerVersionInstalled ${LANG_ITALIAN} "Una versione più recente di ${PRODUCTNAME} è già installata! Non è consigliato installare una versione più vecchia. Se vuoi comunque procedere, è meglio prima disinstallare la versione corrente. Seleziona l'operazione che vuoi eseguire e clicca Avanti per continuare."
LangString older ${LANG_ITALIAN} "più vecchia"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_JAPANESE} "コンポーネントの追加・再インストール"
LangString alreadyInstalled ${LANG_JAPANESE} "既にインストールされています"
LangString alreadyInstalledLong ${LANG_JAPANESE} "${PRODUCTNAME} ${VERSION} は既にインストールされています。実行したい操作を選択し、「次へ」をクリックして続行します。"
LangString appRunning ${LANG_JAPANESE} "${PRODUCTNAME} は動作中です。動作中のプログラムを終了し、もう一度やり直してください。"
LangString appRunningOkKill ${LANG_JAPANESE} "${PRODUCTNAME} は動作中です。$\n「OK」を押すと動作中のプログラムを終了します。"
LangString appRunning ${LANG_JAPANESE} "{{product_name}} は動作中です。動作中のプログラムを終了し、もう一度やり直してください。"
LangString appRunningOkKill ${LANG_JAPANESE} "{{product_name}} は動作中です。$\n「OK」を押すと動作中のプログラムを終了します。"
LangString chooseMaintenanceOption ${LANG_JAPANESE} "メンテナンスオプションを選択して実行します。"
LangString choowHowToInstall ${LANG_JAPANESE} "${PRODUCTNAME} のインストール方法を選択してください。"
LangString createDesktop ${LANG_JAPANESE} "デスクトップショートカットを作成する"
LangString dontUninstall ${LANG_JAPANESE} "アンインストールしない"
LangString dontUninstallDowngrade ${LANG_JAPANESE} "アンインストールしない (このインストーラーでは、アンインストールをせずにダウングレードすることはできません)"
LangString failedToKillApp ${LANG_JAPANESE} "${PRODUCTNAME} の終了に失敗しました。動作中のプログラムを終了し、もう一度やり直してください。"
LangString failedToKillApp ${LANG_JAPANESE} "{{product_name}} の終了に失敗しました。動作中のプログラムを終了し、もう一度やり直してください。"
LangString installingWebview2 ${LANG_JAPANESE} "WebView2 をインストール中です..."
LangString newerVersionInstalled ${LANG_JAPANESE} "既に新しいバージョンの ${PRODUCTNAME} がインストールされています。古いバージョンをインストールすることは推奨されません。どうしてもこの旧バージョンをインストールしたい場合は、先に現行バージョンをアンインストールしておく方がよいでしょう。実行したい操作を選択し、「次へ」をクリックして続行します。"
LangString older ${LANG_JAPANESE} ""

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_KOREAN} "컴포넌트 추가 및 재설치"
LangString alreadyInstalled ${LANG_KOREAN} "이미 설치되어 있습니다"
LangString alreadyInstalledLong ${LANG_KOREAN} "${PRODUCTNAME} ${VERSION}이(가) 이미 설치되어 있습니다. 수행하고자 하는 작업을 선택하고 '다음'을 클릭하여 계속합니다."
LangString appRunning ${LANG_KOREAN} "${PRODUCTNAME}이(가) 실행 중입니다! 먼저 닫은 후 다시 시도하세요."
LangString appRunningOkKill ${LANG_KOREAN} "${PRODUCTNAME}이(가) 실행 중입니다!$\n'OK'를 누르면 실행 중인 프로그램을 종료합니다."
LangString appRunning ${LANG_KOREAN} "{{product_name}}이(가) 실행 중입니다! 먼저 닫은 후 다시 시도하세요."
LangString appRunningOkKill ${LANG_KOREAN} "{{product_name}}이(가) 실행 중입니다!$\n'OK'를 누르면 실행 중인 프로그램을 종료합니다."
LangString chooseMaintenanceOption ${LANG_KOREAN} "수행하려는 관리 옵션을 선택합니다."
LangString choowHowToInstall ${LANG_KOREAN} "${PRODUCTNAME}의 설치 방법을 선택하세요.."
LangString createDesktop ${LANG_KOREAN} "바탕화면 바로가기 만들기"
LangString dontUninstall ${LANG_KOREAN} "제거하지 않기"
LangString dontUninstallDowngrade ${LANG_KOREAN} "제거하지 않기 (이 설치 프로그램에서는 제거하지 않고 다운그레이드할 수 없습니다.)"
LangString failedToKillApp ${LANG_KOREAN} "${PRODUCTNAME}을(를) 종료하지 못했습니다. 먼저 닫은 후 다시 시도하세요."
LangString failedToKillApp ${LANG_KOREAN} "{{product_name}}을(를) 종료하지 못했습니다. 먼저 닫은 후 다시 시도하세요."
LangString installingWebview2 ${LANG_KOREAN} "WebView2를 설치하는 중입니다..."
LangString newerVersionInstalled ${LANG_KOREAN} "${PRODUCTNAME}의 최신 버전이 이미 설치되어 있습니다! 이전 버전을 설치하지 않는 것이 좋습니다. 이 이전 버전을 꼭 설치하려면 먼저 현재 버전을 제거하는 것이 좋습니다. 수행하려는 작업을 선택하고 '다음'을 클릭하여 계속합니다."
LangString older ${LANG_KOREAN} ""

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_PERSIAN} "اضافه کردن/نصب مجدد کامپونتت"
LangString alreadyInstalled ${LANG_PERSIAN} "قبلا نصب شده است"
LangString alreadyInstalledLong ${LANG_PERSIAN} "${PRODUCTNAME} ${VERSION} قبلا نصب شده است. عملیات مدنظر را انتخاب کنید و بروی بعدی کلیک کنید."
LangString appRunning ${LANG_PERSIAN} "${PRODUCTNAME} در حال اجر می باشد ! لطفا اول الان را ببندید و دوباره تلاش کنید"
LangString appRunningOkKill ${LANG_PERSIAN} "${PRODUCTNAME} در حال اجرا می باشد!$\nبرای از بین بردن اوکی را انتخاب کنید"
LangString appRunning ${LANG_PERSIAN} "{{product_name}} در حال اجر می باشد ! لطفا اول الان را ببندید و دوباره تلاش کنید"
LangString appRunningOkKill ${LANG_PERSIAN} "{{product_name}} در حال اجرا می باشد!$\nبرای از بین بردن اوکی را انتخاب کنید"
LangString chooseMaintenanceOption ${LANG_PERSIAN} "عملیات نگهداری مدنظر را برای اجرا انتخاب کنید"
LangString choowHowToInstall ${LANG_PERSIAN} "نحوه نصب ${PRODUCTNAME} را انتخاب کنید"
LangString createDesktop ${LANG_PERSIAN} "ایجاد میانبر دسکتاپ"
LangString dontUninstall ${LANG_PERSIAN} "حذف نکنید"
LangString dontUninstallDowngrade ${LANG_PERSIAN} "حذف نکنید (تنزل ورژن بدون حذف برای نصب کننده غیرفعال است)"
LangString failedToKillApp ${LANG_PERSIAN} "${PRODUCTNAME} قابل کشته شدن نیست. اول آن را ببندید و دوباره تلاش کنید"
LangString failedToKillApp ${LANG_PERSIAN} "{{product_name}} قابل کشته شدن نیست. اول آن را ببندید و دوباره تلاش کنید"
LangString installingWebview2 ${LANG_PERSIAN} "در حال نصب WebView2 ..."
LangString newerVersionInstalled ${LANG_PERSIAN} "ورژن جدید ${PRODUCTNAME} قبلا نصب شده است! نصب ورژن قدیمی تر به هیچ عنوان پیشنهاد نمی شود. اگر از این بابت اطمینان دارید , بهتر است ورژن فعلی را حذف کنید. عملیات مدنظر را انتخاب کنید و بروی بعدی کلیک کنید."
LangString older ${LANG_PERSIAN} "قدیمی تر"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_PORTUGUESE} "Adicionar/Reinstalar componentes"
LangString alreadyInstalled ${LANG_PORTUGUESE} "Já instalado"
LangString alreadyInstalledLong ${LANG_PORTUGUESE} "${PRODUCTNAME} ${VERSION} já está instalado. Selecione a operação que deseja realizar e clique em Seguinte para continuar."
LangString appRunning ${LANG_PORTUGUESE} "${PRODUCTNAME} está em execução! Por favor, feche-o primeiro e tente novamente."
LangString appRunningOkKill ${LANG_PORTUGUESE} "${PRODUCTNAME} está em execução!$\nClique em OK para encerrá-lo."
LangString appRunning ${LANG_PORTUGUESE} "{{product_name}} está em execução! Por favor, feche-o primeiro e tente novamente."
LangString appRunningOkKill ${LANG_PORTUGUESE} "{{product_name}} está em execução!$\nClique em OK para encerrá-lo."
LangString chooseMaintenanceOption ${LANG_PORTUGUESE} "Escolha a opção de manutenção a realizar."
LangString choowHowToInstall ${LANG_PORTUGUESE} "Escolha como deseja instalar o ${PRODUCTNAME}."
LangString createDesktop ${LANG_PORTUGUESE} "Criar atalho no ambiente de trabalho"
LangString dontUninstall ${LANG_PORTUGUESE} "Não desinstalar"
LangString dontUninstallDowngrade ${LANG_PORTUGUESE} "Não desinstalar (Instalar uma versão anterior sem desinstalar está desativado neste instalador)"
LangString failedToKillApp ${LANG_PORTUGUESE} "Falha ao encerrar ${PRODUCTNAME}. Por favor, feche-o primeiro e tente novamente."
LangString failedToKillApp ${LANG_PORTUGUESE} "Falha ao encerrar {{product_name}}. Por favor, feche-o primeiro e tente novamente."
LangString installingWebview2 ${LANG_PORTUGUESE} "A instalar WebView2..."
LangString newerVersionInstalled ${LANG_PORTUGUESE} "Uma versão mais recente do ${PRODUCTNAME} já está instalada! Não é recomendada a instalação de uma versão mais antiga. Se realmente deseja instalar esta versão mais antiga, é melhor desinstalar a versão atual primeiro. Selecione a operação que deseja realizar e clique em Seguinte para continuar."
LangString older ${LANG_PORTUGUESE} "mais antiga"

View File

@@ -1,14 +1,14 @@
LangString addOrReinstall ${LANG_PORTUGUESEBR} "Adicionar/Reinstalar componentes"
LangString alreadyInstalled ${LANG_PORTUGUESEBR} "Já instalado"
LangString alreadyInstalledLong ${LANG_PORTUGUESEBR} "${PRODUCTNAME} ${VERSION} já está instalado. Selecione a operação que deseja realizar e clique Próximo para continuar."
LangString appRunning ${LANG_PORTUGUESEBR} "${PRODUCTNAME} está aberto! Por favor feche a janela dele e tente novamente."
LangString appRunningOkKill ${LANG_PORTUGUESEBR} "${PRODUCTNAME} está aberto!$\nClique OK para fechar ele."
LangString appRunning ${LANG_PORTUGUESEBR} "{{product_name}} está aberto! Por favor feche a janela dele e tente novamente."
LangString appRunningOkKill ${LANG_PORTUGUESEBR} "{{product_name}} está aberto!$\nClique OK para fechar ele."
LangString chooseMaintenanceOption ${LANG_PORTUGUESEBR} "Escolha a opção de manutenção a realizar."
LangString choowHowToInstall ${LANG_PORTUGUESEBR} "Escolha como deseja instalar ${PRODUCTNAME}."
LangString createDesktop ${LANG_PORTUGUESEBR} "Criar atalho na área de trabalho"
LangString dontUninstall ${LANG_PORTUGUESEBR} "Não desinstalar"
LangString dontUninstallDowngrade ${LANG_PORTUGUESEBR} "Não desinstalar (Instalar versão anterior sem desinstalar está desabilitado nesse instalador)"
LangString failedToKillApp ${LANG_PORTUGUESEBR} "Falha ao fechar ${PRODUCTNAME}. Por favor feche a janela dele primeiro e tente novamente"
LangString failedToKillApp ${LANG_PORTUGUESEBR} "Falha ao fechar {{product_name}}. Por favor feche a janela dele primeiro e tente novamente"
LangString installingWebview2 ${LANG_PORTUGUESEBR} "Instalando WebView2..."
LangString newerVersionInstalled ${LANG_PORTUGUESEBR} "Uma nova versão do ${PRODUCTNAME} já está instalado! Não é recomendado instalar uma versão anterior. Se realmente deseja instalar essa versão antiga, é recomendado desinstalar a versão atual primeirl. Selecione a operação que deseja executare clique Próximo para continuar."
LangString older ${LANG_PORTUGUESEBR} "mais antiga"

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