mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-11 10:43:31 +02:00
Compare commits
462 Commits
@tauri-app
...
tauri-cli-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd63f229d5 | ||
|
|
af95fb6014 | ||
|
|
65bb24b9ae | ||
|
|
332ec355a1 | ||
|
|
2c46b1873e | ||
|
|
96439c2c42 | ||
|
|
ab97f36b64 | ||
|
|
6a4451bcd9 | ||
|
|
56277e4722 | ||
|
|
7a6fd5b75d | ||
|
|
7f3c989111 | ||
|
|
bda8304107 | ||
|
|
fb9d9c7fd1 | ||
|
|
8263b412c6 | ||
|
|
3025d90951 | ||
|
|
96391467e9 | ||
|
|
c0a654b863 | ||
|
|
b821796add | ||
|
|
33d079392a | ||
|
|
7bc77a038a | ||
|
|
371ee34383 | ||
|
|
22cd1e2846 | ||
|
|
1c5df96fe8 | ||
|
|
4f96ed41ca | ||
|
|
24eb2b1cd3 | ||
|
|
0f248b111f | ||
|
|
4ba871c5d2 | ||
|
|
f94af90359 | ||
|
|
cfc5bb8196 | ||
|
|
916aeaa486 | ||
|
|
12e3590613 | ||
|
|
232265c70e | ||
|
|
02440b875c | ||
|
|
f2dbe73097 | ||
|
|
152d971bcd | ||
|
|
acd7574284 | ||
|
|
e296e4bc38 | ||
|
|
11b4a03881 | ||
|
|
0277596341 | ||
|
|
cbd9629729 | ||
|
|
0079d08ba9 | ||
|
|
effd106adf | ||
|
|
4053ad1b58 | ||
|
|
f010ca5e91 | ||
|
|
6b2b9d6cbf | ||
|
|
b6de1c89c2 | ||
|
|
a3ae2cebbf | ||
|
|
a3f11b4f3b | ||
|
|
4b7370e9e0 | ||
|
|
5bbcaaec89 | ||
|
|
3eb3162404 | ||
|
|
0cbfd8923c | ||
|
|
0a552a868c | ||
|
|
560067cd7e | ||
|
|
6a4ea10274 | ||
|
|
349bbfc5c7 | ||
|
|
32a84650c0 | ||
|
|
b19f16aba1 | ||
|
|
594822aa55 | ||
|
|
e4aa35e083 | ||
|
|
9c16eefa31 | ||
|
|
3242e1c946 | ||
|
|
4a880ca697 | ||
|
|
d1ce9af628 | ||
|
|
ec6065fa4a | ||
|
|
18b5299952 | ||
|
|
eb3f0248c2 | ||
|
|
c03cc586e3 | ||
|
|
488bcea970 | ||
|
|
d48e7a39a7 | ||
|
|
221254738a | ||
|
|
9fb0586909 | ||
|
|
8ee14a8648 | ||
|
|
0f0d6a4e02 | ||
|
|
f9bdb9b230 | ||
|
|
f34acf161d | ||
|
|
87b3cdce48 | ||
|
|
aa1131a047 | ||
|
|
bd8a7cf39d | ||
|
|
f1891540bf | ||
|
|
d15da3daae | ||
|
|
414619c36e | ||
|
|
25757fece4 | ||
|
|
78d15e892d | ||
|
|
6a39f49991 | ||
|
|
a35600cbd7 | ||
|
|
1c53640ac3 | ||
|
|
e7f2d8cba4 | ||
|
|
7322f05792 | ||
|
|
06c75fd98b | ||
|
|
c8a30a61d2 | ||
|
|
650c91c114 | ||
|
|
5a5291d66c | ||
|
|
dbcfaa18d7 | ||
|
|
923b7c7bc6 | ||
|
|
626165eeb4 | ||
|
|
638804e9c4 | ||
|
|
1686296463 | ||
|
|
85baacd18b | ||
|
|
c31c75fffc | ||
|
|
9687a9b4fb | ||
|
|
4acae1ec02 | ||
|
|
d38d90b8d9 | ||
|
|
b52da29d5d | ||
|
|
574a4d4d36 | ||
|
|
1777406a16 | ||
|
|
d3586a2afa | ||
|
|
b7cdb3b39e | ||
|
|
96ecfca428 | ||
|
|
0e616dbbcb | ||
|
|
bc2f0e48ac | ||
|
|
efcc840ff0 | ||
|
|
7897ed257d | ||
|
|
1a018878ab | ||
|
|
e15f665efc | ||
|
|
479cee3d36 | ||
|
|
09c19932d2 | ||
|
|
4221124c4e | ||
|
|
b985eaf0a2 | ||
|
|
c84b162374 | ||
|
|
4f75bf5bdb | ||
|
|
b5c549d189 | ||
|
|
208f4bcadc | ||
|
|
f0662e41f4 | ||
|
|
dfacb656d2 | ||
|
|
db03f00693 | ||
|
|
a60a383360 | ||
|
|
aa8661acfd | ||
|
|
e045fe32c9 | ||
|
|
197da6fe78 | ||
|
|
94b77b36e3 | ||
|
|
527bf0031e | ||
|
|
35aa7e1218 | ||
|
|
23b9da75b9 | ||
|
|
8a8c1f9f3b | ||
|
|
50ebddaa2d | ||
|
|
568efb4568 | ||
|
|
6ce10ab773 | ||
|
|
039f44b7b1 | ||
|
|
00dfc32a2d | ||
|
|
76cbeef208 | ||
|
|
267368fd4f | ||
|
|
4e00b27913 | ||
|
|
766bccc341 | ||
|
|
31becbd1d1 | ||
|
|
da2a6ae5e3 | ||
|
|
87fdc3b9cd | ||
|
|
30e76c7d3a | ||
|
|
85b1912529 | ||
|
|
82da4f17f5 | ||
|
|
977c4b496c | ||
|
|
48b12b4404 | ||
|
|
9356fa15d8 | ||
|
|
2dccfab532 | ||
|
|
5d3687e8c3 | ||
|
|
0cf2d9933f | ||
|
|
1734273bbe | ||
|
|
690146e311 | ||
|
|
f888502fd2 | ||
|
|
577c7ffc45 | ||
|
|
82406c61e0 | ||
|
|
07953fb9c3 | ||
|
|
8d994f60fe | ||
|
|
8a1d490820 | ||
|
|
89c6e436ea | ||
|
|
b0babb6df1 | ||
|
|
b8f86669ab | ||
|
|
7ed877a0ae | ||
|
|
ea36294cbc | ||
|
|
0d39ff6b09 | ||
|
|
ad3fd3890f | ||
|
|
3752fed282 | ||
|
|
b072e2b296 | ||
|
|
0802529031 | ||
|
|
66e6325f43 | ||
|
|
0aa48fb9e4 | ||
|
|
628f4a97e4 | ||
|
|
fca5154e7a | ||
|
|
fbd57a1afd | ||
|
|
dd4f13ce4b | ||
|
|
7b14531f24 | ||
|
|
7a86e0f8a1 | ||
|
|
c1cd0a2ddb | ||
|
|
073dbc3953 | ||
|
|
47df696dfb | ||
|
|
9ea76503dc | ||
|
|
bb5faa21f4 | ||
|
|
b32153b437 | ||
|
|
c71755fd5f | ||
|
|
7b81825144 | ||
|
|
ebd3dcb92f | ||
|
|
80dccb6a2e | ||
|
|
cf0b3588a3 | ||
|
|
b8c0d7e402 | ||
|
|
e4982dff73 | ||
|
|
8cf662e34b | ||
|
|
b154826881 | ||
|
|
dade232592 | ||
|
|
aa6b4d4edf | ||
|
|
794af778e4 | ||
|
|
4e22ae29d3 | ||
|
|
4ae14bf2f2 | ||
|
|
f805061d11 | ||
|
|
30beb6fee7 | ||
|
|
22c7a877e3 | ||
|
|
2138bbc212 | ||
|
|
5c2b3b8b65 | ||
|
|
dd13728334 | ||
|
|
f235ec0113 | ||
|
|
f182b0bb93 | ||
|
|
a851b6597f | ||
|
|
be31675fbc | ||
|
|
dea8bbf6cd | ||
|
|
be3a79c864 | ||
|
|
ba42a1f553 | ||
|
|
1cd8f55eed | ||
|
|
8603e42a6b | ||
|
|
c32bd722d3 | ||
|
|
bcdd510254 | ||
|
|
71cb1e26d7 | ||
|
|
b459f1d405 | ||
|
|
8cc0067165 | ||
|
|
f2c94aaca0 | ||
|
|
cefefa930b | ||
|
|
4a01299e31 | ||
|
|
5a23146566 | ||
|
|
4062e49914 | ||
|
|
a483ba7c27 | ||
|
|
f981a5ee8b | ||
|
|
2b960dfd9f | ||
|
|
eec08a18b6 | ||
|
|
35018eed02 | ||
|
|
de7f34bff4 | ||
|
|
72df2e4a04 | ||
|
|
0d1446857c | ||
|
|
658e5f5d1d | ||
|
|
7930dde85c | ||
|
|
b05f82d35b | ||
|
|
51bcafe323 | ||
|
|
013f8f6523 | ||
|
|
5591a4f0b4 | ||
|
|
2fa33d5c44 | ||
|
|
cedb24d494 | ||
|
|
30f5a1553d | ||
|
|
08de8a172b | ||
|
|
c108024257 | ||
|
|
dc90cd3919 | ||
|
|
0ec71a844c | ||
|
|
0c4700e990 | ||
|
|
b83921226c | ||
|
|
3cc4ad3c38 | ||
|
|
d91bfa5cb9 | ||
|
|
f67a4a6bfe | ||
|
|
d8059bad3c | ||
|
|
be2e6b85fe | ||
|
|
b9ee806724 | ||
|
|
339a075e33 | ||
|
|
93124ad2eb | ||
|
|
3a74dc8f34 | ||
|
|
3626b7a92b | ||
|
|
eea12c196e | ||
|
|
4ce5c74ab4 | ||
|
|
2d029a9f53 | ||
|
|
f268b3dbdf | ||
|
|
755533c518 | ||
|
|
dc78dfecab | ||
|
|
f98598817c | ||
|
|
060de5bbdd | ||
|
|
c698a6d6f3 | ||
|
|
20c1906912 | ||
|
|
3fb8d7ca6b | ||
|
|
55ffa23c9e | ||
|
|
cab7f76d01 | ||
|
|
e103e87f15 | ||
|
|
bca02967a9 | ||
|
|
887db0813f | ||
|
|
4f26dcf309 | ||
|
|
4bffc326ea | ||
|
|
b859dc43fc | ||
|
|
9332132239 | ||
|
|
22e9bf74a4 | ||
|
|
b495fe0fdc | ||
|
|
7d618f12d8 | ||
|
|
385a41dea2 | ||
|
|
955832e56b | ||
|
|
c116dfcdee | ||
|
|
d6520a21ce | ||
|
|
ab81adb71b | ||
|
|
6e417c9435 | ||
|
|
ddc469367a | ||
|
|
d7b998fe71 | ||
|
|
d9a07e66af | ||
|
|
0adeb4e7c5 | ||
|
|
70d8557cc3 | ||
|
|
95fc3cd424 | ||
|
|
4633705da7 | ||
|
|
3f680588cd | ||
|
|
7d8252679d | ||
|
|
ee95c1b1ed | ||
|
|
741e44b45c | ||
|
|
8e9339e880 | ||
|
|
053b57c1df | ||
|
|
b6a56f3616 | ||
|
|
11945e561c | ||
|
|
b6ad316460 | ||
|
|
5eba0785c4 | ||
|
|
6038f09d85 | ||
|
|
e3b0260871 | ||
|
|
a6ada76a9f | ||
|
|
bfc71e845b | ||
|
|
0a11b8741a | ||
|
|
6b70fbcc84 | ||
|
|
e9c9c4d6f6 | ||
|
|
abdd558075 | ||
|
|
3dbcbe7685 | ||
|
|
a2d36b8c34 | ||
|
|
5a3647bdfe | ||
|
|
477e9c0496 | ||
|
|
82d634f4a9 | ||
|
|
8e9134c4a2 | ||
|
|
dc1997b77d | ||
|
|
1a86974aa3 | ||
|
|
fb294af8e3 | ||
|
|
46c7b16111 | ||
|
|
9dac2863af | ||
|
|
9a9d1205b0 | ||
|
|
27096cdc05 | ||
|
|
6cbfc4878d | ||
|
|
f5a59b93bf | ||
|
|
5432752e51 | ||
|
|
bf912b8e08 | ||
|
|
9a30bed98c | ||
|
|
9d02c18ac2 | ||
|
|
de8600b4d9 | ||
|
|
0ea8894579 | ||
|
|
fbe7c9ead7 | ||
|
|
b8eb28877f | ||
|
|
90c6546faf | ||
|
|
4ed2ab76e2 | ||
|
|
bc43c738ba | ||
|
|
0b79af7114 | ||
|
|
a70e690fe7 | ||
|
|
72748cc45c | ||
|
|
cf771bf69a | ||
|
|
07ccdc499c | ||
|
|
d2c8f0eb5c | ||
|
|
b643dcc1c4 | ||
|
|
cd7d08b63f | ||
|
|
4c3f047735 | ||
|
|
61e69db9e4 | ||
|
|
75d56e8364 | ||
|
|
a8aca70151 | ||
|
|
cad5504455 | ||
|
|
f8e50e8e5b | ||
|
|
cfe1af2848 | ||
|
|
89c6f08e82 | ||
|
|
cde0ff7798 | ||
|
|
b0d7527250 | ||
|
|
a28b5013c5 | ||
|
|
b9a99a5c69 | ||
|
|
98f62e65a2 | ||
|
|
c130af6b06 | ||
|
|
ef21ed9ac1 | ||
|
|
cd1d026f97 | ||
|
|
848d0e060e | ||
|
|
ae75a353d0 | ||
|
|
70f96e3222 | ||
|
|
3acf679c87 | ||
|
|
22d5852208 | ||
|
|
701778a195 | ||
|
|
a0f2c84d51 | ||
|
|
f86e2387c9 | ||
|
|
26fc9558fe | ||
|
|
90dc7b19fc | ||
|
|
c681d835d5 | ||
|
|
208d8968ce | ||
|
|
aaa7d9bb13 | ||
|
|
d150a40b09 | ||
|
|
881729448c | ||
|
|
cd841d8e33 | ||
|
|
3d8a39aa4a | ||
|
|
50e92d097b | ||
|
|
bc4dfcd798 | ||
|
|
832ad10e3a | ||
|
|
0198354961 | ||
|
|
c0a5a10cff | ||
|
|
d7f48cb324 | ||
|
|
a16796a555 | ||
|
|
231e9a5ee1 | ||
|
|
b0ddee8992 | ||
|
|
5319325886 | ||
|
|
ccce63f8c8 | ||
|
|
da958395ff | ||
|
|
1737df3e33 | ||
|
|
2df426ed1d | ||
|
|
361fdb3585 | ||
|
|
838b2b8b3b | ||
|
|
f87e0485ca | ||
|
|
b794ca4a27 | ||
|
|
8f282c6305 | ||
|
|
ed118da266 | ||
|
|
c8700656be | ||
|
|
cdd1ebf81f | ||
|
|
253b1872f1 | ||
|
|
3ac76bec77 | ||
|
|
faeec8e965 | ||
|
|
e349dfe572 | ||
|
|
fdaf48fc4a | ||
|
|
7d38ee987e | ||
|
|
f1167143cd | ||
|
|
2601811cef | ||
|
|
ca7f025fd8 | ||
|
|
17bcec8abe | ||
|
|
f5eacf3283 | ||
|
|
6bbf3649f9 | ||
|
|
22b26a2e36 | ||
|
|
2a120bdcc0 | ||
|
|
0ae06c5ca8 | ||
|
|
afad8067d1 | ||
|
|
b37741da6a | ||
|
|
2b8a981050 | ||
|
|
1f65fd2bb7 | ||
|
|
9f51bbf1de | ||
|
|
475597f97c | ||
|
|
e61225bcc4 | ||
|
|
01c74f4424 | ||
|
|
882b0aded1 | ||
|
|
89e30ef20d | ||
|
|
a692c8937f | ||
|
|
8ba5e16384 | ||
|
|
b50a1ac0ef | ||
|
|
0e2f0b29cd | ||
|
|
983634a41f | ||
|
|
2c3e000f73 | ||
|
|
a6e84f7d2c | ||
|
|
d6bed20a0e | ||
|
|
18bd639f6e | ||
|
|
53f808674b | ||
|
|
9f0d902f6b | ||
|
|
df27b4d94c | ||
|
|
5188c0fae2 | ||
|
|
ba6f370147 | ||
|
|
f884bae75b | ||
|
|
fc30b20bea | ||
|
|
7a9b920c3e | ||
|
|
12a48d1e26 | ||
|
|
020ea05561 | ||
|
|
a09e48e396 | ||
|
|
b37c208d61 | ||
|
|
72feaf99fc | ||
|
|
d86aaccb0b | ||
|
|
93a3a043d3 | ||
|
|
2bc46b061c | ||
|
|
b63262cd4d | ||
|
|
f2814ed538 | ||
|
|
ff39ad93b7 | ||
|
|
9cb6cad284 | ||
|
|
cccb308c7b | ||
|
|
dc4d794776 | ||
|
|
46935212b6 | ||
|
|
74212d40d8 | ||
|
|
c3b1fced38 |
@@ -4,7 +4,7 @@
|
||||
"feat": "New Features",
|
||||
"enhance": "Enhancements",
|
||||
"bug": "Bug Fixes",
|
||||
"pref": "Performance Improvements",
|
||||
"perf": "Performance Improvements",
|
||||
"changes": "What's Changed",
|
||||
"sec": "Security fixes",
|
||||
"deps": "Dependencies",
|
||||
|
||||
41
.github/CONTRIBUTING.md
vendored
41
.github/CONTRIBUTING.md
vendored
@@ -51,25 +51,47 @@ 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`).
|
||||
|
||||
Some Tauri packages will be automatically built when running one of the examples. Others, however, will need to be built beforehand. To build these automatically, run the `.scripts/setup.sh` (Linux and macOS) or `.scripts/setup.ps1` (Windows) script. This will install the Rust and Node.js CLI and build the JS API. After that, you should be able to run all the examples. Note that the setup script should be executed from the root folder of the repository in order to run correctly.
|
||||
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:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Overview
|
||||
|
||||
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
|
||||
|
||||
@@ -81,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
|
||||
|
||||
|
||||
5
.github/RELEASING.md
vendored
5
.github/RELEASING.md
vendored
@@ -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?
|
||||
|
||||
2
.github/workflows/audit.yml
vendored
2
.github/workflows/audit.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
4
.github/workflows/check-generated-files.yml
vendored
4
.github/workflows/check-generated-files.yml
vendored
@@ -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:
|
||||
@@ -44,7 +46,7 @@ jobs:
|
||||
if: needs.changes.outputs.api == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
|
||||
- name: Create Pull Request With Versions Bumped
|
||||
if: steps.covector.outputs.commandRan == 'version'
|
||||
uses: tauri-apps/create-pull-request@v3
|
||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # 7.0.6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: release/version-updates
|
||||
@@ -119,12 +119,13 @@ jobs:
|
||||
commit-message: 'apply version updates'
|
||||
labels: 'version updates'
|
||||
body: ${{ steps.covector.outputs.change }}
|
||||
sign-commits: true
|
||||
|
||||
- name: Trigger doc update
|
||||
if: |
|
||||
steps.covector.outputs.successfulPublish == 'true' &&
|
||||
steps.covector.outputs.packagesPublished != ''
|
||||
uses: peter-evans/repository-dispatch@v1
|
||||
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # 3.0.0
|
||||
with:
|
||||
token: ${{ secrets.ORG_TAURI_BOT_PAT }}
|
||||
repository: tauri-apps/tauri-docs
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
with:
|
||||
targets: ${{ matrix.target.name }}
|
||||
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
||||
2
.github/workflows/fmt.yml
vendored
2
.github/workflows/fmt.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
4
.github/workflows/lint-js.yml
vendored
4
.github/workflows/lint-js.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
2
.github/workflows/lint-rust.yml
vendored
2
.github/workflows/lint-rust.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
- dev
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/lint-cli.yml'
|
||||
- '.github/workflows/lint-rust.yml'
|
||||
- 'crates/**'
|
||||
|
||||
env:
|
||||
|
||||
34
.github/workflows/publish-cli-js.yml
vendored
34
.github/workflows/publish-cli-js.yml
vendored
@@ -43,15 +43,16 @@ jobs:
|
||||
- host: windows-latest
|
||||
architecture: x64
|
||||
target: aarch64-pc-windows-msvc
|
||||
build: pnpm build --target aarch64-pc-windows-msvc --features native-tls-vendored --cargo-flags="--no-default-features"
|
||||
- host: ubuntu-20.04
|
||||
build: pnpm build --target aarch64-pc-windows-msvc
|
||||
- host: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
build: |
|
||||
npm i -g --force corepack
|
||||
cd packages/cli
|
||||
pnpm build --target x86_64-unknown-linux-gnu
|
||||
strip *.node
|
||||
- host: ubuntu-20.04
|
||||
- host: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-musl
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
build: |
|
||||
@@ -63,14 +64,15 @@ jobs:
|
||||
build: |
|
||||
pnpm build --features native-tls-vendored --target=aarch64-apple-darwin
|
||||
strip -x *.node
|
||||
- host: ubuntu-20.04
|
||||
- host: ubuntu-22.04
|
||||
target: aarch64-unknown-linux-gnu
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
build: |
|
||||
npm i -g --force corepack
|
||||
cd packages/cli
|
||||
pnpm build --target aarch64-unknown-linux-gnu
|
||||
aarch64-unknown-linux-gnu-strip *.node
|
||||
- host: ubuntu-20.04
|
||||
- host: ubuntu-22.04
|
||||
architecture: x64
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
setup: |
|
||||
@@ -79,7 +81,7 @@ jobs:
|
||||
build: |
|
||||
pnpm build --target=armv7-unknown-linux-gnueabihf
|
||||
arm-linux-gnueabihf-strip *.node
|
||||
- host: ubuntu-20.04
|
||||
- host: ubuntu-22.04
|
||||
architecture: x64
|
||||
target: aarch64-unknown-linux-musl
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
@@ -88,11 +90,20 @@ jobs:
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
pnpm build --target aarch64-unknown-linux-musl
|
||||
/aarch64-linux-musl-cross/bin/aarch64-linux-musl-strip *.node
|
||||
- host: ubuntu-22.04
|
||||
architecture: x64
|
||||
target: riscv64gc-unknown-linux-gnu
|
||||
setup: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu -y
|
||||
build: |
|
||||
pnpm build --target=riscv64gc-unknown-linux-gnu
|
||||
riscv64-linux-gnu-strip *.node
|
||||
name: stable - ${{ matrix.settings.target }} - node@20
|
||||
runs-on: ${{ matrix.settings.host }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
@@ -202,7 +213,7 @@ jobs:
|
||||
runs-on: ${{ matrix.settings.host }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -233,7 +244,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -270,7 +281,7 @@ jobs:
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -308,7 +319,6 @@ jobs:
|
||||
- '20'
|
||||
image:
|
||||
- ghcr.io/napi-rs/napi-rs/nodejs:aarch64-16
|
||||
- ghcr.io/napi-rs/napi-rs/nodejs:armhf-16
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
@@ -361,7 +371,7 @@ jobs:
|
||||
id-token: write # npm provenance
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
||||
4
.github/workflows/publish-cli-rs.yml
vendored
4
.github/workflows/publish-cli-rs.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
rust_target: x86_64-unknown-linux-gnu
|
||||
ext: ''
|
||||
args: ''
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- os: windows-latest
|
||||
rust_target: aarch64-pc-windows-msvc
|
||||
ext: '.exe'
|
||||
args: '--no-default-features --features native-tls-vendored'
|
||||
args: ''
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
6
.github/workflows/test-android.yml
vendored
6
.github/workflows/test-android.yml
vendored
@@ -33,8 +33,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: install Rust 1.77.2
|
||||
uses: dtolnay/rust-toolchain@1.77.2
|
||||
|
||||
- name: install Linux dependencies
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1
|
||||
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
||||
5
.github/workflows/test-cli-js.yml
vendored
5
.github/workflows/test-cli-js.yml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/test-cli-js.yml'
|
||||
- 'packages/cli/**'
|
||||
# currently` @tauri-apps/cli` only tests the template
|
||||
- 'crates/tauri-cli/templates/app/**'
|
||||
|
||||
@@ -37,7 +38,7 @@ jobs:
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- run: corepack enable
|
||||
- run: npm i -g --force corepack
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -48,7 +49,7 @@ jobs:
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev
|
||||
sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
|
||||
4
.github/workflows/test-cli-rs.yml
vendored
4
.github/workflows/test-cli-rs.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 'Setup Rust'
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
uses: dtolnay/rust-toolchain@1.77.2
|
||||
with:
|
||||
targets: ${{ matrix.platform.target }}
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
if: matrix.platform.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1 libayatana-appindicator3-dev
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
|
||||
13
.github/workflows/test-core.yml
vendored
13
.github/workflows/test-core.yml
vendored
@@ -88,18 +88,9 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y webkit2gtk-4.1 libxdo-dev libayatana-appindicator3-dev
|
||||
|
||||
- name: downgrade crates with MSRV conflict
|
||||
run: |
|
||||
cargo update -p aws-config --precise 1.5.5
|
||||
cargo update -p aws-sdk-ssooidc --precise 1.40.0
|
||||
cargo update -p aws-sdk-s3 --precise 1.46.0
|
||||
cargo update -p aws-sdk-sts --precise 1.39.0
|
||||
cargo update -p aws-sdk-sso --precise 1.39.0
|
||||
cargo update -p bitstream-io --precise 2.3.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: v2
|
||||
prefix-key: v3
|
||||
save-if: ${{ matrix.features.key == 'all' }}
|
||||
|
||||
- name: test
|
||||
@@ -109,5 +100,5 @@ jobs:
|
||||
- name: test (using cross)
|
||||
if: ${{ matrix.platform.cross }}
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev ac4c11cedc97cd7c27faed36e55377a90e6ed618 --locked
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev 51f46f296253d8122c927c5bb933e3c4f27cc317 --locked
|
||||
cross ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml
|
||||
|
||||
107
.gitignore
vendored
107
.gitignore
vendored
@@ -1,52 +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
|
||||
|
||||
# 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*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"trailingComma": "none"
|
||||
"trailingComma": "none",
|
||||
"experimentalOperatorPosition": "start"
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ const ignore = [
|
||||
|
||||
async function checkFile(file) {
|
||||
if (
|
||||
extensions.some((e) => file.endsWith(e)) &&
|
||||
!ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) == i)
|
||||
extensions.some((e) => file.endsWith(e))
|
||||
&& !ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) == i)
|
||||
) {
|
||||
const fileStream = fs.createReadStream(file)
|
||||
const rl = readline.createInterface({
|
||||
@@ -42,11 +42,11 @@ async function checkFile(file) {
|
||||
for await (let line of rl) {
|
||||
// ignore empty lines, allow shebang and bundler license
|
||||
if (
|
||||
line.length === 0 ||
|
||||
line.startsWith('#!') ||
|
||||
line.startsWith('// swift-tools-version:') ||
|
||||
line === bundlerLicense ||
|
||||
line === denoLicense
|
||||
line.length === 0
|
||||
|| line.startsWith('#!')
|
||||
|| line.startsWith('// swift-tools-version:')
|
||||
|| line === bundlerLicense
|
||||
|| line === denoLicense
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
4118
Cargo.lock
generated
4118
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -70,3 +70,4 @@ opt-level = "s"
|
||||
[patch.crates-io]
|
||||
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" }
|
||||
|
||||
@@ -6,7 +6,7 @@ PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
|
||||
PackageHomePage: https://tauri.app
|
||||
PackageLicenseDeclared: Apache-2.0
|
||||
PackageLicenseDeclared: MIT
|
||||
PackageCopyrightText: 2019-2024, The Tauri Programme in the Commons Conservancy
|
||||
PackageCopyrightText: 2019-2025, The Tauri Programme in the Commons Conservancy
|
||||
PackageSummary: <text>Tauri is a rust project that enables developers to make secure
|
||||
and small desktop applications using a web frontend.
|
||||
</text>
|
||||
|
||||
18
README.md
18
README.md
@@ -4,7 +4,7 @@
|
||||
[](https://opencollective.com/tauri)
|
||||
[](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield)
|
||||
[](https://discord.gg/SpmNs4S)
|
||||
[](https://discord.com/invite/tauri)
|
||||
[](https://tauri.app)
|
||||
[](https://good-labs.github.io/greater-good-affirmation)
|
||||
[](https://opencollective.com/tauri)
|
||||
@@ -35,7 +35,7 @@ The list of Tauri's features includes, but is not limited to:
|
||||
- Built-in self updater (desktop only)
|
||||
- System tray icons
|
||||
- Native notifications
|
||||
- [Localhost free (🔥)](https://github.com/tauri-apps/tauri/issues/10510)
|
||||
- Native WebView Protocol (tauri doesn't create a localhost http(s) server to serve the WebView contents)
|
||||
- GitHub action for streamlined CI
|
||||
- VS Code extension
|
||||
|
||||
@@ -43,13 +43,13 @@ The list of Tauri's features includes, but is not limited to:
|
||||
|
||||
Tauri currently supports development and distribution on the following platforms:
|
||||
|
||||
| Platform | Versions |
|
||||
| :---------------- | :-------------------------------------------------------------------------------------------------------------- |
|
||||
| Windows | 7 and above |
|
||||
| macOS | 10.15 and above |
|
||||
| Linux | webkit2gtk 4.0 for Tauri v1 (for example Ubuntu 18.04). webkit2gtk 4.1 for Tauri v2 (for example Ubuntu 22.04). |
|
||||
| iOS/iPadOS (beta) | 9 and above |
|
||||
| Android (beta) | 7 and above |
|
||||
| Platform | Versions |
|
||||
| :--------- | :-------------------------------------------------------------------------------------------------------------- |
|
||||
| Windows | 7 and above |
|
||||
| macOS | 10.15 and above |
|
||||
| Linux | webkit2gtk 4.0 for Tauri v1 (for example Ubuntu 18.04). webkit2gtk 4.1 for Tauri v2 (for example Ubuntu 22.04). |
|
||||
| iOS/iPadOS | 9 and above |
|
||||
| Android | 7 and above (currently 8 and above) |
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ description = "Cross-platform WebView rendering library"
|
||||
repository = "https://github.com/tauri-apps/wry"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.40"
|
||||
anyhow = "1"
|
||||
time = { version = "0.3", features = ["formatting"] }
|
||||
tempfile = "3.2.0"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tempfile = "3"
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[[bin]]
|
||||
name = "run_benchmark"
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! [](https://tauri.app)
|
||||
//!
|
||||
//! 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_**
|
||||
@@ -54,7 +52,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
|
||||
@@ -62,5 +60,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:?}"));
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! [](https://tauri.app)
|
||||
//!
|
||||
//! 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_**
|
||||
@@ -91,7 +89,7 @@ fn run_max_mem_benchmark() -> 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 = utils::target_dir().join(format!("mprof{name}_.dat"));
|
||||
let benchmark_file = benchmark_file.to_str().unwrap();
|
||||
|
||||
let proc = Command::new("mprof")
|
||||
@@ -107,7 +105,7 @@ fn run_max_mem_benchmark() -> Result<HashMap<String, u64>> {
|
||||
.spawn()?;
|
||||
|
||||
let proc_result = proc.wait_with_output()?;
|
||||
println!("{:?}", proc_result);
|
||||
println!("{proc_result:?}");
|
||||
results.insert(
|
||||
name.to_string(),
|
||||
utils::parse_max_mem(benchmark_file).unwrap(),
|
||||
@@ -128,11 +126,11 @@ fn rlib_size(target_dir: &std::path::Path, prefix: &str) -> u64 {
|
||||
if name.starts_with(prefix) && name.ends_with(".rlib") {
|
||||
let start = name.split('-').next().unwrap().to_string();
|
||||
if seen.contains(&start) {
|
||||
println!("skip {}", name);
|
||||
println!("skip {name}");
|
||||
} else {
|
||||
seen.insert(start);
|
||||
size += entry.metadata().unwrap().len();
|
||||
println!("check size {} {}", name, size);
|
||||
println!("check size {name} {size}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +142,7 @@ fn get_binary_sizes(target_dir: &Path) -> Result<HashMap<String, u64>> {
|
||||
let mut sizes = HashMap::<String, u64>::new();
|
||||
|
||||
let wry_size = rlib_size(target_dir, "libwry");
|
||||
println!("wry {} bytes", wry_size);
|
||||
println!("wry {wry_size} bytes");
|
||||
sizes.insert("wry_rlib".to_string(), wry_size);
|
||||
|
||||
// add size for all EXEC_TIME_BENCHMARKS
|
||||
|
||||
@@ -96,8 +96,8 @@ pub fn run_collect(cmd: &[&str]) -> (String, String) {
|
||||
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);
|
||||
eprintln!("stdout: <<<{stdout}>>>");
|
||||
eprintln!("stderr: <<<{stderr}>>>");
|
||||
panic!("Unexpected exit code: {:?}", status.code());
|
||||
}
|
||||
(stdout, stderr)
|
||||
@@ -230,7 +230,7 @@ pub fn download_file(url: &str, filename: PathBuf) {
|
||||
|
||||
// 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")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
|
||||
Arial, sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
|
||||
sans-serif;
|
||||
margin: auto;
|
||||
max-width: 38rem;
|
||||
padding: 2rem;
|
||||
|
||||
@@ -11,6 +11,6 @@ tauri-build = { path = "../../../../crates/tauri-build", features = [
|
||||
] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tauri = { path = "../../../../crates/tauri", features = [] }
|
||||
|
||||
@@ -11,6 +11,6 @@ tauri-build = { path = "../../../../crates/tauri-build", features = [
|
||||
] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tauri = { path = "../../../../crates/tauri", features = [] }
|
||||
|
||||
@@ -18,7 +18,7 @@ async fn read_file<R: Runtime>(app: AppHandle<R>) -> Result<Response, String> {
|
||||
.path()
|
||||
.resolve(".tauri_3mb.json", BaseDirectory::Home)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let contents = read(&path).map_err(|e| e.to_string())?;
|
||||
let contents = read(path).map_err(|e| e.to_string())?;
|
||||
Ok(Response::new(contents))
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@ tauri-build = { path = "../../../../crates/tauri-build", features = [
|
||||
] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tauri = { path = "../../../../crates/tauri", features = [] }
|
||||
|
||||
@@ -1,5 +1,70 @@
|
||||
# Changelog
|
||||
|
||||
## \[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
|
||||
|
||||
- Upgraded to `tauri-utils@2.4.0`
|
||||
- Upgraded to `tauri-codegen@2.2.0`
|
||||
- [`48b12b440`](https://www.github.com/tauri-apps/tauri/commit/48b12b440478937c46fdfef9f9d95194be117020) Update to `tauri-utils@2.4.0`
|
||||
|
||||
## \[2.1.1]
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.3.1`
|
||||
- Upgraded to `tauri-codegen@2.1.1`
|
||||
|
||||
## \[2.1.0]
|
||||
|
||||
### New Features
|
||||
|
||||
- [`013f8f652`](https://www.github.com/tauri-apps/tauri/commit/013f8f652302f2d49c5ec0a075582033d8b074fb) ([#12890](https://www.github.com/tauri-apps/tauri/pull/12890) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Added `build > removeUnusedCommands` to trigger the build scripts and macros to remove unused commands based on the capabilities you defined. Note this won't be accounting for dynamically added ACLs so make sure to check it when using this.
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
- [`1cd8f55ee`](https://www.github.com/tauri-apps/tauri/commit/1cd8f55eed326d61860fee62ba2d2f4464bdcfcc) ([#13033](https://www.github.com/tauri-apps/tauri/pull/13033) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Don't ship global `bundle.global.js` if `app > withGlobalTauri` is set to false
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.3.0`
|
||||
- Upgraded to `tauri-codegen@2.1.0`
|
||||
|
||||
## \[2.0.6]
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.2.0`
|
||||
- Upgraded to `tauri-codegen@2.0.5`
|
||||
|
||||
## \[2.0.5]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`848d0e060`](https://www.github.com/tauri-apps/tauri/commit/848d0e060e6eb3c8e9e8175adc7896587b5a947d) ([#12270](https://www.github.com/tauri-apps/tauri/pull/12270) by [@aurelj](https://www.github.com/tauri-apps/tauri/../../aurelj)) Update `cargo_toml` to `0.21.0`. This adds compatibility with Rust's 2024 Edition.
|
||||
- [`cd1d026f9`](https://www.github.com/tauri-apps/tauri/commit/cd1d026f9799c26b04acb64f49e7ee0a8b193049) ([#11961](https://www.github.com/tauri-apps/tauri/pull/11961) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix tauri fails to build if the project path contains glob characters
|
||||
|
||||
## \[2.0.4]
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.1.1`
|
||||
- Upgraded to `tauri-codegen@2.0.4`
|
||||
|
||||
## \[2.0.3]
|
||||
|
||||
### Dependencies
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tauri-build"
|
||||
version = "2.0.3"
|
||||
version = "2.3.1"
|
||||
description = "build time code to pair with https://crates.io/crates/tauri"
|
||||
exclude = ["CHANGELOG.md", "/target"]
|
||||
readme = "README.md"
|
||||
@@ -28,23 +28,24 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
quote = { version = "1", optional = true }
|
||||
tauri-codegen = { version = "2.0.3", path = "../tauri-codegen", optional = true }
|
||||
tauri-utils = { version = "2.1.0", path = "../tauri-utils", features = [
|
||||
tauri-codegen = { version = "2.3.1", path = "../tauri-codegen", optional = true }
|
||||
tauri-utils = { version = "2.6.0", path = "../tauri-utils", features = [
|
||||
"build",
|
||||
"resources",
|
||||
] }
|
||||
cargo_toml = "0.17"
|
||||
cargo_toml = "0.22"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
heck = "0.5"
|
||||
json-patch = "3.0"
|
||||
json-patch = "3"
|
||||
walkdir = "2"
|
||||
tauri-winres = "0.1"
|
||||
tauri-winres = "0.3"
|
||||
semver = "1"
|
||||
dirs = "5"
|
||||
dirs = "6"
|
||||
glob = "0.3"
|
||||
toml = "0.8"
|
||||
schemars = { version = "0.8.18", features = ["preserve_order"] }
|
||||
# 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"]
|
||||
|
||||
@@ -11,7 +11,9 @@ use std::{
|
||||
use anyhow::{Context, Result};
|
||||
use tauri_utils::{
|
||||
acl::{
|
||||
capability::Capability, manifest::Manifest, schema::CAPABILITIES_SCHEMA_FOLDER_PATH,
|
||||
capability::Capability,
|
||||
manifest::{Manifest, PermissionFile},
|
||||
schema::CAPABILITIES_SCHEMA_FOLDER_PATH,
|
||||
ACL_MANIFESTS_FILE_NAME, APP_ACL_KEY, CAPABILITIES_FILE_NAME,
|
||||
},
|
||||
platform::Target,
|
||||
@@ -155,11 +157,17 @@ fn read_plugins_manifests() -> Result<BTreeMap<String, Manifest>> {
|
||||
Ok(manifests)
|
||||
}
|
||||
|
||||
struct InlinedPuginsAcl {
|
||||
manifests: BTreeMap<String, Manifest>,
|
||||
permission_files: BTreeMap<String, Vec<PermissionFile>>,
|
||||
}
|
||||
|
||||
fn inline_plugins(
|
||||
out_dir: &Path,
|
||||
inlined_plugins: HashMap<&'static str, InlinedPlugin>,
|
||||
) -> Result<BTreeMap<String, Manifest>> {
|
||||
) -> Result<InlinedPuginsAcl> {
|
||||
let mut acl_manifests = BTreeMap::new();
|
||||
let mut permission_files_map = BTreeMap::new();
|
||||
|
||||
for (name, plugin) in inlined_plugins {
|
||||
let plugin_out_dir = out_dir.join("plugins").join(name);
|
||||
@@ -199,7 +207,9 @@ permissions = [{default_permissions}]
|
||||
}
|
||||
|
||||
tauri_utils::acl::build::define_permissions(
|
||||
&plugin_out_dir.join("*").to_string_lossy(),
|
||||
&PathBuf::from(glob::Pattern::escape(&plugin_out_dir.to_string_lossy()))
|
||||
.join("*")
|
||||
.to_string_lossy(),
|
||||
name,
|
||||
&plugin_out_dir,
|
||||
|_| true,
|
||||
@@ -222,28 +232,41 @@ permissions = [{default_permissions}]
|
||||
);
|
||||
}
|
||||
permission_files.extend(tauri_utils::acl::build::define_permissions(
|
||||
&default_permissions_path
|
||||
.join("**")
|
||||
.join("*")
|
||||
.to_string_lossy(),
|
||||
&PathBuf::from(glob::Pattern::escape(
|
||||
&default_permissions_path.to_string_lossy(),
|
||||
))
|
||||
.join("**")
|
||||
.join("*")
|
||||
.to_string_lossy(),
|
||||
name,
|
||||
&plugin_out_dir,
|
||||
|_| true,
|
||||
)?);
|
||||
}
|
||||
|
||||
permission_files_map.insert(name.into(), permission_files.clone());
|
||||
|
||||
let manifest = tauri_utils::acl::manifest::Manifest::new(permission_files, None);
|
||||
acl_manifests.insert(name.into(), manifest);
|
||||
}
|
||||
|
||||
Ok(acl_manifests)
|
||||
Ok(InlinedPuginsAcl {
|
||||
manifests: acl_manifests,
|
||||
permission_files: permission_files_map,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AppManifestAcl {
|
||||
manifest: Manifest,
|
||||
permission_files: Vec<PermissionFile>,
|
||||
}
|
||||
|
||||
fn app_manifest_permissions(
|
||||
out_dir: &Path,
|
||||
manifest: AppManifest,
|
||||
inlined_plugins: &HashMap<&'static str, InlinedPlugin>,
|
||||
) -> Result<Manifest> {
|
||||
) -> Result<AppManifestAcl> {
|
||||
let app_out_dir = out_dir.join("app-manifest");
|
||||
fs::create_dir_all(&app_out_dir)?;
|
||||
let pkg_name = "__app__";
|
||||
@@ -286,6 +309,7 @@ fn app_manifest_permissions(
|
||||
let inlined_plugins_permissions: Vec<_> = inlined_plugins
|
||||
.keys()
|
||||
.map(|name| permissions_root.join(name))
|
||||
.flat_map(|p| p.canonicalize())
|
||||
.collect();
|
||||
|
||||
permission_files.extend(tauri_utils::acl::build::define_permissions(
|
||||
@@ -304,10 +328,10 @@ fn app_manifest_permissions(
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(tauri_utils::acl::manifest::Manifest::new(
|
||||
permission_files,
|
||||
None,
|
||||
))
|
||||
Ok(AppManifestAcl {
|
||||
permission_files: permission_files.clone(),
|
||||
manifest: tauri_utils::acl::manifest::Manifest::new(permission_files, None),
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_capabilities(
|
||||
@@ -376,19 +400,21 @@ fn validate_capabilities(
|
||||
pub fn build(out_dir: &Path, target: Target, attributes: &Attributes) -> super::Result<()> {
|
||||
let mut acl_manifests = read_plugins_manifests()?;
|
||||
|
||||
let app_manifest = app_manifest_permissions(
|
||||
let app_acl = app_manifest_permissions(
|
||||
out_dir,
|
||||
attributes.app_manifest,
|
||||
&attributes.inlined_plugins,
|
||||
)?;
|
||||
if app_manifest.default_permission.is_some()
|
||||
|| !app_manifest.permission_sets.is_empty()
|
||||
|| !app_manifest.permissions.is_empty()
|
||||
{
|
||||
acl_manifests.insert(APP_ACL_KEY.into(), app_manifest);
|
||||
let has_app_manifest = app_acl.manifest.default_permission.is_some()
|
||||
|| !app_acl.manifest.permission_sets.is_empty()
|
||||
|| !app_acl.manifest.permissions.is_empty();
|
||||
if has_app_manifest {
|
||||
acl_manifests.insert(APP_ACL_KEY.into(), app_acl.manifest);
|
||||
}
|
||||
|
||||
acl_manifests.extend(inline_plugins(out_dir, attributes.inlined_plugins.clone())?);
|
||||
let inline_plugins_acl = inline_plugins(out_dir, attributes.inlined_plugins.clone())?;
|
||||
|
||||
acl_manifests.extend(inline_plugins_acl.manifests);
|
||||
|
||||
let acl_manifests_path = save_acl_manifests(&acl_manifests)?;
|
||||
fs::copy(acl_manifests_path, out_dir.join(ACL_MANIFESTS_FILE_NAME))?;
|
||||
@@ -406,7 +432,12 @@ pub fn build(out_dir: &Path, target: Target, attributes: &Attributes) -> super::
|
||||
let capabilities_path = save_capabilities(&capabilities)?;
|
||||
fs::copy(capabilities_path, out_dir.join(CAPABILITIES_FILE_NAME))?;
|
||||
|
||||
tauri_utils::plugin::save_global_api_scripts_paths(out_dir);
|
||||
let mut permissions_map = inline_plugins_acl.permission_files;
|
||||
if has_app_manifest {
|
||||
permissions_map.insert(APP_ACL_KEY.to_string(), app_acl.permission_files);
|
||||
}
|
||||
|
||||
tauri_utils::acl::build::generate_allowed_commands(out_dir, Some(capabilities), permissions_map)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! [](https://tauri.app)
|
||||
//!
|
||||
//! This applies the macros at build-time in order to rig some special features needed by `cargo`.
|
||||
|
||||
#![doc(
|
||||
@@ -70,7 +68,7 @@ fn copy_binaries(
|
||||
.to_string_lossy()
|
||||
.replace(&format!("-{target_triple}"), "");
|
||||
|
||||
if package_name.map_or(false, |n| n == &file_name) {
|
||||
if package_name == Some(&file_name) {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Cannot define a sidecar with the same name as the Cargo package name `{}`. Please change the sidecar name in the filesystem and the Tauri configuration.",
|
||||
file_name
|
||||
@@ -369,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
|
||||
@@ -421,23 +421,26 @@ pub fn is_dev() -> bool {
|
||||
|
||||
/// Run all build time helpers for your Tauri Application.
|
||||
///
|
||||
/// The current helpers include the following:
|
||||
/// * Generates a Windows Resource file when targeting Windows.
|
||||
/// To provide extra configuration, such as [`AppManifest::commands`]
|
||||
/// for fine-grained control over command permissions, see [`try_build`].
|
||||
/// See [`Attributes`] for the complete list of configuration options.
|
||||
///
|
||||
/// # Platforms
|
||||
///
|
||||
/// [`build()`] should be called inside of `build.rs` regardless of the platform:
|
||||
/// * New helpers may target more platforms in the future.
|
||||
/// * Platform specific code is handled by the helpers automatically.
|
||||
/// * A build script is required in order to activate some cargo environmental variables that are
|
||||
/// used when generating code and embedding assets - so [`build()`] may as well be called.
|
||||
/// [`build()`] should be called inside of `build.rs` regardless of the platform, so **DO NOT** use a [conditional compilation]
|
||||
/// check that prevents it from running on any of your targets.
|
||||
///
|
||||
/// In short, this is saying don't put the call to [`build()`] behind a `#[cfg(windows)]`.
|
||||
/// Platform specific code is handled by the helpers automatically.
|
||||
///
|
||||
/// A build script is required in order to activate some cargo environmental variables that are
|
||||
/// used when generating code and embedding assets.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If any of the build time helpers fail, they will [`std::panic!`] with the related error message.
|
||||
/// This is typically desirable when running inside a build script; see [`try_build`] for no panics.
|
||||
///
|
||||
/// [conditional compilation]: https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/conditional-compilation.html
|
||||
pub fn build() {
|
||||
if let Err(error) = try_build(Attributes::default()) {
|
||||
let error = format!("{error:#}");
|
||||
@@ -452,18 +455,12 @@ pub fn build() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-panicking [`build()`].
|
||||
/// Same as [`build()`], but takes an extra configuration argument, and does not panic.
|
||||
#[allow(unused_variables)]
|
||||
pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
use anyhow::anyhow;
|
||||
|
||||
println!("cargo:rerun-if-env-changed=TAURI_CONFIG");
|
||||
#[cfg(feature = "config-json")]
|
||||
println!("cargo:rerun-if-changed=tauri.conf.json");
|
||||
#[cfg(feature = "config-json5")]
|
||||
println!("cargo:rerun-if-changed=tauri.conf.json5");
|
||||
#[cfg(feature = "config-toml")]
|
||||
println!("cargo:rerun-if-changed=Tauri.toml");
|
||||
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let mobile = target_os == "ios" || target_os == "android";
|
||||
@@ -473,12 +470,11 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
let target_triple = env::var("TARGET").unwrap();
|
||||
let target = tauri_utils::platform::Target::from_triple(&target_triple);
|
||||
|
||||
let (config, merged_config_path) =
|
||||
tauri_utils::config::parse::read_from(target, env::current_dir().unwrap())?;
|
||||
if let Some(merged_config_path) = merged_config_path {
|
||||
println!("cargo:rerun-if-changed={}", merged_config_path.display());
|
||||
let (mut config, config_paths) =
|
||||
tauri_utils::config::parse::read_from(target, &env::current_dir().unwrap())?;
|
||||
for config_file_path in config_paths {
|
||||
println!("cargo:rerun-if-changed={}", config_file_path.display());
|
||||
}
|
||||
let mut config = serde_json::from_value(config)?;
|
||||
if let Ok(env) = env::var("TAURI_CONFIG") {
|
||||
let merge_config: serde_json::Value = serde_json::from_str(&env)?;
|
||||
json_patch::merge(&mut config, &merge_config);
|
||||
@@ -508,19 +504,8 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
|
||||
cfg_alias("dev", is_dev());
|
||||
|
||||
let ws_path = get_workspace_dir()?;
|
||||
let mut manifest =
|
||||
Manifest::<cargo_toml::Value>::from_slice_with_metadata(&fs::read("Cargo.toml")?)?;
|
||||
|
||||
if let Ok(ws_manifest) = Manifest::from_path(ws_path.join("Cargo.toml")) {
|
||||
Manifest::complete_from_path_and_workspace(
|
||||
&mut manifest,
|
||||
Path::new("Cargo.toml"),
|
||||
Some((&ws_manifest, ws_path.as_path())),
|
||||
)?;
|
||||
} else {
|
||||
Manifest::complete_from_path(&mut manifest, Path::new("Cargo.toml"))?;
|
||||
}
|
||||
let cargo_toml_path = Path::new("Cargo.toml").canonicalize()?;
|
||||
let mut manifest = Manifest::<cargo_toml::Value>::from_path_with_metadata(cargo_toml_path)?;
|
||||
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
@@ -528,6 +513,8 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
|
||||
acl::build(&out_dir, target, &attributes)?;
|
||||
|
||||
tauri_utils::plugin::save_global_api_scripts_paths(&out_dir, None);
|
||||
|
||||
println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}");
|
||||
// when running codegen in this build script, we need to access the env var directly
|
||||
env::set_var("TAURI_ENV_TARGET_TRIPLE", &target_triple);
|
||||
@@ -543,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),
|
||||
@@ -626,7 +613,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
|
||||
if let Some(version_str) = &config.version {
|
||||
if let Ok(v) = Version::parse(version_str) {
|
||||
let version = v.major << 48 | v.minor << 32 | v.patch << 16;
|
||||
let version = (v.major << 48) | (v.minor << 32) | (v.patch << 16);
|
||||
res.set_version_info(VersionInfo::FILEVERSION, version);
|
||||
res.set_version_info(VersionInfo::PRODUCTVERSION, version);
|
||||
}
|
||||
@@ -636,6 +623,17 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
res.set("ProductName", product_name);
|
||||
}
|
||||
|
||||
let company_name = config.bundle.publisher.unwrap_or_else(|| {
|
||||
config
|
||||
.identifier
|
||||
.split('.')
|
||||
.nth(1)
|
||||
.unwrap_or(&config.identifier)
|
||||
.to_string()
|
||||
});
|
||||
|
||||
res.set("CompanyName", &company_name);
|
||||
|
||||
let file_description = config
|
||||
.product_name
|
||||
.or_else(|| manifest.package.as_ref().map(|p| p.name.clone()))
|
||||
@@ -688,7 +686,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
}
|
||||
}
|
||||
"msvc" => {
|
||||
if env::var("STATIC_VCRUNTIME").map_or(false, |v| v == "true") {
|
||||
if env::var("STATIC_VCRUNTIME").is_ok_and(|v| v == "true") {
|
||||
static_vcruntime::build();
|
||||
}
|
||||
}
|
||||
@@ -703,23 +701,3 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct CargoMetadata {
|
||||
workspace_root: PathBuf,
|
||||
}
|
||||
|
||||
fn get_workspace_dir() -> Result<PathBuf> {
|
||||
let output = std::process::Command::new("cargo")
|
||||
.args(["metadata", "--no-deps", "--format-version", "1"])
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"cargo metadata command exited with a non zero exit code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
));
|
||||
}
|
||||
|
||||
Ok(serde_json::from_slice::<CargoMetadata>(&output.stdout)?.workspace_root)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,140 @@
|
||||
# Changelog
|
||||
|
||||
## \[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
|
||||
|
||||
- [`b0babb6df`](https://www.github.com/tauri-apps/tauri/commit/b0babb6df12dafe45c21a2c9c424fd86ffd75ca7) ([#12938](https://www.github.com/tauri-apps/tauri/pull/12938)) Added hebrew translation for the custom Tauri messages in the NSIS bundle.
|
||||
- [`0aa48fb9e`](https://www.github.com/tauri-apps/tauri/commit/0aa48fb9e4b9d7b5bf3522000a76ebc1836394ed) ([#13030](https://www.github.com/tauri-apps/tauri/pull/13030)) Added `bundleVersion` to iOS and macOS configuration to support specifying a `CFBundleVersion`.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`8d994f60f`](https://www.github.com/tauri-apps/tauri/commit/8d994f60fe05ec0f45cbe926506bbe10b0d36e3c) ([#11676](https://www.github.com/tauri-apps/tauri/pull/11676)) Sign NSIS and WiX DLLs when bundling
|
||||
- [`8d994f60f`](https://www.github.com/tauri-apps/tauri/commit/8d994f60fe05ec0f45cbe926506bbe10b0d36e3c) ([#11676](https://www.github.com/tauri-apps/tauri/pull/11676)) Sign DLLs from resources.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`9ea76503d`](https://www.github.com/tauri-apps/tauri/commit/9ea76503dcf8da11fab65550f4ab8d3565a424ef) ([#13186](https://www.github.com/tauri-apps/tauri/pull/13186)) Fix NSIS bundler can't include resources and sidecars with `$` in the path
|
||||
- [`2dccfab53`](https://www.github.com/tauri-apps/tauri/commit/2dccfab5321fef55d45f3a4c674b6151b1c4424a) ([#13236](https://www.github.com/tauri-apps/tauri/pull/13236)) Fix `fileAssociations` missing `LSHandlerRank` on macOS.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.4.0`
|
||||
|
||||
## \[2.3.1]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`2138bbc21`](https://www.github.com/tauri-apps/tauri/commit/2138bbc21294785df5f4144670104387289f79c1) ([#13087](https://www.github.com/tauri-apps/tauri/pull/13087) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix NSIS installer displaying in wrong language if `SpanishInternational` is included
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.3.1`
|
||||
|
||||
## \[2.3.0]
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`f981a5ee8`](https://www.github.com/tauri-apps/tauri/commit/f981a5ee8b292b9ea09329f60cecc7f688dda734) ([#12602](https://www.github.com/tauri-apps/tauri/pull/12602) by [@kxxt](https://www.github.com/tauri-apps/tauri/../../kxxt)) Add basic support for linux riscv64 platform.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`3626b7a92`](https://www.github.com/tauri-apps/tauri/commit/3626b7a92be2890a82e8d5bd00d13887e199ea4a) ([#12759](https://www.github.com/tauri-apps/tauri/pull/12759) by [@ninjadev64](https://www.github.com/tauri-apps/tauri/../../ninjadev64)) Fix resources being bundled to the wrong path during RPM bundling when resources are specified as a map.
|
||||
- [`2b960dfd9`](https://www.github.com/tauri-apps/tauri/commit/2b960dfd9fdc995bd6474958c05783ff53b64b7e) ([#12643](https://www.github.com/tauri-apps/tauri/pull/12643) by [@animeshchaudhri](https://www.github.com/tauri-apps/tauri/../../animeshchaudhri)) Remove the autostart plugin registry entry when the app is uninstalled (NSIS only).
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.3.0`
|
||||
|
||||
## \[2.2.4]
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`5eba0785c`](https://www.github.com/tauri-apps/tauri/commit/5eba0785c461a0d0bec47653eaf6ccdf5f05d347) ([#12605](https://www.github.com/tauri-apps/tauri/pull/12605) by [@niusia-ua](https://www.github.com/tauri-apps/tauri/../../niusia-ua)) Added Ukrainian translation for the custom tauri messages in the nsis bundle
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.2.0`
|
||||
- Upgraded to `tauri-macos-sign@2.1.0`
|
||||
|
||||
## \[2.2.3]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`de8600b4d`](https://www.github.com/tauri-apps/tauri/commit/de8600b4d9a04e809e078c8aea61825d1328201f) ([#12471](https://www.github.com/tauri-apps/tauri/pull/12471) by [@anatawa12](https://www.github.com/tauri-apps/tauri/../../anatawa12)) Bumped `nsis-tauri-utils` to `0.4.2` which fixes the following bugs:
|
||||
|
||||
- Fixed launch on start checkbox in nsis installer does not work well with applications that require elevated permissions
|
||||
- Fixed nsis installer may fail to install if launched by updater plugin
|
||||
- [`fbe7c9ead`](https://www.github.com/tauri-apps/tauri/commit/fbe7c9ead76e71ca258c6f48bbb62185fcc37b1c) ([#12466](https://www.github.com/tauri-apps/tauri/pull/12466) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the compiled AppImage to miss webkitgtk's internal `libwebkit2gtkinjectedbundle.so` file.
|
||||
- [`f5a59b93b`](https://www.github.com/tauri-apps/tauri/commit/f5a59b93bfefb43ff131a7870b3c5d5e48c1ca1e) ([#12136](https://www.github.com/tauri-apps/tauri/pull/12136) by [@unknovvn](https://www.github.com/tauri-apps/tauri/../../unknovvn)) The NSIS bundler will now replace non-numeric build metadata with `0` instead of returning an error.
|
||||
- [`9dac2863a`](https://www.github.com/tauri-apps/tauri/commit/9dac2863afa70fb0bcddf859b284afba917f28ae) ([#12323](https://www.github.com/tauri-apps/tauri/pull/12323) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Skip signing the .dmg if self signing via `"signingIdentity": "-"` is used.
|
||||
- [`b8eb28877`](https://www.github.com/tauri-apps/tauri/commit/b8eb28877fe822dbe17999fc8af98ed7d0983679) ([#12427](https://www.github.com/tauri-apps/tauri/pull/12427) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Clean up `Software\${MANUFACTURER}\${PRODUCTNAME}` registry key in the NSIS uninstaller if "Delete application data" option is checked when uninstalling.
|
||||
|
||||
## \[2.2.2]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`72748cc45`](https://www.github.com/tauri-apps/tauri/commit/72748cc45cf670dd03c86c8deceb5942598f5ad9) ([#12365](https://www.github.com/tauri-apps/tauri/pull/12365) by [@don41382](https://www.github.com/tauri-apps/tauri/../../don41382)) Fixed an issue that caused the `.msi` installer not to lookup the `INSTALLDIR` set in the `nsis` installer.
|
||||
- [`cf771bf69`](https://www.github.com/tauri-apps/tauri/commit/cf771bf69aa26b62d11a54a69131c631505d8c55) ([#12402](https://www.github.com/tauri-apps/tauri/pull/12402) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the .msi installer to not contain root resources when there were .dll files present in the target directory.
|
||||
- [`07ccdc499`](https://www.github.com/tauri-apps/tauri/commit/07ccdc499c3240e7240be3abf95ef2d7d00b2dc7) ([#12324](https://www.github.com/tauri-apps/tauri/pull/12324) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue leading to NSIS based installers to not contain the `WebView2Loader.dll` file when targetting `windows-gnu`.
|
||||
|
||||
## \[2.2.1]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`cd1d026f9`](https://www.github.com/tauri-apps/tauri/commit/cd1d026f9799c26b04acb64f49e7ee0a8b193049) ([#11961](https://www.github.com/tauri-apps/tauri/pull/11961) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix tauri fails to build if the project path contains glob characters
|
||||
|
||||
## \[2.2.0]
|
||||
|
||||
### New Features
|
||||
|
||||
- [`cccb308c7`](https://www.github.com/tauri-apps/tauri/commit/cccb308c7b559b0838138d6cea280665f060c925) ([#11562](https://www.github.com/tauri-apps/tauri/pull/11562) by [@jLynx](https://www.github.com/tauri-apps/tauri/../../jLynx)) Generate signature for `.deb` packages when `createUpdaterArtifacts` option is enabled.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`93a3a043d`](https://www.github.com/tauri-apps/tauri/commit/93a3a043d39cc96515d51d98beeb14261d3a246b) ([#11727](https://www.github.com/tauri-apps/tauri/pull/11727) by [@Kiyozz](https://www.github.com/tauri-apps/tauri/../../Kiyozz)) Add support for `Portuguese` language for NSIS windows installer.
|
||||
- [`53f808674`](https://www.github.com/tauri-apps/tauri/commit/53f808674b2c0012bc44a41ced90e742afbb41e8) ([#11799](https://www.github.com/tauri-apps/tauri/pull/11799) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler now reads the `TAURI_BUNDLER_DMG_IGNORE_CI` env var to decide whether to check for `CI: true` when building DMG files.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.1.1`
|
||||
|
||||
## \[2.1.0]
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tauri-bundler"
|
||||
version = "2.1.0"
|
||||
version = "2.5.2"
|
||||
authors = [
|
||||
"George Burton <burtonageo@gmail.com>",
|
||||
"Tauri Programme within The Commons Conservancy",
|
||||
@@ -15,57 +15,59 @@ rust-version = "1.77.2"
|
||||
exclude = ["CHANGELOG.md", "/target", "rustfmt.toml"]
|
||||
|
||||
[dependencies]
|
||||
tauri-utils = { version = "2.1.0", path = "../tauri-utils", features = [
|
||||
tauri-utils = { version = "2.6.0", path = "../tauri-utils", features = [
|
||||
"resources",
|
||||
] }
|
||||
image = "0.25.0"
|
||||
flate2 = "1.0"
|
||||
anyhow = "1.0"
|
||||
image = "0.25"
|
||||
flate2 = "1"
|
||||
anyhow = "1"
|
||||
thiserror = "2"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
strsim = "0.11.0"
|
||||
tar = "0.4.40"
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
strsim = "0.11"
|
||||
tar = "0.4"
|
||||
walkdir = "2"
|
||||
handlebars = "6"
|
||||
tempfile = "3.10.1"
|
||||
tempfile = "3"
|
||||
log = { version = "0.4.21", features = ["kv"] }
|
||||
dirs = "5"
|
||||
dirs = "6"
|
||||
os_pipe = "1"
|
||||
ureq = { version = "2.9.6", default-features = false, features = [
|
||||
"socks-proxy",
|
||||
] }
|
||||
ureq = { version = "3", default-features = false, features = ["socks-proxy"] }
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
hex = "0.4"
|
||||
semver = "1"
|
||||
sha1 = "0.10"
|
||||
sha2 = "0.10"
|
||||
zip = { version = "2.0", 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"
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies]
|
||||
bitness = "0.4"
|
||||
windows-registry = "0.3.0"
|
||||
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.0.1", path = "../tauri-macos-sign" }
|
||||
tauri-macos-sign = { version = "2.1.0", path = "../tauri-macos-sign" }
|
||||
|
||||
[target."cfg(target_os = \"linux\")".dependencies]
|
||||
heck = "0.5"
|
||||
ar = "0.9.0"
|
||||
md5 = "0.7.0"
|
||||
rpm = "0.15.0"
|
||||
ar = "0.9"
|
||||
md5 = "0.8"
|
||||
rpm = { version = "0.16", features = ["bzip2-compression"] }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
which = "7"
|
||||
|
||||
[lib]
|
||||
name = "tauri_bundler"
|
||||
@@ -75,4 +77,4 @@ path = "src/lib.rs"
|
||||
default = ["rustls"]
|
||||
native-tls = ["ureq/native-tls"]
|
||||
native-tls-vendored = ["native-tls", "native-tls/vendored"]
|
||||
rustls = ["ureq/tls"]
|
||||
rustls = ["ureq/rustls"]
|
||||
|
||||
@@ -4,25 +4,49 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
mod category;
|
||||
mod common;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
mod path_utils;
|
||||
mod platform;
|
||||
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, MacOsSettings, PackageSettings, PackageType, Position, RpmSettings, Settings,
|
||||
SettingsBuilder, Size, UpdaterSettings,
|
||||
DmgSettings, IosSettings, MacOsSettings, PackageSettings, PackageType, Position, RpmSettings,
|
||||
Settings, SettingsBuilder, Size, UpdaterSettings,
|
||||
},
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -50,21 +74,20 @@ 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 matches!(target_os, TargetPlatform::Windows) {
|
||||
if settings.can_sign() {
|
||||
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)?;
|
||||
}
|
||||
@@ -72,8 +95,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
// Sign the sidecar binaries
|
||||
for bin in settings.external_binaries() {
|
||||
let path = bin?;
|
||||
let skip =
|
||||
std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").map_or(false, |v| v == "true");
|
||||
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
@@ -95,6 +117,12 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
}
|
||||
}
|
||||
|
||||
let main_binary = settings
|
||||
.binaries()
|
||||
.iter()
|
||||
.find(|b| b.main())
|
||||
.expect("Main binary missing in settings");
|
||||
|
||||
let mut bundles = Vec::<Bundle>::new();
|
||||
for package_type in &package_types {
|
||||
// bundle was already built! e.g. DMG already built .app
|
||||
@@ -102,6 +130,16 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(e) = patch_binary(&settings.binary_path(main_binary), 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.can_sign() {
|
||||
let bin_path = settings.binary_path(main_binary);
|
||||
windows::sign::try_sign(&bin_path, settings)?;
|
||||
}
|
||||
|
||||
let bundle_paths = match package_type {
|
||||
#[cfg(target_os = "macos")]
|
||||
PackageType::MacOsBundle => macos::app::bundle_project(settings)?,
|
||||
@@ -151,6 +189,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
| PackageType::MacOsBundle
|
||||
| PackageType::Nsis
|
||||
| PackageType::WindowsMsi
|
||||
| PackageType::Deb
|
||||
)
|
||||
} else {
|
||||
matches!(package_type, PackageType::MacOsBundle)
|
||||
@@ -166,7 +205,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
// Self contained updater, no need to zip
|
||||
matches!(
|
||||
package_type,
|
||||
PackageType::AppImage | PackageType::Nsis | PackageType::WindowsMsi
|
||||
PackageType::AppImage | PackageType::Nsis | PackageType::WindowsMsi | PackageType::Deb
|
||||
)
|
||||
})
|
||||
{
|
||||
|
||||
@@ -249,7 +249,7 @@ struct AppCategoryVisitor {
|
||||
did_you_mean: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl<'d> serde::de::Visitor<'d> for AppCategoryVisitor {
|
||||
impl serde::de::Visitor<'_> for AppCategoryVisitor {
|
||||
type Value = AppCategory;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
||||
270
crates/tauri-bundler/src/bundle/linux/appimage.rs
Normal file
270
crates/tauri-bundler/src/bundle/linux/appimage.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::debian;
|
||||
use crate::{
|
||||
bundle::settings::Arch,
|
||||
utils::{fs_utils, http_utils::download, CommandExt},
|
||||
Settings,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
/// Bundles the project.
|
||||
/// Returns a vector of PathBuf that shows where the AppImage was created.
|
||||
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
// generate the deb binary name
|
||||
let appimage_arch: &str = match settings.binary_arch() {
|
||||
Arch::X86_64 => "amd64",
|
||||
Arch::X86 => "i386",
|
||||
Arch::AArch64 => "aarch64",
|
||||
Arch::Armhf => "armhf",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"Unsupported architecture: {target:?}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let tools_arch = if settings.binary_arch() == Arch::Armhf {
|
||||
"armhf"
|
||||
} else {
|
||||
settings.target().split('-').next().unwrap()
|
||||
};
|
||||
|
||||
let output_path = settings.project_out_directory().join("bundle/appimage");
|
||||
if output_path.exists() {
|
||||
fs::remove_dir_all(&output_path)?;
|
||||
}
|
||||
|
||||
let tools_path = settings
|
||||
.local_tools_directory()
|
||||
.map(|d| d.join(".tauri"))
|
||||
.unwrap_or_else(|| {
|
||||
dirs::cache_dir().map_or_else(|| output_path.to_path_buf(), |p| p.join("tauri"))
|
||||
});
|
||||
|
||||
fs::create_dir_all(&tools_path)?;
|
||||
|
||||
let linuxdeploy_path = prepare_tools(&tools_path, tools_arch)?;
|
||||
|
||||
let package_dir = settings.project_out_directory().join("bundle/appimage_deb");
|
||||
|
||||
let main_binary = settings.main_binary()?;
|
||||
let product_name = settings.product_name();
|
||||
|
||||
let mut settings = settings.clone();
|
||||
if main_binary.name().contains(' ') {
|
||||
let main_binary_path = settings.binary_path(main_binary);
|
||||
let project_out_directory = settings.project_out_directory();
|
||||
|
||||
let main_binary_name_kebab = heck::AsKebabCase(main_binary.name()).to_string();
|
||||
let new_path = project_out_directory.join(&main_binary_name_kebab);
|
||||
fs::copy(main_binary_path, new_path)?;
|
||||
|
||||
let main_binary = settings.main_binary_mut()?;
|
||||
main_binary.set_name(main_binary_name_kebab);
|
||||
}
|
||||
|
||||
// generate deb_folder structure
|
||||
let (data_dir, icons) = debian::generate_data(&settings, &package_dir)
|
||||
.with_context(|| "Failed to build data folders and files")?;
|
||||
fs_utils::copy_custom_files(&settings.appimage().files, &data_dir)
|
||||
.with_context(|| "Failed to copy custom files")?;
|
||||
|
||||
fs::create_dir_all(&output_path)?;
|
||||
let app_dir_path = output_path.join(format!("{}.AppDir", settings.product_name()));
|
||||
let appimage_filename = format!(
|
||||
"{}_{}_{}.AppImage",
|
||||
settings.product_name(),
|
||||
settings.version_string(),
|
||||
appimage_arch
|
||||
);
|
||||
let appimage_path = output_path.join(&appimage_filename);
|
||||
fs_utils::create_dir(&app_dir_path, true)?;
|
||||
|
||||
fs::create_dir_all(&tools_path)?;
|
||||
let larger_icon = icons
|
||||
.iter()
|
||||
.filter(|i| i.width == i.height)
|
||||
.max_by_key(|i| i.width)
|
||||
.expect("couldn't find a square icon to use as AppImage icon");
|
||||
let larger_icon_path = larger_icon
|
||||
.path
|
||||
.strip_prefix(package_dir.join("data"))
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
log::info!(action = "Bundling"; "{} ({})", appimage_filename, appimage_path.display());
|
||||
|
||||
let app_dir_usr = app_dir_path.join("usr/");
|
||||
let app_dir_usr_bin = app_dir_usr.join("bin/");
|
||||
let app_dir_usr_lib = app_dir_usr.join("lib/");
|
||||
|
||||
fs_utils::copy_dir(&data_dir.join("usr/"), &app_dir_usr)?;
|
||||
|
||||
// Using create_dir_all for a single dir so we don't get errors if the path already exists
|
||||
fs::create_dir_all(&app_dir_usr_bin)?;
|
||||
fs::create_dir_all(app_dir_usr_lib)?;
|
||||
|
||||
// Copy bins and libs that linuxdeploy doesn't know about
|
||||
|
||||
// we also check if the user may have provided their own copy already
|
||||
// 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")?;
|
||||
}
|
||||
|
||||
// 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")?;
|
||||
}
|
||||
|
||||
let search_dirs = [
|
||||
match settings.binary_arch() {
|
||||
Arch::X86_64 => "/usr/lib/x86_64-linux-gnu/",
|
||||
Arch::X86 => "/usr/lib/i386-linux-gnu/",
|
||||
Arch::AArch64 => "/usr/lib/aarch64-linux-gnu/",
|
||||
Arch::Armhf => "/usr/lib/arm-linux-gnueabihf/",
|
||||
_ => unreachable!(),
|
||||
},
|
||||
"/usr/lib64",
|
||||
"/usr/lib",
|
||||
"/usr/libexec",
|
||||
];
|
||||
|
||||
for file in [
|
||||
"WebKitNetworkProcess",
|
||||
"WebKitWebProcess",
|
||||
"injected-bundle/libwebkit2gtkinjectedbundle.so",
|
||||
] {
|
||||
for source in search_dirs.map(PathBuf::from) {
|
||||
// TODO: Check if it's the same dir name on all systems
|
||||
let source = source.join("webkit2gtk-4.1").join(file);
|
||||
if source.exists() {
|
||||
fs_utils::copy_file(
|
||||
&source,
|
||||
&app_dir_path.join(source.strip_prefix("/").unwrap()),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs::copy(
|
||||
tools_path.join(format!("AppRun-{tools_arch}")),
|
||||
app_dir_path.join("AppRun"),
|
||||
)?;
|
||||
fs::copy(
|
||||
app_dir_path.join(larger_icon_path),
|
||||
app_dir_path.join(format!("{product_name}.png")),
|
||||
)?;
|
||||
std::os::unix::fs::symlink(
|
||||
app_dir_path.join(format!("{product_name}.png")),
|
||||
app_dir_path.join(".DirIcon"),
|
||||
)?;
|
||||
std::os::unix::fs::symlink(
|
||||
app_dir_path.join(format!("usr/share/applications/{product_name}.desktop")),
|
||||
app_dir_path.join(format!("{product_name}.desktop")),
|
||||
)?;
|
||||
|
||||
let log_level = match settings.log_level() {
|
||||
log::Level::Error => "3",
|
||||
log::Level::Warn => "2",
|
||||
log::Level::Info => "1",
|
||||
_ => "0",
|
||||
};
|
||||
|
||||
let mut cmd = Command::new(linuxdeploy_path);
|
||||
cmd.env("OUTPUT", &appimage_path);
|
||||
cmd.env("ARCH", tools_arch);
|
||||
cmd.args([
|
||||
"--appimage-extract-and-run",
|
||||
"--verbosity",
|
||||
log_level,
|
||||
"--appdir",
|
||||
&app_dir_path.display().to_string(),
|
||||
"--plugin",
|
||||
"gtk",
|
||||
]);
|
||||
if settings.appimage().bundle_media_framework {
|
||||
cmd.args(["--plugin", "gstreamer"]);
|
||||
}
|
||||
cmd.args(["--output", "appimage"]);
|
||||
|
||||
// Linuxdeploy logs everything into stderr so we have to ignore the output ourselves here
|
||||
if settings.log_level() == log::Level::Error {
|
||||
log::debug!(action = "Running"; "Command `linuxdeploy {}`", cmd.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
|
||||
if !cmd.output()?.status.success() {
|
||||
return Err(crate::Error::GenericError(
|
||||
"failed to run linuxdeploy".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
cmd.output_ok()?;
|
||||
}
|
||||
|
||||
fs::remove_dir_all(&package_dir)?;
|
||||
Ok(vec![appimage_path])
|
||||
}
|
||||
|
||||
// returns the linuxdeploy path to keep linuxdeploy_arch contained
|
||||
fn prepare_tools(tools_path: &Path, arch: &str) -> crate::Result<PathBuf> {
|
||||
let apprun = tools_path.join(format!("AppRun-{arch}"));
|
||||
if !apprun.exists() {
|
||||
let data = download(&format!(
|
||||
"https://github.com/tauri-apps/binary-releases/releases/download/apprun-old/AppRun-{arch}"
|
||||
))?;
|
||||
write_and_make_executable(&apprun, data)?;
|
||||
}
|
||||
|
||||
let linuxdeploy_arch = if arch == "i686" { "i383" } else { arch };
|
||||
let linuxdeploy = tools_path.join(format!("linuxdeploy-{linuxdeploy_arch}.AppImage"));
|
||||
if !linuxdeploy.exists() {
|
||||
let data = download(&format!("https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-{linuxdeploy_arch}.AppImage"))?;
|
||||
write_and_make_executable(&linuxdeploy, data)?;
|
||||
}
|
||||
|
||||
let gtk = tools_path.join("linuxdeploy-plugin-gtk.sh");
|
||||
if !gtk.exists() {
|
||||
let data = download("https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh")?;
|
||||
write_and_make_executable(>k, data)?;
|
||||
}
|
||||
|
||||
let gstreamer = tools_path.join("linuxdeploy-plugin-gstreamer.sh");
|
||||
if !gstreamer.exists() {
|
||||
let data = download("https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh")?;
|
||||
write_and_make_executable(&gstreamer, data)?;
|
||||
}
|
||||
|
||||
// This should prevent linuxdeploy to be detected by appimage integration tools
|
||||
let _ = Command::new("dd")
|
||||
.args([
|
||||
"if=/dev/zero",
|
||||
"bs=1",
|
||||
"count=3",
|
||||
"seek=8",
|
||||
"conv=notrunc",
|
||||
&format!("of={}", linuxdeploy.display()),
|
||||
])
|
||||
.output();
|
||||
|
||||
Ok(linuxdeploy)
|
||||
}
|
||||
|
||||
fn write_and_make_executable(path: &Path, data: Vec<u8>) -> std::io::Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
fs::write(path, data)?;
|
||||
fs::set_permissions(path, fs::Permissions::from_mode(0o770))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
export ARCH={{arch}}
|
||||
APPIMAGE_BUNDLE_XDG_OPEN=${APPIMAGE_BUNDLE_XDG_OPEN-0}
|
||||
APPIMAGE_BUNDLE_XDG_MIME=${APPIMAGE_BUNDLE_XDG_MIME-0}
|
||||
APPIMAGE_BUNDLE_GSTREAMER=${APPIMAGE_BUNDLE_GSTREAMER-0}
|
||||
TAURI_TRAY_LIBRARY_PATH=${TAURI_TRAY_LIBRARY_PATH-0}
|
||||
|
||||
if [ "$ARCH" == "i686" ]; then
|
||||
linuxdeploy_arch="i386"
|
||||
else
|
||||
linuxdeploy_arch="$ARCH"
|
||||
fi
|
||||
|
||||
mkdir -p "{{product_name}}.AppDir"
|
||||
cp -r ../appimage_deb/data/usr "{{product_name}}.AppDir"
|
||||
|
||||
cd "{{product_name}}.AppDir"
|
||||
mkdir -p "usr/bin"
|
||||
mkdir -p "usr/lib"
|
||||
|
||||
if [[ "$APPIMAGE_BUNDLE_XDG_OPEN" != "0" ]] && [[ -f "/usr/bin/xdg-open" ]]; then
|
||||
echo "Copying /usr/bin/xdg-open"
|
||||
cp /usr/bin/xdg-open usr/bin
|
||||
fi
|
||||
|
||||
if [[ "$APPIMAGE_BUNDLE_XDG_MIME" != "0" ]] && [[ -f "/usr/bin/xdg-mime" ]]; then
|
||||
echo "Copying /usr/bin/xdg-mime"
|
||||
cp /usr/bin/xdg-mime usr/bin
|
||||
fi
|
||||
|
||||
if [[ "$TAURI_TRAY_LIBRARY_PATH" != "0" ]]; then
|
||||
echo "Copying appindicator library ${TAURI_TRAY_LIBRARY_PATH}"
|
||||
cp ${TAURI_TRAY_LIBRARY_PATH} usr/lib
|
||||
# It looks like we're practicing good hygiene by adding the ABI version.
|
||||
# But for compatibility we'll symlink this file to what we did before.
|
||||
# Specifically this prevents breaking libappindicator-sys v0.7.1 and v0.7.2.
|
||||
if [[ "$TAURI_TRAY_LIBRARY_PATH" == *.so.1 ]]; then
|
||||
readonly soname=$(basename "$TAURI_TRAY_LIBRARY_PATH")
|
||||
readonly old_name=$(basename "$TAURI_TRAY_LIBRARY_PATH" .1)
|
||||
echo "Adding compatibility symlink ${old_name} -> ${soname}"
|
||||
ln -s ${soname} usr/lib/${old_name}
|
||||
fi
|
||||
fi
|
||||
|
||||
# Copy WebKit files. Follow symlinks in case `/usr/lib64` is a symlink to `/usr/lib`
|
||||
find -L /usr/lib* -name WebKitNetworkProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true
|
||||
find -L /usr/lib* -name WebKitWebProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true
|
||||
find -L /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true
|
||||
|
||||
( cd "{{tauri_tools_path}}" && ( wget -q -4 -N https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${ARCH} || wget -q -4 -N https://github.com/AppImage/AppImageKit/releases/download/12/AppRun-${ARCH} ) )
|
||||
chmod +x "{{tauri_tools_path}}/AppRun-${ARCH}"
|
||||
|
||||
# We need AppRun to be installed as {{product_name}}.AppDir/AppRun.
|
||||
# Otherwise the linuxdeploy scripts will default to symlinking our main bin instead and will crash on trying to launch.
|
||||
cp "{{tauri_tools_path}}/AppRun-${ARCH}" AppRun
|
||||
|
||||
cp "{{icon_path}}" .DirIcon
|
||||
ln -sf "{{icon_path}}" "{{product_name}}.png"
|
||||
|
||||
ln -sf "usr/share/applications/{{product_name}}.desktop" "{{product_name}}.desktop"
|
||||
|
||||
cd ..
|
||||
|
||||
if [[ "$APPIMAGE_BUNDLE_GSTREAMER" != "0" ]]; then
|
||||
gst_plugin="--plugin gstreamer"
|
||||
wget -q -4 -N "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh"
|
||||
chmod +x linuxdeploy-plugin-gstreamer.sh
|
||||
else
|
||||
gst_plugin=""
|
||||
fi
|
||||
|
||||
( cd "{{tauri_tools_path}}" && wget -q -4 -N https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh )
|
||||
( cd "{{tauri_tools_path}}" && wget -q -4 -N https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-${linuxdeploy_arch}.AppImage )
|
||||
|
||||
chmod +x "{{tauri_tools_path}}/linuxdeploy-plugin-gtk.sh"
|
||||
chmod +x "{{tauri_tools_path}}/linuxdeploy-${linuxdeploy_arch}.AppImage"
|
||||
|
||||
dd if=/dev/zero bs=1 count=3 seek=8 conv=notrunc of="{{tauri_tools_path}}/linuxdeploy-${linuxdeploy_arch}.AppImage"
|
||||
|
||||
OUTPUT="{{appimage_filename}}" "{{tauri_tools_path}}/linuxdeploy-${linuxdeploy_arch}.AppImage" --appimage-extract-and-run --appdir "{{product_name}}.AppDir" --plugin gtk ${gst_plugin} --output appimage
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
common::{self, CommandExt},
|
||||
path_utils,
|
||||
},
|
||||
debian,
|
||||
};
|
||||
use crate::{bundle::settings::Arch, Settings};
|
||||
use anyhow::Context;
|
||||
use handlebars::Handlebars;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
/// Bundles the project.
|
||||
/// Returns a vector of PathBuf that shows where the AppImage was created.
|
||||
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
// generate the deb binary name
|
||||
let arch: &str = match settings.binary_arch() {
|
||||
Arch::X86_64 => "amd64",
|
||||
Arch::X86 => "i386",
|
||||
Arch::AArch64 => "aarch64",
|
||||
Arch::Armhf => "armhf",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"Unsupported architecture: {:?}",
|
||||
target
|
||||
)));
|
||||
}
|
||||
};
|
||||
let package_dir = settings.project_out_directory().join("bundle/appimage_deb");
|
||||
|
||||
let main_binary = settings.main_binary()?;
|
||||
|
||||
let mut settings = settings.clone();
|
||||
if main_binary.name().contains(" ") {
|
||||
let main_binary_path = settings.binary_path(main_binary);
|
||||
let project_out_directory = settings.project_out_directory();
|
||||
|
||||
let main_binary_name_kebab = heck::AsKebabCase(main_binary.name()).to_string();
|
||||
let new_path = project_out_directory.join(&main_binary_name_kebab);
|
||||
fs::copy(main_binary_path, new_path)?;
|
||||
|
||||
let main_binary = settings.main_binary_mut()?;
|
||||
main_binary.set_name(main_binary_name_kebab);
|
||||
}
|
||||
|
||||
// generate deb_folder structure
|
||||
let (data_dir, icons) = debian::generate_data(&settings, &package_dir)
|
||||
.with_context(|| "Failed to build data folders and files")?;
|
||||
common::copy_custom_files(&settings.appimage().files, &data_dir)
|
||||
.with_context(|| "Failed to copy custom files")?;
|
||||
|
||||
let output_path = settings.project_out_directory().join("bundle/appimage");
|
||||
if output_path.exists() {
|
||||
fs::remove_dir_all(&output_path)?;
|
||||
}
|
||||
fs::create_dir_all(output_path.clone())?;
|
||||
let app_dir_path = output_path.join(format!("{}.AppDir", settings.product_name()));
|
||||
let appimage_filename = format!(
|
||||
"{}_{}_{}.AppImage",
|
||||
settings.product_name(),
|
||||
settings.version_string(),
|
||||
arch
|
||||
);
|
||||
let appimage_path = output_path.join(&appimage_filename);
|
||||
path_utils::create(app_dir_path, true)?;
|
||||
|
||||
// setup data to insert into shell script
|
||||
let mut sh_map = BTreeMap::new();
|
||||
sh_map.insert("arch", settings.target().split('-').next().unwrap());
|
||||
sh_map.insert("product_name", settings.product_name());
|
||||
sh_map.insert("appimage_filename", &appimage_filename);
|
||||
|
||||
let tauri_tools_path = settings
|
||||
.local_tools_directory()
|
||||
.map(|d| d.join(".tauri"))
|
||||
.unwrap_or_else(|| {
|
||||
dirs::cache_dir().map_or_else(|| output_path.to_path_buf(), |p| p.join("tauri"))
|
||||
});
|
||||
|
||||
std::fs::create_dir_all(&tauri_tools_path)?;
|
||||
let tauri_tools_path_str = tauri_tools_path.to_string_lossy();
|
||||
sh_map.insert("tauri_tools_path", &tauri_tools_path_str);
|
||||
let larger_icon = icons
|
||||
.iter()
|
||||
.filter(|i| i.width == i.height)
|
||||
.max_by_key(|i| i.width)
|
||||
.expect("couldn't find a square icon to use as AppImage icon");
|
||||
let larger_icon_path = larger_icon
|
||||
.path
|
||||
.strip_prefix(package_dir.join("data"))
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
sh_map.insert("icon_path", &larger_icon_path);
|
||||
|
||||
// initialize shell script template.
|
||||
let mut handlebars = Handlebars::new();
|
||||
handlebars.register_escape_fn(handlebars::no_escape);
|
||||
let temp = handlebars.render_template(include_str!("./appimage"), &sh_map)?;
|
||||
|
||||
// create the shell script file in the target/ folder.
|
||||
let sh_file = output_path.join("build_appimage.sh");
|
||||
|
||||
log::info!(action = "Bundling"; "{} ({})", appimage_filename, appimage_path.display());
|
||||
|
||||
fs::write(&sh_file, temp)?;
|
||||
|
||||
// chmod script for execution
|
||||
Command::new("chmod")
|
||||
.arg("777")
|
||||
.arg(&sh_file)
|
||||
.current_dir(output_path.clone())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.expect("Failed to chmod script");
|
||||
|
||||
// execute the shell script to build the appimage.
|
||||
Command::new(&sh_file)
|
||||
.current_dir(output_path)
|
||||
.output_ok()
|
||||
.context("error running build_appimage.sh")?;
|
||||
|
||||
fs::remove_dir_all(&package_dir)?;
|
||||
Ok(vec![appimage_path])
|
||||
}
|
||||
@@ -23,8 +23,8 @@
|
||||
// metadata, as well as generating the md5sums file. Currently we do not
|
||||
// generate postinst or prerm files.
|
||||
|
||||
use super::{super::common, freedesktop};
|
||||
use crate::{bundle::settings::Arch, Settings};
|
||||
use super::freedesktop;
|
||||
use crate::{bundle::settings::Arch, utils::fs_utils, Settings};
|
||||
use anyhow::Context;
|
||||
use flate2::{write::GzEncoder, Compression};
|
||||
use tar::HeaderMode;
|
||||
@@ -46,10 +46,10 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
Arch::AArch64 => "arm64",
|
||||
Arch::Armhf => "armhf",
|
||||
Arch::Armel => "armel",
|
||||
Arch::Riscv64 => "riscv64",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"Unsupported architecture: {:?}",
|
||||
target
|
||||
"Unsupported architecture: {target:?}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
@@ -73,7 +73,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
|
||||
let (data_dir, _) = generate_data(settings, &package_dir)
|
||||
.with_context(|| "Failed to build data folders and files")?;
|
||||
common::copy_custom_files(&settings.deb().files, &data_dir)
|
||||
fs_utils::copy_custom_files(&settings.deb().files, &data_dir)
|
||||
.with_context(|| "Failed to copy custom files")?;
|
||||
|
||||
// Generate control files.
|
||||
@@ -113,7 +113,7 @@ pub fn generate_data(
|
||||
|
||||
for bin in settings.binaries() {
|
||||
let bin_path = settings.binary_path(bin);
|
||||
common::copy_file(&bin_path, bin_dir.join(bin.name()))
|
||||
fs_utils::copy_file(&bin_path, &bin_dir.join(bin.name()))
|
||||
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ fn generate_changelog_file(settings: &Settings, data_dir: &Path) -> crate::Resul
|
||||
let product_name = settings.product_name();
|
||||
let dest_path = data_dir.join(format!("usr/share/doc/{product_name}/changelog.gz"));
|
||||
|
||||
let changelog_file = common::create_file(&dest_path)?;
|
||||
let changelog_file = fs_utils::create_file(&dest_path)?;
|
||||
let mut gzip_encoder = GzEncoder::new(changelog_file, Compression::new(9));
|
||||
io::copy(&mut src_file, &mut gzip_encoder)?;
|
||||
|
||||
@@ -161,9 +161,9 @@ fn generate_control_file(
|
||||
// For more information about the format of this file, see
|
||||
// https://www.debian.org/doc/debian-policy/ch-controlfields.html
|
||||
let dest_path = control_dir.join("control");
|
||||
let mut file = common::create_file(&dest_path)?;
|
||||
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
|
||||
@@ -182,16 +182,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();
|
||||
@@ -294,7 +294,7 @@ fn create_script_file_from_path(from: &PathBuf, to: &PathBuf) -> crate::Result<(
|
||||
/// for each file within the `data_dir`.
|
||||
fn generate_md5sums(control_dir: &Path, data_dir: &Path) -> crate::Result<()> {
|
||||
let md5sums_path = control_dir.join("md5sums");
|
||||
let mut md5sums_file = common::create_file(&md5sums_path)?;
|
||||
let mut md5sums_file = fs_utils::create_file(&md5sums_path)?;
|
||||
for entry in WalkDir::new(data_dir) {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
@@ -304,7 +304,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)?;
|
||||
@@ -327,7 +327,7 @@ fn copy_resource_files(settings: &Settings, data_dir: &Path) -> crate::Result<()
|
||||
/// Create an empty file at the given path, creating any parent directories as
|
||||
/// needed, then write `data` into the file.
|
||||
fn create_file_with_data<P: AsRef<Path>>(path: P, data: &str) -> crate::Result<()> {
|
||||
let mut file = common::create_file(path.as_ref())?;
|
||||
let mut file = fs_utils::create_file(path.as_ref())?;
|
||||
file.write_all(data.as_bytes())?;
|
||||
file.flush()?;
|
||||
Ok(())
|
||||
@@ -376,7 +376,7 @@ fn create_tar_from_dir<P: AsRef<Path>, W: Write>(src_dir: P, dest_file: W) -> cr
|
||||
fn tar_and_gzip_dir<P: AsRef<Path>>(src_dir: P) -> crate::Result<PathBuf> {
|
||||
let src_dir = src_dir.as_ref();
|
||||
let dest_path = src_dir.with_extension("tar.gz");
|
||||
let dest_file = common::create_file(&dest_path)?;
|
||||
let dest_file = fs_utils::create_file(&dest_path)?;
|
||||
let gzip_encoder = GzEncoder::new(dest_file, Compression::default());
|
||||
let gzip_encoder = create_tar_from_dir(src_dir, gzip_encoder)?;
|
||||
let mut dest_file = gzip_encoder.finish()?;
|
||||
@@ -387,7 +387,7 @@ fn tar_and_gzip_dir<P: AsRef<Path>>(src_dir: P) -> crate::Result<PathBuf> {
|
||||
/// Creates an `ar` archive from the given source files and writes it to the
|
||||
/// given destination path.
|
||||
fn create_archive(srcs: Vec<PathBuf>, dest: &Path) -> crate::Result<()> {
|
||||
let mut builder = ar::Builder::new(common::create_file(dest)?);
|
||||
let mut builder = ar::Builder::new(fs_utils::create_file(dest)?);
|
||||
for path in &srcs {
|
||||
builder.append_path(path)?;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ Categories={{categories}}
|
||||
Comment={{comment}}
|
||||
{{/if}}
|
||||
Exec={{exec}}
|
||||
StartupWMClass={{exec}}
|
||||
Icon={{icon}}
|
||||
Name={{name}}
|
||||
Terminal=false
|
||||
|
||||
@@ -26,8 +26,10 @@ use handlebars::Handlebars;
|
||||
use image::{self, codecs::png::PngDecoder, ImageDecoder};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::bundle::common;
|
||||
use crate::Settings;
|
||||
use crate::{
|
||||
utils::{self, fs_utils},
|
||||
Settings,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Icon {
|
||||
@@ -65,7 +67,7 @@ pub fn list_icon_files(
|
||||
let decoder = PngDecoder::new(BufReader::new(File::open(&icon_path)?))?;
|
||||
let width = decoder.dimensions().0;
|
||||
let height = decoder.dimensions().1;
|
||||
let is_high_density = common::is_retina(&icon_path);
|
||||
let is_high_density = utils::is_retina(&icon_path);
|
||||
let dest_path = get_dest_path(width, height, is_high_density);
|
||||
Icon {
|
||||
width,
|
||||
@@ -84,7 +86,7 @@ pub fn list_icon_files(
|
||||
pub fn copy_icon_files(settings: &Settings, data_dir: &Path) -> crate::Result<Vec<Icon>> {
|
||||
let icons = list_icon_files(settings, data_dir)?;
|
||||
for (icon, src) in &icons {
|
||||
common::copy_file(src, &icon.path)?;
|
||||
fs_utils::copy_file(src, &icon.path)?;
|
||||
}
|
||||
|
||||
Ok(icons.into_keys().collect())
|
||||
@@ -105,7 +107,7 @@ pub fn generate_desktop_file(
|
||||
let path = PathBuf::from("usr/share/applications").join(desktop_file_name);
|
||||
let dest_path = PathBuf::from("/").join(&path);
|
||||
let file_path = data_dir.join(&path);
|
||||
let file = &mut common::create_file(&file_path)?;
|
||||
let file = &mut fs_utils::create_file(&file_path)?;
|
||||
|
||||
let mut handlebars = Handlebars::new();
|
||||
handlebars.register_escape_fn(handlebars::no_escape);
|
||||
@@ -151,7 +153,7 @@ pub fn generate_desktop_file(
|
||||
|
||||
let mime_type = (!mime_type.is_empty()).then_some(mime_type.join(";"));
|
||||
|
||||
let bin_name_exec = if bin_name.contains(" ") {
|
||||
let bin_name_exec = if bin_name.contains(' ') {
|
||||
format!("\"{bin_name}\"")
|
||||
} else {
|
||||
bin_name.to_string()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,10 +29,10 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
Arch::AArch64 => "aarch64",
|
||||
Arch::Armhf => "armhfp",
|
||||
Arch::Armel => "armel",
|
||||
Arch::Riscv64 => "riscv64",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"Unsupported architecture: {:?}",
|
||||
target
|
||||
"Unsupported architecture: {target:?}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
@@ -67,6 +67,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
_ => rpm::CompressionWithLevel::None,
|
||||
})
|
||||
// This matches .deb compression. On a 240MB source binary the bundle will be 100KB larger than rpm's default while reducing build times by ~25%.
|
||||
// TODO: Default to Zstd in v3 to match rpm-rs new default in 0.16
|
||||
.unwrap_or(rpm::CompressionWithLevel::Gzip(6));
|
||||
|
||||
let mut builder = rpm::PackageBuilder::new(&name, version, &license, arch, summary)
|
||||
@@ -185,10 +186,10 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
FileOptions::new(resource_dir.to_string_lossy()).mode(FileMode::Dir { permissions: 0o755 }),
|
||||
)?;
|
||||
// Then add the resources files in that directory
|
||||
for src in settings.resource_files() {
|
||||
let src = src?;
|
||||
let dest = resource_dir.join(tauri_utils::resources::resource_relpath(&src));
|
||||
builder = builder.with_file(&src, FileOptions::new(dest.to_string_lossy()))?;
|
||||
for resource in settings.resource_files().iter() {
|
||||
let resource = resource?;
|
||||
let dest = resource_dir.join(resource.target());
|
||||
builder = builder.with_file(resource.path(), FileOptions::new(dest.to_string_lossy()))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
59
crates/tauri-bundler/src/bundle/linux/util.rs
Normal file
59
crates/tauri-bundler/src/bundle/linux/util.rs
Normal 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
|
||||
}
|
||||
@@ -23,11 +23,13 @@
|
||||
// files into the `Contents` directory of the bundle.
|
||||
|
||||
use super::{
|
||||
super::common::{self, CommandExt},
|
||||
icon::create_icns_file,
|
||||
sign::{notarize, notarize_auth, sign, NotarizeAuthError, SignTarget},
|
||||
};
|
||||
use crate::Settings;
|
||||
use crate::{
|
||||
utils::{fs_utils, CommandExt},
|
||||
Settings,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
@@ -63,15 +65,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
|
||||
if app_bundle_path.exists() {
|
||||
fs::remove_dir_all(&app_bundle_path)
|
||||
.with_context(|| format!("Failed to remove old {}", app_product_name))?;
|
||||
.with_context(|| format!("Failed to remove old {app_product_name}"))?;
|
||||
}
|
||||
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)
|
||||
.with_context(|| format!("Failed to create bundle directory at {bundle_directory:?}"))?;
|
||||
|
||||
let resources_dir = bundle_directory.join("Resources");
|
||||
let bin_dir = bundle_directory.join("MacOS");
|
||||
@@ -157,8 +155,8 @@ fn copy_binaries_to_bundle(
|
||||
for bin in settings.binaries() {
|
||||
let bin_path = settings.binary_path(bin);
|
||||
let dest_path = dest_dir.join(bin.name());
|
||||
common::copy_file(&bin_path, &dest_path)
|
||||
.with_context(|| format!("Failed to copy binary from {:?}", bin_path))?;
|
||||
fs_utils::copy_file(&bin_path, &dest_path)
|
||||
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
|
||||
paths.push(dest_path);
|
||||
}
|
||||
Ok(paths)
|
||||
@@ -173,11 +171,11 @@ fn copy_custom_files_to_bundle(bundle_directory: &Path, settings: &Settings) ->
|
||||
contents_path
|
||||
};
|
||||
if path.is_file() {
|
||||
common::copy_file(path, bundle_directory.join(contents_path))
|
||||
.with_context(|| format!("Failed to copy file {:?} to {:?}", path, contents_path))?;
|
||||
fs_utils::copy_file(path, &bundle_directory.join(contents_path))
|
||||
.with_context(|| format!("Failed to copy file {path:?} to {contents_path:?}"))?;
|
||||
} else {
|
||||
common::copy_dir(path, &bundle_directory.join(contents_path))
|
||||
.with_context(|| format!("Failed to copy directory {:?} to {:?}", path, contents_path))?;
|
||||
fs_utils::copy_dir(path, &bundle_directory.join(contents_path))
|
||||
.with_context(|| format!("Failed to copy directory {path:?} to {contents_path:?}"))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -189,12 +187,6 @@ fn create_info_plist(
|
||||
bundle_icon_file: Option<PathBuf>,
|
||||
settings: &Settings,
|
||||
) -> crate::Result<()> {
|
||||
let format = time::format_description::parse("[year][month][day].[hour][minute][second]")
|
||||
.map_err(time::error::Error::from)?;
|
||||
let build_number = time::OffsetDateTime::now_utc()
|
||||
.format(&format)
|
||||
.map_err(time::error::Error::from)?;
|
||||
|
||||
let mut plist = plist::Dictionary::new();
|
||||
plist.insert("CFBundleDevelopmentRegion".into(), "English".into());
|
||||
plist.insert("CFBundleDisplayName".into(), settings.product_name().into());
|
||||
@@ -218,13 +210,29 @@ 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(),
|
||||
settings.version_string().into(),
|
||||
);
|
||||
plist.insert("CFBundleVersion".into(), build_number.into());
|
||||
plist.insert(
|
||||
"CFBundleVersion".into(),
|
||||
settings
|
||||
.macos()
|
||||
.bundle_version
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| settings.version_string())
|
||||
.into(),
|
||||
);
|
||||
plist.insert("CSResourcesFileMapped".into(), true.into());
|
||||
if let Some(category) = settings.app_category() {
|
||||
plist.insert(
|
||||
@@ -267,6 +275,7 @@ fn create_info_plist(
|
||||
"CFBundleTypeRole".into(),
|
||||
association.role.to_string().into(),
|
||||
);
|
||||
dict.insert("LSHandlerRank".into(), association.rank.to_string().into());
|
||||
plist::Value::Dictionary(dict)
|
||||
})
|
||||
.collect(),
|
||||
@@ -346,10 +355,10 @@ 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() {
|
||||
common::copy_dir(&src_path, &dest_dir.join(&src_name))?;
|
||||
fs_utils::copy_dir(&src_path, &dest_dir.join(&src_name))?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
@@ -374,7 +383,7 @@ fn copy_frameworks_to_bundle(
|
||||
}
|
||||
let dest_dir = bundle_directory.join("Frameworks");
|
||||
fs::create_dir_all(bundle_directory)
|
||||
.with_context(|| format!("Failed to create Frameworks directory at {:?}", dest_dir))?;
|
||||
.with_context(|| format!("Failed to create Frameworks directory at {dest_dir:?}"))?;
|
||||
for framework in frameworks.iter() {
|
||||
if framework.ends_with(".framework") {
|
||||
let src_path = PathBuf::from(framework);
|
||||
@@ -382,20 +391,19 @@ fn copy_frameworks_to_bundle(
|
||||
.file_name()
|
||||
.expect("Couldn't get framework filename");
|
||||
let dest_path = dest_dir.join(src_name);
|
||||
common::copy_dir(&src_path, &dest_path)?;
|
||||
fs_utils::copy_dir(&src_path, &dest_path)?;
|
||||
add_framework_sign_path(&src_path, &dest_path, &mut paths);
|
||||
continue;
|
||||
} 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
|
||||
"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);
|
||||
common::copy_file(&src_path, &dest_path)?;
|
||||
fs_utils::copy_file(&src_path, &dest_path)?;
|
||||
paths.push(SignTarget {
|
||||
path: dest_path,
|
||||
is_an_executable: false,
|
||||
@@ -403,8 +411,7 @@ fn copy_frameworks_to_bundle(
|
||||
continue;
|
||||
} else if framework.contains('/') {
|
||||
return Err(crate::Error::GenericError(format!(
|
||||
"Framework path should have .framework extension: {}",
|
||||
framework
|
||||
"Framework path should have .framework extension: {framework}"
|
||||
)));
|
||||
}
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
@@ -422,8 +429,7 @@ fn copy_frameworks_to_bundle(
|
||||
continue;
|
||||
}
|
||||
return Err(crate::Error::GenericError(format!(
|
||||
"Could not locate framework: {}",
|
||||
framework
|
||||
"Could not locate framework: {framework}"
|
||||
)));
|
||||
}
|
||||
Ok(paths)
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
use super::{app, icon::create_icns_file};
|
||||
use crate::{
|
||||
bundle::{common::CommandExt, settings::Arch, Bundle},
|
||||
bundle::{settings::Arch, Bundle},
|
||||
utils::CommandExt,
|
||||
PackageType, Settings,
|
||||
};
|
||||
|
||||
@@ -48,8 +49,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:?}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,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
|
||||
@@ -68,10 +68,10 @@ 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).with_context(|| format!("Failed to remove old {dmg_name}"))?;
|
||||
}
|
||||
fs::create_dir_all(path)
|
||||
.with_context(|| format!("Failed to create output directory at {:?}", path))?;
|
||||
.with_context(|| format!("Failed to create output directory at {path:?}"))?;
|
||||
}
|
||||
|
||||
// create paths for script
|
||||
@@ -173,9 +173,11 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
|
||||
// Issue #592 - Building MacOS dmg files on CI
|
||||
// https://github.com/tauri-apps/tauri/issues/592
|
||||
if let Some(value) = env::var_os("CI") {
|
||||
if value == "true" {
|
||||
bundle_dmg_cmd.arg("--skip-jenkins");
|
||||
if env::var_os("TAURI_BUNDLER_DMG_IGNORE_CI").unwrap_or_default() != "true" {
|
||||
if let Some(value) = env::var_os("CI") {
|
||||
if value == "true" {
|
||||
bundle_dmg_cmd.arg("--skip-jenkins");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,16 +193,19 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
fs::rename(bundle_dir.join(dmg_name), dmg_path.clone())?;
|
||||
|
||||
// Sign DMG if needed
|
||||
|
||||
if let Some(keychain) = super::sign::keychain(settings.macos().signing_identity.as_deref())? {
|
||||
super::sign::sign(
|
||||
&keychain,
|
||||
vec![super::sign::SignTarget {
|
||||
path: dmg_path.clone(),
|
||||
is_an_executable: false,
|
||||
}],
|
||||
settings,
|
||||
)?;
|
||||
// skipping self-signing DMGs https://github.com/tauri-apps/tauri/issues/12288
|
||||
let identity = settings.macos().signing_identity.as_deref();
|
||||
if identity != Some("-") {
|
||||
if let Some(keychain) = super::sign::keychain(identity)? {
|
||||
super::sign::sign(
|
||||
&keychain,
|
||||
vec![super::sign::SignTarget {
|
||||
path: dmg_path.clone(),
|
||||
is_an_executable: false,
|
||||
}],
|
||||
settings,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Bundled {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::bundle::{common, Settings};
|
||||
use crate::bundle::Settings;
|
||||
use crate::utils::{self, fs_utils};
|
||||
use std::{
|
||||
cmp::min,
|
||||
ffi::OsStr,
|
||||
@@ -28,7 +29,7 @@ pub fn create_icns_file(out_dir: &Path, settings: &Settings) -> crate::Result<Op
|
||||
if icon_path.extension() == Some(OsStr::new("icns")) {
|
||||
let mut dest_path = out_dir.to_path_buf();
|
||||
dest_path.push(icon_path.file_name().expect("Could not get icon filename"));
|
||||
common::copy_file(&icon_path, &dest_path)?;
|
||||
fs_utils::copy_file(&icon_path, &dest_path)?;
|
||||
return Ok(Some(dest_path));
|
||||
}
|
||||
}
|
||||
@@ -63,7 +64,7 @@ pub fn create_icns_file(out_dir: &Path, settings: &Settings) -> crate::Result<Op
|
||||
for icon_path in settings.icon_files() {
|
||||
let icon_path = icon_path?;
|
||||
let icon = image::open(&icon_path)?;
|
||||
let density = if common::is_retina(&icon_path) { 2 } else { 1 };
|
||||
let density = if utils::is_retina(&icon_path) { 2 } else { 1 };
|
||||
let (w, h) = icon.dimensions();
|
||||
let orig_size = min(w, h);
|
||||
let next_size_down = 2f32.powf((orig_size as f32).log2().floor()) as u32;
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
// See https://developer.apple.com/go/?id=bundle-structure for a full
|
||||
// explanation.
|
||||
|
||||
use crate::{bundle::common, Settings};
|
||||
use crate::{
|
||||
utils::{self, fs_utils},
|
||||
Settings,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use image::{codecs::png::PngDecoder, GenericImageView, ImageDecoder};
|
||||
@@ -42,16 +45,16 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
|
||||
if app_bundle_path.exists() {
|
||||
fs::remove_dir_all(&app_bundle_path)
|
||||
.with_context(|| format!("Failed to remove old {}", app_product_name))?;
|
||||
.with_context(|| format!("Failed to remove old {app_product_name}"))?;
|
||||
}
|
||||
fs::create_dir_all(&app_bundle_path)
|
||||
.with_context(|| format!("Failed to create bundle directory at {:?}", app_bundle_path))?;
|
||||
.with_context(|| format!("Failed to create bundle directory at {app_bundle_path:?}"))?;
|
||||
|
||||
for src in settings.resource_files() {
|
||||
let src = src?;
|
||||
let dest = app_bundle_path.join(tauri_utils::resources::resource_relpath(&src));
|
||||
common::copy_file(&src, &dest)
|
||||
.with_context(|| format!("Failed to copy resource file {:?}", src))?;
|
||||
fs_utils::copy_file(&src, &dest)
|
||||
.with_context(|| format!("Failed to copy resource file {src:?}"))?;
|
||||
}
|
||||
|
||||
let icon_filenames = generate_icon_files(&app_bundle_path, settings)
|
||||
@@ -61,8 +64,8 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
|
||||
for bin in settings.binaries() {
|
||||
let bin_path = settings.binary_path(bin);
|
||||
common::copy_file(&bin_path, app_bundle_path.join(bin.name()))
|
||||
.with_context(|| format!("Failed to copy binary from {:?}", bin_path))?;
|
||||
fs_utils::copy_file(&bin_path, &app_bundle_path.join(bin.name()))
|
||||
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
|
||||
}
|
||||
|
||||
Ok(vec![app_bundle_path])
|
||||
@@ -93,11 +96,11 @@ fn generate_icon_files(bundle_dir: &Path, settings: &Settings) -> crate::Result<
|
||||
let decoder = PngDecoder::new(BufReader::new(File::open(&icon_path)?))?;
|
||||
let width = decoder.dimensions().0;
|
||||
let height = decoder.dimensions().1;
|
||||
let is_retina = common::is_retina(&icon_path);
|
||||
let is_retina = utils::is_retina(&icon_path);
|
||||
if !sizes.contains(&(width, height, is_retina)) {
|
||||
sizes.insert((width, height, is_retina));
|
||||
let dest_path = get_dest_path(width, height, is_retina);
|
||||
common::copy_file(&icon_path, &dest_path)?;
|
||||
fs_utils::copy_file(&icon_path, &dest_path)?;
|
||||
}
|
||||
}
|
||||
// Fall back to non-PNG files for any missing sizes.
|
||||
@@ -121,12 +124,12 @@ fn generate_icon_files(bundle_dir: &Path, settings: &Settings) -> crate::Result<
|
||||
} else {
|
||||
let icon = image::open(&icon_path)?;
|
||||
let (width, height) = icon.dimensions();
|
||||
let is_retina = common::is_retina(&icon_path);
|
||||
let is_retina = utils::is_retina(&icon_path);
|
||||
if !sizes.contains(&(width, height, is_retina)) {
|
||||
sizes.insert((width, height, is_retina));
|
||||
let dest_path = get_dest_path(width, height, is_retina);
|
||||
icon.write_to(
|
||||
&mut common::create_file(&dest_path)?,
|
||||
&mut fs_utils::create_file(&dest_path)?,
|
||||
image::ImageFormat::Png,
|
||||
)?;
|
||||
}
|
||||
@@ -142,7 +145,7 @@ fn generate_info_plist(
|
||||
settings: &Settings,
|
||||
icon_filenames: &[String],
|
||||
) -> crate::Result<()> {
|
||||
let file = &mut common::create_file(&bundle_dir.join("Info.plist"))?;
|
||||
let file = &mut fs_utils::create_file(&bundle_dir.join("Info.plist"))?;
|
||||
writeln!(
|
||||
file,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
|
||||
@@ -175,7 +178,11 @@ fn generate_info_plist(
|
||||
writeln!(
|
||||
file,
|
||||
" <key>CFBundleVersion</key>\n <string>{}</string>",
|
||||
settings.version_string()
|
||||
settings
|
||||
.ios()
|
||||
.bundle_version
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| settings.version_string())
|
||||
)?;
|
||||
writeln!(
|
||||
file,
|
||||
@@ -190,7 +197,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>")?;
|
||||
}
|
||||
|
||||
@@ -48,9 +48,14 @@ 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
|
||||
};
|
||||
keychain.sign(
|
||||
&target.path,
|
||||
settings.macos().entitlements.as_ref().map(Path::new),
|
||||
entitlements_path,
|
||||
target.is_an_executable && settings.macos().hardened_runtime,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
fs::{create_dir, create_dir_all, read_dir, remove_dir_all},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// Directory options.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct DirOpts {
|
||||
pub depth: u64,
|
||||
}
|
||||
|
||||
/// File options.
|
||||
pub struct FileOpts {
|
||||
pub overwrite: bool,
|
||||
pub skip: bool,
|
||||
#[allow(dead_code)]
|
||||
pub buffer_size: usize,
|
||||
}
|
||||
|
||||
/// Copy options.
|
||||
#[derive(Clone)]
|
||||
pub struct Options {
|
||||
pub overwrite: bool,
|
||||
pub skip: bool,
|
||||
pub buffer_size: usize,
|
||||
pub copy_files: bool,
|
||||
pub content_only: bool,
|
||||
pub depth: u64,
|
||||
}
|
||||
|
||||
/// Directory information descriptor
|
||||
pub struct DirInfo {
|
||||
pub size: u64,
|
||||
pub files: Vec<String>,
|
||||
pub directories: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Options {
|
||||
Options {
|
||||
overwrite: false,
|
||||
skip: false,
|
||||
buffer_size: 64000,
|
||||
copy_files: false,
|
||||
content_only: false,
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FileOpts {
|
||||
fn default() -> FileOpts {
|
||||
FileOpts {
|
||||
overwrite: false,
|
||||
skip: false,
|
||||
buffer_size: 64000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the given directory path,
|
||||
/// erasing it first if specified.
|
||||
pub fn create<P>(path: P, erase: bool) -> crate::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
if erase && path.as_ref().exists() {
|
||||
remove(&path)?;
|
||||
}
|
||||
Ok(create_dir(&path)?)
|
||||
}
|
||||
|
||||
/// Creates all of the directories of the specified path,
|
||||
/// erasing it first if specified.
|
||||
pub fn create_all<P>(path: P, erase: bool) -> crate::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
if erase && path.as_ref().exists() {
|
||||
remove(&path)?;
|
||||
}
|
||||
Ok(create_dir_all(&path)?)
|
||||
}
|
||||
|
||||
/// Removes the directory if it exists.
|
||||
pub fn remove<P: AsRef<Path>>(path: P) -> crate::Result<()> {
|
||||
if path.as_ref().exists() {
|
||||
Ok(remove_dir_all(path)?)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy file with the given options.
|
||||
pub fn copy_file<P, Q>(from: P, to: Q, options: &FileOpts) -> crate::Result<u64>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
let from = from.as_ref();
|
||||
if !from.exists() {
|
||||
if let Some(msg) = from.to_str() {
|
||||
let msg = format!("Path \"{msg}\" does not exist or you don't have access");
|
||||
return Err(crate::Error::PathUtilError(msg));
|
||||
}
|
||||
return Err(crate::Error::PathUtilError(
|
||||
"Path does not exist or you don't have access!".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
if !from.is_file() {
|
||||
if let Some(msg) = from.to_str() {
|
||||
let msg = format!("Path \"{msg}\" is not a file!");
|
||||
return Err(crate::Error::PathUtilError(msg));
|
||||
}
|
||||
return Err(crate::Error::PathUtilError(
|
||||
"Path is not a file!".to_owned(),
|
||||
));
|
||||
}
|
||||
if !options.overwrite && to.as_ref().exists() {
|
||||
if options.skip {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if let Some(msg) = to.as_ref().to_str() {
|
||||
let msg = format!("Path \"{msg}\" is exist");
|
||||
return Err(crate::Error::PathUtilError(msg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(std::fs::copy(from, to)?)
|
||||
}
|
||||
|
||||
/// Copies the directory with the given options.
|
||||
#[allow(dead_code)]
|
||||
pub fn copy<P, Q>(from: P, to: Q, options: &Options) -> crate::Result<u64>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
let from = from.as_ref();
|
||||
if !from.exists() {
|
||||
if let Some(msg) = from.to_str() {
|
||||
let msg = format!("Path \"{msg}\" does not exist or you don't have access!");
|
||||
return Err(crate::Error::PathUtilError(msg));
|
||||
}
|
||||
return Err(crate::Error::PathUtilError(
|
||||
"Path does not exist or you don't have access".to_owned(),
|
||||
));
|
||||
}
|
||||
if !from.is_dir() {
|
||||
if let Some(msg) = from.to_str() {
|
||||
let msg = format!("Path \"{msg}\" is not a directory!");
|
||||
return Err(crate::Error::PathUtilError(msg));
|
||||
}
|
||||
return Err(crate::Error::PathUtilError(
|
||||
"Path is not a directory".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
let dir_name = if let Some(val) = from.components().last() {
|
||||
val.as_os_str()
|
||||
} else {
|
||||
return Err(crate::Error::PathUtilError(
|
||||
"Invalid Folder form".to_owned(),
|
||||
));
|
||||
};
|
||||
let mut to: PathBuf = to.as_ref().to_path_buf();
|
||||
if !options.content_only && (!options.copy_files || to.exists()) {
|
||||
to.push(dir_name);
|
||||
}
|
||||
|
||||
let mut read_options = DirOpts::default();
|
||||
if options.depth > 0 {
|
||||
read_options.depth = options.depth;
|
||||
}
|
||||
|
||||
let dir_content = get_dir_info(from, &read_options)?;
|
||||
for directory in dir_content.directories {
|
||||
let tmp_to = Path::new(&directory).strip_prefix(from)?;
|
||||
let dir = to.join(tmp_to);
|
||||
if !dir.exists() {
|
||||
if options.copy_files {
|
||||
create_all(dir, false)?;
|
||||
} else {
|
||||
create(dir, false)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut result: u64 = 0;
|
||||
for file in dir_content.files {
|
||||
let to = to.to_path_buf();
|
||||
let tp = Path::new(&file).strip_prefix(from)?;
|
||||
let path = to.join(tp);
|
||||
|
||||
let file_options = FileOpts {
|
||||
overwrite: options.overwrite,
|
||||
skip: options.skip,
|
||||
buffer_size: options.buffer_size,
|
||||
};
|
||||
let mut result_copy: crate::Result<u64>;
|
||||
let mut work = true;
|
||||
|
||||
while work {
|
||||
#[allow(clippy::needless_borrow)]
|
||||
{
|
||||
result_copy = copy_file(&file, &path, &file_options);
|
||||
}
|
||||
match result_copy {
|
||||
Ok(val) => {
|
||||
result += val;
|
||||
work = false;
|
||||
}
|
||||
Err(err) => {
|
||||
let err_msg = err.to_string();
|
||||
return Err(crate::Error::PathUtilError(err_msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Gets the DirInfo from the directory path with the given options.
|
||||
pub fn get_dir_info<P>(path: P, options: &DirOpts) -> crate::Result<DirInfo>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let depth = if options.depth == 0 {
|
||||
0
|
||||
} else {
|
||||
options.depth + 1
|
||||
};
|
||||
|
||||
_get_dir_info(path, depth)
|
||||
}
|
||||
|
||||
/// Gets the DirInfo from the directory with the given depth.
|
||||
fn _get_dir_info<P>(path: P, mut depth: u64) -> crate::Result<DirInfo>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut directories = Vec::new();
|
||||
let mut files = Vec::new();
|
||||
let mut size = 0;
|
||||
let item = path.as_ref().to_str();
|
||||
if item.is_none() {
|
||||
return Err(crate::Error::PathUtilError("Invalid Path".to_owned()));
|
||||
}
|
||||
let item = item.expect("Item had no data").to_string();
|
||||
|
||||
if path.as_ref().is_dir() {
|
||||
directories.push(item);
|
||||
if depth == 0 || depth > 1 {
|
||||
if depth > 1 {
|
||||
depth -= 1;
|
||||
}
|
||||
for entry in read_dir(&path)? {
|
||||
let _path = entry?.path();
|
||||
|
||||
match _get_dir_info(_path, depth) {
|
||||
Ok(items) => {
|
||||
let mut _files = items.files;
|
||||
let mut _directories = items.directories;
|
||||
size += items.size;
|
||||
files.append(&mut _files);
|
||||
directories.append(&mut _directories);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size = path.as_ref().metadata()?.len();
|
||||
files.push(item);
|
||||
}
|
||||
Ok(DirInfo {
|
||||
size,
|
||||
files,
|
||||
directories,
|
||||
})
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::common::CommandExt;
|
||||
use crate::utils::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
@@ -57,6 +57,8 @@ pub fn target_triple() -> Result<String, crate::Error> {
|
||||
"armv7".into()
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"aarch64".into()
|
||||
} else if cfg!(target_arch = "riscv64") {
|
||||
"riscv64".into()
|
||||
} else {
|
||||
return Err(crate::Error::ArchError(String::from(
|
||||
"Unable to determine target-architecture",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::category::AppCategory;
|
||||
use crate::bundle::{common, platform::target_triple};
|
||||
use crate::{bundle::platform::target_triple, utils::fs_utils};
|
||||
use anyhow::Context;
|
||||
pub use tauri_utils::config::WebviewInstallMode;
|
||||
use tauri_utils::{
|
||||
@@ -12,6 +12,7 @@ use tauri_utils::{
|
||||
BundleType, DeepLinkProtocol, FileAssociation, NSISInstallerMode, NsisCompression,
|
||||
RpmCompression,
|
||||
},
|
||||
platform::Target as TargetPlatform,
|
||||
resources::{external_binaries, ResourcePaths},
|
||||
};
|
||||
|
||||
@@ -220,6 +221,10 @@ pub struct DebianSettings {
|
||||
pub struct AppImageSettings {
|
||||
/// The files to include in the Appimage Binary.
|
||||
pub files: HashMap<PathBuf, PathBuf>,
|
||||
/// Whether to include gstreamer plugins for audio/media support.
|
||||
pub bundle_media_framework: bool,
|
||||
/// Whether to include the `xdg-open` binary.
|
||||
pub bundle_xdg_open: bool,
|
||||
}
|
||||
|
||||
/// The RPM bundle settings.
|
||||
@@ -235,7 +240,7 @@ pub struct RpmSettings {
|
||||
/// in order for the package to be installed.
|
||||
pub conflicts: Option<Vec<String>>,
|
||||
/// The list of RPM dependencies your application supersedes - if this package is installed,
|
||||
/// packages listed as “obsoletes” will be automatically removed (if they are present).
|
||||
/// packages listed as "obsoletes" will be automatically removed (if they are present).
|
||||
pub obsoletes: Option<Vec<String>>,
|
||||
/// The RPM release tag.
|
||||
pub release: String,
|
||||
@@ -302,6 +307,13 @@ pub struct DmgSettings {
|
||||
pub application_folder_position: Position,
|
||||
}
|
||||
|
||||
/// The iOS bundle settings.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct IosSettings {
|
||||
/// The version of the build that identifies an iteration of the bundle.
|
||||
pub bundle_version: Option<String>,
|
||||
}
|
||||
|
||||
/// The macOS bundle settings.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MacOsSettings {
|
||||
@@ -319,6 +331,12 @@ pub struct MacOsSettings {
|
||||
/// List of custom files to add to the application bundle.
|
||||
/// Maps the path in the Contents directory in the app to the path of the file to include (relative to the current working directory).
|
||||
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>,
|
||||
@@ -404,7 +422,7 @@ pub struct WixSettings {
|
||||
pub banner_path: Option<PathBuf>,
|
||||
/// Path to a bitmap file to use on the installation user interface dialogs.
|
||||
/// It is used on the welcome and completion dialogs.
|
||||
|
||||
///
|
||||
/// The required dimensions are 493px × 312px.
|
||||
pub dialog_image_path: Option<PathBuf>,
|
||||
/// Enables FIPS compliant algorithms.
|
||||
@@ -639,6 +657,8 @@ pub struct BundleSettings {
|
||||
pub rpm: RpmSettings,
|
||||
/// DMG-specific settings.
|
||||
pub dmg: DmgSettings,
|
||||
/// iOS-specific settings.
|
||||
pub ios: IosSettings,
|
||||
/// MacOS-specific settings.
|
||||
pub macos: MacOsSettings,
|
||||
/// Updater configuration.
|
||||
@@ -719,6 +739,8 @@ pub enum Arch {
|
||||
Armhf,
|
||||
/// For the AArch32 / ARM32 instruction sets with soft-float (32 bits).
|
||||
Armel,
|
||||
/// For the RISC-V instruction sets (64 bits).
|
||||
Riscv64,
|
||||
/// For universal macOS applications.
|
||||
Universal,
|
||||
}
|
||||
@@ -743,6 +765,8 @@ pub struct Settings {
|
||||
bundle_settings: BundleSettings,
|
||||
/// the binaries to bundle.
|
||||
binaries: Vec<BundleBinary>,
|
||||
/// The target platform.
|
||||
target_platform: TargetPlatform,
|
||||
/// The target triple.
|
||||
target: String,
|
||||
}
|
||||
@@ -838,6 +862,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),
|
||||
@@ -855,9 +880,10 @@ 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,
|
||||
})
|
||||
}
|
||||
@@ -884,6 +910,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") {
|
||||
@@ -896,6 +927,8 @@ impl Settings {
|
||||
Arch::Armel
|
||||
} else if self.target.starts_with("aarch64") {
|
||||
Arch::AArch64
|
||||
} else if self.target.starts_with("riscv64") {
|
||||
Arch::Riscv64
|
||||
} else if self.target.starts_with("universal") {
|
||||
Arch::Universal
|
||||
} else {
|
||||
@@ -936,19 +969,23 @@ impl Settings {
|
||||
|
||||
/// 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.
|
||||
@@ -966,18 +1003,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."
|
||||
@@ -1064,7 +1096,7 @@ impl Settings {
|
||||
.to_string_lossy()
|
||||
.replace(&format!("-{}", self.target), ""),
|
||||
);
|
||||
common::copy_file(&src, &dest)?;
|
||||
fs_utils::copy_file(&src, &dest)?;
|
||||
paths.push(dest);
|
||||
}
|
||||
Ok(paths)
|
||||
@@ -1075,7 +1107,7 @@ impl Settings {
|
||||
for resource in self.resource_files().iter() {
|
||||
let resource = resource?;
|
||||
let dest = path.join(resource.target());
|
||||
common::copy_file(resource.path(), dest)?;
|
||||
fs_utils::copy_file(resource.path(), &dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1182,6 +1214,11 @@ impl Settings {
|
||||
&self.bundle_settings.dmg
|
||||
}
|
||||
|
||||
/// Returns the iOS settings.
|
||||
pub fn ios(&self) -> &IosSettings {
|
||||
&self.bundle_settings.ios
|
||||
}
|
||||
|
||||
/// Returns the MacOS settings.
|
||||
pub fn macos(&self) -> &MacOsSettings {
|
||||
&self.bundle_settings.macos
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::common;
|
||||
|
||||
use crate::{
|
||||
bundle::{
|
||||
windows::{
|
||||
@@ -13,9 +11,10 @@ use crate::{
|
||||
},
|
||||
Bundle,
|
||||
},
|
||||
utils::fs_utils,
|
||||
Settings,
|
||||
};
|
||||
use tauri_utils::display_path;
|
||||
use tauri_utils::{display_path, platform::Target as TargetPlatform};
|
||||
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
@@ -28,14 +27,9 @@ 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);
|
||||
}
|
||||
|
||||
@@ -194,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));
|
||||
|
||||
@@ -210,7 +204,7 @@ fn bundle_update_windows(settings: &Settings, bundles: &[Bundle]) -> crate::Resu
|
||||
pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
|
||||
let parent_dir = dst_file.parent().expect("No data in parent");
|
||||
fs::create_dir_all(parent_dir)?;
|
||||
let writer = common::create_file(dst_file)?;
|
||||
let writer = fs_utils::create_file(dst_file)?;
|
||||
|
||||
let file_name = src_file
|
||||
.file_name()
|
||||
@@ -235,7 +229,7 @@ pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
|
||||
fn create_tar(src_dir: &Path, dest_path: &Path) -> crate::Result<PathBuf> {
|
||||
use flate2::{write::GzEncoder, Compression};
|
||||
|
||||
let dest_file = common::create_file(dest_path)?;
|
||||
let dest_file = fs_utils::create_file(dest_path)?;
|
||||
let gzip_encoder = GzEncoder::new(dest_file, Compression::default());
|
||||
|
||||
let gzip_encoder = create_tar_from_src(src_dir, gzip_encoder)?;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -70,9 +70,12 @@
|
||||
<Property Id="ARPURLUPDATEINFO" Value="{{homepage}}"/>
|
||||
{{/if}}
|
||||
|
||||
<!-- initialize with previous InstallDir -->
|
||||
<Property Id="INSTALLDIR">
|
||||
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
|
||||
<!-- First attempt: Search for "InstallDir" -->
|
||||
<RegistrySearch Id="PrevInstallDirWithName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw" />
|
||||
|
||||
<!-- Second attempt: If the first fails, search for the default key value (this is how the nsis installer currently stores the path) -->
|
||||
<RegistrySearch Id="PrevInstallDirNoName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Type="raw" />
|
||||
</Property>
|
||||
|
||||
<!-- launch app checkbox -->
|
||||
|
||||
@@ -3,17 +3,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::bundle::{
|
||||
common::CommandExt,
|
||||
path_utils::{copy_file, FileOpts},
|
||||
settings::{Arch, Settings},
|
||||
windows::{
|
||||
sign::try_sign,
|
||||
util::{
|
||||
download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer,
|
||||
extract_zip, HashAlgorithm, WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
|
||||
use crate::{
|
||||
bundle::{
|
||||
settings::{Arch, Settings},
|
||||
windows::{
|
||||
sign::try_sign,
|
||||
util::{
|
||||
download_webview2_bootstrapper, download_webview2_offline_installer,
|
||||
WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
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};
|
||||
@@ -172,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>"#,
|
||||
@@ -198,14 +203,7 @@ fn copy_icon(settings: &Settings, filename: &str, path: &Path) -> crate::Result<
|
||||
|
||||
let icon_path = std::env::current_dir()?.join(path);
|
||||
|
||||
copy_file(
|
||||
icon_path,
|
||||
&icon_target_path,
|
||||
&FileOpts {
|
||||
overwrite: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
copy_file(&icon_path, &icon_target_path)?;
|
||||
|
||||
Ok(icon_target_path)
|
||||
}
|
||||
@@ -223,8 +221,7 @@ fn app_installer_output_path(
|
||||
Arch::AArch64 => "arm64",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"Unsupported architecture: {:?}",
|
||||
target
|
||||
"Unsupported architecture: {target:?}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
@@ -354,8 +351,7 @@ fn run_candle(
|
||||
Arch::AArch64 => "arm64",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"unsupported architecture: {:?}",
|
||||
target
|
||||
"unsupported architecture: {target:?}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
@@ -445,8 +441,7 @@ pub fn build_wix_app_installer(
|
||||
Arch::AArch64 => "arm64",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"unsupported architecture: {:?}",
|
||||
target
|
||||
"unsupported architecture: {target:?}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
@@ -474,6 +469,16 @@ 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_path = output_path.join("wix");
|
||||
crate::utils::fs_utils::copy_dir(wix_toolset_path, &wix_path)
|
||||
.context("failed to copy wix directory")?;
|
||||
wix_path
|
||||
} else {
|
||||
wix_toolset_path.to_path_buf()
|
||||
};
|
||||
|
||||
let mut data = BTreeMap::new();
|
||||
|
||||
let silent_webview_install = if let WebviewInstallMode::DownloadBootstrapper { silent }
|
||||
@@ -765,7 +770,11 @@ pub fn build_wix_app_installer(
|
||||
let fragment = fragment_handlebars.render_template(&fragment_content, &data)?;
|
||||
let mut extensions = Vec::new();
|
||||
for cap in extension_regex.captures_iter(&fragment) {
|
||||
extensions.push(wix_toolset_path.join(format!("Wix{}.dll", &cap[1])));
|
||||
let path = wix_toolset_path.join(format!("Wix{}.dll", &cap[1]));
|
||||
if settings.can_sign() {
|
||||
try_sign(&path, settings)?;
|
||||
}
|
||||
extensions.push(path);
|
||||
}
|
||||
candle_inputs.push((fragment_path, extensions));
|
||||
}
|
||||
@@ -775,11 +784,18 @@ pub fn build_wix_app_installer(
|
||||
fragment_extensions.insert(wix_toolset_path.join("WixUIExtension.dll"));
|
||||
fragment_extensions.insert(wix_toolset_path.join("WixUtilExtension.dll"));
|
||||
|
||||
// sign default extensions
|
||||
if settings.can_sign() {
|
||||
for path in &fragment_extensions {
|
||||
try_sign(path, settings)?;
|
||||
}
|
||||
}
|
||||
|
||||
for (path, extensions) in candle_inputs {
|
||||
for ext in &extensions {
|
||||
fragment_extensions.insert(ext.clone());
|
||||
}
|
||||
run_candle(settings, wix_toolset_path, &output_path, path, extensions)?;
|
||||
run_candle(settings, &wix_toolset_path, &output_path, path, extensions)?;
|
||||
}
|
||||
|
||||
let mut output_paths = Vec::new();
|
||||
@@ -826,7 +842,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");
|
||||
{
|
||||
@@ -855,7 +871,7 @@ pub fn build_wix_app_installer(
|
||||
log::info!(action = "Running"; "light to produce {}", display_path(&msi_path));
|
||||
|
||||
run_light(
|
||||
wix_toolset_path,
|
||||
&wix_toolset_path,
|
||||
&output_path,
|
||||
arguments,
|
||||
&(fragment_extensions.clone().into_iter().collect()),
|
||||
@@ -931,12 +947,11 @@ fn get_merge_modules(settings: &Settings) -> crate::Result<Vec<MergeModule>> {
|
||||
let mut merge_modules = Vec::new();
|
||||
let regex = Regex::new(r"[^\w\d\.]")?;
|
||||
for msm in glob::glob(
|
||||
settings
|
||||
.project_out_directory()
|
||||
.join("*.msm")
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
.as_str(),
|
||||
&PathBuf::from(glob::Pattern::escape(
|
||||
&settings.project_out_directory().to_string_lossy(),
|
||||
))
|
||||
.join("*.msm")
|
||||
.to_string_lossy(),
|
||||
)? {
|
||||
let path = msm?;
|
||||
let filename = path
|
||||
@@ -971,9 +986,12 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
if added_resources.contains(&resource_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
added_resources.push(resource_path.clone());
|
||||
|
||||
if settings.can_sign() {
|
||||
try_sign(&resource_path, settings)?;
|
||||
}
|
||||
|
||||
let resource_entry = ResourceFile {
|
||||
id: format!("I{}", Uuid::new_v4().as_simple()),
|
||||
guid: Uuid::new_v4().to_string(),
|
||||
@@ -1043,8 +1061,13 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
|
||||
let mut dlls = Vec::new();
|
||||
|
||||
// TODO: The bundler should not include all DLLs it finds. Instead it should only include WebView2Loader.dll if present and leave the rest to the resources config.
|
||||
let out_dir = settings.project_out_directory();
|
||||
for dll in glob::glob(out_dir.join("*.dll").to_string_lossy().to_string().as_str())? {
|
||||
for dll in glob::glob(
|
||||
&PathBuf::from(glob::Pattern::escape(&out_dir.to_string_lossy()))
|
||||
.join("*.dll")
|
||||
.to_string_lossy(),
|
||||
)? {
|
||||
let path = dll?;
|
||||
let resource_path = dunce::simplified(&path);
|
||||
let relative_path = path
|
||||
@@ -1053,6 +1076,10 @@ 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() {
|
||||
try_sign(resource_path, settings)?;
|
||||
}
|
||||
|
||||
dlls.push(ResourceFile {
|
||||
id: format!("I{}", Uuid::new_v4().as_simple()),
|
||||
guid: Uuid::new_v4().to_string(),
|
||||
@@ -1062,15 +1089,15 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
}
|
||||
|
||||
if !dlls.is_empty() {
|
||||
resources.insert(
|
||||
"".to_string(),
|
||||
ResourceDirectory {
|
||||
resources
|
||||
.entry("".to_string())
|
||||
.and_modify(|r| r.files.append(&mut dlls))
|
||||
.or_insert(ResourceDirectory {
|
||||
path: "".to_string(),
|
||||
name: "".to_string(),
|
||||
directories: vec![],
|
||||
files: dlls,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(resources)
|
||||
|
||||
@@ -47,7 +47,7 @@ ${StrLoc}
|
||||
!define COPYRIGHT "{{copyright}}"
|
||||
!define OUTFILE "{{out_file}}"
|
||||
!define ARCH "{{arch}}"
|
||||
!define PLUGINSPATH "{{additional_plugins_path}}"
|
||||
!define ADDITIONALPLUGINSPATH "{{additional_plugins_path}}"
|
||||
!define ALLOWDOWNGRADES "{{allow_downgrades}}"
|
||||
!define DISPLAYLANGUAGESELECTOR "{{display_language_selector}}"
|
||||
!define INSTALLWEBVIEW2MODE "{{install_webview2_mode}}"
|
||||
@@ -56,7 +56,8 @@ ${StrLoc}
|
||||
!define WEBVIEW2INSTALLERPATH "{{webview2_installer_path}}"
|
||||
!define MINIMUMWEBVIEW2VERSION "{{minimum_webview2_version}}"
|
||||
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCTNAME}"
|
||||
!define MANUPRODUCTKEY "Software\${MANUFACTURER}\${PRODUCTNAME}"
|
||||
!define MANUKEY "Software\${MANUFACTURER}"
|
||||
!define MANUPRODUCTKEY "${MANUKEY}\${PRODUCTNAME}"
|
||||
!define UNINSTALLERSIGNCOMMAND "{{uninstaller_sign_cmd}}"
|
||||
!define ESTIMATEDSIZE "{{estimated_size}}"
|
||||
!define STARTMENUFOLDER "{{start_menu_folder}}"
|
||||
@@ -84,10 +85,8 @@ VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
|
||||
VIAddVersionKey "FileVersion" "${VERSION}"
|
||||
VIAddVersionKey "ProductVersion" "${VERSION}"
|
||||
|
||||
; Plugins path, currently exists for linux only
|
||||
!if "${PLUGINSPATH}" != ""
|
||||
!addplugindir "${PLUGINSPATH}"
|
||||
!endif
|
||||
# additional plugins
|
||||
!addplugindir "${ADDITIONALPLUGINSPATH}"
|
||||
|
||||
; Uninstaller signing command
|
||||
!if "${UNINSTALLERSIGNCOMMAND}" != ""
|
||||
@@ -96,7 +95,7 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
|
||||
|
||||
; Handle install mode, `perUser`, `perMachine` or `both`
|
||||
!if "${INSTALLMODE}" == "perMachine"
|
||||
RequestExecutionLevel highest
|
||||
RequestExecutionLevel admin
|
||||
!endif
|
||||
|
||||
!if "${INSTALLMODE}" == "currentUser"
|
||||
@@ -619,7 +618,7 @@ Section Install
|
||||
!insertmacro NSIS_HOOK_PREINSTALL
|
||||
!endif
|
||||
|
||||
!insertmacro CheckIfAppIsRunning
|
||||
!insertmacro CheckIfAppIsRunning "${MAINBINARYNAME}.exe" "${PRODUCTNAME}"
|
||||
|
||||
; Copy main executable
|
||||
File "${MAINBINARYSRCPATH}"
|
||||
@@ -629,12 +628,12 @@ Section Install
|
||||
CreateDirectory "$INSTDIR\\{{this}}"
|
||||
{{/each}}
|
||||
{{#each resources}}
|
||||
File /a "/oname={{this.[1]}}" "{{@key}}"
|
||||
File /a "/oname={{this.[1]}}" "{{no-escape @key}}"
|
||||
{{/each}}
|
||||
|
||||
; Copy external binaries
|
||||
{{#each binaries}}
|
||||
File /a "/oname={{this}}" "{{@key}}"
|
||||
File /a "/oname={{this}}" "{{no-escape @key}}"
|
||||
{{/each}}
|
||||
|
||||
; Create file associations
|
||||
@@ -756,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
|
||||
@@ -834,12 +833,27 @@ Section Uninstall
|
||||
DeleteRegKey HKCU "${UNINSTKEY}"
|
||||
!endif
|
||||
|
||||
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
|
||||
; Removes the Autostart entry for ${PRODUCTNAME} from the HKCU Run key if it exists.
|
||||
; This ensures the program does not launch automatically after uninstallation if it exists.
|
||||
; If it doesn't exist, it does nothing.
|
||||
; We do this when not updating (to preserve the registry value on updates)
|
||||
${If} $UpdateMode <> 1
|
||||
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "${PRODUCTNAME}"
|
||||
${EndIf}
|
||||
|
||||
; Delete app data if the checkbox is selected
|
||||
; and if not updating
|
||||
${If} $DeleteAppDataCheckboxState = 1
|
||||
${AndIf} $UpdateMode <> 1
|
||||
; Clear the install location $INSTDIR from registry
|
||||
DeleteRegKey SHCTX "${MANUPRODUCTKEY}"
|
||||
DeleteRegKey /ifempty SHCTX "${MANUKEY}"
|
||||
|
||||
; Clear the install language from registry
|
||||
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
|
||||
DeleteRegKey /ifempty HKCU "${MANUPRODUCTKEY}"
|
||||
DeleteRegKey /ifempty HKCU "${MANUKEY}"
|
||||
|
||||
SetShellVarContext current
|
||||
RmDir /r "$APPDATA\${BUNDLEID}"
|
||||
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
|
||||
|
||||
@@ -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} "أقدم"
|
||||
|
||||
@@ -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} "по-стара"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
LangString addOrReinstall ${LANG_HEBREW} "הוסף או התקן מחדש"
|
||||
LangString alreadyInstalled ${LANG_HEBREW} "כבר מותקן"
|
||||
LangString alreadyInstalledLong ${LANG_HEBREW} "${PRODUCTNAME} ${VERSION} כבר מותקן. בחר את הפעולה שברצונך לבצע ולחץ על הבא כדי להמשיך."
|
||||
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} "עצירת {{product_name}} נכשלה. נא לסגור את היישום ולנסות שוב."
|
||||
LangString installingWebview2 ${LANG_HEBREW} "מתקין את WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_HEBREW} "גרסה חדשה יותר של ${PRODUCTNAME} כבר מותקנת! לא מומלץ להתקין גרסה ישנה. אם בכל זאת תרצה להתקין את הגרסה הזו, מומלץ קודם להסיר את הגרסה הנוכחית. בחר את הפעולה שברצונך לבצע ולחץ הבא להמשך."
|
||||
LangString older ${LANG_HEBREW} "ישנה"
|
||||
LangString olderOrUnknownVersionInstalled ${LANG_HEBREW} "גרסה $R4 של ${PRODUCTNAME} מותקנת במערכת שלך. מומלץ להסיר את הגרסה הנוכחית לפני ההתקנה. בחר את הפעולה שברצונך לבצע ולחץ הבא להמשך."
|
||||
LangString silentDowngrades ${LANG_HEBREW} "התקנת גרסה ישנה לא נתמכת בהתקנה זו, אין אפשרות להמשיך עם ההתקנה השקטה, נא להמשיך עם ההתקנה בממשק הגרפי.$\n"
|
||||
LangString unableToUninstall ${LANG_HEBREW} "לא ניתן להסיר!"
|
||||
LangString uninstallApp ${LANG_HEBREW} "הסר את ${PRODUCTNAME}"
|
||||
LangString uninstallBeforeInstalling ${LANG_HEBREW} "הסר את הגרסה הנוכחית לפני התקנת גרסה זו"
|
||||
LangString unknown ${LANG_HEBREW} "לא ידועה"
|
||||
LangString webview2AbortError ${LANG_HEBREW} "התקנת WebView2 נכשלה! היישום אינו יכולה לפעול בלי זה. נסה להפעיל את ההתקנה שוב."
|
||||
LangString webview2DownloadError ${LANG_HEBREW} "שגיאה: הורדת WebView2 נכשלה - $0"
|
||||
LangString webview2DownloadSuccess ${LANG_HEBREW} "מאתחל WebView2 הורד בהצלחה"
|
||||
LangString webview2Downloading ${LANG_HEBREW} "מוריד את מאתחל WebView2..."
|
||||
LangString webview2InstallError ${LANG_HEBREW} "שגיאה: התקנת WebView2 נכשלה עם קוד שגיאה $1"
|
||||
LangString webview2InstallSuccess ${LANG_HEBREW} "WebView2 הותקן בהצלחה"
|
||||
LangString deleteAppData ${LANG_HEBREW} "מחק את נתוני היישום"
|
||||
@@ -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"
|
||||
|
||||
@@ -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} "旧"
|
||||
|
||||
@@ -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} "구"
|
||||
|
||||
@@ -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} "قدیمی تر"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
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} "{{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 {{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"
|
||||
LangString olderOrUnknownVersionInstalled ${LANG_PORTUGUESE} "Uma versão $R4 do ${PRODUCTNAME} está instalada no sistema. Recomenda-se desinstalar a versão atual antes de instalar. Selecione a operação que deseja realizar e clique em Seguinte para continuar."
|
||||
LangString silentDowngrades ${LANG_PORTUGUESE} "Rebaixamentos estão desativados neste instalador, não é possível prosseguir com a instalação silenciosa. Por favor, utilize o instalador com interface gráfica.$\n"
|
||||
LangString unableToUninstall ${LANG_PORTUGUESE} "Não foi possível desinstalar!"
|
||||
LangString uninstallApp ${LANG_PORTUGUESE} "Desinstalar ${PRODUCTNAME}"
|
||||
LangString uninstallBeforeInstalling ${LANG_PORTUGUESE} "Desinstalar antes de instalar"
|
||||
LangString unknown ${LANG_PORTUGUESE} "desconhecida"
|
||||
LangString webview2AbortError ${LANG_PORTUGUESE} "Falha ao instalar o WebView2! A aplicação não pode ser executada sem ele. Tente reiniciar o instalador."
|
||||
LangString webview2DownloadError ${LANG_PORTUGUESE} "Erro: Falha ao transferir o WebView2 - $0"
|
||||
LangString webview2DownloadSuccess ${LANG_PORTUGUESE} "Bootstrapper do WebView2 transferido com sucesso"
|
||||
LangString webview2Downloading ${LANG_PORTUGUESE} "A transferir o Bootstrapper do WebView2..."
|
||||
LangString webview2InstallError ${LANG_PORTUGUESE} "Erro: Instalação do WebView2 falhou com o código $1"
|
||||
LangString webview2InstallSuccess ${LANG_PORTUGUESE} "WebView2 instalado com sucesso"
|
||||
LangString deleteAppData ${LANG_PORTUGUESE} "Eliminar os dados da aplicação"
|
||||
@@ -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"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
LangString addOrReinstall ${LANG_RUSSIAN} "Добавить/Переустановить компоненты"
|
||||
LangString alreadyInstalled ${LANG_RUSSIAN} "Уже установлено"
|
||||
LangString alreadyInstalledLong ${LANG_RUSSIAN} "${PRODUCTNAME} ${VERSION} уже установлен. Выберите действие, которое вы хотите выполнить и нажмите Далее для продолжения."
|
||||
LangString appRunning ${LANG_RUSSIAN} "${PRODUCTNAME} запущен! Пожалуйста, закройте приложение и попробуйте еще раз."
|
||||
LangString appRunningOkKill ${LANG_RUSSIAN} "${PRODUCTNAME} запущен!$\nНажмите OK чтобы закрыть приложение"
|
||||
LangString appRunning ${LANG_RUSSIAN} "{{product_name}} запущен! Пожалуйста, закройте приложение и попробуйте еще раз."
|
||||
LangString appRunningOkKill ${LANG_RUSSIAN} "{{product_name}} запущен!$\nНажмите OK чтобы закрыть приложение"
|
||||
LangString chooseMaintenanceOption ${LANG_RUSSIAN} "Выберите действие, которое вы хотите выполнить."
|
||||
LangString choowHowToInstall ${LANG_RUSSIAN} "Выберите, как вы хотите установить ${PRODUCTNAME}."
|
||||
LangString createDesktop ${LANG_RUSSIAN} "Добавить ярлык на рабочий стол"
|
||||
LangString dontUninstall ${LANG_RUSSIAN} "Не удалять"
|
||||
LangString dontUninstallDowngrade ${LANG_RUSSIAN} "Не удалять (Установка более ранних версий без удаления невозможна)"
|
||||
LangString failedToKillApp ${LANG_RUSSIAN} "Не удалось закрыть ${PRODUCTNAME}. Пожалуйста, закройте приложение и попробуйте еще раз"
|
||||
LangString failedToKillApp ${LANG_RUSSIAN} "Не удалось закрыть {{product_name}}. Пожалуйста, закройте приложение и попробуйте еще раз"
|
||||
LangString installingWebview2 ${LANG_RUSSIAN} "Установка WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_RUSSIAN} "Более новая версия ${PRODUCTNAME} уже установлена! Не рекомендуется устанавливать более раннюю версию. Если вы действительно хотите установить эту версию, рекомендуется сначала удалить текущую. Выберите действие, которое вы хотите выполнить и нажмите Далее для продолжения."
|
||||
LangString older ${LANG_RUSSIAN} "Более ранняя"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
LangString addOrReinstall ${LANG_SIMPCHINESE} "添加/重新安装组件"
|
||||
LangString alreadyInstalled ${LANG_SIMPCHINESE} "已安装"
|
||||
LangString alreadyInstalledLong ${LANG_SIMPCHINESE} "${PRODUCTNAME} ${VERSION} 已经安装了。选择你想要执行的操作后点击下一步以继续。"
|
||||
LangString appRunning ${LANG_SIMPCHINESE} "${PRODUCTNAME} 正在运行!请关闭后再试。"
|
||||
LangString appRunningOkKill ${LANG_SIMPCHINESE} "${PRODUCTNAME} 正在运行!$\n点击确定以终止运行。"
|
||||
LangString appRunning ${LANG_SIMPCHINESE} "{{product_name}} 正在运行!请关闭后再试。"
|
||||
LangString appRunningOkKill ${LANG_SIMPCHINESE} "{{product_name}} 正在运行!$\n点击确定以终止运行。"
|
||||
LangString chooseMaintenanceOption ${LANG_SIMPCHINESE} "选择要执行的维护操作。"
|
||||
LangString choowHowToInstall ${LANG_SIMPCHINESE} "选择你想要安装 ${PRODUCTNAME} 的方式。"
|
||||
LangString createDesktop ${LANG_SIMPCHINESE} "创建桌面快捷方式"
|
||||
LangString dontUninstall ${LANG_SIMPCHINESE} "请勿卸载"
|
||||
LangString dontUninstallDowngrade ${LANG_SIMPCHINESE} "请勿卸载(此安装程序禁止未卸载就进行版本降级的操作)"
|
||||
LangString failedToKillApp ${LANG_SIMPCHINESE} "无法终止 ${PRODUCTNAME}。请关闭后再试。"
|
||||
LangString failedToKillApp ${LANG_SIMPCHINESE} "无法终止 {{product_name}}。请关闭后再试。"
|
||||
LangString installingWebview2 ${LANG_SIMPCHINESE} "正在安装 WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_SIMPCHINESE} "有一个更新版本的 ${PRODUCTNAME} 已经安装了!不推荐你安装旧的版本。如果你真的想要安装这个旧的版本,推荐先卸载当前版本。选择你想要执行的操作后点击下一步以继续。"
|
||||
LangString older ${LANG_SIMPCHINESE} "旧的"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
LangString addOrReinstall ${LANG_SPANISH} "Añadir o reinstalar componentes"
|
||||
LangString alreadyInstalled ${LANG_SPANISH} "Ya está instalado"
|
||||
LangString alreadyInstalledLong ${LANG_SPANISH} "${PRODUCTNAME} ${VERSION} ya está instalado. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString appRunning ${LANG_SPANISH} "¡${PRODUCTNAME} está abierto! Por favor ciérrelo e intente de nuevo."
|
||||
LangString appRunningOkKill ${LANG_SPANISH} "¡${PRODUCTNAME} está abierto!$\nPulse Aceptar para cerrarlo."
|
||||
LangString appRunning ${LANG_SPANISH} "¡{{product_name}} está abierto! Por favor ciérrelo e intente de nuevo."
|
||||
LangString appRunningOkKill ${LANG_SPANISH} "¡{{product_name}} está abierto!$\nPulse Aceptar para cerrarlo."
|
||||
LangString chooseMaintenanceOption ${LANG_SPANISH} "Elija la operación de mantenimiento que desee realizar."
|
||||
LangString choowHowToInstall ${LANG_SPANISH} "Elija cómo desea instalar ${PRODUCTNAME}."
|
||||
LangString createDesktop ${LANG_SPANISH} "Crear acceso directo en el escritorio"
|
||||
LangString dontUninstall ${LANG_SPANISH} "No desinstalar"
|
||||
LangString dontUninstallDowngrade ${LANG_SPANISH} "No desinstalar (Disminuir la versión sin desinstalar está deshabilitado para este instalador)"
|
||||
LangString failedToKillApp ${LANG_SPANISH} "No se ha podido cerrar ${PRODUCTNAME}. Por favor ciérrelo e intente de nuevo."
|
||||
LangString failedToKillApp ${LANG_SPANISH} "No se ha podido cerrar {{product_name}}. Por favor ciérrelo e intente de nuevo."
|
||||
LangString installingWebview2 ${LANG_SPANISH} "Instalando WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_SPANISH} "Ya está instalada una versión más reciente de ${PRODUCTNAME}. No se recomienda que instale una versión anterior. Si realmente desea instalar esta versión anterior, es recomendable desinstalar la versión actual antes de continuar. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString older ${LANG_SPANISH} "anterior"
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
LangString addOrReinstall ${LANG_SPANISH} "Añadir o reinstalar componentes"
|
||||
LangString alreadyInstalled ${LANG_SPANISH} "Ya está instalado"
|
||||
LangString alreadyInstalledLong ${LANG_SPANISH} "${PRODUCTNAME} ${VERSION} ya está instalado. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString appRunning ${LANG_SPANISH} "¡${PRODUCTNAME} está abierto! Por favor ciérrelo e intente de nuevo."
|
||||
LangString appRunningOkKill ${LANG_SPANISH} "¡${PRODUCTNAME} está abierto!$\nPulse Aceptar para cerrarlo."
|
||||
LangString chooseMaintenanceOption ${LANG_SPANISH} "Elija la operación de mantenimiento que desee realizar."
|
||||
LangString choowHowToInstall ${LANG_SPANISH} "Elija cómo desea instalar ${PRODUCTNAME}."
|
||||
LangString createDesktop ${LANG_SPANISH} "Crear acceso directo en el escritorio"
|
||||
LangString dontUninstall ${LANG_SPANISH} "No desinstalar"
|
||||
LangString dontUninstallDowngrade ${LANG_SPANISH} "No desinstalar (Disminuir la versión sin desinstalar está deshabilitado para este instalador)"
|
||||
LangString failedToKillApp ${LANG_SPANISH} "No se ha podido cerrar ${PRODUCTNAME}. Por favor ciérrelo e intente de nuevo."
|
||||
LangString installingWebview2 ${LANG_SPANISH} "Instalando WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_SPANISH} "Ya está instalada una versión más reciente de ${PRODUCTNAME}. No se recomienda que instale una versión anterior. Si realmente desea instalar esta versión anterior, es recomendable desinstalar la versión actual antes de continuar. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString older ${LANG_SPANISH} "anterior"
|
||||
LangString olderOrUnknownVersionInstalled ${LANG_SPANISH} "Una versión $R4 de ${PRODUCTNAME} está instalada en su sistema. Es recomendable desinstalar la versión actual antes de continuar. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString silentDowngrades ${LANG_SPANISH} "Disminuir la versión está deshabilitado para este instalador. No se puede continuar con el instalador silencioso, por favor use el instalador de interfaz gráfica.$\n"
|
||||
LangString unableToUninstall ${LANG_SPANISH} "No se ha podido desinstalar."
|
||||
LangString uninstallApp ${LANG_SPANISH} "Desinstalar ${PRODUCTNAME}"
|
||||
LangString uninstallBeforeInstalling ${LANG_SPANISH} "Desinstalar antes de instalar"
|
||||
LangString unknown ${LANG_SPANISH} "desconocida"
|
||||
LangString webview2AbortError ${LANG_SPANISH} "No se ha podido instalar WebView2. Intente reiniciar el instalador."
|
||||
LangString webview2DownloadError ${LANG_SPANISH} "Error: No se ha podido descargar WebView2 - $0"
|
||||
LangString webview2DownloadSuccess ${LANG_SPANISH} "El bootstrapper de WebView2 fue descargado con éxito."
|
||||
LangString webview2Downloading ${LANG_SPANISH} "Descargando el bootstrapper de WebView2..."
|
||||
LangString webview2InstallError ${LANG_SPANISH} "Error: La instalación de WebView2 falló con el código $1."
|
||||
LangString webview2InstallSuccess ${LANG_SPANISH} "WebView2 fue instalado con éxito."
|
||||
LangString deleteAppData ${LANG_SPANISH} "Eliminar los datos de aplicación"
|
||||
LangString addOrReinstall ${LANG_SPANISHINTERNATIONAL} "Añadir o reinstalar componentes"
|
||||
LangString alreadyInstalled ${LANG_SPANISHINTERNATIONAL} "Ya está instalado"
|
||||
LangString alreadyInstalledLong ${LANG_SPANISHINTERNATIONAL} "${PRODUCTNAME} ${VERSION} ya está instalado. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString appRunning ${LANG_SPANISHINTERNATIONAL} "¡{{product_name}} está abierto! Por favor ciérrelo e intente de nuevo."
|
||||
LangString appRunningOkKill ${LANG_SPANISHINTERNATIONAL} "¡{{product_name}} está abierto!$\nPulse Aceptar para cerrarlo."
|
||||
LangString chooseMaintenanceOption ${LANG_SPANISHINTERNATIONAL} "Elija la operación de mantenimiento que desee realizar."
|
||||
LangString choowHowToInstall ${LANG_SPANISHINTERNATIONAL} "Elija cómo desea instalar ${PRODUCTNAME}."
|
||||
LangString createDesktop ${LANG_SPANISHINTERNATIONAL} "Crear acceso directo en el escritorio"
|
||||
LangString dontUninstall ${LANG_SPANISHINTERNATIONAL} "No desinstalar"
|
||||
LangString dontUninstallDowngrade ${LANG_SPANISHINTERNATIONAL} "No desinstalar (Disminuir la versión sin desinstalar está deshabilitado para este instalador)"
|
||||
LangString failedToKillApp ${LANG_SPANISHINTERNATIONAL} "No se ha podido cerrar {{product_name}}. Por favor ciérrelo e intente de nuevo."
|
||||
LangString installingWebview2 ${LANG_SPANISHINTERNATIONAL} "Instalando WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_SPANISHINTERNATIONAL} "Ya está instalada una versión más reciente de ${PRODUCTNAME}. No se recomienda que instale una versión anterior. Si realmente desea instalar esta versión anterior, es recomendable desinstalar la versión actual antes de continuar. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString older ${LANG_SPANISHINTERNATIONAL} "anterior"
|
||||
LangString olderOrUnknownVersionInstalled ${LANG_SPANISHINTERNATIONAL} "Una versión $R4 de ${PRODUCTNAME} está instalada en su sistema. Es recomendable desinstalar la versión actual antes de continuar. Seleccione la operación que desee realizar y pulse Siguiente para continuar."
|
||||
LangString silentDowngrades ${LANG_SPANISHINTERNATIONAL} "Disminuir la versión está deshabilitado para este instalador. No se puede continuar con el instalador silencioso, por favor use el instalador de interfaz gráfica.$\n"
|
||||
LangString unableToUninstall ${LANG_SPANISHINTERNATIONAL} "No se ha podido desinstalar."
|
||||
LangString uninstallApp ${LANG_SPANISHINTERNATIONAL} "Desinstalar ${PRODUCTNAME}"
|
||||
LangString uninstallBeforeInstalling ${LANG_SPANISHINTERNATIONAL} "Desinstalar antes de instalar"
|
||||
LangString unknown ${LANG_SPANISHINTERNATIONAL} "desconocida"
|
||||
LangString webview2AbortError ${LANG_SPANISHINTERNATIONAL} "No se ha podido instalar WebView2. Intente reiniciar el instalador."
|
||||
LangString webview2DownloadError ${LANG_SPANISHINTERNATIONAL} "Error: No se ha podido descargar WebView2 - $0"
|
||||
LangString webview2DownloadSuccess ${LANG_SPANISHINTERNATIONAL} "El bootstrapper de WebView2 fue descargado con éxito."
|
||||
LangString webview2Downloading ${LANG_SPANISHINTERNATIONAL} "Descargando el bootstrapper de WebView2..."
|
||||
LangString webview2InstallError ${LANG_SPANISHINTERNATIONAL} "Error: La instalación de WebView2 falló con el código $1."
|
||||
LangString webview2InstallSuccess ${LANG_SPANISHINTERNATIONAL} "WebView2 fue instalado con éxito."
|
||||
LangString deleteAppData ${LANG_SPANISHINTERNATIONAL} "Eliminar los datos de aplicación"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
LangString addOrReinstall ${LANG_SWEDISH} "Lägg till/Installera om komponenter"
|
||||
LangString alreadyInstalled ${LANG_SWEDISH}} "Redan installerad"
|
||||
LangString alreadyInstalledLong ${LANG_SWEDISH}} "${PRODUCTNAME} ${VERSION} är redan installerad. Välj åtgärd och klicka på Nästa för att fortsätta."
|
||||
LangString appRunning ${LANG_SWEDISH} "${PRODUCTNAME} körs! Stäng det först och försök igen."
|
||||
LangString appRunningOkKill ${LANG_SWEDISH} "${PRODUCTNAME} körs!$\nKlicka på OK för att avsluta det."
|
||||
LangString appRunning ${LANG_SWEDISH} "{{product_name}} körs! Stäng det först och försök igen."
|
||||
LangString appRunningOkKill ${LANG_SWEDISH} "{{product_name}} körs!$\nKlicka på OK för att avsluta det."
|
||||
LangString chooseMaintenanceOption ${LANG_SWEDISH} "Välj underhållsåtgärd."
|
||||
LangString choowHowToInstall ${LANG_SWEDISH} "Välj hur du vill installera ${PRODUCTNAME}."
|
||||
LangString createDesktop ${LANG_SWEDISH} "Skapa genväg på skrivbordet"
|
||||
LangString dontUninstall ${LANG_SWEDISH} "Avinstallera inte"
|
||||
LangString dontUninstallDowngrade ${LANG_SWEDISH} "Avinstallera inte (nedgradering utan avinstallation är inaktiverad för den här installationsprogrammet)"
|
||||
LangString failedToKillApp ${LANG_SWEDISH} "Kunde inte avsluta ${PRODUCTNAME}. Stäng det först och försök igen."
|
||||
LangString failedToKillApp ${LANG_SWEDISH} "Kunde inte avsluta {{product_name}}. Stäng det först och försök igen."
|
||||
LangString installingWebview2 ${LANG_SWEDISH} "Installerar WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_SWEDISH} "En nyare version av ${PRODUCTNAME} är redan installerad! Det rekommenderas inte att installera en äldre version. Om du verkligen vill installera denna äldre version är det bättre att avinstallera den nuvarande versionen först. Välj åtgärd och klicka på Nästa för att fortsätta."
|
||||
LangString older ${LANG_SWEDISH} "äldre"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
LangString addOrReinstall ${LANG_TRADCHINESE} "增加或重新安裝元件"
|
||||
LangString alreadyInstalled ${LANG_TRADCHINESE} "已安裝"
|
||||
LangString alreadyInstalledLong ${LANG_TRADCHINESE} "${PRODUCTNAME} ${VERSION} 已經安裝了。選擇你想要進行的操作並且點選下一步。"
|
||||
LangString appRunning ${LANG_TRADCHINESE} "${PRODUCTNAME} 正在執行中!請先關閉再進行嘗試。"
|
||||
LangString appRunningOkKill ${LANG_TRADCHINESE} "${PRODUCTNAME} 正在執行中!點選確定後終止。"
|
||||
LangString appRunning ${LANG_TRADCHINESE} "{{product_name}} 正在執行中!請先關閉再進行嘗試。"
|
||||
LangString appRunningOkKill ${LANG_TRADCHINESE} "{{product_name}} 正在執行中!點選確定後終止。"
|
||||
LangString chooseMaintenanceOption ${LANG_TRADCHINESE} "請選擇你要進行的維護選項。"
|
||||
LangString choowHowToInstall ${LANG_TRADCHINESE} "選擇你要如何安裝 ${PRODUCTNAME}。"
|
||||
LangString createDesktop ${LANG_TRADCHINESE} "建立桌面捷徑"
|
||||
LangString dontUninstall ${LANG_TRADCHINESE} "請勿解除安裝"
|
||||
LangString dontUninstallDowngrade ${LANG_TRADCHINESE} "請勿解除安裝(本安裝程式不允許未解除安裝就進行版本降低的操作)"
|
||||
LangString failedToKillApp ${LANG_TRADCHINESE} "無法終止 ${PRODUCTNAME}。請先關閉再進行嘗試。"
|
||||
LangString failedToKillApp ${LANG_TRADCHINESE} "無法終止 {{product_name}}。請先關閉再進行嘗試。"
|
||||
LangString installingWebview2 ${LANG_TRADCHINESE} "WebView2 安裝中..."
|
||||
LangString newerVersionInstalled ${LANG_TRADCHINESE} "已安裝更新版本的 ${PRODUCTNAME}!不建議安裝舊版。如果真的想要安裝舊版的話,最好先解除安裝現在的版本。選擇你想要進行的操作後再進行下一步。"
|
||||
LangString older ${LANG_TRADCHINESE} "舊版"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
LangString addOrReinstall ${LANG_TURKISH} "Bileşen Ekle/Yeniden Yükle"
|
||||
LangString alreadyInstalled ${LANG_TURKISH} "Daha Önceden Yüklenmiş"
|
||||
LangString alreadyInstalledLong ${LANG_TURKISH} "${PRODUCTNAME} ${VERSION} daha önceden yüklenmiş. Gerçekleştirmek istediğiniz işlemi seçin ve devam etmek için İleri'ye tıklayın."
|
||||
LangString appRunning ${LANG_TURKISH} "${PRODUCTNAME} çalışır durumda! Lütfen önce uygulamayı kapatın ve sonra tekrar deneyin."
|
||||
LangString appRunningOkKill ${LANG_TURKISH} "${PRODUCTNAME} çalışır durumda!$\nUygulamayı sonlandırmak için Tamam'a tıklayın."
|
||||
LangString appRunning ${LANG_TURKISH} "{{product_name}} çalışır durumda! Lütfen önce uygulamayı kapatın ve sonra tekrar deneyin."
|
||||
LangString appRunningOkKill ${LANG_TURKISH} "{{product_name}} çalışır durumda!$\nUygulamayı sonlandırmak için Tamam'a tıklayın."
|
||||
LangString chooseMaintenanceOption ${LANG_TURKISH} "Gerçekleştirmek istediğiniz bakım seçeneğini belirleyin."
|
||||
LangString choowHowToInstall ${LANG_TURKISH} "${PRODUCTNAME} uygulamasını nasıl yüklemek istediğinizi seçin."
|
||||
LangString createDesktop ${LANG_TURKISH} "Masaüstü kısayolu oluştur"
|
||||
LangString dontUninstall ${LANG_TURKISH} "Kaldırma işlemini gerçekleştirme"
|
||||
LangString dontUninstallDowngrade ${LANG_TURKISH} "Kaldırma işlemini gerçekleştirme (Kaldırma işlemi yapmadan sürüm düşürme bu yükleyici için devre dışı bırakılmıştır)"
|
||||
LangString failedToKillApp ${LANG_TURKISH} "${PRODUCTNAME} sonlandırılamadı. Lütfen önce kapatın sonra tekrar deneyin."
|
||||
LangString failedToKillApp ${LANG_TURKISH} "{{product_name}} sonlandırılamadı. Lütfen önce kapatın sonra tekrar deneyin."
|
||||
LangString installingWebview2 ${LANG_TURKISH} "WebView2 yükleniyor..."
|
||||
LangString newerVersionInstalled ${LANG_TURKISH} "${PRODUCTNAME} uygulamasının daha yeni bir sürümü zaten yüklü! Daha eski bir sürümü yüklemeniz önerilmez. Bu eski sürümü gerçekten yüklemek istiyorsanız, önce mevcut sürümü kaldırmanız daha uygundur. Gerçekleştirmek istediğiniz işlemi seçin ve devam etmek için İleri'ye tıklayın."
|
||||
LangString older ${LANG_TURKISH} "daha eski"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
LangString addOrReinstall ${LANG_UKRAINIAN} "Додати/Перевстановити компоненти"
|
||||
LangString alreadyInstalled ${LANG_UKRAINIAN} "Вже встановлено"
|
||||
LangString alreadyInstalledLong ${LANG_UKRAINIAN} "${PRODUCTNAME} ${VERSION} вже встановлено. Виберіть дію, яку ви хочете виконати, і натисніть Далі, щоб продовжити."
|
||||
LangString appRunning ${LANG_UKRAINIAN} "{{product_name}} запущено! Будь ласка, спочатку закрийте його, а потім спробуйте ще раз."
|
||||
LangString appRunningOkKill ${LANG_UKRAINIAN} "{{product_name}} запущено!$\nНатисніть ОК, щоб примусово закрити його"
|
||||
LangString chooseMaintenanceOption ${LANG_UKRAINIAN} "Виберіть дію, яку треба виконати."
|
||||
LangString choowHowToInstall ${LANG_UKRAINIAN} "Виберіть, як ви хочете встановити ${PRODUCTNAME}."
|
||||
LangString createDesktop ${LANG_UKRAINIAN} "Створити ярлик на робочому столі"
|
||||
LangString dontUninstall ${LANG_UKRAINIAN} "Не видаляти"
|
||||
LangString dontUninstallDowngrade ${LANG_UKRAINIAN} "Не видаляти (для цього встановлювача вимкнено зниження версії без видалення)"
|
||||
LangString failedToKillApp ${LANG_UKRAINIAN} "Не вдалося примусово закрити {{product_name}}. Будь ласка, спочатку закрийте його, а потім спробуйте ще раз"
|
||||
LangString installingWebview2 ${LANG_UKRAINIAN} "Встановлення WebView2..."
|
||||
LangString newerVersionInstalled ${LANG_UKRAINIAN} "Новіша версія ${PRODUCTNAME} вже встановлена! Встановлювати старішу версію не рекомендується. Якщо ви дійсно хочете встановити цю версію, краще спочатку видаліть поточну. Виберіть дію, яку ви хочете виконати, і натисніть Далі, щоб продовжити."
|
||||
LangString older ${LANG_UKRAINIAN} "старішу"
|
||||
LangString olderOrUnknownVersionInstalled ${LANG_UKRAINIAN} "У вашій системі вже встановлено $R4 версію ${PRODUCTNAME}. Рекомендується видалити поточну версію перед встановленням. Виберіть дію, яку ви хочете виконати, і натисніть Далі, щоб продовжити."
|
||||
LangString silentDowngrades ${LANG_UKRAINIAN} "Для цього встановлювача вимкнено зниження версій. Неможливо продовжити роботу з фоновим встановлювачем. Будь ласка, скористайтеся встановлювачем з графічним інтерфейсом.$\n"
|
||||
LangString unableToUninstall ${LANG_UKRAINIAN} "Не вдалося видалити!"
|
||||
LangString uninstallApp ${LANG_UKRAINIAN} "Видалити ${PRODUCTNAME}"
|
||||
LangString uninstallBeforeInstalling ${LANG_UKRAINIAN} "Видалити перед встановленням"
|
||||
LangString unknown ${LANG_UKRAINIAN} "невідому"
|
||||
LangString webview2AbortError ${LANG_UKRAINIAN} "Не вдалося встановити WebView2! Без нього програма не може працювати. Спробуйте перезапустити встановлювач."
|
||||
LangString webview2DownloadError ${LANG_UKRAINIAN} "Помилка: не вдалося завантажити WebView2 - $0"
|
||||
LangString webview2DownloadSuccess ${LANG_UKRAINIAN} "WebView2 успішно завантажено"
|
||||
LangString webview2Downloading ${LANG_UKRAINIAN} "Завантаження WebView2..."
|
||||
LangString webview2InstallError ${LANG_UKRAINIAN} "Помилка: не вдалося встановити WebView2, код виходу - $1"
|
||||
LangString webview2InstallSuccess ${LANG_UKRAINIAN} "WebView2 успішно встановлено "
|
||||
LangString deleteAppData ${LANG_UKRAINIAN} "Видалити дані програми"
|
||||
@@ -2,17 +2,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::bundle::settings::Arch;
|
||||
use crate::bundle::windows::sign::{sign_command, try_sign};
|
||||
|
||||
use crate::{
|
||||
bundle::{
|
||||
common::CommandExt,
|
||||
windows::util::{
|
||||
download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer,
|
||||
verify_file_hash, HashAlgorithm, NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME,
|
||||
settings::Arch,
|
||||
windows::{
|
||||
sign::{sign_command, try_sign},
|
||||
util::{
|
||||
download_webview2_bootstrapper, download_webview2_offline_installer,
|
||||
NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
utils::{
|
||||
http_utils::{download_and_verify, verify_file_hash, HashAlgorithm},
|
||||
CommandExt,
|
||||
},
|
||||
Settings,
|
||||
};
|
||||
use tauri_utils::display_path;
|
||||
@@ -35,8 +39,8 @@ const NSIS_URL: &str =
|
||||
#[cfg(target_os = "windows")]
|
||||
const NSIS_SHA1: &str = "057e83c7d82462ec394af76c87d06733605543d4";
|
||||
const NSIS_TAURI_UTILS_URL: &str =
|
||||
"https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.4.1/nsis_tauri_utils.dll";
|
||||
const NSIS_TAURI_UTILS_SHA1: &str = "F99A50209A345185A84D34D0E5F66D04C75FF52F";
|
||||
"https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.5.1/nsis_tauri_utils.dll";
|
||||
const NSIS_TAURI_UTILS_SHA1: &str = "B053B2E5FDB97257954C8F935D80964F056520AE";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const NSIS_REQUIRED_FILES: &[&str] = &[
|
||||
@@ -44,18 +48,25 @@ const NSIS_REQUIRED_FILES: &[&str] = &[
|
||||
"Bin/makensis.exe",
|
||||
"Stubs/lzma-x86-unicode",
|
||||
"Stubs/lzma_solid-x86-unicode",
|
||||
"Plugins/x86-unicode/nsis_tauri_utils.dll",
|
||||
"Plugins/x86-unicode/additional/nsis_tauri_utils.dll",
|
||||
"Include/MUI2.nsh",
|
||||
"Include/FileFunc.nsh",
|
||||
"Include/x64.nsh",
|
||||
"Include/nsDialogs.nsh",
|
||||
"Include/WinMessages.nsh",
|
||||
];
|
||||
const NSIS_PLUGIN_FILES: &[&str] = &[
|
||||
"NSISdl.dll",
|
||||
"StartMenu.dll",
|
||||
"System.dll",
|
||||
"nsDialogs.dll",
|
||||
"additional/nsis_tauri_utils.dll",
|
||||
];
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const NSIS_REQUIRED_FILES: &[&str] = &["Plugins/x86-unicode/nsis_tauri_utils.dll"];
|
||||
const NSIS_REQUIRED_FILES: &[&str] = &["Plugins/x86-unicode/additional/nsis_tauri_utils.dll"];
|
||||
|
||||
const NSIS_REQUIRED_FILES_HASH: &[(&str, &str, &str, HashAlgorithm)] = &[(
|
||||
"Plugins/x86-unicode/nsis_tauri_utils.dll",
|
||||
"Plugins/x86-unicode/additional/nsis_tauri_utils.dll",
|
||||
NSIS_TAURI_UTILS_URL,
|
||||
NSIS_TAURI_UTILS_SHA1,
|
||||
HashAlgorithm::Sha1,
|
||||
@@ -92,7 +103,10 @@ pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<P
|
||||
log::warn!("NSIS directory contains mis-hashed files. Redownloading them.");
|
||||
for (path, url, hash, hash_algorithm) in mismatched {
|
||||
let data = download_and_verify(url, hash, *hash_algorithm)?;
|
||||
fs::write(nsis_toolset_path.join(path), data)?;
|
||||
let out_path = nsis_toolset_path.join(path);
|
||||
std::fs::create_dir_all(out_path.parent().context("output path has no parent")?)
|
||||
.context("failed to create file output directory")?;
|
||||
fs::write(out_path, data).with_context(|| format!("failed to save {path}"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,10 +122,11 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
|
||||
{
|
||||
let data = download_and_verify(NSIS_URL, NSIS_SHA1, HashAlgorithm::Sha1)?;
|
||||
log::info!("extracting NSIS");
|
||||
crate::bundle::windows::util::extract_zip(&data, _tauri_tools_path)?;
|
||||
crate::utils::http_utils::extract_zip(&data, _tauri_tools_path)?;
|
||||
fs::rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?;
|
||||
}
|
||||
|
||||
// download additional plugins
|
||||
let nsis_plugins = nsis_toolset_path.join("Plugins");
|
||||
|
||||
let data = download_and_verify(
|
||||
@@ -120,14 +135,14 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
|
||||
HashAlgorithm::Sha1,
|
||||
)?;
|
||||
|
||||
let target_folder = nsis_plugins.join("x86-unicode");
|
||||
let target_folder = nsis_plugins.join("x86-unicode").join("additional");
|
||||
fs::create_dir_all(&target_folder)?;
|
||||
fs::write(target_folder.join("nsis_tauri_utils.dll"), data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_build_number_if_needed(version_str: &str) -> anyhow::Result<String> {
|
||||
fn try_add_numeric_build_number(version_str: &str) -> anyhow::Result<String> {
|
||||
let version = semver::Version::parse(version_str).context("invalid app version")?;
|
||||
if !version.build.is_empty() {
|
||||
let build = version.build.parse::<u64>();
|
||||
@@ -137,7 +152,10 @@ fn add_build_number_if_needed(version_str: &str) -> anyhow::Result<String> {
|
||||
version.major, version.minor, version.patch, version.build
|
||||
));
|
||||
} else {
|
||||
anyhow::bail!("optional build metadata in app version must be numeric-only");
|
||||
log::warn!(
|
||||
"Unable to parse version build metadata. Numeric value expected, received: `{}`. This will be replaced with `0` in `VIProductVersion` because Windows requires this field to be numeric.",
|
||||
version.build
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +164,10 @@ fn add_build_number_if_needed(version_str: &str) -> anyhow::Result<String> {
|
||||
version.major, version.minor, version.patch,
|
||||
))
|
||||
}
|
||||
|
||||
fn build_nsis_app_installer(
|
||||
settings: &Settings,
|
||||
_nsis_toolset_path: &Path,
|
||||
#[allow(unused_variables)] nsis_toolset_path: &Path,
|
||||
tauri_tools_path: &Path,
|
||||
updater: bool,
|
||||
) -> crate::Result<Vec<PathBuf>> {
|
||||
@@ -158,8 +177,7 @@ fn build_nsis_app_installer(
|
||||
Arch::AArch64 => "arm64",
|
||||
target => {
|
||||
return Err(crate::Error::ArchError(format!(
|
||||
"unsupported architecture: {:?}",
|
||||
target
|
||||
"unsupported architecture: {target:?}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
@@ -172,6 +190,65 @@ fn build_nsis_app_installer(
|
||||
}
|
||||
fs::create_dir_all(&output_path)?;
|
||||
|
||||
// we make a copy of the NSIS directory if we're going to sign its DLLs
|
||||
// because we don't want to change the DLL hashes so the cache can reuse it
|
||||
let maybe_plugin_copy_path = if settings.can_sign() {
|
||||
// find nsis path
|
||||
#[cfg(target_os = "linux")]
|
||||
let system_nsis_toolset_path = std::env::var_os("NSIS_PATH")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from("/usr/share/nsis"));
|
||||
#[cfg(target_os = "macos")]
|
||||
let system_nsis_toolset_path = std::env::var_os("NSIS_PATH")
|
||||
.map(PathBuf::from)
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to resolve NSIS path"))
|
||||
.or_else(|_| {
|
||||
let mut makensis_path =
|
||||
which::which("makensis").context("failed to resolve `makensis`; did you install nsis? See https://tauri.app/distribute/windows-installer/#install-nsis for more information")?;
|
||||
// homebrew installs it as a symlink
|
||||
if makensis_path.is_symlink() {
|
||||
// read_link might return a path relative to makensis_path so we must use join() and canonicalize
|
||||
makensis_path = makensis_path
|
||||
.parent()
|
||||
.context("missing makensis parent")?
|
||||
.join(std::fs::read_link(&makensis_path).context("failed to resolve makensis symlink")?)
|
||||
.canonicalize()
|
||||
.context("failed to resolve makensis path")?;
|
||||
}
|
||||
// file structure:
|
||||
// ├── bin
|
||||
// │ ├── makensis
|
||||
// ├── share
|
||||
// │ ├── nsis
|
||||
let bin_folder = makensis_path.parent().context("missing makensis parent")?;
|
||||
let root_folder = bin_folder.parent().context("missing makensis root")?;
|
||||
crate::Result::Ok(root_folder.join("share").join("nsis"))
|
||||
})?;
|
||||
#[cfg(windows)]
|
||||
let system_nsis_toolset_path = nsis_toolset_path.to_path_buf();
|
||||
|
||||
let plugins_path = output_path.join("Plugins");
|
||||
// copy system plugins (we don't want to modify system installed DLLs, and on some systems there will even be permission errors if we try)
|
||||
crate::utils::fs_utils::copy_dir(
|
||||
&system_nsis_toolset_path.join("Plugins").join("x86-unicode"),
|
||||
&plugins_path.join("x86-unicode"),
|
||||
)
|
||||
.context("failed to copy system NSIS Plugins folder to local copy")?;
|
||||
// copy our downloaded DLLs
|
||||
crate::utils::fs_utils::copy_dir(
|
||||
&nsis_toolset_path
|
||||
.join("Plugins")
|
||||
.join("x86-unicode")
|
||||
.join("additional"),
|
||||
&plugins_path.join("x86-unicode").join("additional"),
|
||||
)
|
||||
.context("failed to copy additional NSIS Plugins folder to local copy")?;
|
||||
Some(plugins_path)
|
||||
} else {
|
||||
// in this case plugin_copy_path can be None, we'll use the system default path
|
||||
None
|
||||
};
|
||||
|
||||
let mut data = BTreeMap::new();
|
||||
|
||||
let bundle_id = settings.bundle_identifier();
|
||||
@@ -179,12 +256,17 @@ fn build_nsis_app_installer(
|
||||
.publisher()
|
||||
.unwrap_or_else(|| bundle_id.split('.').nth(1).unwrap_or(bundle_id));
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let mut dir = dirs::cache_dir().unwrap();
|
||||
dir.extend(["tauri", "NSIS", "Plugins", "x86-unicode"]);
|
||||
data.insert("additional_plugins_path", to_json(dir));
|
||||
}
|
||||
let additional_plugins_path = maybe_plugin_copy_path
|
||||
.clone()
|
||||
.unwrap_or_else(|| nsis_toolset_path.join("Plugins"))
|
||||
.join("x86-unicode")
|
||||
.join("additional");
|
||||
|
||||
data.insert(
|
||||
"additional_plugins_path",
|
||||
// either our Plugins copy (when signing) or the cache/Plugins/x86-unicode path
|
||||
to_json(&additional_plugins_path),
|
||||
);
|
||||
|
||||
data.insert("arch", to_json(arch));
|
||||
data.insert("bundle_id", to_json(bundle_id));
|
||||
@@ -210,7 +292,7 @@ fn build_nsis_app_installer(
|
||||
data.insert("version", to_json(version));
|
||||
data.insert(
|
||||
"version_with_build",
|
||||
to_json(add_build_number_if_needed(version)?),
|
||||
to_json(try_add_numeric_build_number(version)?),
|
||||
);
|
||||
|
||||
data.insert(
|
||||
@@ -459,6 +541,7 @@ fn build_nsis_app_installer(
|
||||
let mut handlebars = Handlebars::new();
|
||||
handlebars.register_helper("or", Box::new(handlebars_or));
|
||||
handlebars.register_helper("association-description", Box::new(association_description));
|
||||
handlebars.register_helper("no-escape", Box::new(handlebars_no_escape));
|
||||
handlebars.register_escape_fn(|s| {
|
||||
let mut output = String::new();
|
||||
for c in s.chars() {
|
||||
@@ -517,13 +600,29 @@ fn build_nsis_app_installer(
|
||||
));
|
||||
fs::create_dir_all(nsis_installer_path.parent().unwrap())?;
|
||||
|
||||
log::info!(action = "Running"; "makensis.exe to produce {}", display_path(&nsis_installer_path));
|
||||
if settings.can_sign() {
|
||||
log::info!("Signing NSIS plugins");
|
||||
for dll in NSIS_PLUGIN_FILES {
|
||||
let path = additional_plugins_path.join(dll);
|
||||
if path.exists() {
|
||||
try_sign(&path, settings)?;
|
||||
} else {
|
||||
log::warn!("Could not find {}, skipping signing", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(action = "Running"; "makensis to produce {}", display_path(&nsis_installer_path));
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut nsis_cmd = Command::new(_nsis_toolset_path.join("makensis.exe"));
|
||||
let mut nsis_cmd = Command::new(nsis_toolset_path.join("makensis.exe"));
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let mut nsis_cmd = Command::new("makensis");
|
||||
|
||||
if let Some(plugins_path) = &maybe_plugin_copy_path {
|
||||
nsis_cmd.env("NSISPLUGINS", plugins_path);
|
||||
}
|
||||
|
||||
nsis_cmd
|
||||
.args(["-INPUTCHARSET", "UTF8", "-OUTPUTCHARSET", "UTF8"])
|
||||
.arg(match settings.log_level() {
|
||||
@@ -587,14 +686,49 @@ fn association_description(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handlebars_no_escape(
|
||||
h: &handlebars::Helper<'_>,
|
||||
_: &Handlebars<'_>,
|
||||
_: &handlebars::Context,
|
||||
_: &mut handlebars::RenderContext<'_, '_>,
|
||||
out: &mut dyn handlebars::Output,
|
||||
) -> handlebars::HelperResult {
|
||||
// get parameter from helper or throw an error
|
||||
let param = h
|
||||
.param(0)
|
||||
.ok_or(handlebars::RenderErrorReason::ParamNotFoundForIndex(
|
||||
"no-escape",
|
||||
0,
|
||||
))?;
|
||||
write!(out, "{}", param.render())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// BTreeMap<OriginalPath, (ParentOfTargetPath, TargetPath)>
|
||||
type ResourcesMap = BTreeMap<PathBuf, (PathBuf, PathBuf)>;
|
||||
fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
let mut resources = ResourcesMap::new();
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
|
||||
let mut added_resources = Vec::new();
|
||||
|
||||
// Adding WebViewer2Loader.dll in case windows-gnu toolchain is used
|
||||
if settings.target().ends_with("-gnu") {
|
||||
let loader_path =
|
||||
dunce::simplified(&settings.project_out_directory().join("WebView2Loader.dll")).to_path_buf();
|
||||
if loader_path.exists() {
|
||||
if settings.can_sign() {
|
||||
try_sign(&loader_path, settings)?;
|
||||
}
|
||||
added_resources.push(loader_path.clone());
|
||||
resources.insert(
|
||||
loader_path,
|
||||
(PathBuf::new(), PathBuf::from("WebView2Loader.dll")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for resource in settings.resource_files().iter() {
|
||||
let resource = resource?;
|
||||
|
||||
@@ -609,6 +743,10 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
}
|
||||
added_resources.push(resource_path.clone());
|
||||
|
||||
if settings.can_sign() {
|
||||
try_sign(&resource_path, settings)?;
|
||||
}
|
||||
|
||||
let target_path = resource.target();
|
||||
resources.insert(
|
||||
resource_path,
|
||||
@@ -697,6 +835,8 @@ fn get_lang_data(lang: &str) -> Option<(String, &[u8])> {
|
||||
"persian" => include_bytes!("./languages/Persian.nsh"),
|
||||
"turkish" => include_bytes!("./languages/Turkish.nsh"),
|
||||
"swedish" => include_bytes!("./languages/Swedish.nsh"),
|
||||
"portuguese" => include_bytes!("./languages/Portuguese.nsh"),
|
||||
"ukrainian" => include_bytes!("./languages/Ukrainian.nsh"),
|
||||
_ => return None,
|
||||
};
|
||||
Some((path, content))
|
||||
|
||||
@@ -19,43 +19,54 @@
|
||||
!macroend
|
||||
|
||||
; Checks whether app is running or not and prompts to kill it.
|
||||
!macro CheckIfAppIsRunning
|
||||
!macro CheckIfAppIsRunning executableName productName
|
||||
!define UniqueID ${__LINE__}
|
||||
|
||||
; Replace {{product_name}} placeholder in the messages with the passed product name
|
||||
nsis_tauri_utils::StrReplace "$(appRunning)" "{{product_name}}" "${productName}"
|
||||
Pop $R1
|
||||
nsis_tauri_utils::StrReplace "$(appRunningOkKill)" "{{product_name}}" "${productName}"
|
||||
Pop $R2
|
||||
nsis_tauri_utils::StrReplace "$(failedToKillApp)" "{{product_name}}" "${productName}"
|
||||
Pop $R3
|
||||
|
||||
!if "${INSTALLMODE}" == "currentUser"
|
||||
nsis_tauri_utils::FindProcessCurrentUser "${MAINBINARYNAME}.exe"
|
||||
nsis_tauri_utils::FindProcessCurrentUser "${executableName}"
|
||||
!else
|
||||
nsis_tauri_utils::FindProcess "${MAINBINARYNAME}.exe"
|
||||
nsis_tauri_utils::FindProcess "${executableName}"
|
||||
!endif
|
||||
Pop $R0
|
||||
${If} $R0 = 0
|
||||
IfSilent kill 0
|
||||
${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK kill IDCANCEL cancel ${|}
|
||||
kill:
|
||||
IfSilent kill_${UniqueID} 0
|
||||
${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL $R2 IDOK kill_${UniqueID} IDCANCEL cancel_${UniqueID} ${|}
|
||||
kill_${UniqueID}:
|
||||
!if "${INSTALLMODE}" == "currentUser"
|
||||
nsis_tauri_utils::KillProcessCurrentUser "${MAINBINARYNAME}.exe"
|
||||
nsis_tauri_utils::KillProcessCurrentUser "${executableName}"
|
||||
!else
|
||||
nsis_tauri_utils::KillProcess "${MAINBINARYNAME}.exe"
|
||||
nsis_tauri_utils::KillProcess "${executableName}"
|
||||
!endif
|
||||
Pop $R0
|
||||
Sleep 500
|
||||
${If} $R0 = 0
|
||||
Goto app_check_done
|
||||
Goto app_check_done_${UniqueID}
|
||||
${Else}
|
||||
IfSilent silent ui
|
||||
silent:
|
||||
IfSilent silent_${UniqueID} ui_${UniqueID}
|
||||
silent_${UniqueID}:
|
||||
System::Call 'kernel32::AttachConsole(i -1)i.r0'
|
||||
${If} $0 != 0
|
||||
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
|
||||
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
|
||||
FileWrite $0 "$(appRunning)$\n"
|
||||
FileWrite $0 "$R1$\n"
|
||||
${EndIf}
|
||||
Abort
|
||||
ui:
|
||||
Abort "$(failedToKillApp)"
|
||||
ui_${UniqueID}:
|
||||
Abort $R3
|
||||
${EndIf}
|
||||
cancel:
|
||||
Abort "$(appRunning)"
|
||||
cancel_${UniqueID}:
|
||||
Abort $R1
|
||||
${EndIf}
|
||||
app_check_done:
|
||||
app_check_done_${UniqueID}:
|
||||
!undef UniqueID
|
||||
!macroend
|
||||
|
||||
; Sets AppUserModelId on a shortcut
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use crate::bundle::settings::CustomSignCommandSettings;
|
||||
#[cfg(windows)]
|
||||
use crate::bundle::windows::util;
|
||||
use crate::{bundle::common::CommandExt, Settings};
|
||||
use crate::{utils::CommandExt, Settings};
|
||||
#[cfg(windows)]
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
@@ -142,12 +142,21 @@ pub fn sign_command_custom<P: AsRef<Path>>(
|
||||
) -> crate::Result<Command> {
|
||||
let path = path.as_ref();
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
|
||||
let mut cmd = Command::new(&command.cmd);
|
||||
for arg in &command.args {
|
||||
if arg == "%1" {
|
||||
cmd.arg(path);
|
||||
} else {
|
||||
cmd.arg(arg);
|
||||
let path = Path::new(arg);
|
||||
// turn relative paths into absolute paths - so the uninstall command can use them
|
||||
// since the !uninstfinalize NSIS hook runs in a different directory
|
||||
if path.exists() && path.is_relative() {
|
||||
cmd.arg(cwd.join(path));
|
||||
} else {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(cmd)
|
||||
@@ -241,9 +250,9 @@ pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_sign(file_path: &std::path::PathBuf, settings: &Settings) -> crate::Result<()> {
|
||||
pub fn try_sign<P: AsRef<Path>>(file_path: P, settings: &Settings) -> crate::Result<()> {
|
||||
if settings.can_sign() {
|
||||
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path));
|
||||
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path.as_ref()));
|
||||
sign(file_path, &settings.sign_params())?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -3,15 +3,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
fs::{create_dir_all, File},
|
||||
io::{Cursor, Read, Write},
|
||||
fs::create_dir_all,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use ureq::ResponseExt;
|
||||
|
||||
use regex::Regex;
|
||||
use sha2::Digest;
|
||||
use url::Url;
|
||||
use zip::ZipArchive;
|
||||
use crate::utils::http_utils::download;
|
||||
|
||||
pub const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703";
|
||||
pub const WEBVIEW2_OFFLINE_INSTALLER_X86_URL: &str =
|
||||
@@ -26,9 +23,12 @@ pub const WIX_OUTPUT_FOLDER_NAME: &str = "msi";
|
||||
pub const WIX_UPDATER_OUTPUT_FOLDER_NAME: &str = "msi-updater";
|
||||
|
||||
pub fn webview2_guid_path(url: &str) -> crate::Result<(String, String)> {
|
||||
let agent = ureq::AgentBuilder::new().try_proxy_from_env(true).build();
|
||||
let agent: ureq::Agent = ureq::Agent::config_builder()
|
||||
.proxy(ureq::Proxy::try_from_env())
|
||||
.build()
|
||||
.into();
|
||||
let response = agent.head(url).call().map_err(Box::new)?;
|
||||
let final_url = response.get_url();
|
||||
let final_url = response.get_uri().to_string();
|
||||
let remaining_url = final_url.strip_prefix(WEBVIEW2_URL_PREFIX).ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"WebView2 URL prefix mismatch. Expected `{}`, found `{}`.",
|
||||
@@ -69,148 +69,6 @@ pub fn download_webview2_offline_installer(base_path: &Path, arch: &str) -> crat
|
||||
Ok(file_path)
|
||||
}
|
||||
|
||||
fn generate_github_mirror_url_from_template(github_url: &str) -> Option<String> {
|
||||
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE")
|
||||
.ok()
|
||||
.and_then(|template| {
|
||||
let re =
|
||||
Regex::new(r"https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.*)").unwrap();
|
||||
re.captures(github_url).map(|caps| {
|
||||
template
|
||||
.replace("<owner>", &caps[1])
|
||||
.replace("<repo>", &caps[2])
|
||||
.replace("<version>", &caps[3])
|
||||
.replace("<asset>", &caps[4])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_github_mirror_url_from_base(github_url: &str) -> Option<String> {
|
||||
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR")
|
||||
.ok()
|
||||
.and_then(|cdn| Url::parse(&cdn).ok())
|
||||
.map(|mut cdn| {
|
||||
cdn.set_path(github_url);
|
||||
cdn.to_string()
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> {
|
||||
if !url.starts_with("https://github.com/") {
|
||||
return None;
|
||||
}
|
||||
|
||||
generate_github_mirror_url_from_template(url)
|
||||
.or_else(|| generate_github_mirror_url_from_base(url))
|
||||
.map(|alt_url| (ureq::AgentBuilder::new().build(), alt_url))
|
||||
}
|
||||
|
||||
fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
|
||||
generate_github_alternative_url(url).unwrap_or((
|
||||
ureq::AgentBuilder::new().try_proxy_from_env(true).build(),
|
||||
url.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn download(url: &str) -> crate::Result<Vec<u8>> {
|
||||
let (agent, final_url) = create_agent_and_url(url);
|
||||
|
||||
log::info!(action = "Downloading"; "{}", final_url);
|
||||
|
||||
let response = agent.get(&final_url).call().map_err(Box::new)?;
|
||||
let mut bytes = Vec::new();
|
||||
response.into_reader().read_to_end(&mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HashAlgorithm {
|
||||
#[cfg(target_os = "windows")]
|
||||
Sha256,
|
||||
Sha1,
|
||||
}
|
||||
|
||||
/// Function used to download a file and checks SHA256 to verify the download.
|
||||
pub fn download_and_verify(
|
||||
url: &str,
|
||||
hash: &str,
|
||||
hash_algorithm: HashAlgorithm,
|
||||
) -> crate::Result<Vec<u8>> {
|
||||
let data = download(url)?;
|
||||
log::info!("validating hash");
|
||||
verify_hash(&data, hash, hash_algorithm)?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn verify_hash(data: &[u8], hash: &str, hash_algorithm: HashAlgorithm) -> crate::Result<()> {
|
||||
match hash_algorithm {
|
||||
#[cfg(target_os = "windows")]
|
||||
HashAlgorithm::Sha256 => {
|
||||
let hasher = sha2::Sha256::new();
|
||||
verify_data_with_hasher(data, hash, hasher)
|
||||
}
|
||||
HashAlgorithm::Sha1 => {
|
||||
let hasher = sha1::Sha1::new();
|
||||
verify_data_with_hasher(data, hash, hasher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_data_with_hasher(data: &[u8], hash: &str, mut hasher: impl Digest) -> crate::Result<()> {
|
||||
hasher.update(data);
|
||||
|
||||
let url_hash = hasher.finalize().to_vec();
|
||||
let expected_hash = hex::decode(hash)?;
|
||||
if expected_hash == url_hash {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::Error::HashError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_file_hash<P: AsRef<Path>>(
|
||||
path: P,
|
||||
hash: &str,
|
||||
hash_algorithm: HashAlgorithm,
|
||||
) -> crate::Result<()> {
|
||||
let data = std::fs::read(path)?;
|
||||
verify_hash(&data, hash, hash_algorithm)
|
||||
}
|
||||
|
||||
/// Extracts the zips from memory into a usable path.
|
||||
#[allow(dead_code)]
|
||||
pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
|
||||
let cursor = Cursor::new(data);
|
||||
|
||||
let mut zipa = ZipArchive::new(cursor)?;
|
||||
|
||||
for i in 0..zipa.len() {
|
||||
let mut file = zipa.by_index(i)?;
|
||||
|
||||
if let Some(name) = file.enclosed_name() {
|
||||
let dest_path = path.join(name);
|
||||
if file.is_dir() {
|
||||
create_dir_all(&dest_path)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = dest_path.parent().expect("Failed to get parent");
|
||||
|
||||
if !parent.exists() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let mut buff: Vec<u8> = Vec::new();
|
||||
file.read_to_end(&mut buff)?;
|
||||
let mut fileout = File::create(dest_path).expect("Failed to open file");
|
||||
|
||||
fileout.write_all(&buff)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn os_bitness<'a>() -> Option<&'a str> {
|
||||
use windows_sys::Win32::System::SystemInformation::{
|
||||
@@ -226,56 +84,76 @@ pub fn os_bitness<'a>() -> Option<&'a str> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::generate_github_mirror_url_from_template;
|
||||
use std::env;
|
||||
pub fn patch_binary(binary_path: &PathBuf, package_type: &crate::PackageType) -> crate::Result<()> {
|
||||
let file_data = std::fs::read(binary_path)?;
|
||||
let mut file_data = file_data; // make mutable
|
||||
|
||||
const GITHUB_ASSET_URL: &str =
|
||||
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
|
||||
const NON_GITHUB_ASSET_URL: &str = "https://someotherwebsite.com/somefile.zip";
|
||||
let pe = match goblin::Object::parse(&file_data)? {
|
||||
goblin::Object::PE(pe) => pe,
|
||||
_ => {
|
||||
return Err(crate::Error::BinaryParseError(
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidInput, "binary is not a PE file").into(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_generate_mirror_url_no_env_var() {
|
||||
env::remove_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE");
|
||||
let tauri_bundle_section = pe
|
||||
.sections
|
||||
.iter()
|
||||
.find(|s| s.name().unwrap_or_default() == ".taubndl")
|
||||
.ok_or(crate::Error::MissingBundleTypeVar)?;
|
||||
|
||||
assert!(generate_github_mirror_url_from_template(GITHUB_ASSET_URL).is_none());
|
||||
let data_offset = tauri_bundle_section.pointer_to_raw_data as usize;
|
||||
|
||||
if data_offset + 8 > file_data.len() {
|
||||
return Err(crate::Error::BinaryOffsetOutOfRange);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_mirror_url_non_github_url() {
|
||||
env::set_var(
|
||||
"TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE",
|
||||
"https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
|
||||
);
|
||||
let ptr_bytes = &file_data[data_offset..data_offset + 8];
|
||||
let ptr_value = u64::from_le_bytes(ptr_bytes.try_into().map_err(|_| {
|
||||
crate::Error::BinaryParseError(
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid pointer bytes").into(),
|
||||
)
|
||||
})?);
|
||||
|
||||
assert!(generate_github_mirror_url_from_template(NON_GITHUB_ASSET_URL).is_none());
|
||||
let rdata_section = pe
|
||||
.sections
|
||||
.iter()
|
||||
.find(|s| s.name().unwrap_or_default() == ".rdata")
|
||||
.ok_or_else(|| {
|
||||
crate::Error::BinaryParseError(
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidInput, ".rdata section not found").into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let rva = ptr_value.checked_sub(pe.image_base as u64).ok_or_else(|| {
|
||||
crate::Error::BinaryParseError(
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid RVA offset").into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let file_offset = rdata_section.pointer_to_raw_data as usize
|
||||
+ (rva as usize).saturating_sub(rdata_section.virtual_address as usize);
|
||||
|
||||
if file_offset + 3 > file_data.len() {
|
||||
return Err(crate::Error::BinaryOffsetOutOfRange);
|
||||
}
|
||||
|
||||
struct TestCase {
|
||||
template: &'static str,
|
||||
expected_url: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_mirror_url_correctly() {
|
||||
let test_cases = vec![
|
||||
TestCase {
|
||||
template: "https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
|
||||
expected_url: "https://mirror.example.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip",
|
||||
},
|
||||
TestCase {
|
||||
template: "https://mirror.example.com/<asset>",
|
||||
expected_url: "https://mirror.example.com/wix311-binaries.zip",
|
||||
},
|
||||
];
|
||||
|
||||
for case in test_cases {
|
||||
env::set_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", case.template);
|
||||
assert_eq!(
|
||||
generate_github_mirror_url_from_template(GITHUB_ASSET_URL),
|
||||
Some(case.expected_url.to_string())
|
||||
);
|
||||
// Overwrite the string at that offset
|
||||
let string_bytes = &mut file_data[file_offset..file_offset + 3];
|
||||
match package_type {
|
||||
crate::PackageType::Nsis => string_bytes.copy_from_slice(b"NSS"),
|
||||
crate::PackageType::WindowsMsi => string_bytes.copy_from_slice(b"MSI"),
|
||||
_ => {
|
||||
return Err(crate::Error::InvalidPackageType(
|
||||
package_type.short_name().to_owned(),
|
||||
"windows".to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
std::fs::write(binary_path, &file_data)
|
||||
.map_err(|e| crate::Error::BinaryWriteError(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -64,6 +64,21 @@ pub enum Error {
|
||||
/// Failed to validate downloaded file hash.
|
||||
#[error("hash mismatch of downloaded file")]
|
||||
HashError,
|
||||
/// Failed to parse binary
|
||||
#[error("Binary parse error: `{0}`")]
|
||||
BinaryParseError(#[from] goblin::error::Error),
|
||||
/// Package type is not supported by target platform
|
||||
#[error("Wrong package type {0} for platform {1}")]
|
||||
InvalidPackageType(String, String),
|
||||
/// Bundle type symbol missing in binary
|
||||
#[error("__TAURI_BUNDLE_TYPE variable not found in binary. Make sure tauri crate and tauri-cli are up to date")]
|
||||
MissingBundleTypeVar,
|
||||
/// Failed to write binary file changed
|
||||
#[error("Failed to write binary file changes: `{0}`")]
|
||||
BinaryWriteError(String),
|
||||
/// Invalid offset while patching binary file
|
||||
#[error("Invalid offset while patching binary file")]
|
||||
BinaryOffsetOutOfRange,
|
||||
/// Unsupported architecture.
|
||||
#[error("Architecture Error: `{0}`")]
|
||||
ArchError(String),
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! [](https://tauri.app)
|
||||
//!
|
||||
//! The Tauri bundler is a tool that generates installers or app bundles for executables.
|
||||
//! It supports auto updating through [tauri](https://docs.rs/tauri).
|
||||
//!
|
||||
@@ -25,5 +23,6 @@
|
||||
/// The bundle API.
|
||||
pub mod bundle;
|
||||
mod error;
|
||||
mod utils;
|
||||
pub use bundle::*;
|
||||
pub use error::{Error, Result};
|
||||
|
||||
@@ -4,28 +4,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs::{self, File},
|
||||
io::{self, BufRead, BufReader, BufWriter},
|
||||
io::{self, BufWriter},
|
||||
path::Path,
|
||||
process::{Command, ExitStatus, Output, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
/// Returns true if the path has a filename indicating that it is a high-density
|
||||
/// "retina" icon. Specifically, returns true the file stem ends with
|
||||
/// "@2x" (a convention specified by the [Apple developer docs](
|
||||
/// <https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html>)).
|
||||
#[allow(dead_code)]
|
||||
pub fn is_retina<P: AsRef<Path>>(path: P) -> bool {
|
||||
path
|
||||
.as_ref()
|
||||
.file_stem()
|
||||
.and_then(OsStr::to_str)
|
||||
.map(|stem| stem.ends_with("@2x"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Creates a new file at the given path, creating any parent directories as
|
||||
/// needed.
|
||||
pub fn create_file(path: &Path) -> crate::Result<BufWriter<File>> {
|
||||
@@ -36,6 +19,36 @@ pub fn create_file(path: &Path) -> crate::Result<BufWriter<File>> {
|
||||
Ok(BufWriter::new(file))
|
||||
}
|
||||
|
||||
/// Creates the given directory path,
|
||||
/// erasing it first if specified.
|
||||
#[allow(dead_code)]
|
||||
pub fn create_dir(path: &Path, erase: bool) -> crate::Result<()> {
|
||||
if erase && path.exists() {
|
||||
remove_dir_all(path)?;
|
||||
}
|
||||
Ok(fs::create_dir(path)?)
|
||||
}
|
||||
|
||||
/// Creates all of the directories of the specified path,
|
||||
/// erasing it first if specified.
|
||||
#[allow(dead_code)]
|
||||
pub fn create_dir_all(path: &Path, erase: bool) -> crate::Result<()> {
|
||||
if erase && path.exists() {
|
||||
remove_dir_all(path)?;
|
||||
}
|
||||
Ok(fs::create_dir_all(path)?)
|
||||
}
|
||||
|
||||
/// Removes the directory and its contents if it exists.
|
||||
#[allow(dead_code)]
|
||||
pub fn remove_dir_all(path: &Path) -> crate::Result<()> {
|
||||
if path.exists() {
|
||||
Ok(fs::remove_dir_all(path)?)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a symbolic link to a directory.
|
||||
#[cfg(unix)]
|
||||
#[allow(dead_code)]
|
||||
@@ -63,11 +76,9 @@ fn symlink_file(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
}
|
||||
|
||||
/// Copies a regular file from one path to another, creating any parent
|
||||
/// directories of the destination path as necessary. Fails if the source path
|
||||
/// directories of the destination path as necessary. Fails if the source path
|
||||
/// is a directory or doesn't exist.
|
||||
pub fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> crate::Result<()> {
|
||||
let from = from.as_ref();
|
||||
let to = to.as_ref();
|
||||
pub fn copy_file(from: &Path, to: &Path) -> crate::Result<()> {
|
||||
if !from.exists() {
|
||||
return Err(crate::Error::GenericError(format!(
|
||||
"{from:?} does not exist"
|
||||
@@ -100,9 +111,6 @@ pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
|
||||
"{from:?} is not a Directory"
|
||||
)));
|
||||
}
|
||||
if to.exists() {
|
||||
return Err(crate::Error::GenericError(format!("{to:?} already exists")));
|
||||
}
|
||||
let parent = to.parent().expect("No data in parent");
|
||||
fs::create_dir_all(parent)?;
|
||||
for entry in walkdir::WalkDir::new(from) {
|
||||
@@ -118,7 +126,7 @@ pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
|
||||
symlink_file(&target, &dest_path)?;
|
||||
}
|
||||
} else if entry.file_type().is_dir() {
|
||||
fs::create_dir(dest_path)?;
|
||||
fs::create_dir_all(dest_path)?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dest_path)?;
|
||||
}
|
||||
@@ -151,7 +159,7 @@ pub fn copy_custom_files(
|
||||
pkg_path
|
||||
};
|
||||
if path.is_file() {
|
||||
copy_file(path, data_dir.join(pkg_path))?;
|
||||
copy_file(path, &data_dir.join(pkg_path))?;
|
||||
} else {
|
||||
copy_dir(path, &data_dir.join(pkg_path))?;
|
||||
}
|
||||
@@ -159,93 +167,10 @@ pub fn copy_custom_files(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait CommandExt {
|
||||
// The `pipe` function sets the stdout and stderr to properly
|
||||
// show the command output in the Node.js wrapper.
|
||||
fn piped(&mut self) -> std::io::Result<ExitStatus>;
|
||||
fn output_ok(&mut self) -> crate::Result<Output>;
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn piped(&mut self) -> std::io::Result<ExitStatus> {
|
||||
self.stdin(os_pipe::dup_stdin()?);
|
||||
self.stdout(os_pipe::dup_stdout()?);
|
||||
self.stderr(os_pipe::dup_stderr()?);
|
||||
let program = self.get_program().to_string_lossy().into_owned();
|
||||
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
|
||||
|
||||
self.status().map_err(Into::into)
|
||||
}
|
||||
|
||||
fn output_ok(&mut self) -> crate::Result<Output> {
|
||||
let program = self.get_program().to_string_lossy().into_owned();
|
||||
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
|
||||
|
||||
self.stdout(Stdio::piped());
|
||||
self.stderr(Stdio::piped());
|
||||
|
||||
let mut child = self.spawn()?;
|
||||
|
||||
let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
|
||||
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
|
||||
let stdout_lines_ = stdout_lines.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut line = String::new();
|
||||
let mut lines = stdout_lines_.lock().unwrap();
|
||||
loop {
|
||||
line.clear();
|
||||
match stdout.read_line(&mut line) {
|
||||
Ok(0) => break,
|
||||
Ok(_) => {
|
||||
log::debug!(action = "stdout"; "{}", line.trim_end());
|
||||
lines.extend(line.as_bytes().to_vec());
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
|
||||
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr_lines_ = stderr_lines.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut line = String::new();
|
||||
let mut lines = stderr_lines_.lock().unwrap();
|
||||
loop {
|
||||
line.clear();
|
||||
match stderr.read_line(&mut line) {
|
||||
Ok(0) => break,
|
||||
Ok(_) => {
|
||||
log::debug!(action = "stderr"; "{}", line.trim_end());
|
||||
lines.extend(line.as_bytes().to_vec());
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let status = child.wait()?;
|
||||
let output = Output {
|
||||
status,
|
||||
stdout: std::mem::take(&mut *stdout_lines.lock().unwrap()),
|
||||
stderr: std::mem::take(&mut *stderr_lines.lock().unwrap()),
|
||||
};
|
||||
|
||||
if output.status.success() {
|
||||
Ok(output)
|
||||
} else {
|
||||
Err(crate::Error::GenericError(format!(
|
||||
"failed to run {program}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{create_file, is_retina};
|
||||
use std::{io::Write, path::PathBuf};
|
||||
use tauri_utils::resources::resource_relpath;
|
||||
use super::create_file;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn create_file_with_parent_dirs() {
|
||||
@@ -263,6 +188,8 @@ mod tests {
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn copy_dir_with_symlinks() {
|
||||
use std::path::PathBuf;
|
||||
|
||||
// Create a directory structure that looks like this:
|
||||
// ${TMP}/orig/
|
||||
// sub/
|
||||
@@ -310,26 +237,4 @@ mod tests {
|
||||
b"Hello, world!\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retina_icon_paths() {
|
||||
assert!(!is_retina("data/icons/512x512.png"));
|
||||
assert!(is_retina("data/icons/512x512@2x.png"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resource_relative_paths() {
|
||||
assert_eq!(
|
||||
resource_relpath(&PathBuf::from("./data/images/button.png")),
|
||||
PathBuf::from("data/images/button.png")
|
||||
);
|
||||
assert_eq!(
|
||||
resource_relpath(&PathBuf::from("../../images/wheel.png")),
|
||||
PathBuf::from("_up_/_up_/images/wheel.png")
|
||||
);
|
||||
assert_eq!(
|
||||
resource_relpath(&PathBuf::from("/home/ferris/crab.png")),
|
||||
PathBuf::from("_root_/home/ferris/crab.png")
|
||||
);
|
||||
}
|
||||
}
|
||||
219
crates/tauri-bundler/src/utils/http_utils.rs
Normal file
219
crates/tauri-bundler/src/utils/http_utils.rs
Normal file
@@ -0,0 +1,219 @@
|
||||
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
fs::{create_dir_all, File},
|
||||
io::{Cursor, Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
use sha2::Digest;
|
||||
use url::Url;
|
||||
use zip::ZipArchive;
|
||||
|
||||
fn generate_github_mirror_url_from_template(github_url: &str) -> Option<String> {
|
||||
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE")
|
||||
.ok()
|
||||
.and_then(|template| {
|
||||
let re =
|
||||
Regex::new(r"https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.*)").unwrap();
|
||||
re.captures(github_url).map(|caps| {
|
||||
template
|
||||
.replace("<owner>", &caps[1])
|
||||
.replace("<repo>", &caps[2])
|
||||
.replace("<version>", &caps[3])
|
||||
.replace("<asset>", &caps[4])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_github_mirror_url_from_base(github_url: &str) -> Option<String> {
|
||||
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR")
|
||||
.ok()
|
||||
.and_then(|cdn| Url::parse(&cdn).ok())
|
||||
.map(|mut cdn| {
|
||||
cdn.set_path(github_url);
|
||||
cdn.to_string()
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> {
|
||||
if !url.starts_with("https://github.com/") {
|
||||
return None;
|
||||
}
|
||||
|
||||
generate_github_mirror_url_from_template(url)
|
||||
.or_else(|| generate_github_mirror_url_from_base(url))
|
||||
.map(|alt_url| (ureq::agent(), alt_url))
|
||||
}
|
||||
|
||||
fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
|
||||
generate_github_alternative_url(url).unwrap_or((
|
||||
ureq::Agent::config_builder()
|
||||
.proxy(ureq::Proxy::try_from_env())
|
||||
.build()
|
||||
.into(),
|
||||
url.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn download(url: &str) -> crate::Result<Vec<u8>> {
|
||||
let (agent, final_url) = create_agent_and_url(url);
|
||||
|
||||
log::info!(action = "Downloading"; "{}", final_url);
|
||||
|
||||
let response = agent.get(&final_url).call().map_err(Box::new)?;
|
||||
let mut bytes = Vec::new();
|
||||
response.into_body().into_reader().read_to_end(&mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HashAlgorithm {
|
||||
#[cfg(target_os = "windows")]
|
||||
Sha256,
|
||||
Sha1,
|
||||
}
|
||||
|
||||
/// Function used to download a file and checks SHA256 to verify the download.
|
||||
#[allow(dead_code)]
|
||||
pub fn download_and_verify(
|
||||
url: &str,
|
||||
hash: &str,
|
||||
hash_algorithm: HashAlgorithm,
|
||||
) -> crate::Result<Vec<u8>> {
|
||||
let data = download(url)?;
|
||||
log::info!("validating hash");
|
||||
verify_hash(&data, hash, hash_algorithm)?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn verify_hash(data: &[u8], hash: &str, hash_algorithm: HashAlgorithm) -> crate::Result<()> {
|
||||
match hash_algorithm {
|
||||
#[cfg(target_os = "windows")]
|
||||
HashAlgorithm::Sha256 => {
|
||||
let hasher = sha2::Sha256::new();
|
||||
verify_data_with_hasher(data, hash, hasher)
|
||||
}
|
||||
HashAlgorithm::Sha1 => {
|
||||
let hasher = sha1::Sha1::new();
|
||||
verify_data_with_hasher(data, hash, hasher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_data_with_hasher(data: &[u8], hash: &str, mut hasher: impl Digest) -> crate::Result<()> {
|
||||
hasher.update(data);
|
||||
|
||||
let url_hash = hasher.finalize().to_vec();
|
||||
let expected_hash = hex::decode(hash)?;
|
||||
if expected_hash == url_hash {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::Error::HashError)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn verify_file_hash<P: AsRef<Path>>(
|
||||
path: P,
|
||||
hash: &str,
|
||||
hash_algorithm: HashAlgorithm,
|
||||
) -> crate::Result<()> {
|
||||
let data = std::fs::read(path)?;
|
||||
verify_hash(&data, hash, hash_algorithm)
|
||||
}
|
||||
|
||||
/// Extracts the zips from memory into a usable path.
|
||||
#[allow(dead_code)]
|
||||
pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
|
||||
let cursor = Cursor::new(data);
|
||||
|
||||
let mut zipa = ZipArchive::new(cursor)?;
|
||||
|
||||
for i in 0..zipa.len() {
|
||||
let mut file = zipa.by_index(i)?;
|
||||
|
||||
if let Some(name) = file.enclosed_name() {
|
||||
let dest_path = path.join(name);
|
||||
if file.is_dir() {
|
||||
create_dir_all(&dest_path)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = dest_path.parent().expect("Failed to get parent");
|
||||
|
||||
if !parent.exists() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let mut buff: Vec<u8> = Vec::new();
|
||||
file.read_to_end(&mut buff)?;
|
||||
let mut fileout = File::create(dest_path).expect("Failed to open file");
|
||||
|
||||
fileout.write_all(&buff)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::generate_github_mirror_url_from_template;
|
||||
use std::env;
|
||||
|
||||
const GITHUB_ASSET_URL: &str =
|
||||
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
|
||||
const NON_GITHUB_ASSET_URL: &str = "https://someotherwebsite.com/somefile.zip";
|
||||
|
||||
#[test]
|
||||
fn test_generate_mirror_url_no_env_var() {
|
||||
env::remove_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE");
|
||||
|
||||
assert!(generate_github_mirror_url_from_template(GITHUB_ASSET_URL).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_mirror_url_non_github_url() {
|
||||
env::set_var(
|
||||
"TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE",
|
||||
"https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
|
||||
);
|
||||
|
||||
assert!(generate_github_mirror_url_from_template(NON_GITHUB_ASSET_URL).is_none());
|
||||
}
|
||||
|
||||
struct TestCase {
|
||||
template: &'static str,
|
||||
expected_url: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_mirror_url_correctly() {
|
||||
let test_cases = vec![
|
||||
TestCase {
|
||||
template: "https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
|
||||
expected_url: "https://mirror.example.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip",
|
||||
},
|
||||
TestCase {
|
||||
template: "https://mirror.example.com/<asset>",
|
||||
expected_url: "https://mirror.example.com/wix311-binaries.zip",
|
||||
},
|
||||
];
|
||||
|
||||
for case in test_cases {
|
||||
env::set_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", case.template);
|
||||
assert_eq!(
|
||||
generate_github_mirror_url_from_template(GITHUB_ASSET_URL),
|
||||
Some(case.expected_url.to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
141
crates/tauri-bundler/src/utils/mod.rs
Normal file
141
crates/tauri-bundler/src/utils/mod.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
io::{BufRead, BufReader},
|
||||
path::Path,
|
||||
process::{Command, ExitStatus, Output, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
pub mod fs_utils;
|
||||
pub mod http_utils;
|
||||
|
||||
/// Returns true if the path has a filename indicating that it is a high-density
|
||||
/// "retina" icon. Specifically, returns true the file stem ends with
|
||||
/// "@2x" (a convention specified by the [Apple developer docs](
|
||||
/// <https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html>)).
|
||||
#[allow(dead_code)]
|
||||
pub fn is_retina(path: &Path) -> bool {
|
||||
path
|
||||
.file_stem()
|
||||
.and_then(OsStr::to_str)
|
||||
.map(|stem| stem.ends_with("@2x"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub trait CommandExt {
|
||||
// The `pipe` function sets the stdout and stderr to properly
|
||||
// show the command output in the Node.js wrapper.
|
||||
fn piped(&mut self) -> std::io::Result<ExitStatus>;
|
||||
fn output_ok(&mut self) -> crate::Result<Output>;
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn piped(&mut self) -> std::io::Result<ExitStatus> {
|
||||
self.stdin(os_pipe::dup_stdin()?);
|
||||
self.stdout(os_pipe::dup_stdout()?);
|
||||
self.stderr(os_pipe::dup_stderr()?);
|
||||
let program = self.get_program().to_string_lossy().into_owned();
|
||||
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
|
||||
|
||||
self.status()
|
||||
}
|
||||
|
||||
fn output_ok(&mut self) -> crate::Result<Output> {
|
||||
let program = self.get_program().to_string_lossy().into_owned();
|
||||
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
|
||||
|
||||
self.stdout(Stdio::piped());
|
||||
self.stderr(Stdio::piped());
|
||||
|
||||
let mut child = self.spawn()?;
|
||||
|
||||
let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
|
||||
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
|
||||
let stdout_lines_ = stdout_lines.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut line = String::new();
|
||||
let mut lines = stdout_lines_.lock().unwrap();
|
||||
loop {
|
||||
line.clear();
|
||||
match stdout.read_line(&mut line) {
|
||||
Ok(0) => break,
|
||||
Ok(_) => {
|
||||
log::debug!(action = "stdout"; "{}", line.trim_end());
|
||||
lines.extend(line.as_bytes().to_vec());
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
|
||||
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr_lines_ = stderr_lines.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut line = String::new();
|
||||
let mut lines = stderr_lines_.lock().unwrap();
|
||||
loop {
|
||||
line.clear();
|
||||
match stderr.read_line(&mut line) {
|
||||
Ok(0) => break,
|
||||
Ok(_) => {
|
||||
log::debug!(action = "stderr"; "{}", line.trim_end());
|
||||
lines.extend(line.as_bytes().to_vec());
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let status = child.wait()?;
|
||||
let output = Output {
|
||||
status,
|
||||
stdout: std::mem::take(&mut *stdout_lines.lock().unwrap()),
|
||||
stderr: std::mem::take(&mut *stderr_lines.lock().unwrap()),
|
||||
};
|
||||
|
||||
if output.status.success() {
|
||||
Ok(output)
|
||||
} else {
|
||||
Err(crate::Error::GenericError(format!(
|
||||
"failed to run {program}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tauri_utils::resources::resource_relpath;
|
||||
|
||||
use super::is_retina;
|
||||
|
||||
#[test]
|
||||
fn retina_icon_paths() {
|
||||
assert!(!is_retina(Path::new("data/icons/512x512.png")));
|
||||
assert!(is_retina(Path::new("data/icons/512x512@2x.png")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resource_relative_paths() {
|
||||
assert_eq!(
|
||||
resource_relpath(Path::new("./data/images/button.png")),
|
||||
PathBuf::from("data/images/button.png")
|
||||
);
|
||||
assert_eq!(
|
||||
resource_relpath(Path::new("../../images/wheel.png")),
|
||||
PathBuf::from("_up_/_up_/images/wheel.png")
|
||||
);
|
||||
assert_eq!(
|
||||
resource_relpath(Path::new("/home/ferris/crab.png")),
|
||||
PathBuf::from("_root_/home/ferris/crab.png")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,241 @@
|
||||
# Changelog
|
||||
|
||||
## \[2.7.1]
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.5.2`
|
||||
|
||||
## \[2.7.0]
|
||||
|
||||
### New Features
|
||||
|
||||
- [`33d079392`](https://www.github.com/tauri-apps/tauri/commit/33d079392ac4a5a153b7d8a6d82fefd6f54a2bdf) ([#13811](https://www.github.com/tauri-apps/tauri/pull/13811) by [@mhbagheri-99](https://www.github.com/tauri-apps/tauri/../../mhbagheri-99)) Allow runner configuration to be an object with cmd, cwd, and args properties. The runner can now be configured as `{ "cmd": "my_runner", "cwd": "/path", "args": ["--quiet"] }` while maintaining backwards compatibility with the existing string format.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`232265c70`](https://www.github.com/tauri-apps/tauri/commit/232265c70e1c213bbb3f84b5541ddc07d330fce1) ([#13209](https://www.github.com/tauri-apps/tauri/pull/13209) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Binaries are patched before bundling to add the type of a bundle they will placed in. This information will be used during update process to select the correct target.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`916aeaa48`](https://www.github.com/tauri-apps/tauri/commit/916aeaa48646a483a78e51cfe1633800ee62c37c) ([#13781](https://www.github.com/tauri-apps/tauri/pull/13781) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes Android dev and build commands reading `tauri.ios.conf.json` instead of `tauri.android.conf.json` to merge platform-specific configuration.
|
||||
- [`acd757428`](https://www.github.com/tauri-apps/tauri/commit/acd7574284056f9c00894cdce6c07f948fd80c87) ([#13743](https://www.github.com/tauri-apps/tauri/pull/13743) by [@owjs3901](https://www.github.com/tauri-apps/tauri/../../owjs3901)) Fix type of CFBundleVersion generated by `tauri ios init` when `bundleVersion` is a single number (for example `1` instead of `1.0.0`).
|
||||
- [`0f248b111`](https://www.github.com/tauri-apps/tauri/commit/0f248b111ffb8af934eaf64bd8f4591e628da786) ([#13799](https://www.github.com/tauri-apps/tauri/pull/13799) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Install iOS dependencies when needed.
|
||||
- [`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.
|
||||
- [`bda830410`](https://www.github.com/tauri-apps/tauri/commit/bda8304107da7ca60caaba5674faa793491898c6) ([#13833](https://www.github.com/tauri-apps/tauri/pull/13833) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fail with an error when trying to migrate from v2 alpha
|
||||
- [`bda830410`](https://www.github.com/tauri-apps/tauri/commit/bda8304107da7ca60caaba5674faa793491898c6) ([#13833](https://www.github.com/tauri-apps/tauri/pull/13833) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Use v2 stable instead of v2-rc when migrating from v2-beta
|
||||
|
||||
### What's Changed
|
||||
|
||||
- [`cfc5bb819`](https://www.github.com/tauri-apps/tauri/commit/cfc5bb819637a97141cbda3285c5d772cfc0ebca) ([#13780](https://www.github.com/tauri-apps/tauri/pull/13780) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Enable edge to edge in `tauri android init` template
|
||||
- [`12e359061`](https://www.github.com/tauri-apps/tauri/commit/12e3590613c7b4370aeddc16db7e29335e3a7684) ([#13759](https://www.github.com/tauri-apps/tauri/pull/13759) by [@owjs3901](https://www.github.com/tauri-apps/tauri/../../owjs3901)) Update compileSdk, targetSdk in android template to 36
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.5.1`
|
||||
- Upgraded to `tauri-utils@2.6.0`
|
||||
|
||||
## \[2.6.2]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`cbd962972`](https://www.github.com/tauri-apps/tauri/commit/cbd9629729ed6eb208ba2234d014c11c4e9f1c8c) ([#13730](https://www.github.com/tauri-apps/tauri/pull/13730) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Load `--config` arguments when running the Xcode and Android Studio build scripts.
|
||||
|
||||
## \[2.6.1]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`4b7370e9e`](https://www.github.com/tauri-apps/tauri/commit/4b7370e9e0ba299361c8ad96c08882337e44f091) ([#13710](https://www.github.com/tauri-apps/tauri/pull/13710) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue in the plugin template (`tauri plugin init`) that prevented the js build command to fail.
|
||||
|
||||
## \[2.6.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.
|
||||
- [`3242e1c94`](https://www.github.com/tauri-apps/tauri/commit/3242e1c946c441b58665ba5d612f3a3f1eafe0b6) ([#13659](https://www.github.com/tauri-apps/tauri/pull/13659) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow passing Cargo arguments to mobile dev and build commands.
|
||||
- [`d1ce9af62`](https://www.github.com/tauri-apps/tauri/commit/d1ce9af62881e3f7d86a495c9c40df5b7f9d1c04) ([#13660](https://www.github.com/tauri-apps/tauri/pull/13660) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow passing `--config` arguments to the `ios init` and `android init` commands to tweak the configuration used to initialize the mobile projects.
|
||||
- [`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"`.
|
||||
- [`4a880ca69`](https://www.github.com/tauri-apps/tauri/commit/4a880ca697bab6d63a2a51ea94e1988cc8c4ea4a) ([#13658](https://www.github.com/tauri-apps/tauri/pull/13658) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Synchronize Tauri config productName changes with the iOS Xcode project.
|
||||
- [`8ee14a864`](https://www.github.com/tauri-apps/tauri/commit/8ee14a86480510c15823586cf28084e615cb7a9c) ([#13618](https://www.github.com/tauri-apps/tauri/pull/13618) by [@Sky-walkerX](https://www.github.com/tauri-apps/tauri/../../Sky-walkerX)) Warn the user that the app id shouldn't end in `.app` because it conflicts with the application bundle extension on macOS
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`574a4d4d3`](https://www.github.com/tauri-apps/tauri/commit/574a4d4d36762b5b09dc3fcfcbcae3a0df0b6d89) ([#13426](https://www.github.com/tauri-apps/tauri/pull/13426) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `dev`, `build` and `bundle` commands always take 2 seconds to start
|
||||
- [`35aa7e121`](https://www.github.com/tauri-apps/tauri/commit/35aa7e1218f34d0805e280e3ec32529d0cb0d733) ([#13294](https://www.github.com/tauri-apps/tauri/pull/13294) by [@kingsword09](https://www.github.com/tauri-apps/tauri/../../kingsword09)) fix: allow the target directory to be inside frontendDir as long as it is not the Rust target directory inside frontendDir.
|
||||
- [`ec6065fa4`](https://www.github.com/tauri-apps/tauri/commit/ec6065fa4a6427266ecfb0c0f62f008574bb7880) ([#13625](https://www.github.com/tauri-apps/tauri/pull/13625) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes Android and iOS dev/build commands not working when the app identifier is being modified by the `--config` option.
|
||||
- [`5a5291d66`](https://www.github.com/tauri-apps/tauri/commit/5a5291d66cb8a955c9d4f8e975782646ac0cc6e7) ([#13483](https://www.github.com/tauri-apps/tauri/pull/13483) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix simulator build detection on Xcode.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.5.0`
|
||||
- Upgraded to `tauri-utils@2.5.0`
|
||||
- [`9c16eefa3`](https://www.github.com/tauri-apps/tauri/commit/9c16eefa319b4697bac1d1019bbb5f93eca63173) ([#13629](https://www.github.com/tauri-apps/tauri/pull/13629) by [@sftse](https://www.github.com/tauri-apps/tauri/../../sftse)) Update html5ever to 0.29 and kuchikiki to version 0.8.8-speedreader.
|
||||
|
||||
## \[2.5.0]
|
||||
|
||||
### New Features
|
||||
|
||||
- [`0aa48fb9e`](https://www.github.com/tauri-apps/tauri/commit/0aa48fb9e4b9d7b5bf3522000a76ebc1836394ed) ([#13030](https://www.github.com/tauri-apps/tauri/pull/13030)) Added `bundleVersion` to iOS and macOS configuration to support specifying a `CFBundleVersion`.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`ad3fd3890`](https://www.github.com/tauri-apps/tauri/commit/ad3fd3890f1fa26a9f9be04ff1bc156d6dd2a8bc) ([#13152](https://www.github.com/tauri-apps/tauri/pull/13152)) Detect package manager from environment variable `npm_config_user_agent` first
|
||||
- [`82406c61e`](https://www.github.com/tauri-apps/tauri/commit/82406c61e0fbb775ef00791ccab45349325bdd45) ([#13231](https://www.github.com/tauri-apps/tauri/pull/13231)) Improve iOS simulator usage, checking if Xcode iOS SDK is installed and allowing usage of Simulator for older iOS releases (previously only supported when running on Xcode via `ios dev --open`).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`2dccfab53`](https://www.github.com/tauri-apps/tauri/commit/2dccfab5321fef55d45f3a4c674b6151b1c4424a) ([#13236](https://www.github.com/tauri-apps/tauri/pull/13236)) Fix `fileAssociations` missing `LSHandlerRank` on macOS.
|
||||
- [`080252903`](https://www.github.com/tauri-apps/tauri/commit/0802529031c4fd309edff374a8694e93ddec161d) ([#13210](https://www.github.com/tauri-apps/tauri/pull/13210)) Fixes iOS dev not working on Xcode 16.3 simulators. To apply the fix, either regenerate the Xcode project with `rm -r src-tauri/gen/apple && tauri ios init` or remove the `arm64-sim` architecture from the Xcode project.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.4.0`
|
||||
- Upgraded to `tauri-bundler@2.4.0`
|
||||
|
||||
## \[2.4.1]
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`f805061d1`](https://www.github.com/tauri-apps/tauri/commit/f805061d1152bc4790dbdb9475a506afcdd1de75) ([#13079](https://www.github.com/tauri-apps/tauri/pull/13079) by [@Pietagorh](https://www.github.com/tauri-apps/tauri/../../Pietagorh)) Add support for passing TOML and JSON5 config files to `--config` arg
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`30beb6fee`](https://www.github.com/tauri-apps/tauri/commit/30beb6fee7b052e588ee72c238e315a557b5d6f2) ([#13096](https://www.github.com/tauri-apps/tauri/pull/13096) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `tauri info` can't find the latest version for rust crates
|
||||
- [`30beb6fee`](https://www.github.com/tauri-apps/tauri/commit/30beb6fee7b052e588ee72c238e315a557b5d6f2) ([#13096](https://www.github.com/tauri-apps/tauri/pull/13096) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `tauri info` logging network operations in `info` level instead of `debug`
|
||||
- [`794af778e`](https://www.github.com/tauri-apps/tauri/commit/794af778e4915ffb6a4fe9bae8fba04bc880503d) ([#13117](https://www.github.com/tauri-apps/tauri/pull/13117) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix setting merge config value to null with `--config` arg no longer works
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.3.1`
|
||||
- Upgraded to `tauri-utils@2.3.1`
|
||||
|
||||
## \[2.4.0]
|
||||
|
||||
### New Features
|
||||
|
||||
- [`d91bfa5cb`](https://www.github.com/tauri-apps/tauri/commit/d91bfa5cb921a078758edd45ef3eaff71358d1eb) ([#12970](https://www.github.com/tauri-apps/tauri/pull/12970) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow merging multiple configuration values on `tauri dev`, `tauri build`, `tauri bundle`, `tauri android dev`, `tauri android build`, `tauri ios dev` and `tauri ios build`.
|
||||
- [`013f8f652`](https://www.github.com/tauri-apps/tauri/commit/013f8f652302f2d49c5ec0a075582033d8b074fb) ([#12890](https://www.github.com/tauri-apps/tauri/pull/12890) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Reads `build > removeUnusedCommands` from the config file and pass in the environment variables on the build command to trigger the build scripts and macros to remove unused commands based on the capabilities you defined. For this to work on inlined plugins you must add a `#![plugin(<insert_plugin_name>)]` inside the `tauri::generate_handler![]` usage and the app manifest must be set.
|
||||
- [`30f5a1553`](https://www.github.com/tauri-apps/tauri/commit/30f5a1553d3c0ce460c9006764200a9210915a44) ([#12366](https://www.github.com/tauri-apps/tauri/pull/12366) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Added `trafficLightPosition` window configuration to set the traffic light buttons position on macOS.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`f981a5ee8`](https://www.github.com/tauri-apps/tauri/commit/f981a5ee8b292b9ea09329f60cecc7f688dda734) ([#12602](https://www.github.com/tauri-apps/tauri/pull/12602) by [@kxxt](https://www.github.com/tauri-apps/tauri/../../kxxt)) Add basic support for linux riscv64 platform.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`0c4700e99`](https://www.github.com/tauri-apps/tauri/commit/0c4700e9907f242eabe579eb6149a1d75174185c) ([#12985](https://www.github.com/tauri-apps/tauri/pull/12985) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The cli will now accept `--bundles updater` again. It's still no-op as it has been for all v2 versions. If you want to build updater artifacts, enable `createUpdaterArtifacts` in `tauri.conf.json`.
|
||||
- [`eec08a18b`](https://www.github.com/tauri-apps/tauri/commit/eec08a18b66525f5544cd30144d0553260ee3a70) ([#12998](https://www.github.com/tauri-apps/tauri/pull/12998) by [@jason89521](https://www.github.com/tauri-apps/tauri/../../jason89521)) For bun's lockfile, check both `bun.lock` and `bun.lockb`.
|
||||
- [`b83921226`](https://www.github.com/tauri-apps/tauri/commit/b83921226cb3084992bb5357e7e39a09ea97843e) ([#12977](https://www.github.com/tauri-apps/tauri/pull/12977) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix `tauri ios` commands using the wrong working directory with `bun@>1.2`.
|
||||
- [`f268b3dbd`](https://www.github.com/tauri-apps/tauri/commit/f268b3dbdf313484c85b4a1f69cd7cec63049f35) ([#12871](https://www.github.com/tauri-apps/tauri/pull/12871) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Ignore parent .gitignore files on the Tauri project path detection.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.3.0`
|
||||
- Upgraded to `tauri-bundler@2.3.0`
|
||||
|
||||
## \[2.3.1]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`22e9bf74a`](https://www.github.com/tauri-apps/tauri/commit/22e9bf74a4684c827279a85bb66548e83c1ea5cf) ([#12538](https://www.github.com/tauri-apps/tauri/pull/12538) by [@DeTeam](https://www.github.com/tauri-apps/tauri/../../DeTeam)) Set initialViewController for LaunchScreen (iOS).
|
||||
|
||||
## \[2.3.0]
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`a2d36b8c3`](https://www.github.com/tauri-apps/tauri/commit/a2d36b8c34a8dcfc6736797ca5cd4665faf75e7e) ([#12181](https://www.github.com/tauri-apps/tauri/pull/12181) by [@bastiankistner](https://www.github.com/tauri-apps/tauri/../../bastiankistner)) Add an option to change the default background throttling policy (currently for WebKit only).
|
||||
- [`6e417c943`](https://www.github.com/tauri-apps/tauri/commit/6e417c9435d8fae0eca9e8d42d6215e887a28d98) ([#12786](https://www.github.com/tauri-apps/tauri/pull/12786) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Added RPM to the list of package types for which signature file will be generated.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-utils@2.2.0`
|
||||
- Upgraded to `tauri-bundler@2.2.4`
|
||||
- Upgraded to `tauri-macos-sign@2.1.0`
|
||||
|
||||
## \[2.2.7]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`8e9134c4a`](https://www.github.com/tauri-apps/tauri/commit/8e9134c4a2047329be0dbb868b7ae061a9d3f190) ([#12511](https://www.github.com/tauri-apps/tauri/pull/12511) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused `tauri dev` to fail because of an incorrect `--bins` flag.
|
||||
|
||||
## \[2.2.6]
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`1a86974aa`](https://www.github.com/tauri-apps/tauri/commit/1a86974aa3d09957c6b1142a17bbfed9998798fd) ([#12406](https://www.github.com/tauri-apps/tauri/pull/12406) by [@bradleat](https://www.github.com/tauri-apps/tauri/../../bradleat)) `ios build --open` will now let xcode start the rust build process.
|
||||
- [`9a30bed98`](https://www.github.com/tauri-apps/tauri/commit/9a30bed98c2d8501328006fad5840eb9d533e1c2) ([#12423](https://www.github.com/tauri-apps/tauri/pull/12423) by [@tr3ysmith](https://www.github.com/tauri-apps/tauri/../../tr3ysmith)) Added conditional logic to MacOS codesigning where only executables get the entitlements file when being signed. This solves an issue where the app may not launch when using 3rd party frameworks if certain entitlements are added. Ex: multicast support (must be applied for through apple developer, and the framework would not have that capability).
|
||||
- [`0b79af711`](https://www.github.com/tauri-apps/tauri/commit/0b79af711430934362602fb950c3e4cb5b59cf9c) ([#12438](https://www.github.com/tauri-apps/tauri/pull/12438) by [@3lpsy](https://www.github.com/tauri-apps/tauri/../../3lpsy)) Log the command used to start the rust app in development.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`bc43c738b`](https://www.github.com/tauri-apps/tauri/commit/bc43c738baf686353690d3d9259b4976881718c8) ([#12442](https://www.github.com/tauri-apps/tauri/pull/12442) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that prevented `tauri add` to work for the `clipboard-manager` plugin.
|
||||
- [`27096cdc0`](https://www.github.com/tauri-apps/tauri/commit/27096cdc05d89b61b2372b4e4a3018c87f240ab8) ([#12445](https://www.github.com/tauri-apps/tauri/pull/12445) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused Tauri's CLI to enable tauri's `native-tls` feature even though it wasn't needed. Moved `reqwest` to a mobile-only dependency in `tauri` and enabled its `rustls-tls` feature flag.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.2.3`
|
||||
|
||||
## \[2.2.5]
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.2.2`
|
||||
|
||||
## \[2.2.4]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`cad550445`](https://www.github.com/tauri-apps/tauri/commit/cad5504455ffa53e297cebff473c113b1afa5d29) ([#12354](https://www.github.com/tauri-apps/tauri/pull/12354) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed and issue that caused `tauri add` to try to install incorrect npm packages.
|
||||
|
||||
## \[2.2.3]
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`a0f2c84d5`](https://www.github.com/tauri-apps/tauri/commit/a0f2c84d51f5086c5055867d6f61ea90c463a26c) ([#12204](https://www.github.com/tauri-apps/tauri/pull/12204) by [@pjf-dev](https://www.github.com/tauri-apps/tauri/../../pjf-dev)) Enhance `tauri icon` command by including 64x64 png size in default icon sizes.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`98f62e65a`](https://www.github.com/tauri-apps/tauri/commit/98f62e65a27a375272c6b4d9f34c23e142b9d3a6) ([#12246](https://www.github.com/tauri-apps/tauri/pull/12246) by [@marcomq](https://www.github.com/tauri-apps/tauri/../../marcomq)) Properly add NPM packages for community plugins when using the `tauri add` command.
|
||||
- [`b9a99a5c6`](https://www.github.com/tauri-apps/tauri/commit/b9a99a5c69d8a2a1a3ff30e500b46872258dca15) ([#12297](https://www.github.com/tauri-apps/tauri/pull/12297) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the built-in dev server to constantly refresh on Linux. This only affected users who do not have `devUrl` point to a URL.
|
||||
- [`ef21ed9ac`](https://www.github.com/tauri-apps/tauri/commit/ef21ed9ac1c045c38b0c04e3d71a441694abc257) ([#12290](https://www.github.com/tauri-apps/tauri/pull/12290) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix iOS build failing when the development team contains spaces.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.2.1`
|
||||
|
||||
## \[2.2.2]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`26fc9558f`](https://www.github.com/tauri-apps/tauri/commit/26fc9558fe7b2fe649f61926da88f36110dd5707) ([#12178](https://www.github.com/tauri-apps/tauri/pull/12178) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the `tauri dev` file watcher to exit after detecting file changes.
|
||||
|
||||
## \[2.2.1]
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`881729448`](https://www.github.com/tauri-apps/tauri/commit/881729448c9abd0d0c7941a8a31c94119ce827af) ([#12164](https://www.github.com/tauri-apps/tauri/pull/12164) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused `tauri dev` to crash before showing the app on Linux.
|
||||
|
||||
## \[2.2.0]
|
||||
|
||||
### New Features
|
||||
|
||||
- [`cccb308c7`](https://www.github.com/tauri-apps/tauri/commit/cccb308c7b559b0838138d6cea280665f060c925) ([#11562](https://www.github.com/tauri-apps/tauri/pull/11562) by [@jLynx](https://www.github.com/tauri-apps/tauri/../../jLynx)) Generate signature for `.deb` packages when `createUpdaterArtifacts` option is enabled.
|
||||
- [`74212d40d`](https://www.github.com/tauri-apps/tauri/commit/74212d40d80dba4501b3d4ae30104fa3d447bdf9) ([#11653](https://www.github.com/tauri-apps/tauri/pull/11653) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Include Linux destkop environment and session type in `tauri info` command.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- [`93a3a043d`](https://www.github.com/tauri-apps/tauri/commit/93a3a043d39cc96515d51d98beeb14261d3a246b) ([#11727](https://www.github.com/tauri-apps/tauri/pull/11727) by [@Kiyozz](https://www.github.com/tauri-apps/tauri/../../Kiyozz)) Add support for `Portuguese` language for NSIS windows installer.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [`c8700656b`](https://www.github.com/tauri-apps/tauri/commit/c8700656be3001a0cc6e087f23aebd482430a85b) ([#11985](https://www.github.com/tauri-apps/tauri/pull/11985) by [@ShaunSHamilton](https://www.github.com/tauri-apps/tauri/../../ShaunSHamilton)) Fix `tauri remove` from removing object type (`{}`) permissions.
|
||||
- [`0ae06c5ca`](https://www.github.com/tauri-apps/tauri/commit/0ae06c5ca89cecd24154affdc69668f5e1e67d85) ([#11914](https://www.github.com/tauri-apps/tauri/pull/11914) by [@wtto00](https://www.github.com/tauri-apps/tauri/../../wtto00)) Fix the exclude path in file `Cargo.toml` of plugin template generated by cli. Path changed in [#9346](https://github.com/tauri-apps/tauri/pull/9346)
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Upgraded to `tauri-bundler@2.2.0`
|
||||
- Upgraded to `tauri-utils@2.1.1`
|
||||
|
||||
## \[2.1.0]
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tauri-cli"
|
||||
version = "2.1.0"
|
||||
version = "2.7.1"
|
||||
authors = ["Tauri Programme within The Commons Conservancy"]
|
||||
edition = "2021"
|
||||
rust-version = "1.77.2"
|
||||
@@ -36,75 +36,72 @@ name = "cargo-tauri"
|
||||
path = "src/main.rs"
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
|
||||
cargo-mobile2 = { version = "0.17.3", default-features = false }
|
||||
cargo-mobile2 = { version = "0.20.2", default-features = false }
|
||||
|
||||
[dependencies]
|
||||
jsonrpsee = { version = "0.24.5", features = ["server"] }
|
||||
jsonrpsee-core = "0.24.5"
|
||||
jsonrpsee-client-transport = { version = "0.24.5", features = ["ws"] }
|
||||
jsonrpsee-ws-client = { version = "0.24.5", default-features = false }
|
||||
jsonrpsee = { version = "0.24", features = ["server"] }
|
||||
jsonrpsee-core = "0.24"
|
||||
jsonrpsee-client-transport = { version = "0.24", features = ["ws"] }
|
||||
jsonrpsee-ws-client = { version = "0.24", default-features = false }
|
||||
sublime_fuzzy = "0.7"
|
||||
clap_complete = "4"
|
||||
clap = { version = "4.5", features = ["derive", "env"] }
|
||||
anyhow = "1.0"
|
||||
tauri-bundler = { version = "2.1.0", default-features = false, path = "../tauri-bundler" }
|
||||
colored = "2.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||
notify = "6.1"
|
||||
notify-debouncer-mini = "0.4"
|
||||
shared_child = "1.0"
|
||||
duct = "0.13"
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
anyhow = "1"
|
||||
tauri-bundler = { version = "2.5.2", default-features = false, path = "../tauri-bundler" }
|
||||
colored = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
json5 = "0.4"
|
||||
notify = "8"
|
||||
notify-debouncer-full = "0.5"
|
||||
shared_child = "1"
|
||||
duct = "1.0"
|
||||
toml_edit = { version = "0.22", features = ["serde"] }
|
||||
json-patch = "3.0"
|
||||
tauri-utils = { version = "2.1.0", path = "../tauri-utils", features = [
|
||||
"isolation",
|
||||
"schema",
|
||||
"config-json5",
|
||||
"config-toml",
|
||||
] }
|
||||
tauri-utils-v1 = { version = "1", package = "tauri-utils", features = [
|
||||
json-patch = "3"
|
||||
tauri-utils = { version = "2.6.0", path = "../tauri-utils", features = [
|
||||
"isolation",
|
||||
"schema",
|
||||
"config-json5",
|
||||
"config-toml",
|
||||
"html-manipulation",
|
||||
] }
|
||||
toml = "0.8"
|
||||
jsonschema = "0.18"
|
||||
handlebars = "6.0"
|
||||
jsonschema = "0.30"
|
||||
handlebars = "6"
|
||||
include_dir = "0.7"
|
||||
minisign = "=0.7.3"
|
||||
base64 = "0.22.0"
|
||||
ureq = { version = "2.9.6", default-features = false, features = ["gzip"] }
|
||||
base64 = "0.22"
|
||||
ureq = { version = "3", default-features = false, features = ["gzip"] }
|
||||
os_info = "3"
|
||||
semver = "1.0"
|
||||
regex = "1.10.3"
|
||||
semver = "1"
|
||||
regex = "1"
|
||||
heck = "0.5"
|
||||
dialoguer = "0.11"
|
||||
url = { version = "2.5", features = ["serde"] }
|
||||
url = { version = "2", features = ["serde"] }
|
||||
os_pipe = "1"
|
||||
ignore = "0.4"
|
||||
ctrlc = "3.4"
|
||||
ctrlc = "3"
|
||||
log = { version = "0.4.21", features = ["kv", "kv_std"] }
|
||||
env_logger = "0.11.5"
|
||||
env_logger = "0.11"
|
||||
icns = { package = "tauri-icns", version = "0.1" }
|
||||
image = { version = "0.25", default-features = false, features = ["ico"] }
|
||||
axum = { version = "0.7.4", features = ["ws"] }
|
||||
html5ever = "0.26"
|
||||
kuchiki = { package = "kuchikiki", version = "0.8" }
|
||||
axum = { version = "0.8", features = ["ws"] }
|
||||
html5ever = "0.29"
|
||||
kuchiki = { package = "kuchikiki", version = "=0.8.8-speedreader" }
|
||||
tokio = { version = "1", features = ["macros", "sync"] }
|
||||
common-path = "1"
|
||||
serde-value = "0.7.0"
|
||||
serde-value = "0.7"
|
||||
itertools = "0.13"
|
||||
local-ip-address = "0.6"
|
||||
css-color = "0.2"
|
||||
resvg = "0.43.0"
|
||||
resvg = "0.45.0"
|
||||
dunce = "1"
|
||||
glob = "0.3"
|
||||
oxc_parser = "0.25"
|
||||
oxc_span = "0.25"
|
||||
oxc_allocator = "0.25"
|
||||
oxc_ast = "0.25"
|
||||
# 0.39 raised msrv to above 1.78 but 0.37+ can't compile on 1.77.2 either.
|
||||
oxc_parser = "0.36"
|
||||
oxc_span = "0.36"
|
||||
oxc_allocator = "0.36"
|
||||
oxc_ast = "0.36"
|
||||
magic_string = "0.3"
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
walkdir = "2"
|
||||
@@ -112,15 +109,16 @@ elf = "0.7"
|
||||
memchr = "2"
|
||||
tempfile = "3"
|
||||
uuid = { version = "1", features = ["v5"] }
|
||||
rand = "0.8"
|
||||
rand = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1"
|
||||
pretty_assertions = "1"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows-sys]
|
||||
version = "0.59"
|
||||
version = "0.60"
|
||||
features = [
|
||||
"Win32_Security",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_IO",
|
||||
"Win32_System_Console",
|
||||
@@ -131,7 +129,7 @@ libc = "0.2"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
plist = "1"
|
||||
tauri-macos-sign = { version = "2.0.1", path = "../tauri-macos-sign" }
|
||||
tauri-macos-sign = { version = "2.1.0", path = "../tauri-macos-sign" }
|
||||
object = { version = "0.36", default-features = false, features = [
|
||||
"macho",
|
||||
"read_core",
|
||||
@@ -147,4 +145,4 @@ native-tls = [
|
||||
"ureq/native-tls",
|
||||
]
|
||||
native-tls-vendored = ["native-tls", "tauri-bundler/native-tls-vendored"]
|
||||
rustls = ["tauri-bundler/rustls", "cargo-mobile2/rustls", "ureq/tls"]
|
||||
rustls = ["tauri-bundler/rustls", "cargo-mobile2/rustls", "ureq/rustls"]
|
||||
|
||||
@@ -17,6 +17,7 @@ These environment variables are inputs to the CLI which may have an equivalent C
|
||||
- `TAURI_BUNDLER_WIX_FIPS_COMPLIANT` — Specify the bundler's WiX `FipsCompliant` option.
|
||||
- `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR` - Specify a GitHub mirror to download files and tools used by tauri bundler.
|
||||
- `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE` - Specify a GitHub mirror template to download files and tools used by tauri bundler, for example: `https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>`.
|
||||
- `TAURI_BUNDLER_DMG_IGNORE_CI` - Disable the check for `CI: true` in the `.dmg` bundler.
|
||||
- `TAURI_SKIP_SIDECAR_SIGNATURE_CHECK` - Skip signing sidecars.
|
||||
- `TAURI_SIGNING_PRIVATE_KEY` — Private key used to sign your app bundles, can be either a string or a path to the file.
|
||||
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` — The signing private key password, see `TAURI_SIGNING_PRIVATE_KEY`.
|
||||
|
||||
@@ -40,28 +40,3 @@ MIT or MIT/Apache 2.0 where applicable.
|
||||
Logo: CC-BY-NC-ND
|
||||
|
||||
- Original Tauri Logo Designs by [Daniel Thompson-Yvetot](https://github.com/nothingismagick) and [Guillaume Chau](https://github.com/akryum)
|
||||
|
||||
## Licensing Errata:
|
||||
|
||||
Because of publishing issues upstream, we soft-forked (and patched) both [`console`](https://github.com/mitsuhiko/console/blob/278de9dc2bf0fa28db69adee351072f668beec8f/Cargo.toml#L7) and [`dialoguer`](https://github.com/mitsuhiko/dialoguer/blob/2c3fe6b64641cfb57eb0e1d428274f63976ec150/Cargo.toml#L12) crates because of untenable issues surrounding expected use on Windows.
|
||||
|
||||
This soft fork was introduced to the Tauri Codebase [here](https://github.com/tauri-apps/tauri/pull/1610).
|
||||
|
||||
`console`
|
||||
|
||||
```
|
||||
license = "MIT"
|
||||
authors = [
|
||||
"Armin Ronacher <armin.ronacher@active-4.com>"
|
||||
]
|
||||
```
|
||||
|
||||
`dialoguer`
|
||||
|
||||
```
|
||||
license = "MIT"
|
||||
authors = [
|
||||
"Armin Ronacher <armin.ronacher@active-4.com>",
|
||||
"Pavan Kumar Sunkara <pavan.sss1991@gmail.com>"
|
||||
]
|
||||
```
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://schema.tauri.app/config/2.1.1",
|
||||
"$id": "https://schema.tauri.app/config/2.7.0",
|
||||
"title": "Config",
|
||||
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://v2.tauri.app/reference/cli/#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"../dist\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
|
||||
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://v2.tauri.app/reference/cli/#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"http://localhost:3000\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"identifier"
|
||||
@@ -24,14 +24,14 @@
|
||||
"pattern": "^[^/\\:*?\"<>|]+$"
|
||||
},
|
||||
"mainBinaryName": {
|
||||
"description": "App main binary filename. Defaults to the name of your cargo crate.",
|
||||
"description": "Overrides app's main binary filename.\n\n By default, Tauri uses the output binary from `cargo`, by setting this, we will rename that binary in `tauri-cli`'s\n `tauri build` command, and target `tauri bundle` to it\n\n If possible, change the [`package name`] or set the [`name field`] instead,\n and if that's not enough and you're using nightly, consider using the [`different-binary-name`] feature instead\n\n Note: this config should not include the binary extension (e.g. `.exe`), we'll add that for you\n\n [`package name`]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-name-field\n [`name field`]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-name-field\n [`different-binary-name`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#different-binary-name",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"version": {
|
||||
"description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field. If removed the version number from `Cargo.toml` is used.\n\n By default version 1.0 is used on Android.",
|
||||
"description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field.\n\n If removed the version number from `Cargo.toml` is used.\n It's recommended to manage the app versioning in the Tauri config.\n\n ## Platform-specific\n\n - **macOS**: Translates to the bundle's CFBundleShortVersionString property and is used as the default CFBundleVersion.\n You can set an specific bundle version using [`bundle > macOS > bundleVersion`](MacConfig::bundle_version).\n - **iOS**: Translates to the bundle's CFBundleShortVersionString property and is used as the default CFBundleVersion.\n You can set an specific bundle version using [`bundle > iOS > bundleVersion`](IosConfig::bundle_version).\n The `tauri ios build` CLI command has a `--build-number <number>` option that lets you append a build number to the app version.\n - **Android**: By default version 1.0 is used. You can set a version code using [`bundle > android > versionCode`](AndroidConfig::version_code).\n\n By default version 1.0 is used on Android.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
@@ -69,7 +69,9 @@
|
||||
},
|
||||
"build": {
|
||||
"description": "The build configuration.",
|
||||
"default": {},
|
||||
"default": {
|
||||
"removeUnusedCommands": false
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BuildConfig"
|
||||
@@ -227,7 +229,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"create": {
|
||||
"description": "Whether Tauri should create this window at app startup or not.\n\n When this is set to `false` you must manually grab the config object via `app.config().app.windows`\n and create it with [`WebviewWindowBuilder::from_config`](https://docs.rs/tauri/2.0.0-rc/tauri/webview/struct.WebviewWindowBuilder.html#method.from_config).",
|
||||
"description": "Whether Tauri should create this window at app startup or not.\n\n When this is set to `false` you must manually grab the config object via `app.config().app.windows`\n and create it with [`WebviewWindowBuilder::from_config`](https://docs.rs/tauri/2/tauri/webview/struct.WebviewWindowBuilder.html#method.from_config).",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -317,6 +319,17 @@
|
||||
],
|
||||
"format": "double"
|
||||
},
|
||||
"preventOverflow": {
|
||||
"description": "Whether or not to prevent the window from overflowing the workarea\n\n ## Platform-specific\n\n - **iOS / Android:** Unsupported.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PreventOverflowConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resizable": {
|
||||
"description": "Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled.",
|
||||
"default": true,
|
||||
@@ -424,6 +437,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"trafficLightPosition": {
|
||||
"description": "The position of the window controls on macOS.\n\n Requires titleBarStyle: Overlay and decorations: true.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/LogicalPosition"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hiddenTitle": {
|
||||
"description": "If `true`, sets the window title to be hidden on macOS.",
|
||||
"default": false,
|
||||
@@ -516,6 +540,32 @@
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"backgroundThrottling": {
|
||||
"description": "Change the default background throttling behaviour.\n\n By default, browsers use a suspend policy that will throttle timers and even unload\n the whole tab (view) to free resources after roughly 5 minutes when a view became\n minimized or hidden. This will pause all tasks until the documents visibility state\n changes back from hidden to visible by bringing the view back to the foreground.\n\n ## Platform-specific\n\n - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.\n - **iOS**: Supported since version 17.0+.\n - **macOS**: Supported since version 14.0+.\n\n see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BackgroundThrottlingPolicy"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"javascriptDisabled": {
|
||||
"description": "Whether we should disable JavaScript code execution on the webview or not.",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"allowLinkPreview": {
|
||||
"description": "on macOS and iOS there is a link preview on long pressing links, this is enabled by default.\n see https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableInputAccessoryView": {
|
||||
"description": "Allows disabling the input accessory view on iOS.\n\n The accessory view is the view that appears above the keyboard when a text input element is focused.\n It usually displays a view with \"Done\", \"Next\" buttons.",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -539,6 +589,46 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"PreventOverflowConfig": {
|
||||
"description": "Prevent overflow with a margin",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Enable prevent overflow or not",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Enable prevent overflow with a margin\n so that the window's size + this margin won't overflow the workarea",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PreventOverflowMargin"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"PreventOverflowMargin": {
|
||||
"description": "Enable prevent overflow with a margin\n so that the window's size + this margin won't overflow the workarea",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height",
|
||||
"width"
|
||||
],
|
||||
"properties": {
|
||||
"width": {
|
||||
"description": "Horizontal margin in physical unit",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"height": {
|
||||
"description": "Vertical margin in physical unit",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Theme": {
|
||||
"description": "System theme.",
|
||||
"oneOf": [
|
||||
@@ -584,6 +674,27 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"LogicalPosition": {
|
||||
"description": "Position coordinates struct.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"description": "X coordinate.",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"y": {
|
||||
"description": "Y coordinate.",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"WindowEffectsConfig": {
|
||||
"description": "The window effects configuration object",
|
||||
"type": "object",
|
||||
@@ -948,6 +1059,32 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"BackgroundThrottlingPolicy": {
|
||||
"description": "Background throttling policy.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "A policy where background throttling is disabled",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"disabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "A policy where a web view that’s not in a window fully suspends tasks. This is usually the default behavior in case no policy is set.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"suspend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "A policy where a web view that’s not in a window limits processing, but does not fully suspend tasks.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"throttle"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"SecurityConfig": {
|
||||
"description": "Security configuration.\n\n See more: <https://v2.tauri.app/reference/config/#securityconfig>",
|
||||
"type": "object",
|
||||
@@ -1209,7 +1346,7 @@
|
||||
]
|
||||
},
|
||||
"Capability": {
|
||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n \"identifier\": \"main-user-files-write\",\n \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n \"windows\": [\n \"main\"\n ],\n \"permissions\": [\n \"core:default\",\n \"dialog:open\",\n {\n \"identifier\": \"fs:allow-write-text-file\",\n \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n },\n ],\n \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
|
||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows' and webviews' fine grained access\n to the Tauri core, application, or plugin commands.\n If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n \"identifier\": \"main-user-files-write\",\n \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\",\n \"windows\": [\n \"main\"\n ],\n \"permissions\": [\n \"core:default\",\n \"dialog:open\",\n {\n \"identifier\": \"fs:allow-write-text-file\",\n \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n },\n ],\n \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"identifier",
|
||||
@@ -1221,7 +1358,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Description of what the capability is intended to allow on associated windows.\n\n It should contain a description of what the grouped permissions should allow.\n\n ## Example\n\n This capability allows the `main` window access to `filesystem` write related\n commands and `dialog` commands to enable programatic access to files selected by the user.",
|
||||
"description": "Description of what the capability is intended to allow on associated windows.\n\n It should contain a description of what the grouped permissions should allow.\n\n ## Example\n\n This capability allows the `main` window access to `filesystem` write related\n commands and `dialog` commands to enable programmatic access to files selected by the user.",
|
||||
"default": "",
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1242,14 +1379,14 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"windows": {
|
||||
"description": "List of windows that are affected by this capability. Can be a glob pattern.\n\n On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n ## Example\n\n `[\"main\"]`",
|
||||
"description": "List of windows that are affected by this capability. Can be a glob pattern.\n\n If a window label matches any of the patterns in this list,\n the capability will be enabled on all the webviews of that window,\n regardless of the value of [`Self::webviews`].\n\n On multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`]\n for a fine grained access control.\n\n ## Example\n\n `[\"main\"]`",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"webviews": {
|
||||
"description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\n This is only required when using on multiwebview contexts, by default\n all child webviews of a window that matches [`Self::windows`] are linked.\n\n ## Example\n\n `[\"sub-webview-one\", \"sub-webview-two\"]`",
|
||||
"description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\n The capability will be enabled on all the webviews\n whose label matches any of the patterns in this list,\n regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n ## Example\n\n `[\"sub-webview-one\", \"sub-webview-two\"]`",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
@@ -1439,7 +1576,7 @@
|
||||
]
|
||||
},
|
||||
"HeaderConfig": {
|
||||
"description": "A struct, where the keys are some specific http header names.\n If the values to those keys are defined, then they will be send as part of a response message.\n This does not include error messages and ipc messages\n\n ## Example configuration\n ```javascript\n {\n //..\n app:{\n //..\n security: {\n headers: {\n \"Cross-Origin-Opener-Policy\": \"same-origin\",\n \"Cross-Origin-Embedder-Policy\": \"require-corp\",\n \"Timing-Allow-Origin\": [\n \"https://developer.mozilla.org\",\n \"https://example.com\",\n ],\n \"Access-Control-Expose-Headers\": \"Tauri-Custom-Header\",\n \"Tauri-Custom-Header\": {\n \"key1\": \"'value1' 'value2'\",\n \"key2\": \"'value3'\"\n }\n },\n csp: \"default-src 'self'; connect-src ipc: http://ipc.localhost\",\n }\n //..\n }\n //..\n }\n ```\n In this example `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` are set to allow for the use of [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer).\n The result is, that those headers are then set on every response sent via the `get_response` function in crates/tauri/src/protocol/tauri.rs.\n The Content-Security-Policy header is defined separately, because it is also handled separately.\n\n For the helloworld example, this config translates into those response headers:\n ```http\n access-control-allow-origin: http://tauri.localhost\n access-control-expose-headers: Tauri-Custom-Header\n content-security-policy: default-src 'self'; connect-src ipc: http://ipc.localhost; script-src 'self' 'sha256-Wjjrs6qinmnr+tOry8x8PPwI77eGpUFR3EEGZktjJNs='\n content-type: text/html\n cross-origin-embedder-policy: require-corp\n cross-origin-opener-policy: same-origin\n tauri-custom-header: key1 'value1' 'value2'; key2 'value3'\n timing-allow-origin: https://developer.mozilla.org, https://example.com\n ```\n Since the resulting header values are always 'string-like'. So depending on the what data type the HeaderSource is, they need to be converted.\n - `String`(JS/Rust): stay the same for the resulting header value\n - `Array`(JS)/`Vec\\<String\\>`(Rust): Item are joined by \", \" for the resulting header value\n - `Object`(JS)/ `Hashmap\\<String,String\\>`(Rust): Items are composed from: key + space + value. Item are then joined by \"; \" for the resulting header value",
|
||||
"description": "A struct, where the keys are some specific http header names.\n\n If the values to those keys are defined, then they will be send as part of a response message.\n This does not include error messages and ipc messages\n\n ## Example configuration\n ```javascript\n {\n //..\n app:{\n //..\n security: {\n headers: {\n \"Cross-Origin-Opener-Policy\": \"same-origin\",\n \"Cross-Origin-Embedder-Policy\": \"require-corp\",\n \"Timing-Allow-Origin\": [\n \"https://developer.mozilla.org\",\n \"https://example.com\",\n ],\n \"Access-Control-Expose-Headers\": \"Tauri-Custom-Header\",\n \"Tauri-Custom-Header\": {\n \"key1\": \"'value1' 'value2'\",\n \"key2\": \"'value3'\"\n }\n },\n csp: \"default-src 'self'; connect-src ipc: http://ipc.localhost\",\n }\n //..\n }\n //..\n }\n ```\n In this example `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` are set to allow for the use of [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer).\n The result is, that those headers are then set on every response sent via the `get_response` function in crates/tauri/src/protocol/tauri.rs.\n The Content-Security-Policy header is defined separately, because it is also handled separately.\n\n For the helloworld example, this config translates into those response headers:\n ```http\n access-control-allow-origin: http://tauri.localhost\n access-control-expose-headers: Tauri-Custom-Header\n content-security-policy: default-src 'self'; connect-src ipc: http://ipc.localhost; script-src 'self' 'sha256-Wjjrs6qinmnr+tOry8x8PPwI77eGpUFR3EEGZktjJNs='\n content-type: text/html\n cross-origin-embedder-policy: require-corp\n cross-origin-opener-policy: same-origin\n tauri-custom-header: key1 'value1' 'value2'; key2 'value3'\n timing-allow-origin: https://developer.mozilla.org, https://example.com\n ```\n Since the resulting header values are always 'string-like'. So depending on the what data type the HeaderSource is, they need to be converted.\n - `String`(JS/Rust): stay the same for the resulting header value\n - `Array`(JS)/`Vec\\<String\\>`(Rust): Item are joined by \", \" for the resulting header value\n - `Object`(JS)/ `Hashmap\\<String,String\\>`(Rust): Items are composed from: key + space + value. Item are then joined by \"; \" for the resulting header value",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Access-Control-Allow-Credentials": {
|
||||
@@ -1541,6 +1678,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"Service-Worker-Allowed": {
|
||||
"description": "The HTTP Service-Worker-Allowed response header is used to broaden the path restriction for a\n service worker's default scope.\n\n By default, the scope for a service worker registration is the directory where the service\n worker script is located. For example, if the script `sw.js` is located in `/js/sw.js`,\n it can only control URLs under `/js/` by default. Servers can use the `Service-Worker-Allowed`\n header to allow a service worker to control URLs outside of its own directory.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Service-Worker-Allowed>",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HeaderSource"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Timing-Allow-Origin": {
|
||||
"description": "The Timing-Allow-Origin response header specifies origins that are allowed to see values\n of attributes retrieved via features of the Resource Timing API, which would otherwise be\n reported as zero due to cross-origin restrictions.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin>",
|
||||
"anyOf": [
|
||||
@@ -1624,7 +1772,13 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"menuOnLeftClick": {
|
||||
"description": "A Boolean value that determines whether the menu should appear when the tray icon receives a left click on macOS.",
|
||||
"description": "A Boolean value that determines whether the menu should appear when the tray icon receives a left click.\n\n ## Platform-specific:\n\n - **Linux**: Unsupported.",
|
||||
"default": true,
|
||||
"deprecated": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"showMenuOnLeftClick": {
|
||||
"description": "A Boolean value that determines whether the menu should appear when the tray icon receives a left click.\n\n ## Platform-specific:\n\n - **Linux**: Unsupported.",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1651,13 +1805,17 @@
|
||||
"properties": {
|
||||
"runner": {
|
||||
"description": "The binary used to build and run the application.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/RunnerConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"devUrl": {
|
||||
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [vite](https://vitejs.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
|
||||
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [Vite](https://vite.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
@@ -1717,10 +1875,54 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"removeUnusedCommands": {
|
||||
"description": "Try to remove unused commands registered from plugins base on the ACL list during `tauri build`,\n the way it works is that tauri-cli will read this and set the environment variables for the build script and macros,\n and they'll try to get all the allowed commands and remove the rest\n\n Note:\n - This won't be accounting for dynamically added ACLs when you use features from the `dynamic-acl` (currently enabled by default) feature flag, so make sure to check it when using this\n - This feature requires tauri-plugin 2.1 and tauri 2.4",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"RunnerConfig": {
|
||||
"description": "The runner configuration.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A string specifying the binary to run.",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "An object with advanced configuration options.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cmd"
|
||||
],
|
||||
"properties": {
|
||||
"cmd": {
|
||||
"description": "The binary to run.",
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The current working directory to run the command from.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"args": {
|
||||
"description": "Arguments to pass to the command.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"FrontendDist": {
|
||||
"description": "Defines the URL or assets to embed in the application.",
|
||||
"anyOf": [
|
||||
@@ -1918,7 +2120,7 @@
|
||||
]
|
||||
},
|
||||
"useLocalToolsDir": {
|
||||
"description": "Whether to use the project's `target` directory, for caching build tools (e.g., Wix and NSIS) when building this application. Defaults to `false`.\n\n If true, tools will be cached in `target\\.tauri-tools`.\n If false, tools will be cached in the current user's platform-specific cache directory.\n\n An example where it can be appropriate to set this to `true` is when building this application as a Windows System user (e.g., AWS EC2 workloads),\n because the Window system's app data directory is restricted.",
|
||||
"description": "Whether to use the project's `target` directory, for caching build tools (e.g., Wix and NSIS) when building this application. Defaults to `false`.\n\n If true, tools will be cached in `target/.tauri/`.\n If false, tools will be cached in the current user's platform-specific cache directory.\n\n An example where it can be appropriate to set this to `true` is when building this application as a Windows System user (e.g., AWS EC2 workloads),\n because the Window system's app data directory is restricted.",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -2110,7 +2312,7 @@
|
||||
"description": "Updater type",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"description": "Generates legacy zipped v1 compatible updaters",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/V1Compatible"
|
||||
@@ -2124,10 +2326,10 @@
|
||||
]
|
||||
},
|
||||
"V1Compatible": {
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"description": "Generates legacy zipped v1 compatible updaters",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Generates lagacy zipped v1 compatible updaters",
|
||||
"description": "Generates legacy zipped v1 compatible updaters",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v1Compatible"
|
||||
@@ -2197,6 +2399,15 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"rank": {
|
||||
"description": "The ranking of this app among apps that declare themselves as editors or viewers of the given file type. Maps to `LSHandlerRank` on macOS.",
|
||||
"default": "Default",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HandlerRank"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -2245,6 +2456,39 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"HandlerRank": {
|
||||
"description": "Corresponds to LSHandlerRank",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "LSHandlerRank.Default. This app is an opener of files of this type; this value is also used if no rank is specified.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "LSHandlerRank.Owner. This app is the primary creator of files of this type.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Owner"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "LSHandlerRank.Alternate. This app is a secondary viewer of files of this type.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Alternate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "LSHandlerRank.None. This app is never selected to open files of this type, but it accepts drops of files of this type.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"WindowsConfig": {
|
||||
"description": "Windows bundler configuration.\n\n See more: <https://v2.tauri.app/reference/config/#windowsconfig>",
|
||||
"type": "object",
|
||||
@@ -2529,7 +2773,7 @@
|
||||
]
|
||||
},
|
||||
"dialogImagePath": {
|
||||
"description": "Path to a bitmap file to use on the installation user interface dialogs.\n It is used on the welcome and completion dialogs.\n The required dimensions are 493px × 312px.",
|
||||
"description": "Path to a bitmap file to use on the installation user interface dialogs.\n It is used on the welcome and completion dialogs.\n\n The required dimensions are 493px × 312px.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
@@ -3205,6 +3449,20 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"bundleVersion": {
|
||||
"description": "The version of the build that identifies an iteration of the bundle.\n\n Translates to the bundle's CFBundleVersion property.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"bundleName": {
|
||||
"description": "The name of the builder that built the bundle.\n\n Translates to the bundle's CFBundleName property.\n\n If not set, defaults to the package's product name.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"minimumSystemVersion": {
|
||||
"description": "A version string indicating the minimum macOS X version that the bundled application supports. Defaults to `10.13`.\n\n Setting it to `null` completely removes the `LSMinimumSystemVersion` field on the bundle's `Info.plist`\n and the `MACOSX_DEPLOYMENT_TARGET` environment variable.\n\n An empty string is considered an invalid value so the default value is used.",
|
||||
"default": "10.13",
|
||||
@@ -3228,7 +3486,7 @@
|
||||
]
|
||||
},
|
||||
"hardenedRuntime": {
|
||||
"description": "Whether the codesign should enable [hardened runtime] (for executables) or not.\n\n [hardened runtime]: <https://developer.apple.com/documentation/security/hardened_runtime>",
|
||||
"description": "Whether the codesign should enable [hardened runtime](https://developer.apple.com/documentation/security/hardened_runtime) (for executables) or not.",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -3406,6 +3664,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"bundleVersion": {
|
||||
"description": "The version of the build that identifies an iteration of the bundle.\n\n Translates to the bundle's CFBundleVersion property.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"minimumSystemVersion": {
|
||||
"description": "A version string indicating the minimum iOS version that the bundled application supports. Defaults to `13.0`.\n\n Maps to the IPHONEOS_DEPLOYMENT_TARGET value.",
|
||||
"default": "13.0",
|
||||
@@ -3415,7 +3680,7 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"AndroidConfig": {
|
||||
"description": "General configuration for the iOS target.",
|
||||
"description": "General configuration for the Android target.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"minSdkVersion": {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"cli.js": {
|
||||
"version": "2.1.0",
|
||||
"version": "2.7.1",
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"tauri": "2.1.1",
|
||||
"tauri-build": "2.0.2",
|
||||
"tauri-plugin": "2.0.2"
|
||||
"tauri": "2.7.0",
|
||||
"tauri-build": "2.3.1",
|
||||
"tauri-plugin": "2.3.1"
|
||||
}
|
||||
|
||||
@@ -301,6 +301,17 @@
|
||||
],
|
||||
"format": "double"
|
||||
},
|
||||
"preventOverflow": {
|
||||
"description": "Whether or not to prevent window overflow",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PreventOverflowMarginConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resizable": {
|
||||
"description": "Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled.",
|
||||
"default": true,
|
||||
@@ -465,6 +476,15 @@
|
||||
"description": "Whether page zooming by hotkeys is enabled\n\n ## Platform-specific:\n\n - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting.\n - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`,\n 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission\n\n - **Android / iOS**: Unsupported.",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"backgroundThrottling": {
|
||||
"description": "Change the default background throttling behaviour.\n\n By default, browsers use a suspend policy that will throttle timers and even unload\n the whole tab (view) to free resources after roughly 5 minutes when a view became\n minimized or hidden. This will pause all tasks until the documents visibility state\n changes back from hidden to visible by bringing the view back to the foreground.\n\n ## Platform-specific\n\n - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.\n - **iOS**: Supported since version 17.0+.\n - **macOS**: Supported since version 14.0+.\n\n see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"disabled",
|
||||
"throttle",
|
||||
"suspend"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -488,6 +508,46 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"PreventOverflowMarginConfig": {
|
||||
"description": "Prevent overflow with a margin",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Enable prevent overflow or not",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Enable prevent overflow with a margin",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PreventOverflowMargin"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"PreventOverflowMargin": {
|
||||
"description": "Enable prevent overflow with a margin",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height",
|
||||
"width"
|
||||
],
|
||||
"properties": {
|
||||
"width": {
|
||||
"description": "Horizontal margin in physical unit",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"height": {
|
||||
"description": "Vertical margin in physical unit",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Theme": {
|
||||
"description": "System theme.",
|
||||
"oneOf": [
|
||||
@@ -1369,7 +1429,7 @@
|
||||
]
|
||||
},
|
||||
"devUrl": {
|
||||
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [vite](https://vitejs.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
|
||||
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [Vite](https://vite.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
@@ -1906,6 +1966,15 @@
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"rank": {
|
||||
"description": "The ranking of this app among apps that declare themselves as editors or viewers of the given file type. Maps to `LSHandlerRank` on macOS.",
|
||||
"default": "Default",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HandlerRank"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1954,6 +2023,39 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"HandlerRank": {
|
||||
"description": "Corresponds to LSHandlerRank",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "LSHandlerRank.Default. This app is an opener of files of this type; this value is also used if no rank is specified.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "LSHandlerRank.Owner. This app is the primary creator of files of this type.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Owner"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "LSHandlerRank.Alternate. This app is a secondary viewer of files of this type.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Alternate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "LSHandlerRank.None. This app is never selected to open files of this type, but it accepts drops of files of this type.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"WindowsConfig": {
|
||||
"description": "Windows bundler configuration.\n\n See more: <https://tauri.app/v1/api/config#windowsconfig>",
|
||||
"type": "object",
|
||||
|
||||
@@ -98,7 +98,7 @@ pub fn command(options: Options) -> Result<()> {
|
||||
PermissionEntry::PermissionRef(
|
||||
p.clone()
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| panic!("invalid permission {}", p)),
|
||||
.unwrap_or_else(|_| panic!("invalid permission {p}")),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user