generate_context! 读的不是你指定的配置文件
主线位置:启动主线第一步。
generate_context!("...")的参数决定编译期读哪份配置,进而决定窗口、CSP、内嵌资源、ACL(见 generate_context! 的「宏的入口」);应用的 build script 里tauri_build::build()另读一次配置(tauri-build)。两处都只用路径里的目录。
flowchart TB
arg["参数 alt.conf.json"] --> chk{"同目录有 tauri.conf.json"}
chk -->|没有| err["no file at path ... exists"]
chk -->|有| dir["取 parent 目录"]
dir --> base["读 目录/tauri.conf.json"]
base --> plat["合并 tauri.macos.conf.json"]
plat --> envm["合并 TAURI_CONFIG"]
envm --> cfg["Config"]
本页实验 crate 是 pit1-conf:由模板 base 复制,crate 根目录的 tauri.conf.json 把 productName 改成 from-tauri-conf;另一份 alt.conf.json 的 productName 是 from-alt-conf、窗口标题是 alt。main 只构造 Context 并打印这两项:
fn main() {
let context: tauri::Context = tauri::generate_context!("alt.conf.json");
let config = context.config();
println!(
"productName = {:?}, window title = {:?}",
config.product_name,
config.app.windows.first().map(|w| w.title.as_str())
);
}
M2 Max、macOS 27、rustc 1.99.0,tauri 2.12.5、tauri-build 2.7.4,--features tauri/custom-protocol。
现象
一:两份配置放在一起
productName = Some("from-tauri-conf"), window title = Some("base")
编译、运行都没有任何提示,拿到的是 tauri.conf.json 的内容。
二:另一份配置单独放进一个目录
把 alt.conf.json 挪到 conf/,宏参数改成 "conf/alt.conf.json",conf/ 里没有别的文件:
error: unexpected end of input, no file at path ~/exp-tauri/pit1-conf/conf/alt.conf.json exists, expected tauri config file
--> src/main.rs:2:33
|
2 | let context: tauri::Context = tauri::generate_context!("conf/alt.conf.json");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `tauri::generate_context` (in Nightly builds, run with -Z macro-backtrace for more info)
error: could not compile `pit1-conf` (bin "pit1-conf") due to 1 previous error
文件明明在,报错说它不存在。开头的「unexpected end of input, 」是 syn 加的:错误是在参数解析完之后用 input.error(..) 构造的,这时输入已经读到了末尾。
出处
宏解析参数时,ContextItems 的 Parse 实现 把相对路径接到 CARGO_MANIFEST_DIR 上,再用 does_supported_file_name_exist 判断「文件存在」:
pub fn does_supported_file_name_exist(target: Target, path: impl Into<PathBuf>) -> bool {
let path = path.into();
let source_file_name = path.file_name().unwrap();
let lookup_platform_config = ENABLED_FORMATS
.iter()
.any(|format| source_file_name == format.into_platform_file_name(target));
ENABLED_FORMATS.iter().any(|format| {
path
.with_file_name(if lookup_platform_config {
format.into_platform_file_name(target)
} else {
format.into_file_name()
})
.exists()
})
}
with_file_name 把文件名换成 tauri.conf.json(或 tauri.conf.json5、Tauri.toml)再看存不存在,传进来的文件名本身从没被检查过。情形二里 conf/tauri.conf.json 不存在,于是报错;情形一里根目录有 tauri.conf.json,于是通过。
接着 get_config 取路径的 parent,交给 read_from:
pub fn read_from(target: Target, root_dir: &Path) -> Result<(Value, Vec<PathBuf>), ConfigError> {
let (mut config, config_file_path) = parse_value(target, root_dir.join("tauri.conf.json"))?;
let mut config_paths = vec![config_file_path];
if let Some((platform_config, path)) = read_platform(target, root_dir)? {
config_paths.push(path);
merge(&mut config, &platform_config);
}
Ok((config, config_paths))
}
读的是「目录 + tauri.conf.json」,同目录下的平台配置(macOS 上是 tauri.macos.conf.json)自动按 JSON Merge Patch 合并,最后再合并环境变量 TAURI_CONFIG。所以「配置文件」在 tauri 里的单位其实是目录。应用 build script 里的 try_build 也一样:没设 config_path 时用当前目录(crate 根目录),设了就取它的 parent,再调同一个 read_from。
修法
单独的目录,文件叫 tauri.conf.json
把另一份配置放成 conf/tauri.conf.json,宏写 generate_context!("conf/tauri.conf.json")。配置里的相对路径都相对于 conf/,frontendDist 要改成 "../dist"。图标也是:第一次构建失败在这里,
= help: message: failed to open icon ~/exp-tauri/pit1-conf/conf/icons/icon.png: No such file or directory (os error 2)
再加上 "bundle": { "icon": ["../icons/icon.png"] },构建通过:
== generate_context!("conf/tauri.conf.json")
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s
productName = Some("from-alt-conf"), window title = Some("alt")
== edit conf/tauri.conf.json productName
Fresh pit1-conf v0.1.0 (~/exp-tauri/pit1-conf)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
productName = Some("from-alt-conf"), window title = Some("alt")
读对了,但第二段暴露了下一个问题:改了 conf/tauri.conf.json 之后 cargo 说 Fresh,程序里还是旧值。宏读配置时不向 cargo 登记任何依赖,监视配置文件的是 build script 打印的 rerun-if-changed,而 build script 读的、监视的都是 crate 根目录的 tauri.conf.json。
让 build script 也用这份配置
fn main() {
tauri_build::try_build(tauri_build::Attributes::new().config_path("conf/tauri.conf.json"))
.expect("failed to run tauri-build");
}
再做一遍同样的修改(rustc 命令行截断):
== build.rs: Attributes::new().config_path("conf/tauri.conf.json")
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.84s
productName = Some("from-alt-conf"), window title = Some("alt")
== edit conf/tauri.conf.json productName
Dirty pit1-conf v0.1.0 (~/exp-tauri/pit1-conf): the file `conf/tauri.conf.json` has changed (1793197716.586713605s, 496552181ns after last build at 1793197716.090161...
Compiling pit1-conf v0.1.0 (~/exp-tauri/pit1-conf)
Running `~/exp-tauri/target-pit1/debug/build/pit1-conf-e218091ea073cebd/build-script-build`
Running `~/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rustc --crate-name pit1_conf`
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.02s
productName = Some("from-alt-conf-v2"), window title = Some("alt")
build script 的输出里多了 cargo:rerun-if-changed=conf/tauri.conf.json。这样 tauri-build 用来核对 feature、收集能力的配置,和宏嵌进二进制的配置,终于是同一份。
#15306(提交标题「fix(tauri-build): add Attributes::config_path」)首次发布于 tauri-build 2.7.0,对应 tauri 2.12.0。CHANGELOG 原文:「Added Attributes::config_path to customize config path, deprecated CodegenContext::config_path in favor of this」。在那之前,tauri_build::build() 只会读 crate 目录下的配置;把配置放在别处的项目,build script 和宏读的是两份不同的配置。
只是想改几项:TAURI_CONFIG
如果另一份配置只是在主配置上改几项,不必另起目录:环境变量 TAURI_CONFIG 里放一段 JSON,build script 和宏都会把它合并进去,build script 还打印了 rerun-if-env-changed=TAURI_CONFIG,变量一变就重编:
== TAURI_CONFIG={"productName":"from-env"}
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s
productName = Some("from-env"), window title = Some("alt")
tauri-cli 合并额外配置用的也是这个变量(见 源码地图)。
怎么确认没中招
- 配置不在 crate 根目录时,检查三件事:文件名是
tauri.conf.json;build.rs用了Attributes::config_path指向同一个文件;配置里的相对路径(frontendDist、bundle.icon、能力文件)都按配置所在目录写。 grep rerun-if-changed target/debug/build/<应用 crate>-*/output,里面应该有你实际用的那份配置。- 同目录下的
tauri.macos.conf.json之类会自动合并,不想要就别放在那里。 - 不确定时在 debug 构建里打印一次
context.config().product_name或identifier。
小结
generate_context!和tauri-build都只用配置路径的目录,在目录里找tauri.conf.json(及.json5、Tauri.toml),再合并平台配置和TAURI_CONFIG;文件名被忽略。- 同目录有
tauri.conf.json时静默读它;没有时报「no file at path ... exists」,哪怕你给的文件就在那里。 - 另一份配置要放进单独目录、叫
tauri.conf.json,相对路径按那个目录写,并在build.rs里用Attributes::config_path(tauri-build 2.7.0 起)让构建脚本读同一份、监视同一份。 - 只改几项时用
TAURI_CONFIG。