window下程序用脚本打包发布(vs+qt)

自己参考网上资料研究的一个bat+nsis的简单打包脚本,做个笔记记录一下。

bat脚本需要了解一些相关的语法,可以参考这个  bat脚本:快速入门https://blog.csdn.net/csfchh/article/details/106795352里面的基本够用了

我的方法是要提前将项目编译好,(实际上在脚本里编译是一样的,个人喜好)

编译好后直接在脚本目录下运行脚本就可以了,pack.bat就是写好的脚本

.\pack.bat release_dir 1.0

release_dir就是vs下编译好的路径,要细致到具体的程序,比如E:\...\x64\Release\app.exe。1.0是发布的版本,这个版本的联动控制没仔细研究(想要弄这个可以尝试用inno setup和git更方便)

1.pack.bat

bat脚本主要是对程序的一些依赖库和资源文件的拷贝,资源可以并到qt里面实现加密保护,不过我这里没做这个,直接用的。

说一下几个路径

set vs_root_path="E:\Program Files (x86)\VS\VS2022\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64\dumpbin.exe"
set vs_vcvar_path="E:\Program Files (x86)\VS\VS2022\VC\Auxiliary\Build\vcvars64.bat"
set nsis_make_path="C:\Program Files (x86)\NSIS\makensis.exe"
set plugins_path=D:\QT\5.15.2\msvc2019_64\plugins

第一个是dumpbin命令的路径,用来解析.exe程序的依赖。

第二个是vs的vcvars64脚本路径,直接call调用,目的是用qt的windeployqt打包生成一些qt环境时将vs的环境也加进去。

第三个是NSIS的makensis路径,用来跑nsi脚本的。

第四个plugins是qt的插件路径,主要是程序用到数据库,音频等时需要。

echo 开始拷贝release下的
for /r %exe_noexe% %%k in (*) do (
copy %%k %app_path% 1>nul)

这里拷贝release下的内容,主要是对一些第三方库的增加。

ps::实际上从拷贝release,qt打包,解析依赖拷贝,大概率会有重复的库拷贝,但是不影响,为了防止遗漏没拷贝。。。

对于解析的依赖里面,用where找不到的dll,会记录在日志里面(对于上面多处拷贝,基本没有找不到的)

echo 找不到%%i,记录于%dll_path%\copy_error_log.txt
echo %%i>>%dll_path%\copy_error_log.txt)

对于许可协议,因为是内部打包给测试自己用,所以没去弄,直接echo个空文件,这个在后面nsis生成安装包时候要用的。

::许可协议,应该直接拷贝
echo>%app_path%\license.txt

其他的就是一些bat的语法了,全部内容:

chcp 65001
@echo off

SETLOCAL ENABLEDELAYEDEXPANSION
::输入的一定要是release编译好的路径下的exe路径
set exe_path=%1
set exe_name=%~n1%~x1
set exe_name_noexten=%~n1
set exe_noexe=%~dp1
set app_version=%2

rd /S /Q .\APP_EXE
md .\APP_EXE\exe_%exe_name_noexten% 2>nul
::copy %1 .\exe_%exe_name_noexten%
set app_path=.\APP_EXE\exe_%exe_name_noexten%
set dll_path=.\APP_EXE\dll_%exe_name_noexten%
echo 路径带空格需要加引号
::set /p vs_root_path=VS的dumpbin.exe路径(eg.E:\...VS\VS2022\VC\...\dumpbin.exe):
::set /p vs_vcvar_path=VS的vcvarsall.bat的路径(eg.E:\...\VS\VS2022\VC\...\vcvars64.bat):
::set /p nsis_make_path=NSIS的makensis.exe路径(eg.C:\...\NSIS\makensis.exe):
set vs_root_path="E:\Program Files (x86)\VS\VS2022\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64\dumpbin.exe"
set vs_vcvar_path="E:\Program Files (x86)\VS\VS2022\VC\Auxiliary\Build\vcvars64.bat"
set nsis_make_path="C:\Program Files (x86)\NSIS\makensis.exe"
set plugins_path=D:\QT\5.15.2\msvc2019_64\plugins

md .\APP_EXE\resources 2>nul
set /p copy_resource=是否需要拷贝resource(y or n)?:
if %copy_resource%==y (
set /p resource_path=resources的路径:
xcopy !resource_path! .\APP_EXE\resources /e 1>nul
)

echo>.\APP_EXE\resource\1.txt
set exe_resource_path=.\APP_EXE

