目前手头的项目都是基于linux环境下搭建的开发框架,使用docker工具分别生成不同环境下的运行程序,现在需要将生成的运行程序文件自动构建打包生成对应操作系统环境下的安装包程序,这里介绍的是linux下打包生成windows安装包,linux版本是ubuntu22.04,使用的打包工具是NSIS
本章内容
- 安装NSIS工具
- 了解NSIS工具以及NSIS脚本
- 编写NSIS脚本
- 利用shell脚本自动输出生成NSIS脚本
1.安装NSIS工具
在命令行直接执行下载命令即可(这里是ubuntu系统,centos自己改成yum安装)
sudo apt-get install nsis
2.了解NSIS工具以及NSIS脚本
NSIS(Nullsoft Scriptable Install System)是一个开源的Windows系统下安装程序制作工具,由Nullsoft开发,该公司也是Winamp媒体播放器的创建者。NSIS的主要功能包括脚本化安装程序的创建,小巧且高度可定制,支持插件,并且是免费和开源的。
NSIS的主要特点是它使用自定义的脚本语言来描述安装程序的行为和界面,这使得开发人员可以根据他们的需求自定义安装过程。此外,NSIS生成的安装程序通常非常小巧,只包含必需的文件和组件,而且它提供了丰富的自定义选项,使开发人员能够创建符合其应用程序需求的安装界面。
NSIS脚本是一种有特定语法规则的规范语言,可以使用任何文本编辑器进行编辑,推荐使用带有行号和语法高亮的编辑器,这样更易于阅读和更新脚本。一个NSIS脚本通常包括Installer Attributes、Page、Sections和Functions等部分,其中OutFile指令是必备的,用于指明安装程序的输出文件。
通过编写NSIS脚本,开发人员可以控制安装程序的各个方面,如安装选项、文件复制、注册表操作等。同时,NSIS还支持插件,这意味着开发人员可以扩展其功能,如添加创建桌面快捷方式、注册文件关联等。
3.编写NSIS脚本
这里不深入教学,有感兴趣的或者不理解的可以搜索NSIS脚本编写的相关文章深入了解学习
下面看一个nsis脚本示例
; 示例NSIS脚本
; 包含MUI宏文件
!include "MUI2.nsh"
; 设置输出文件
Outfile "MyAppSetup.exe"
; 设置安装程序图标(可选)
; Icon "path\to\your\icon.ico"
; 设置安装程序名称
Name "MyApp_Installer"
; 设置安装程序版本信息
VIProductVersion "1.0.0.0"
VIAddVersionKey "ProductName" "MyApp"
VIAddVersionKey "FileDescription" "MyApp Installer"
VIAddVersionKey "FileVersion" "1.0.0.0"
VIAddVersionKey "LegalCopyright" "Your Company Name"
; 请求管理员权限运行
RequestExecutionLevel admin
; 设置默认安装目录
InstallDir "$PROGRAMFILES\MyApp"
; 页面设置
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
; 语言设置
;!insertmacro MUI_LANGUAGE "English"
; 安装程序段
Section "Main Program" SecMain
; 设置该段的描述
SectionIn RO
; 在这里添加你的安装逻辑
; 例如,复制文件到安装目录
SetOutPath $INSTDIR
File "/home/leo/Debug/TestNSIS.exe"
File "/home/leo/Debug/TestNSIS.ilk"
File "/home/leo/Debug/TestNSIS.pdb"
File "/home/leo/Debug/readme.txt"
; 创建快捷方式(可选)
; CreateShortCut "$DESKTOP\MyApp.lnk" "$INSTDIR\file.exe"
CreateShortCut "$DESKTOP\MyApp.lnk" "$INSTDIR\TestNSIS.exe" "" "$INSTDIR\TestNSIS.exe" 0 SW_SHOWNORMAL
; 写入卸载信息到注册表(如果需要的话)
; WriteRegStr HKLM "Software\YourCompany\MyApp" "UninstallString" '"$INSTDIR\uninst.exe"'
SectionEnd
; 卸载程序段
Section "Uninstall"
; 设置该段的描述
SectionIn RO
; 在这里添加你的卸载逻辑
; 例如,删除文件和目录
Delete "$INSTDIR\TestNSIS.exe"
; 删除快捷方式(如果创建了的话)
; Delete "$DESKTOP\MyApp.lnk"
; 从注册表中删除卸载信息(如果写入了的话)
; DeleteRegKey HKLM "Software\YourCompany\MyApp"
; 删除安装目录(如果为空的话)
; RMDir /r "$INSTDIR"
SectionEnd
; 函数:.onInit
Function .onInit
; 检查是否已经安装过,如果是则退出安装程序
; 可以通过检查注册表或其他方式来实现
FunctionEnd
; 函数:un.onInit
Function un.onInit
; 卸载时的初始化操作,例如停止服务、关闭应用程序等
FunctionEnd
可以根据自己的目录和文件替换修改脚本中的参数,测试生成安装文件,比如上面这个示例代码,只需要修改这一段
SetOutPath $INSTDIR
File "/home/leo/Debug/TestNSIS.exe"
File "/home/leo/Debug/TestNSIS.ilk"
File "/home/leo/Debug/TestNSIS.pdb"
File "/home/leo/Debug/readme.txt"
将 File 后的路径替换为自己需要测试打包的执行文件以及相关依赖即可(第一次测试建议测试程序越简单,相关依赖越少越好)
在命令行执行以下命令,test.nsi就是刚才编辑的nsis脚本
makensis test.nsi
执行成功会生成一个安装包程序exe,名称就是在nsis脚本中指定的这个
; 设置输出文件
Outfile "MyAppSetup.exe"
4.利用shell脚本自动输出生成NSIS脚本
关于为什么要单独用一个shell文件来生成NSIS脚本,主要原因是shell脚本的语法和逻辑比NSIS更简单,方便控制NSIS脚本中的各个变量输入。我们只需要在shell脚本中替换对应变量,然后直接生成输出NSIS脚本代码到文件即可
以下是shell脚本内容示例
#!/bin/bash
# 检查参数数量
if [ "$#" -ne 7 ]; then
echo "Usage: $0 <source_dir> <installer_name> <installer_output> <install_dir> <shortcut_pathname> <app_version> <output_file> "
exit 1
fi
# 定义变量 # 获取参数
SOURCE_DIR="$1" #源路径/安装文件源目录
INSTALLER_NAME="$2" #"My_Installer" #源程序名
INSTALLER_OUTPUT="$3" #"myInstaller.exe" #安装程序输出文件(安装包exe)
INSTALL_DIR="$4" #"\$PROGRAMFILES\MyInstaller" #默认安装目录
SHORTCUT_PATHNAME="\$INSTDIR\\$5" #快捷方式指向的文件路径 全称
APP_VERSION="$6" #"1.0.0.0" #版本号
OUTPUT_FILE="$7" #"Installer.nsi" #输出的nsis脚本名
# 清空或创建NSIS脚本文件
> "$OUTPUT_FILE"
# 写入NSIS脚本头部
echo "" >> "$OUTPUT_FILE"
echo "; 包含MUI宏文件" >> "$OUTPUT_FILE"
echo "!include MUI2.nsh" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 设置安装程序图标(可选)" >> "$OUTPUT_FILE"
#echo "Icon \"path\to\your\icon.ico\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 设置输出文件" >> "$OUTPUT_FILE"
echo "OutFile \"$INSTALLER_OUTPUT\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 设置安装程序名称" >> "$OUTPUT_FILE"
echo "Name \"$INSTALLER_NAME\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 设置安装程序版本信息" >> "$OUTPUT_FILE"
echo "VIProductVersion \"$APP_VERSION\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 请求管理员权限运行" >> "$OUTPUT_FILE"
echo "RequestExecutionLevel admin" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 设置默认安装目录" >> "$OUTPUT_FILE"
echo "InstallDir \"$INSTALL_DIR\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 页面设置" >> "$OUTPUT_FILE"
echo "!insertmacro MUI_PAGE_DIRECTORY" >> "$OUTPUT_FILE"
echo "!insertmacro MUI_PAGE_INSTFILES" >> "$OUTPUT_FILE"
echo "!insertmacro MUI_PAGE_FINISH" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 语言设置" >> "$OUTPUT_FILE"
echo "!insertmacro MUI_LANGUAGE \"SimpChinese\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 安装程序段" >> "$OUTPUT_FILE"
echo "Section \"Main Program\" SecMain" >> "$OUTPUT_FILE"
echo " SectionIn RO" >> "$OUTPUT_FILE"
#echo " SetOutPath \$INSTDIR" >> "$OUTPUT_FILE"
echo " ; 添加安装文件" >> "$OUTPUT_FILE"
last_dir="\$INSTDIR"
# 递归函数,用于添加目录和文件到NSIS脚本
function add_to_nsis() {
local dir="$1"
local parent_dir="$2"
local rel_path=${dir#$SOURCE_DIR} # 去除源目录前缀
rel_path=${rel_path//\//\\} # 将"/"替换为"\"
rel_path="${rel_path%/}" # 去除尾部的反斜杠(如果是目录的话)
local outdir=\$INSTDIR$rel_path
# 如果是目录,则创建目录并递归添加子目录和文件
if [ -d "$dir" ]; then
# 检查是否是根目录
#if [ "$base_dir" != "" ]; then
# 如果不是根目录,则添加CreateDirectory指令
#echo " CreateDirectory $outdir$rel_path" >> "$OUTPUT_FILE"
#fi
echo " SetOutPath $outdir" >> "$OUTPUT_FILE"
last_dir=$outdir
# 递归添加子目录和文件
for item in "$dir"/*; do
add_to_nsis "$item" "$outdir"
done
if [ "$parent_dir" != "$last_dir" ];then
echo " SetOutPath $parent_dir" >> "$OUTPUT_FILE"
last_dir=$parent_dir
fi
# 如果是文件,则添加文件
elif [ -f "$dir" ]; then
# 使用绝对路径添加文件,NSIS 将处理路径问题
echo "File \"$dir\"" >> "$OUTPUT_FILE"
fi
}
# 从源目录开始递归处理
add_to_nsis "$SOURCE_DIR" "\$INSTDIR"
echo "" >> "$OUTPUT_FILE"
echo " ; 创建快捷方式(可选)" >> "$OUTPUT_FILE"
echo " CreateShortCut \"\$DESKTOP\\$INSTALLER_NAME.lnk\" \"$SHORTCUT_PATHNAME\" \"\" \"$SHORTCUT_PATHNAME\" 0 SW_SHOWNORMAL" >> "$OUTPUT_FILE"
echo " ; 写入卸载信息到注册表(如果需要的话)" >> "$OUTPUT_FILE"
echo " ; WriteRegStr HKLM \"Software\YourCompany\MyApp\" \"UninstallString\" \"$INSTDIR\uninst.exe\"" >> "$OUTPUT_FILE"
echo "SectionEnd" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "; 卸载程序段" >> "$OUTPUT_FILE"
echo "Section \"Uninstall\"" >> "$OUTPUT_FILE"
echo " ; 设置该段的描述" >> "$OUTPUT_FILE"
echo " SectionIn RO" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo " ; 在这里添加你的卸载逻辑" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo " ; 例如,删除文件和目录" >> "$OUTPUT_FILE"
echo " Delete \"\$INSTDIR\\$INSTALLER_OUTPUT\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo " ; 删除快捷方式(如果创建了的话)" >> "$OUTPUT_FILE"
echo " ; Delete \"$DESKTOP\MyApp.lnk\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo " ; 从注册表中删除卸载信息(如果写入了的话)" >> "$OUTPUT_FILE"
echo " ; DeleteRegKey HKLM \"Software\YourCompany\MyApp\"" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo " ; 删除安装目录(如果为空的话)" >> "$OUTPUT_FILE"
echo " ; RMDir /r \"$INSTDIR\"" >> "$OUTPUT_FILE"
echo "SectionEnd" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Function .onInit" >> "$OUTPUT_FILE"
echo " ; 检查是否已经安装过,如果是则退出安装程序" >> "$OUTPUT_FILE"
echo " ; 可以通过检查注册表或其他方式来实现" >> "$OUTPUT_FILE"
echo "FunctionEnd" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Function un.onInit" >> "$OUTPUT_FILE"
echo " ; 卸载时的初始化操作,例如停止服务、关闭应用程序等" >> "$OUTPUT_FILE"
echo "FunctionEnd" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
# 完成NSIS脚本
首先读取输入参数,包含了源文件目录、程序名、版本号等等,接着一行行向nsis脚本中输出代码内容,后续对NSIS脚本内容的修改和编辑都是在这里控制处理,每一个“echo”语句都会输出指定内容到NSIS脚本文件中
整体的逻辑很好理解,基本和nsis脚本保持一致,有一个地方需要提一下,就是关于add_to_nsis() 这个函数,主要的作用是递归读取指定的目录,将文件按照读取的结构写入nsis脚本中,保证生成安装包时,所有的文件和目录可以维持原有的结构,对应的就是上面例子里 File 后面对应的部分,只不过那里只有文件,这里增加了目录的概念。
在执行这个脚本时,需要输入参数,用于指定安装程序的相关属性,即脚本开头那里读取的参数内容。
bash ./makeNSIS.sh "/home/leo/" "MyApp" "Installer.exe" "\$PROGRAMFILES\Installer" "MyApp.exe" "1.0.0.0" "Installer.nsi"
这是输出的NSIS脚本示例
; 包含MUI宏文件
!include MUI2.nsh
; 设置安装程序图标(可选)
; 设置输出文件
OutFile "Installer.exe"
; 设置安装程序名称
Name "MyApp"
; 设置安装程序版本信息
VIProductVersion "1.0.0.0"
; 请求管理员权限运行
RequestExecutionLevel admin
; 设置默认安装目录
InstallDir "$PROGRAMFILES\Installer"
; 页面设置
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
; 语言设置
!insertmacro MUI_LANGUAGE "SimpChinese"
; 安装程序段
Section "Main Program" SecMain
SectionIn RO
; 添加安装文件
SetOutPath $INSTDIR
File "/home/leo/NSIS_example/example3/testDir/license.dat"
File "/home/leo/NSIS_example/example3/testDir/MyApp.exe"
SetOutPath $INSTDIR\qt-win64
SetOutPath $INSTDIR\qt-win64\lib
File "/home/leo/NSIS_example/example3/testDir/qt-win64/lib/Qt5Bluetooth.dll"
SetOutPath $INSTDIR\qt-win64
SetOutPath $INSTDIR\qt-win64\plugins
SetOutPath $INSTDIR\qt-win64\plugins\audio
File "/home/leo/NSIS_example/example3/testDir/qt-win64/plugins/audio/qtaudio_windows.dll"
SetOutPath $INSTDIR\qt-win64\plugins
SetOutPath $INSTDIR\qt-win64\plugins\bearer
File "/home/leo/NSIS_example/example3/testDir/qt-win64/plugins/bearer/qgenericbearer.dll"
SetOutPath $INSTDIR\qt-win64\plugins
SetOutPath $INSTDIR\qt-win64
SetOutPath $INSTDIR
; 创建快捷方式(可选)
CreateShortCut "$DESKTOP\MyApp.lnk" "$INSTDIR\MyApp.exe" "" "$INSTDIR\MyApp.exe" 0 SW_SHOWNORMAL
; 写入卸载信息到注册表(如果需要的话)
; WriteRegStr HKLM "Software\YourCompany\MyApp" "UninstallString" "\uninst.exe"
SectionEnd
; 卸载程序段
Section "Uninstall"
; 设置该段的描述
SectionIn RO
; 在这里添加你的卸载逻辑
; 例如,删除文件和目录
Delete "$INSTDIR\Installer.exe"
; 删除快捷方式(如果创建了的话)
; Delete "\MyApp.lnk"
; 从注册表中删除卸载信息(如果写入了的话)
; DeleteRegKey HKLM "Software\YourCompany\MyApp"
; 删除安装目录(如果为空的话)
; RMDir /r ""
SectionEnd
Function .onInit
; 检查是否已经安装过,如果是则退出安装程序
; 可以通过检查注册表或其他方式来实现
FunctionEnd
Function un.onInit
; 卸载时的初始化操作,例如停止服务、关闭应用程序等
FunctionEnd
将整个打包过程结合到项目里,需要把这段执行逻辑加到项目整体的构建流程中,示例如下
#!/bin/bash
# 编译你的程序,生成执行文件和相关依赖
#...
#...
#...
#指定nsis生成脚本相关输入
NSIS_ARG_1="/home/leo" #源路径/安装文件源目录
NSIS_ARG_2="MyApp" #源程序名
NSIS_ARG_3="Installer.exe" #安装程序输出文件(安装包exe)
NSIS_ARG_4="\$PROGRAMFILES\Installer" #默认安装目录
NSIS_ARG_5="MyApp.exe" #快捷方式指向的文件基于NSIS_ARG_1的相对路径及文件名
NSIS_ARG_6="1.0.0.0" #版本号
NSIS_ARG_7="Installer.nsi" #输出的nsis脚本名
# 调用shell生成NSIS脚本
bash ./makeNSIS.sh $NSIS_ARG_1 $NSIS_ARG_2 $NSIS_ARG_3 $NSIS_ARG_4 $NSIS_ARG_5 $NSIS_ARG_6 $NSIS_ARG_7
# 调用NSIS脚本生成安装包
makensis ./$NSIS_ARG_7
# 结束构建脚本
exit 0
在主流程脚本中,编译生成执行文件和依赖,指定好相关参数变量,执行指定shell脚本生成NSIS脚本文件,然后再执行生成的NSIS脚本生成对应的安装包程序,到此就实现了整个流程的自动化构建