Rust in Everything
基于 tauri 2.12.5

failed to open icon

主线位置:启动主线第一步,编译期的 generate_context!。它在展开时把默认窗口图标解码成 RGBA 写进 OUT_DIR,生成 Image::new(include_bytes!(..), 宽, 高)(见 generate_context! 的「图标、包信息和 Info.plist」)。图标读不到或格式不对,宏就 panic,错误落在 generate_context!() 那一行。

flowchart TB
  start["generate_context!"] --> tgt{"目标是 Windows"}
  tgt -->|是| res["用 tauri-build 嵌的资源图标"]
  tgt -->|否| pick{"bundle.icon 里有 .png"}
  pick -->|有| first["第一个 .png"]
  pick -->|没有| deflt["icons/icon.png"]
  first --> load["读文件 解码"]
  deflt --> load
  load -->|读不到| e1["failed to open icon"]
  load -->|不是 RGBA| e2["is not RGBA"]
  load -->|成功| okn["Image::new 嵌入"]

本页实验 crate 是 pit1-icon:由模板 base 复制,依次改 icons/ 和配置里的 bundle.icon,cargo build --offline --features tauri/custom-protocol。M2 Max、macOS 27、rustc 1.99.0,tauri 2.12.5。

现象

一:没有 icons/icon.png

error: proc macro panicked
  --> src/main.rs:20:10
   |
20 |     .run(tauri::generate_context!())
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: message: failed to open icon ~/exp-tauri/pit1-icon/icons/icon.png: No such file or directory (os error 2)

error: could not compile `pit1-icon` (bin "pit1-icon") due to 1 previous error

二:有,但是 RGB 的

用 Python 写一张 32×32、颜色类型 2(RGB,没有 alpha)的 PNG,file 认作「PNG image data, 32 x 32, 8-bit/color RGB, non-interlaced」,放到 icons/icon.png:

error: proc macro panicked
  --> src/main.rs:20:10
   |
20 |     .run(tauri::generate_context!())
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: message: icon ~/exp-tauri/pit1-icon/icons/icon.png is not RGBA

error: could not compile `pit1-icon` (bin "pit1-icon") due to 1 previous error

三:配了图标,可是没有 PNG

icons/icon.png 仍然不存在,配置里写上打包用的两种图标:

"bundle": { "icon": ["icons/icon.icns", "icons/icon.ico"] }
error: proc macro panicked
   = help: message: failed to open icon ~/exp-tauri/pit1-icon/icons/icon.png: No such file or directory (os error 2)
error: could not compile `pit1-icon` (bin "pit1-icon") due to 1 previous error

(这一段只保留了 grep 出的三行。)报错里的路径还是 icons/icon.png:配置里列出的两个文件一个都没被看。

出处

context_codegen 里选默认窗口图标的分支:Windows 目标生成 default_window_icon_from_app_icon_resource(),运行时从 tauri-build 编进可执行文件的资源里取;其他目标调 find_icon,条件是「以 .png 结尾」,默认值 icons/icon.png:

fn find_icon(
  config: &Config,
  config_parent: &Path,
  predicate: impl Fn(&&String) -> bool,
  default: &str,
) -> PathBuf {
  let icon_path = config
    .bundle
    .icon
    .iter()
    .find(predicate)
    .map(AsRef::as_ref)
    .unwrap_or(default);
  config_parent.join(icon_path)
}

它不检查文件在不在:情形三里 bundle.icon 没有 .png,find 落空,返回默认路径。路径相对于配置文件所在的目录,不是 crate 根目录(配置不在 crate 根目录时的后果见 generate_context! 读的不是你指定的配置文件)。

拿到路径之后,CachedIcon::new_png 读文件、用 png crate 解码,两处 panic! 就是上面的两条消息:

  pub fn new_png(root: &TokenStream, icon: &Path) -> EmbeddedAssetsResult<Self> {
    let buf = Self::open(icon);
    let decoder = png::Decoder::new(Cursor::new(&buf));
    let mut reader = decoder
      .read_info()
      .unwrap_or_else(|e| panic!("failed to read icon {}: {}", icon.display(), e));

    if reader.output_color_type().0 != png::ColorType::Rgba {
      panic!("icon {} is not RGBA", icon.display());
    }

open 里是 panic!("failed to open icon {}: {}", ..)。这里用 panic! 而不是返回错误,所以 rustc 报的是「proc macro panicked」,消息在 help: message: 后面。

📝NOTE
冷知识:「is not RGBA」是 tauri 1.4.0 加的

#6712(提交标题「feat(core): early panic if the PNG icon is not RGBA, closes #6706」)首次发布于 tauri 1.4.0(tauri-codegen 1.4.0),tauri-codegen 的 CHANGELOG 原文:「Early panic if the PNG icon is not RGBA.」它加的就是这句 panic!,当时写在 tauri-codegen 的 context.rs 里,现在在 image.rs。生成的 Image::new(include_bytes!(..), 宽, 高) 把字节一律当 RGBA 用,所以在宏展开时挡下别的颜色类型。

开发模式(没有 custom-protocol)的 macOS 构建还会多读一次图标:app_icon 先找 bundle.icon 里的 .icns,没有再按上面的规则找 .png,原样嵌进二进制(见 开发模式启动后 bus error 退出)。所以只放 .icns 的项目在开发模式下也一样需要那张 PNG:窗口图标那一次读取先失败。

修法

放一张 RGBA 的 PNG,并让 find_icon 能找到它:要么就叫 icons/icon.png,要么在 bundle.icon 里列出来。pit1-icon 删掉 icons/icon.png,把模板的图标改名为 icons/app.png,配置写:

"bundle": { "icon": ["icons/app.png"] }

构建通过,运行:

REPORT greet=hello, rie origin=tauri://localhost

打包时还需要 .icns(macOS)和 .ico(Windows),一起列在 bundle.icon 里,顺序无关:窗口图标只取第一个 .png,macOS 开发模式只取第一个 .icns。tauri icon 命令能从一张大图生成整套图标,本卷不讲 CLI。

怎么确认没中招

  • file icons/icon.png(或 bundle.icon 里第一个 .png)显示的颜色类型应该是 RGBA,比如「8-bit/color RGBA」。
  • bundle.icon 里至少有一个 .png,或者 icons/icon.png 存在;路径相对于配置文件所在目录。
  • Windows 目标走资源图标这条路,要的是 .ico(tauri-build 默认找 bundle.icon 里的 .ico,再找 icons/icon.ico,见 WindowsAttributes::window_icon_path 的文档注释)。本页没在 Windows 上实测。

小结

  • 非 Windows 目标上,generate_context! 在编译期读 bundle.icon 里第一个 .png,没有就读 icons/icon.png,路径相对于配置文件所在目录。
  • 文件不存在:「failed to open icon ...」;不是 RGBA:「icon ... is not RGBA」;都是过程宏 panic,报错落在 generate_context!() 上。
  • 只列 .icns、.ico 不够,窗口图标要一张 RGBA 的 PNG。

💬 关联讨论

发起讨论
加载中...