Rust in Everything
基于 tauri 2.12.5

tauri-build

主线位置:启动主线开始之前。启动主线的第一步是 generate_context! 把 Context 编出来,而它要读的 ACL 清单和能力文件,是应用 crate 的 build script 先写进 OUT_DIR 的。本页讲这个 build script:build.rs 里那一行 tauri_build::build() 从哪里拿输入、产出什么、怎么让 cargo 知道何时重跑。

flowchart TB
  tauribs["tauri 的 build.rs"] -->|"cargo:dev 和权限路径"| depenv["DEP_TAURI_* 环境变量"]
  plugbs["插件的 build.rs"] -->|links 元数据| depenv
  depenv --> trybuild["try_build"]
  conf["tauri.conf.json"] --> trybuild
  trybuild --> cfgs["cfg desktop mobile dev"]
  trybuild --> check["manifest::check"]
  trybuild --> aclb["acl::build"]
  caps["capabilities 目录"] --> aclb
  aclb --> outdir["OUT_DIR 两份 JSON"]
  aclb --> gen["gen/schemas"]
  outdir --> ctxmacro["generate_context!"]

本页的实验 crate 是 build-app:从模板 base 复制,build.rs 在调用 tauri_build::build() 之前先把所有 DEP_* 环境变量写进一个文件。另有 build-acl(AppManifest 加一个能力文件)和 build-mismatch(feature 和配置不一致)。依赖是 tauri =2.12.5、tauri-build =2.7.4,一律 --features tauri/custom-protocol 编译。平台:M2 Max、macOS 27、rustc 1.99.0。Windows、Linux、iOS、Android 分支只读了源码,没有实测。

两个 build script

tauri_build::build() 不是第一个跑的。tauri 本体的 Cargo.toml 里有一行 links = "Tauri",它自己的 build.rs 先跑,打印几条带自定义键的 cargo: 指令。cargo 对声明了 links 的包有一条规则:这些键值会以 DEP_<links>_<KEY> 的形式,作为环境变量交给直接依赖它、且有 build script 的包。应用 crate 依赖 tauri,所以应用的 build script 能读到它们。build-app 的 build.rs 记下来的是这些(路径中段用 ... 省略):

DEP_TAURI_CORE:APP__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-app-permission-files
DEP_TAURI_CORE:EVENT__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-event-permission-files
DEP_TAURI_CORE:IMAGE__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-image-permission-files
DEP_TAURI_CORE:MENU__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-menu-permission-files
DEP_TAURI_CORE:PATH__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-path-permission-files
DEP_TAURI_CORE:RESOURCES__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-resources-permission-files
DEP_TAURI_CORE:TRAY__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-tray-permission-files
DEP_TAURI_CORE:WEBVIEW__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-webview-permission-files
DEP_TAURI_CORE:WINDOW__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-window-permission-files
DEP_TAURI_CORE__CORE_PLUGIN___PERMISSION_FILES_PATH=.../build/tauri-fdd5acda78a81a21/out/tauri-core-permission-files
DEP_TAURI_DEV=false
DEP_TAURI_GLOBAL_API_SCRIPT_PATH=~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tauri-2.12.5/scripts/bundle.global.js

三类东西从这条通道过来:

  • 是否开发模式。tauri 的 build.rs 看自己有没有开 custom-protocol feature,println!("cargo:dev={dev}")。应用这边的 is_dev 读 DEP_TAURI_DEV,读不到就 panic,提示「missing cargo:dev instruction, please update tauri to latest」。这里是 false,因为编译时带了 tauri/custom-protocol。
  • 核心插件的权限文件。tauri 的 build.rs 里有一张 PLUGINS 表,列出 core:path、core:event、core:window、core:webview、core:app、core:image、core:resources、core:menu、core:tray 九个核心插件的每个命令,以及它是否进默认权限集。define_permissions 为每个命令生成一对 allow-* / deny-* 权限,写进 tauri 自己的 OUT_DIR,再把「权限文件列表」的路径打印出去;core 本身是一个只含默认集的清单。
  • 全局 API 脚本。withGlobalTauri 时要注入的 bundle.global.js 的路径。

