前言

前面简单地进行了打包

【Tauri2】031 windows系统下的打包-CSDN博客https://blog.csdn.net/qq_63401240/article/details/147466278?spm=1001.2014.3001.5501遇到了bug,这篇就来解决一下。

正文

报错的解决

在目前的tauri的代码中,看看源码

tauri/crates/tauri-bundler/src/bundle/windows/nsis/mod.rs at dev · tauri-apps/taurihttps://github.com/tauri-apps/tauri/blob/dev/crates/tauri-bundler/src/bundle/windows/nsis/mod.rs直接给出关键代码

 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"));
  #[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() {
      log::Level::Error => "-V1",
      log::Level::Warn => "-V2",
      log::Level::Info => "-V3",
      _ => "-V4",
    })
    .arg(installer_nsi_path)
    .env_remove("NSISDIR")
    .env_remove("NSISCONFDIR")
    .current_dir(output_path)
    .piped()
    .context("error running makensis.exe")?;

  fs::rename(nsis_output_path, &nsis_installer_path)?;

首先执行日志,没问题

     Running makensis to produce F:\Users\26644\RustProject\tb\src-tauri\target\release\bundle\nsis\tb_0.1.0_x64-setup.exe

 然后获取nsis_cmd,本质是获取makensis.exe

接着添加参数与环境,字符集UTF8

其中installer_nsi_path 是固定的installer.nsi

  let installer_nsi_path = output_path.join("installer.nsi");
  write_utf8_with_bom(
    &installer_nsi_path,
    handlebars.render("installer.nsi", &data)?,
  )?;

获取到这个文件然后渲染数据

如果使用默认模板,就渲染默认模板

如果是自定义,比如tb.nsi,就会把tb.nsi中的内容渲染到其中

实际上,可以发现

tb.nsiinstaller.nsi一模一样

执行installer_nsi_path。

即F:\Users\26644\RustProject\tb\src-tauri\target\release\nsis\x64\installer.nsi

这前面其实都没有问题

下面这一步就出现了问题


  fs::rename(nsis_output_path, &nsis_installer_path)?;

进行了rename。

这个nsis_installer_path 本质上就是前面打印语句的输出的路径

 let nsis_installer_path = settings.project_out_directory().to_path_buf().join(format!(
    "bundle/{}/{}.exe",
    if updater {
      NSIS_UPDATER_OUTPUT_FOLDER_NAME
    } else {
      NSIS_OUTPUT_FOLDER_NAME
    },
    package_base_name
  ));

而这个nsis_output_path,就是指输出文件

可以看到是一个output_path+out_file

  let nsis_output_path = output_path.join(out_file);

而这个out_file在源代码如下

  let out_file = "nsis-output.exe";
  data.insert("out_file", to_json(out_file));

是写死的。这一步是有问题,因此,可以得出报错的原因

代码中希望找到nsis-output.exe,但是笔者是自定义的输出名字——setup.exe,有问题

因此,修改打包输出即可

OutFile "nsis-output.exe"

这一步居然是固定的。只能写成这个。

代码是这么写的。。。

运行没有问题

简单使用handlebars

It uses handlebars so the Tauri CLI can brand your installer according to your tauri.conf.json

——Tauri官网

前面渲染模板使用的模板引擎就是handlebars,

 handlerbar是JavaScript 的模板引擎,将模板编译为 JavaScript 函数。

 但是也有Rust版本的handlebars - Rusthttps://docs.rs/handlebars/latest/handlebars/

新建一个crate test-handlerbar,试试使用这个模板引擎。

注册依赖

[dependencies]
handlebars={version = "6.3.2"}
serde_json= {version = "1"}

简单的使用

use handlebars::{Handlebars};
use serde_json::json;

fn render() {
    let mut reg = Handlebars::new();
    reg.register_template_string(
        "render-id","
        Hello {{name}}" 
    ).unwrap();

    let data = json!({
        "name": "world",
    });

    let rendered = reg.render("render-id", &data).unwrap();
    println!("{}", rendered);
}
fn main() {
    render()
}

内容其实很简单,

很明显,register_template_string这个函数,第一个参数传一个"id",第二个参数就是模板字符串。后面根据"id"渲染,以 {{}} 作为标准,替换里面的内容。

读取文件再渲染

目录结构如下

读取fixture中的a.nsi,保存到result目录下,代码如下

use handlebars::{Handlebars};
use serde_json::json;
use std::fs;

fn render() -> Result<(), Box<dyn std::error::Error>> {
    let mut reg = Handlebars::new();

    // 读取模板文件内容
    let template_content = fs::read_to_string("fixture/a.nsi")?;
    reg.register_template_string("render-id", &template_content)?;

    // 读取数据文件内容
    let data = json!({
        "product_name": "tb",
    });
    
    let rendered = reg.render("render-id", &data)?;
    println!("{}", rendered);
    let output_file = "result/res_a.nsi";
    fs::write(output_file, rendered)?;

    Ok(())
}
fn main() {
    render();
}

渲染前的文件内容

!define PRODUCT_NAME "{{product_name}}"

渲染后

!define PRODUCT_NAME "tb"

实际上就可以操作了

与tauri项目连用

一些关于NSIS的参考

NSIS 从入门到编写完整打包脚本——2024/12/02NSIS是功能强大的安装包制作工具,但其高质量的教程稀少,上手 - 掘金https://juejin.cn/post/7207410405857034301根据tb.nsi建立属于自己的模板文件,比如说叫installer.nsi

fixture/installer.nsi的内容如下

; Script generated by the HM NIS Edit Script Wizard.

