使用CPack+WiX制作MSI:超2GB安装包拆分多CAB及DiskId分配方法
解决CPack+WiX拆分MSI CAB文件的DiskId分配问题
要给CMake中安装的文件指定DiskId从而拆分到不同CAB包,CPack WiX本身没有直接的内置变量,但可以通过自定义属性+XSLT补丁或者路径匹配XSLT补丁两种方式实现,下面是具体步骤:
方法一:通过INSTALL属性传递自定义标识(推荐,灵活可控)
这种方法可以在CMake层面为不同文件/目录标记对应的DiskId,再通过WiX补丁自动映射。
1. 修改CMakeLists.txt,添加自定义属性
给需要拆分的文件/目录添加CPACK_WIX_DISK_ID自定义属性,对应你在WiX补丁中定义的Media Id:
cmake_minimum_required(VERSION 3.14) project(content 1.0.0 LANGUAGES CXX) # 给video目录下的视频文件标记DiskId=2 install(DIRECTORY video DESTINATION "." FILES_MATCHING REGEX ".+\.(avi|mp4)" PROPERTIES CPACK_WIX_DISK_ID "2") # 给images目录下的图片文件标记DiskId=3 install(DIRECTORY images DESTINATION "." FILES_MATCHING PATTERN "*.png" PROPERTIES CPACK_WIX_DISK_ID "3") set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/misc/wix_patch.xml") include(CPack)
2. 更新wix_patch.xml,添加XSLT补丁处理属性
用XSLT读取CMake传递的自定义属性,为WiX的<File>元素添加DiskId,同时移除自定义属性避免WiX报错:
<CPackWiXPatch> <CPackWiXFragment Id="#PRODUCT"> <Media Id='2' Cabinet='package1.cab' EmbedCab='no'/> <Media Id='3' Cabinet='package2.cab' EmbedCab='no'/> </CPackWiXFragment> <!-- XSLT补丁:将自定义属性转换为DiskId --> <CPackWiXXslPatch> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"> <!-- 匹配带有自定义属性的File元素 --> <xsl:template match="wix:File[@CPACK_WIX_DISK_ID]"> <xsl:copy> <!-- 复制原有所有属性 --> <xsl:apply-templates select="@*"/> <!-- 添加DiskId属性,值来自自定义标识 --> <xsl:attribute name="DiskId"> <xsl:value-of select="@CPACK_WIX_DISK_ID"/> </xsl:attribute> <!-- 移除自定义属性,防止WiX解析报错 --> <xsl:attribute name="CPACK_WIX_DISK_ID" xmlns="" /> </xsl:copy> </xsl:template> <!-- 保留其他所有元素和属性不变 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> </CPackWiXXslPatch> </CPackWiXPatch>
方法二:直接通过文件路径匹配(无需修改CMake)
如果你的文件路径规则明确,可以跳过CMake属性设置,直接在WiX补丁中用XPath匹配文件路径,分配对应的DiskId:
<CPackWiXPatch> <CPackWiXFragment Id="#PRODUCT"> <Media Id='2' Cabinet='package1.cab' EmbedCab='no'/> <Media Id='3' Cabinet='package2.cab' EmbedCab='no'/> </CPackWiXFragment> <CPackWiXXslPatch> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"> <!-- 匹配video目录下的avi/mp4文件,分配DiskId=2 --> <xsl:template match="wix:File[contains(@Source, 'video/') and (ends-with(@Source, '.avi') or ends-with(@Source, '.mp4'))]"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="DiskId">2</xsl:attribute> </xsl:copy> </xsl:template> <!-- 匹配images目录下的png文件,分配DiskId=3 --> <xsl:template match="wix:File[contains(@Source, 'images/') and ends-with(@Source, '.png')]"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:attribute name="DiskId">3</xsl:attribute> </xsl:copy> </xsl:template> <!-- 保留其他元素不变 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> </CPackWiXXslPatch> </CPackWiXPatch>
关键注意事项
- 确保
Media元素的Id和你设置的DiskId完全对应,比如Media Id='2'对应DiskId="2" - 必须设置
EmbedCab='no',拆分的CAB文件不能嵌入到MSI中,会作为独立的外部文件生成 - 如果存在单个文件超过2GB的情况,这种拆分方法也无法解决,需要将该文件单独分配一个
Media节点 - 可以通过
CPACK_WIX_DEBUG=1参数生成MSI时保留中间WiX文件,检查<File>元素的DiskId是否正确设置
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