键名里的 core:app__CORE_PLUGIN__ 是 tauri-utils 的 define_permissions 拼出来的:包名以 tauri: 开头时打印 cargo:{plugin_name}__CORE_PLUGIN___PERMISSION_FILES_PATH=...,否则打印 cargo:PERMISSION_FILES_PATH=...。读的一侧 read_permissions 遍历所有 DEP_*_PERMISSION_FILES_PATH,去掉 __CORE_PLUGIN__ 和 TAURI_ 前缀,转小写、_ 换成 -,再去掉 tauri-plugin- 前缀,得到 core:app、core:window 这样的清单名。第三方插件走后一种写法:插件 crate 在自己的 Cargo.toml 里声明 links(仓库里的示例插件是 links = "tauri-plugin-sample"),应用就会收到 DEP_TAURI_PLUGIN_SAMPLE_PERMISSION_FILES_PATH,清单名是 sample。

📝NOTE
没有蠢问题

问: 应用自己的 build script 也打印了 cargo:PERMISSION_FILES_PATH=...(见下一节的输出),谁读它?

答: 没有人。cargo:KEY=VALUE 只有在包声明了 links 时才会传给依赖它的包,应用 crate 没有 links,也没有包依赖它。这一行是共用代码的副产品:应用的权限文件和插件的权限文件走同一个 define_permissions,函数不知道调用者是谁,照样打印。对插件 crate(tauri-plugin 的构建辅助在 build/mod.rs 调同一个函数)来说,这一行就是上面那条通道的入口。

try_build 一步一步

build 只是用默认 Attributes 调 try_build,出错时把错误 println! 到标准输出、exit(1);错误以「unknown field」开头时再补一句「CLI 比 tauri-build 新」的提示。try_build 按顺序做这些事:

顺序做什么留下什么
1声明 TAURI_CONFIG 变化时重跑cargo:rerun-if-env-changed=TAURI_CONFIG
2按 CARGO_CFG_TARGET_OS 定 desktop / mobilecfg(desktop) 或 cfg(mobile)
3读 tauri.conf.json(及 tauri.macos.conf.json 这类平台文件,JSON Merge Patch 合并),再合并 TAURI_CONFIG 环境变量里的 JSON,反序列化成 Config每个配置文件一条 rerun-if-changed
4由 identifier 拼 Android 包名两条 rustc-env(桌面上也照样打印)
5is_dev()cfg(dev)(开发模式才有)
6manifest::check:核对 Cargo feature不一致就报错退出
7acl::build:权限、能力、schemaOUT_DIR 和 gen/schemas 里的文件
8save_global_api_scripts_pathsOUT_DIR/__global-api-script.js
9记下目标三元组rustc-env=TAURI_ENV_TARGET_TRIPLE,tauri-codegen 用它判断目标平台
10把 bundle > externalBin、bundle > resources 复制到 target 目录cargo run 时程序旁边就有这些文件
11macOS:复制 bundle > macOS > frameworks、加 rpath;非开发模式设部署目标rustc-link-arg、rustc-env=MACOSX_DEPLOYMENT_TARGET
12Windows:编译资源文件(图标、版本信息、应用清单),按需静态链接 VC 运行时只读了源码
13开了 codegen feature 且设置了 Attributes::codegen 时,在 build script 里生成 Context默认不开

第 2、5 步的 cfg 用 cfg_alias 发出:先 rustc-check-cfg 声明这个 cfg 名合法,条件成立时再 rustc-cfg。所以不成立的 mobile、dev 只有声明,没有设置,应用代码里写 #[cfg(dev)] 不会触发 unexpected_cfgs 警告。tauri 本体的 build.rs 给自己发了一套同名的 cfg,tauri 源码里的 #[cfg(desktop)]、#[cfg(dev)] 用的是那一套;这里发的是给应用代码用的。

第 10 步要知道 target 目录,而 cargo 不把它告诉 build script。target_dir_from_out_dir 从 OUT_DIR 往上找名为 build 的那一级,取它的父目录;注释写明稳定版的布局是 <target dir>/build/<pkg>-<hash>/out,新的 nightly 是 <target dir>/build/<pkg>/<hash>/out,所以不按固定层数截。