; HM NIS Edit Wizard helper defines
!define PRODUCT_NAME "{{product_name}}"
!define PRODUCT_VERSION "{{product_version}}"
!define PRODUCT_PUBLISHER "{{product_publisher}}"
!define PRODUCT_WEB_SITE "{{product_web_site}}"
!define PRODUCT_PATH_BEFORE "{{product_path_before}}"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\{{product_name}}.exe"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir"


!include "MUI.nsh"

; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${PRODUCT_PATH_BEFORE}/${PRODUCT_NAME}/src-tauri/icons/icon.ico"
!define MUI_UNICON "{{#with uninstall_icon}}{{this}} {{else}}${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico{{/with}}"

; Welcome page
!insertmacro MUI_PAGE_WELCOME
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Start menu page
var ICONS_GROUP
!define MUI_STARTMENUPAGE_NODISABLE
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "${PRODUCT_NAME}"
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}"
!insertmacro MUI_PAGE_STARTMENU Application $ICONS_GROUP
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_NAME}.exe"
!insertmacro MUI_PAGE_FINISH

; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES

; Language files
!insertmacro MUI_LANGUAGE "English"

; MUI end ------

Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "nsis-output.exe"
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUnInstDetails show

Section "MainSection" SEC01
  SetOutPath "$INSTDIR"
  SetOverwrite ifnewer
  File "${PRODUCT_PATH_BEFORE}\${PRODUCT_NAME}\src-tauri\target\release\${PRODUCT_NAME}.exe"

; Shortcuts
  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
  CreateDirectory "$SMPROGRAMS\$ICONS_GROUP"
  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe"
  CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe"
  !insertmacro MUI_STARTMENU_WRITE_END
SectionEnd

Section -AdditionalIcons
  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
  WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"
  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url"
  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninst.exe"
  !insertmacro MUI_STARTMENU_WRITE_END
SectionEnd

Section -Post
  WriteUninstaller "$INSTDIR\uninst.exe"
  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\${PRODUCT_NAME}.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_NAME}.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd


Function un.onUninstSuccess
  HideWindow
  MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) 删除成功"
FunctionEnd

Function un.onInit
  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "确实删除  $(^Name) 所有内容吗" IDYES +2
  Abort
FunctionEnd

Section Uninstall
  !insertmacro MUI_STARTMENU_GETFOLDER "Application" $ICONS_GROUP
  Delete "$INSTDIR\${PRODUCT_NAME}.url"
  Delete "$INSTDIR\uninst.exe"
  Delete "$INSTDIR\${PRODUCT_NAME}.exe"

  Delete "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk"
  Delete "$SMPROGRAMS\$ICONS_GROUP\Website.lnk"
  Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
  Delete "$SMPROGRAMS\$ICONS_GROUP\${PRODUCT_NAME}.lnk"

  RMDir "$SMPROGRAMS\$ICONS_GROUP"
  RMDir "$INSTDIR"

  DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
  SetAutoClose true
SectionEnd

内容可以根据实际需要修改。

笔者设置 product_nameproduct_version、product_publisher、product_web_site 

和product_path_before,看个人需求。无所谓。

product_path_before笔者想的是,比如说,建立一个tb目录。路径在xxx/xx/xxx/tb

其中xxx/xx/xxx/就是指product_path_before

product_name就是项目的名字,

笔者使用src-tauir下的默认的icon.ico。

输出的安装包笔者固定nsis-output.exe

其他无关紧要

渲染的关键代码

    let template_content = fs::read_to_string("fixture/installer.nsi")?;
    reg.register_template_string("render-id", &template_content)?;
    let name="tb";
    let data = json!({
        "product_name": name,
        "product_version":"1.0.0",
        "product_publisher":"you",
        "product_web_site":"https://www.baidu.com",
        "product_path_before":"F:/Users/26644/RustProject"
    });
    
    let rendered = reg.render("render-id", &data)?;
    let output_file = format!("result/{}.nsi", name);

运行后生成tb.nsi,笔者通过NSIS edit编译运行,生成了nsis-output.exe,可以安装,没问题。

笔者是Windows操作系统,其他操作系统类似的,随机应变。

怎么在运行build命令的时候生成对应项目的nsi文件?

pnpm run tauri build

这其实很简单,在src-tauri目录下有build.rs,写在下面这行代码之前就可以

    tauri_build::build()

在Cargo.toml文件中 

[build-dependencies]
tauri-build = { version = "2", features = [] }
serde_json = "1"
handlebars= {version = "6.3.2"}

运行命令,结果成功。

实际上还可以更进一步,这些参数都可以获取的,通过toml文件。这就不细说了。

修改图标

这没什么好说的,直接运行命令 pnpm tarui icon + 图片路径 

有点要求

Source image must be square
       Error Source image must be square

 运行成功

➜ pnpm tauri icon "C:\Users\26644\Downloads\124107496_p0_resized.jpg"

> tb@0.1.0 tauri F:\Users\26644\RustProject\tb
> tauri "icon" "C:\\Users\\26644\\Downloads\\124107496_p0_resized.jpg"

    .....

此时icon.ico文件就变成所需要的图片。

总结

截止到目前,使用NSIS打包,没有配置自定义模板的情况下,

先使用handlebars渲染默认的installer.nsi文件,生成渲染后的installer.nsi

然后通过makensis.exe渲染后的installer.nsi,生成nsis-output.exe文件,修改nsis-output.exe的名字和路径。放在在target/release/bundle/nsis目录下。就这样操作的。

没有使用默认的文件,使用自定义的模板,还是一样的,最后渲染后的文件是installer.nsi。

其他不变。

更多推荐