set dumpbin_path=%vs_root_path%
echo Step1. set dumpbin root: %dumpbin_path%
echo Step2. set exe file path: %exe_path%
md %dll_path%>nul
 
echo Step3. 开始解析依赖dll
%dumpbin_path% /dependents %exe_path% | findstr "\.dll$" >%dll_path%\_dependent_dll_log.txt
 
echo 解析完毕,总计
type %dll_path%\_dependent_dll_log.txt | find /v /c ""

::许可协议,应该直接拷贝
echo>%app_path%\license.txt

echo>%dll_path%\temp.txt

echo 开始拷贝release下的
for /r %exe_noexe% %%k in (*) do (
copy %%k %app_path% 1>nul)

echo 开始qt打包
call %vs_vcvar_path%
::windeployqt注意电脑下的是64位还是32位的,两者同时存在建议指定路径
windeployqt --force %app_path%\%exe_name% 1>nul

md %app_path%\plugins 2>nul
xcopy %plugins_path% %app_path%\plugins /e 1>nul
::删除windeployqt打包重复的文件夹
rd /S /Q %app_path%\iconengines %app_path%\imageformats %app_path%\platforms %app_path%\styles 1>nul

set error_flag=0

echo 开始拷贝解析的dll
for /f %%i in (%dll_path%\_dependent_dll_log.txt) do ( 
WHERE %%i 1>%dll_path%\temp.txt 2>nul
if errorlevel 1 ( 
for /r %exe_noexe% %%k in (%%i) do (
set error_flag=1)
if error_flag==0 (
echo 找不到%%i,记录于%dll_path%\copy_error_log.txt
echo %%i>>%dll_path%\copy_error_log.txt)
)
for /f %%j in (%dll_path%\temp.txt) do (
copy %%j %app_path% 1>nul)
)
 
::删除一些不用的
del %dll_path%\temp.txt 1>nul
del /S *.pdb 1>nul 2>nul
echo 所有拷贝结束
%nsis_make_path% ".\nsis.nsi"
echo 所有打包结束
:end
pause

2.nsis.nsi

对于nsi语法没太多去研究,因为感觉没inno setup好用。nsis.nsi跟bat脚本是同一目录下(这个不强求,不同目录就要手动改下路径)。

nsi脚本里面主要是对安装程序的制作,套模板就行了,网上一堆,语法的话可以直接去他官网看就ok了,官网很详细。

首先定义了一些宏,都是bat脚本的一些值,后面做数据的拷贝,注册表写入以及卸载程序的制作需要用到。

!define PRODUCT_NAME $%exe_name_noexten%
!define PACKAGE_PATH $%app_path%
!define PRODUCT_VERSIO
!define RESOURCE_PATH $%exe_resource_path%
!define DIR_MNAME $%exe_name_noexten%N $%app_version%

数据的拷贝主要是处理之前bat里面围绕.exe生成或拷贝的一些文件

  File "${PACKAGE_PATH}\*.dll"
  File /r "${PACKAGE_PATH}\*.exe"
  ;File /r "${PACKAGE_PATH}\*.conf"
  ;File /nonfatal "${PACKAGE_PATH}\*.xml"

  SetOutPath "$INSTDIR\resources"
  File /r "${RESOURCE_PATH}\resources\*"

  SetOutPath "$INSTDIR\${DIR_MNAME}\translations"
  File /r "${PACKAGE_PATH}\translations\*"

  SetOutPath "$INSTDIR\${DIR_MNAME}\plugins"
  File /r "${PACKAGE_PATH}\plugins\*"

ps::这里file遍历的文件如果不存在是会报错的(因为有些程序可能不需要resources,或者插件文件,那样的话在bat里就没必要拷贝),我的想法是用iffileexists来判断跳过的,但是按照官网的语法写一直不能成功跳过,很懵,最终还是选择全部文件都拷贝草草了事(反正都是精简的内部用)

其他的内容就没啥要管的了,注册表和安装界面那些现成的套用,改一下路径和名称就行了。全部内容:

;强制压缩,可以不用,速度较慢
;SetCompressor /SOLID LZMA
;SetCompressorDictSize 32
;SetCompress force
;是否做数据块优化,节约部分空间
SetDatablockOptimize on

; HM NIS Edit Wizard helper defines
!define DOLLAR "$"
!define PRODUCT_NAME $%exe_name_noexten%
!define "CHECK_NAME_${PRODUCT_NAME}"
!echo "${DOLLAR}%exe_name_noexten%"
!if ${PRODUCT_NAME} != "${DOLLAR}%exe_name_noexten%"
  !echo "exe_name_noexten:${PRODUCT_NAME}"