build-app 这次运行的全部输出(target-build/debug/build/build-app-*/output):

cargo:rerun-if-env-changed=TAURI_CONFIG
cargo:rustc-check-cfg=cfg(desktop)
cargo:rustc-cfg=desktop
cargo:rustc-check-cfg=cfg(mobile)
cargo:rerun-if-changed=~/exp-tauri/build-app/tauri.conf.json
cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_APP_NAME=base
cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_PREFIX=app_rustineverything_exp
cargo:rustc-check-cfg=cfg(dev)
cargo:PERMISSION_FILES_PATH=~/exp-tauri/target-build/debug/build/build-app-fff6b7aff4ec31ae/out/app-manifest/__app__-permission-files
cargo:rerun-if-changed=~/exp-tauri/build-app/capabilities
cargo:rerun-if-env-changed=REMOVE_UNUSED_COMMANDS
cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE=aarch64-apple-darwin
cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.13

和目录里的描述对一下:没有 cargo:rustc-cfg=dev,因为不是开发模式;MACOSX_DEPLOYMENT_TARGET=10.13 是 bundle > macOS > minimumSystemVersion 的默认值,开发模式下不打印。macOS 上这个 build script 不碰 Info.plist,也不处理图标:开发模式下嵌入 Info.plist 和应用图标的是 generate_context!,见下一页;图标在 build script 里只有 Windows 分支用到(.ico 编进资源文件)。

核对 feature:「does not match the allowlist」

有几个 tauri 的 Cargo feature 和配置项一一对应。AppConfig::features 从配置推出「应该开哪些 feature」:

  pub fn features(&self) -> Vec<&str> {
    let mut features = Vec::new();
    if self.tray_icon.is_some() {
      features.push("tray-icon");
    }
    // TODO: Remove in v3
    if self.macos_private_api {
      features.push("macos-private-api");
    }
    if self.security.asset_protocol.enable {
      features.push("protocol-asset");
    }

    if let PatternKind::Isolation { .. } = self.security.pattern {
      features.push("isolation");
    }

    features.sort_unstable();
    features
  }

