feat(tauri): support patch context at runtime
Discussed at https://github.com/tauri-apps/tauri/pull/13033#issuecomment-2928745052
This allows simulating the behavior of generate_context! at runtime and dynamically loading the Context.
CC @Legend-Master.
In examples/precompiled, you can see that I only set the most basic tauri.conf.json at build time, and then dynamically set the identifier, provide frontend assets, etc. at runtime based on examples/precompiled/src-tauri.
Package Changes Through 8a56ea7222401a5b69027e543aec1c80ed382ee8
There are 8 changes which include tauri-bundler with minor, tauri with minor, tauri-cli with minor, tauri-codegen with minor, tauri-utils with minor, @tauri-apps/api with minor, @tauri-apps/cli with minor, tauri-runtime-wry with patch
Planned Package Versions
The following package releases are the planned based on the context of changes in this pull request.
| package | current | next |
|---|---|---|
| @tauri-apps/api | 2.5.0 | 2.6.0 |
| tauri-utils | 2.4.0 | 2.5.0 |
| tauri-bundler | 2.4.0 | 2.5.0 |
| tauri-runtime | 2.6.0 | 2.6.1 |
| tauri-runtime-wry | 2.6.0 | 2.6.1 |
| tauri-codegen | 2.2.0 | 2.3.0 |
| tauri-macros | 2.2.0 | 2.2.1 |
| tauri-plugin | 2.2.0 | 2.2.1 |
| tauri-build | 2.2.0 | 2.2.1 |
| tauri | 2.5.1 | 2.6.0 |
| @tauri-apps/cli | 2.5.0 | 2.6.0 |
| tauri-cli | 2.5.0 | 2.6.0 |
Add another change file through the GitHub UI by following this link.
Read about change files or the docs at github.com/jbolda/covector
So basically, this is duplicating the code from context_codegen in tauri-codegen to tauri so that it can be used at runtime, and they would be normal functions instead of ones that works with rust TokenStream right?
To be honest, this is just moving the responsibility of maintaining it from pytauri to tauri, and I don't think anyone on the team really wants to do it...
I don't know how feasible is it, but I think it might be better/easier to move out Context and its fields to a shared crate (maybe tauri-utils) and implement quote::ToTokens, then change the code inside tauri-codegen to 2 parts, one part generates the Context and another part quote! it to TokenStream that the macro could use, and then you can use the one generated from the first part directly
To be honest, this is just moving the responsibility of maintaining it from pytauri to tauri, and I don't think anyone on the team really wants to do it...
I would argue that if Tauri wants to support bindings for other languages, it must provide a way to configure at runtime. Otherwise:
Each user has their own configuration → their own tauri.conf.json → needs to be converted into a runtime-usable Context by the Rust compiler (tauri-codegen) → every user needs the Rust compiler (even if they're using bindings for other languages) → users will complain, "If I need the Rust compiler anyway, why not just write Rust directly?" 🤔
Moreover, both Qt and CEF provide runtime configuration, allowing them to be compiled as dynamic libraries. If Tauri doesn't make changes, it can only be used as an executable.
I do really want to support using another language binding without the rust compiler, and that's why I suggested that we change the codegen to generate the Context instead just of rust source code, that way, you can just ship that code, and then generate the Context dynamically at runtime
In fact, I think the context should only be used to control runtime behavior, and should not be used for embedding assets.
Ideally, we should split tauri.conf.json into two parts: one solely for controlling build-time behavior, and the other solely for controlling runtime behavior. This is why I said that build configuration should only be controlled by BuildConfig.
static embedded_assets: {
icon_bytes: &[u8],
plugin_global_api_scripts: &[u8],
// ...
} = generate_embedded_assets!();
let context: Context = serde::from_str(include_str!("tauri.runtime-conf.json"));
Builder::default()
.embedded_assets(Some(embedded_assets))
.runtime_context(context)
You should specify in the Context whether you want to use embedded assets or runtime assets.
The Builder should initialize the app by calling existing tauri api based on the provided Context (used only for configuration), rather than relying on macros to dynamically generate initialization rust code.
impl Builder {
fn init(self) {
if self.runtime_context.use_embedded_assets {
app.set_icon(Image::from_bytes(embedded_assets.icon_bytes));
} else {
self.set_icon(Image::from_path(self.runtime_context.icon_path));
}
}
}
I like the idea of splitting them up so it's clear which one is used in build and which one is used at runtime
The Builder should initialize the app by calling existing tauri api based on the provided Context (used only for configuration), rather than relying on macros to dynamically generate initialization rust code.
The method that I suggested is just basically moving this to tauri-codegen, and let it produce the Context instead of just rust code, so if you want to generate the Context at runtime, you can
And if you would like to consume it not through tauri-codegen, but through tauri, we can gate that in a feature and re-export them, so we don't have so much duplicated code, we might also want to enable it in dev for faster compile speed by moving them to runtime
You should specify in the Context whether you want to use embedded assets or runtime assets.
I believe this is only for your currently implementation of pytauri, if we have the Context generation as a function (instead of a macro that generates the rust code), you can just do that in the runtime, and swap to use your own Assets implementation
For example, currently you do:
- Context generation at your library's build time
- Modify this context at runtime based on library consumer's configs
What we can do if that's a function:
- Ship the context generation code
- Generate this context at runtime based on library consumer's configs
And there's no need for
if self.runtime_context.use_embedded_assets { app.set_icon(Image::from_bytes(embedded_assets.icon_bytes)); } else { self.set_icon(Image::from_path(self.runtime_context.icon_path)); }
The method that I suggested is just basically moving this to tauri-codegen, and let it produce the Context instead of just rust code, so if you want to generate the Context at runtime, you can
What we can do if that's a function:
- Ship the context generation code
- Generate this context at runtime based on library consumer's configs
Sorry, I don't quite understand. Could you provide some pseudocode to describe how users/downstream consumers would use this new API?
So currently, we have
https://github.com/tauri-apps/tauri/blob/a472d51f683eea040fe68aadc92210f470bdaa6f/crates/tauri-codegen/src/context.rs#L134
And we change it to pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<Context> {}, and then you can use this function to generate the context directly