!else
  !echo CHECK_NAME_$%%exe_name_noexten%
  !error "exe_name_noexten not specified!"
!endif

!define PACKAGE_PATH $%app_path%

!define "CHECK_NAME_${PACKAGE_PATH}"
!if ${PACKAGE_PATH} != "${DOLLAR}%app_path%"
  !echo "app_path:${PACKAGE_PATH}"
!else
  !error "app_path not specified!"
!endif

!define PRODUCT_VERSION $%app_version%
!define "CHECK_NAME_${PRODUCT_VERSION}"
!if ${PRODUCT_VERSION} != "${DOLLAR}%app_version%"
  !echo "app_version:${PRODUCT_VERSION}"
!else
  !error "app_version not specified!"
!endif

!define RESOURCE_PATH $%exe_resource_path%
!define DIR_MNAME $%exe_name_noexten%

!define PRODUCT_PUBLISHER "INNFOS, Inc."
!define PRODUCT_WEB_SITE "http://www.innfos.com"
!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"

; MUI 1.67 compatible ------
!include "MUI.nsh"

; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\Classic-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"

; Welcome page
!insertmacro MUI_PAGE_WELCOME
; License page
!insertmacro MUI_PAGE_LICENSE "${PACKAGE_PATH}\license.txt"
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\${DIR_MNAME}\${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 "${PRODUCT_NAME}_Setup.exe"
;RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUninstDetails show
;RequestExecutionLevel admin

Section "MainSection" SEC01
  SetOutPath "$INSTDIR\${DIR_MNAME}"
  SetOverwrite ifnewer
  File "${PACKAGE_PATH}\*.dll"
  File /r "${PACKAGE_PATH}\*.exe"
  ;File /r "${PACKAGE_PATH}\*.conf"
  ;File /nonfatal "${PACKAGE_PATH}\*.xml"

  SetOutPath "$INSTDIR\resources"
  File /r "${RESOURCE_PATH}\resources\*"

  SetOutPath "$INSTDIR\${DIR_MNAME}\translations"
  File /r "${PACKAGE_PATH}\translations\*"

  SetOutPath "$INSTDIR\${DIR_MNAME}\plugins"
  File /r "${PACKAGE_PATH}\plugins\*"

  ;创建快捷方式
  SetOutPath $INSTDIR\${DIR_MNAME}
  CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
  CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.exe"
  CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.exe"
  ;File "${PACKAGE_PATH}\D3Dcompiler_47.dll"
  SetDetailsPrint textonly
  DetailPrint 'add input rule ..'
  ExecWait '"cmd.exe" /c netsh advfirewall firewall add rule name="${PRODUCT_NAME}" dir=in action=allow protocol=udp program="$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.exe"'
  DetailPrint 'install VC++ library...'
  ;SetOutPath $TEMP
  ;File "${PACKAGE_PATH}\vcredist_x64.exe"
  ;ExecWait '"$TEMP\vcredist_x64.exe" /Q'
  ;Delete "$TEMP\vcredist_x64.exe"
  ;DetailPrint 'install serial port driver...'
  ;SetOutPath $TEMP
  ;File "${PACKAGE_PATH}\driver.exe"
  ;ExecWait '"$TEMP\driver.exe" /Q'
  ;Delete "$TEMP\driver.exe"
  SetDetailsPrint both
SectionEnd

Section -AdditionalIcons
  WriteIniStr "$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"
  ;CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Website.lnk" "$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.url"
  CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\${DIR_MNAME}\uninst.exe"
SectionEnd

Section -Post
  WriteUninstaller "$INSTDIR\${DIR_MNAME}\uninst.exe"
  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\${DIR_MNAME}\uninst.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${DIR_MNAME}\${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
  Delete "$INSTDIR\${DIR_MNAME}\${PRODUCT_NAME}.url"
  Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk"
  Delete "$SMPROGRAMS\${PRODUCT_NAME}\Website.lnk"
  Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
  Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"

  RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
  RMDir /r "$INSTDIR"

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

3总结

这个脚本只能做一些简单的打包发布,对于文件的保护性不够,安装界面的自定义不够,版本的联动不够。对于nsis了解不多,不知道能不能做好这些,但是inno setup是可以做得很好的,包括权限的管理,电脑不同分辨率之类,但是脚本量和内容可能就多很多了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值