manifest::check 用 cargo_toml 解析应用的 Cargo.toml,找到 tauri 依赖(以及 [target.*] 下的同名依赖),只看 macos-private-api、protocol-asset、isolation 这几个由 CLI 管理的 feature(tray-icon 被排除在外),和上面的期望值做差集;tauri-build 依赖的 isolation feature 也按同样方式核对。差集不空就返回这个错误:

    for dep in deps {
      if let Err(error) = check_features(dep, &metadata) {
        return Err(anyhow!("
      The `{}` dependency features on the `Cargo.toml` file does not match the allowlist defined under `tauri.conf.json`.
      Please run `tauri dev` or `tauri build` or {}.
    ", name, error));
      }
    }

build-mismatch 在 tauri 的 feature 里加了 protocol-asset,配置里没有 assetProtocol.enable:

error: failed to run custom build command for `build-mismatch v0.1.0 (~/exp-tauri/build-mismatch)`

Caused by:
  process didn't exit successfully: `~/exp-tauri/target-build/debug/build/build-mismatch-03be60f9796511e2/build-script-build` (exit status: 1)
  --- stdout
  cargo:rerun-if-env-changed=TAURI_CONFIG
  cargo:rustc-check-cfg=cfg(desktop)
  cargo:rustc-cfg=desktop
  cargo:rustc-check-cfg=cfg(mobile)
  cargo:rerun-if-changed=~/exp-tauri/build-mismatch/tauri.conf.json
  cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_APP_NAME=base
  cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_PREFIX=app_rustineverything_exp
  cargo:rustc-check-cfg=cfg(dev)

        The `tauri` dependency features on the `Cargo.toml` file does not match the allowlist defined under `tauri.conf.json`.
        Please run `tauri dev` or `tauri build` or remove the `protocol-asset` feature.
      

错误出自应用 crate 的 build script,也就是 tauri-build 的 manifest.rs;因为 build() 用 println! 报错,它出现在 cargo 输出的 --- stdout 一段里,exit status: 1 也来自 build() 自己的 exit(1)。「allowlist」是 1.x 配置里的 allowlist 段,这个检查在 1.0 发布之前就加了(tauri-build 的 CHANGELOG:「Validate tauri dependency features under Cargo.toml matching tauri.conf.json's allowlist.」)。2.x 的配置里已经没有 allowlist,命令权限交给了 ACL,这个检查只剩下「配置用到的功能有没有编进来」这一层意思,提示文字没改。修法照提示:要么删 feature,要么在配置里打开对应项(反过来,配置开了而 feature 没开,提示是「add the ... feature」)。提示里的 tauri dev / tauri build 指 CLI 会按配置改写 Cargo.toml 里的这些 feature(tauri-cli 的 rewrite_manifest),所以用 CLI 构建的项目一般碰不到它。

收集权限

acl::build 把三个来源的权限合成一张「清单名 → Manifest」的表:

来源怎么拿清单名
tauri 和插件 crateread_plugins_manifests:上一节的 DEP_*_PERMISSION_FILES_PATH,外加插件用 GLOBAL_SCOPE_SCHEMA_PATH 传来的作用域 schemacore:window、fs 等
应用自己app_manifest_permissions:AppManifest::commands 列出的命令各生成一对权限,加上项目 permissions/ 目录下的 .toml / .json__app-acl__
内联插件inline_plugins:Attributes::plugin 登记的插件,生成的文件放在 OUT_DIR/plugins/<name>,另读 permissions/<name>/插件名

应用清单只在非空时加进表里(有默认集、权限集或权限之一)。这一点后面有用:表里有没有 __app-acl__,决定了运行时对 app 自己的命令查不查 ACL,见运行时检查。

build-acl 的 build.rs 换成了:

fn main() {
  let attributes = tauri_build::Attributes::new()
    .app_manifest(tauri_build::AppManifest::new().commands(&["greet", "report"]));
  tauri_build::try_build(attributes).expect("failed to run tauri-build");
}

再加一个 capabilities/default.json:

{
  "identifier": "default",
  "windows": ["main"],
  "permissions": ["core:default", "allow-greet", "allow-report"]
}

构建之后,项目目录里多出 permissions/autogenerated/greet.toml 和 report.toml。注意它们写在项目目录,不在 OUT_DIR:

# Automatically generated - DO NOT EDIT!

[[permission]]
identifier = "allow-greet"
description = "Enables the greet command without any pre-configured scope."
commands.allow = ["greet"]

[[permission]]
identifier = "deny-greet"
description = "Denies the greet command without any pre-configured scope."
commands.deny = ["greet"]

生成它的是 autogenerate_command_permissions,命令名里的 _ 换成 - 作标识符。随后按 permissions/**/* 收集时,这两个文件又被读进来,所以 build script 的输出里 cargo:PERMISSION_FILES_PATH= 出现两次,第二次写的列表覆盖第一次,内容是全集。表的键列出来是:

['__app-acl__', 'core', 'core:app', 'core:event', 'core:image', 'core:menu', 'core:path', 'core:resources', 'core:tray', 'core:webview', 'core:window']

读能力并校验

能力文件默认按 ./capabilities/**/* 找,扩展名是 json、toml(开 config-json5 时加 json5),parse_capabilities 遇到重复的 identifier 直接报错。真正参与解析的能力集由 get_capabilities 决定:配置里 app > security > capabilities 非空时,只取它列出的(可以是内联的能力对象,也可以是按 identifier 引用文件里的),否则取文件里的全部。

validate_capabilities 对当前目标平台适用的每个能力、每条权限查一遍:按 : 前缀找清单(没有前缀就是 __app-acl__),再看权限名或权限集名在不在里面;default 永远算存在。找不到就报错,并用 suggest_identifier 猜一个最像的。把 allow-greet 改成 allow-gret:

  thread 'main' (2432453) panicked at build.rs:4:38:
  failed to run tauri-build: Permission allow-gret not found (did you mean "allow-greet"?), expected one of allow-greet, allow-report, deny-greet, deny-report, core:default, core:app:default, core:app:allow-app-hide, core:app:allow-app-show, core:app:allow-bundle-type, core:app:allow-default-window-icon, core:app:allow-exit, ...

这里是 panic 而不是 exit status: 1,因为 build-acl 自己用 try_build(..).expect(..)。猜测的规则在源码注释和测试里写得很细:按 - 拆成段逐段比,段长不小于 4 时允许一处编辑距离,read 和 write、listen 和 unlisten 不算像;漏写 allow- 前缀也能猜出来。

这一步只回答「引用的权限存不存在」。哪个命令在哪个窗口、哪个来源下被允许,要等 generate_context! 调 Resolved::resolve 才算出来,规则见编译期解析。

写出的文件

校验通过后,acl::build 写这几样:

  • gen/schemas/acl-manifests.json:上面那张清单表,再复制一份到 OUT_DIR/acl-manifests.json。
  • gen/schemas/capabilities.json:从文件读到的能力(不含配置里内联的),复制到 OUT_DIR/capabilities.json。generate_context! 读它之后会再调一次 get_capabilities 合并配置。
  • gen/schemas/<目标>-schema.json 和 desktop-schema.json:generate_capability_schema 按清单生成的能力文件 JSON schema,编辑器补全用。macOS 上是 macOS-schema.json。
  • OUT_DIR/allowed-commands.json:只在设置了 REMOVE_UNUSED_COMMANDS 环境变量(build > removeUnusedCommands 时由 CLI 设置)时由 generate_allowed_commands 写出,否则删掉旧的。generate_handler! 读它来删掉没被任何能力允许的命令,见 #[command] 与 generate_handler!。

gen/schemas 是相对路径,相对的是 build script 的工作目录,也就是应用 crate 的目录。这些文件出现在源码树里,CLI 的应用模板在 .gitignore 里写了 /gen/schemas。

OUT_DIR 里有什么

build-app 构建完之后的 OUT_DIR:

./__global-api-script.js
./acl-manifests.json
./app-manifest/__app__-permission-files
./capabilities.json
./cb184881903692ec3fa7e723fd4ab370a605b0308df8b4a07814e0ac609ff474
./tauri-codegen-assets/79e7ed6655fff8f70f7ef80b436f3c9a011ec83ee047d6d4bf7b2ac87dd3f680.html
文件谁写内容
acl-manifests.jsonbuild script清单表(67 KB,10 个核心清单)
capabilities.jsonbuild script文件里的能力,这里是 {}
app-manifest/__app__-permission-filesbuild script应用权限文件的路径列表,这里是 []
__global-api-script.jsbuild script名字是 .js,内容是 JSON:全局 API 脚本的路径列表
cb18…generate_context!32×32 图标解码后的 RGBA,4 096 字节
tauri-codegen-assets/79e7….htmlgenerate_context!brotli 压缩后的 index.html

后两类是编译应用 crate 时由过程宏写的:cargo 给 rustc 设了同一个 OUT_DIR 环境变量,过程宏在 rustc 进程里读得到。它们为什么用哈希命名、修改时间为什么是 2000 年 1 月 1 日,见 generate_context!。

build script 和过程宏之间没有别的通道:generate_context! 用 OUT_DIR 拼出 acl-manifests.json 和 capabilities.json 的路径,文件不存在就当作空表。配置文件两边各读一遍,都会合并 TAURI_CONFIG。

rerun-if-changed 与每次都重编

build script 打印的 rerun-if-changed 决定 cargo 什么时候重跑它。build-app 的输出里有三条:配置文件、capabilities 目录(绝对路径),以及环境变量 TAURI_CONFIG、REMOVE_UNUSED_COMMANDS。permissions 目录只在存在时才被监视。

❗IMPORTANT
动脑筋:build-app 没有 capabilities 目录、配置里也没有 capabilities。刚 cargo build 完,什么都不改再 cargo build -v 一次,build-app 会是 Fresh 还是重新编译?

能力目录的监视条件在 acl::build 里:

    let capabilities_dir = std::env::current_dir()
      .context("resolve current dir for capabilities watch path")?
      .join("capabilities");
    // For the same reason, skip the watch when the directory doesn't exist and the
    // configuration lists capabilities in `app > security > capabilities`: that list
    // only selects the files it references by identifier, so creating the directory
    // alone doesn't change the selected capabilities.
    if capabilities_dir.exists() || config.app.security.capabilities.is_empty() {
      println!("cargo:rerun-if-changed={}", capabilities_dir.display());
    }

cargo 把一个不存在的被监视路径当作「已变化」。目录不存在、配置也没列能力时,这里照样监视它,因为之后新建 capabilities/ 放进能力文件,确实会改变结果,build script 必须重跑。代价是在目录建出来之前,每次构建都重跑 build script、重编应用 crate。CLI 生成的项目自带 capabilities/default.json,碰不到这种情况;手写的最小项目(比如本卷的 base 模板)会碰到。建一个空的 capabilities 目录就够了。

📝NOTE
冷知识:这个条件是 tauri-build 2.7.4 才加的

在它之前,不管配置里有没有列能力,只要目录不存在就一直监视,所以在 app > security > capabilities 里内联能力、不建目录的应用,每次构建都会重跑 build script 并重编。#16326(首次发布于 tauri-build 2.7.4,也就是本卷用的版本)把条件改成现在的样子,CHANGELOG 原文:「Don't watch the capabilities directory when it doesn't exist and the configuration lists capabilities in app > security > capabilities. Cargo treats a missing watched path as changed, so these apps re-ran the build script and recompiled on every build.」配置里没列能力的情况保留了监视,原因就是上一段说的:那时目录里的文件就是全部能力的来源。同一处还有更早的一次修复 #15918(tauri-build 2.7.0),把这条路径改成绝对路径:cargo 按拥有 build script 的包解析相对路径,而能力文件的 glob 按进程工作目录解析,先 set_current_dir 再调用的人会监视一个不存在的目录。

permissions 那一条是相对路径(cargo:rerun-if-changed=permissions),而且只在目录存在时打印,所以不会触发这个问题。

动脑筋答案

会重新编译。-v 打印出原因:

       Dirty build-app v0.1.0 (~/exp-tauri/build-app): the file `capabilities` is missing
   Compiling build-app v0.1.0 (~/exp-tauri/build-app)
     Running `~/exp-tauri/target-build/debug/build/build-app-a559251810c0d668/build-script-build`

Dirty 一行说的就是被监视的 capabilities 不存在;Running .../build-script-build 是 build script 重跑,随后应用 crate 重编(这个小应用每次约 0.8 秒)。之后 mkdir capabilities,第一次构建因为「capabilities has changed」再重编一次,第二次就是 Fresh:

       Dirty build-app v0.1.0 (~/exp-tauri/build-app): the file `capabilities` has changed (1793193550.694107706s, 4m 45s after last build at 1793193265.892897776s)
   Compiling build-app v0.1.0 (~/exp-tauri/build-app)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.74s

       Fresh build-app v0.1.0 (~/exp-tauri/build-app)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s

小结

  • tauri_build::build() 的输入有两路:配置文件(加 TAURI_CONFIG),以及 tauri 和插件 crate 经 links 元数据传来的 DEP_* 环境变量(是否开发模式、核心插件和插件的权限文件路径、全局 API 脚本)。
  • 它发 desktop / mobile / dev 三个 cfg 给应用代码用;dev 跟着 tauri 的 custom-protocol feature 走。
  • 「does not match the allowlist」出自 tauri-build 的 manifest.rs:只核对 macos-private-api、protocol-asset、isolation 这几个 feature 和配置是否一致。
  • 权限来自三处:依赖 crate、应用(__app-acl__,AppManifest 生成的文件写在项目的 permissions/autogenerated/)、内联插件;能力只校验「引用的权限存在」,解析留给 generate_context!。
  • 写进 OUT_DIR 的是 acl-manifests.json、capabilities.json、__global-api-script.js(以及可选的 allowed-commands.json);schema 写进项目的 gen/schemas。Info.plist 和 macOS 图标不归它管。
  • 项目里没有 capabilities 目录、配置里也没列能力时,cargo 每次都认为 build script 的输入变了;建一个空目录就好。

💬 关联讨论

发起讨论
加载中...