CMake使用CPack制作安装程序

CPack的功能很强大,笔者前面有一博文使用CMake的CPack工具打包项目介绍了一下使用CPack来打包成7z压缩文件,不仅如此,它还可以生成各平台的安装包。

CPack支持以下类型的生成器:

名称文件类型平台及说明
STGZSTGZ(.sh)自解压文件,支持shell脚本的平台
7Z7zip(.7z)跨平台
ZIPZIP(.zip)跨平台
TGZTGZ(.tar.gz)跨平台
TXZTXZ(.tar.xz)跨平台
TBZ2TBZ2(.tar.bz2)跨平台
TZTZ(.tar)跨平台
TZSTTZST(.tar.zst)跨平台
RPMRPM(.rpm)Linux,用于redhat系产品
DEBDEB(.deb)Linux,用于Debian, ubuntu系列产品
DragNDropDMG(.dmg)macOS
productbuildPKG(.pkg)macOS
BundleBundle(.bundle)macOS
NSISBinary(.exe)Windows
INNOSETUPBinary(.exe)Windows
NuGetNuGet(.nupkg)Windows
WIXMSI(.msi)Windows
IFWBinaryLinux, Windows, macOS,使用QtIFW编译器生成
ExternalJSON(.json)与外部打包工具集成
FreeBSDPKG(pkg)BSD,Linux, OSX

本文就介绍一下如何使用CPack来制作各个主流平台的应用程序安装程序。

关于CPack的知识,可以参考https://cmake.org/cmake/help/latest/module/CPack.html

一、Linux

1. RPM

RPM安装包格式适用于red hat出品的系统RHEL以及centos

安装rpmbuild

要制作RPM包,必须要先安装rpmbuild命令。

ubuntu下使用下面的命令安装:

sudo apt install rpm

安装后既可以使用rpm命令,也可以使用rpmbuild命令。

如果是CentOS系统,默认是有rpm命令的,需要使用下面的命令单独安装rpmbuild

 yum install rpm-build

CMakeLists.txt设置

#设置安装规则
install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin)
#设置CPack生成器为RPM
set(CPACK_GENERATOR "RPM")
#设置CPack项目名称
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack项目版本
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack的cmake文件
include(CPack)

2. DEB

DEB格式适用于Debian以及Ubuntu系统。CMakeLists.txt设置:

#设置安装规则
install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin)
# DEB要求设置`CPACK_PACKAGE_CONTACT`或者`CPACK_DEBIAN_PACKAGE_MAINTAINER`变量
set(CPACK_PACKAGE_CONTACT "witton@163.com")
#设置CPack生成器为RPM
set(CPACK_GENERATOR "DEB")
#设置CPack项目名称
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack项目版本
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack的cmake文件
include(CPack)

生成好DEB格式后,可以使用apt命令安装:

sudo apt install ./demo-0.1.0-Linux.deb

如果想要生成多种安装包格式,可以在CPACK_GENERATOR变量中填写多个,以分号隔开:

set(CPACK_GENERATOR "DEB;RPM")

二、MacOS

macOS下制作安装包,稍麻烦一点,最主要的问题是动态库的搜索路径问题,在Linux下可以使用ldconfig进行配置,但是MacOS下没有这样的工具,虽然有环境变量DYLD_LIBRARY_PATHDYLD_FALLBACK_LIBRARY_PATH可以设置,但是如果生成程序的时候rpath没有设置为@rpath开头的路径,而是使用的绝对路径,就是灾难。

所以在生成程序时,最好是设置程序的动态库路径为@rpath开头的路径。有关MacOS程序的@rpath@loader_path@executable_path可以在网络上搜索相关资料。

这里我们以一个实例来讲,更好理解。下面为t项目结构:

在这里插入图片描述

有一个lib库,提供了一个函数foo,供main.cc调用,lib.hpriv.h分别为lib库的公开头文件与私有头文件。

lib/lib.cc

#include <iostream>
using namespace std;

void foo() {
    cout << "foo" << endl;
}

lib/lib.h

#ifndef __LIB__H__
#define __LIB__H__

#include "priv/priv.h"

void foo();

#endif //__LIB__H__

lib/priv/priv.h

#pragma once

#ifndef __LIB__H__
#error 私有头文件不能直接包含,需要include `lib.h`
#endif

// 这是私有头文件

为了展示动态库和静态的安装,这里把lib库,既编译成动态库,也编译成静态库:

lib/CMakeLists.txt

cmake_minimum_required(VERSION 3.25.0)
project(tlib VERSION 0.1.0)

add_library(${PROJECT_NAME} SHARED lib.cc)
add_library(${PROJECT_NAME}s lib.cc)

#设置库的公开头文件,注意,这里一定要加上${CMAKE_CURRENT_SOURCE_DIR},不然安装时会找不到文件
set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/lib.h)
#设置库的私有头文件
set_target_properties(${PROJECT_NAME} PROPERTIES PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/priv/priv.h)

main.cc

#include <stdio.h>
#include <stdlib.h>
#include "lib.h"

int main(int argc, char *argv[])
{
    foo();
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.25.0)
project(t VERSION 0.1.0)

add_subdirectory(lib)
aux_source_directory(. SRC)

add_executable(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} PUBLIC lib)
target_link_libraries(${PROJECT_NAME} tlib)

#设置安装规则
install(TARGETS ${PROJECT_NAME} tlib tlibs
	ARCHIVE DESTINATION ${PROJECT_NAME}/lib #静态库,macOS中标记为“FRAMEWORK”除外
	LIBRARY DESTINATION ${PROJECT_NAME}/lib #动态库,macOS中标记为“FRAMEWORK”除外
	RUNTIME DESTINATION ${PROJECT_NAME}/bin #可执行文件,macOS中标记为“MACOSX_BUNDLE”除外
	FRAMEWORK DESTINATION ${PROJECT_NAME}/framework # 在 macOS上,标有“FRAMEWORK”属性的静态库和共享库都被视为“FRAMEWORK”目标。
	PUBLIC_HEADER DESTINATION ${PROJECT_NAME}/include # 与库关联的任何公开头文件
	PRIVATE_HEADER DESTINATION ${PROJECT_NAME}/include/priv # 与库关联的任何私有头文件
)

我们期望最终制作的安装包,安装后的结构:

在这里插入图片描述

1. dmg

为了制作dmg格式的安装包,CMakeLists.txt文件中添加

# 需要设置`${PROJECT_NAME}`目标的属性`INSTALL_RPATH`为`@loader_path/../lib`,
# 即安装后的运行时路径为`@loader_path/../lib`,@loader_path是重点,
# 是相对于可执行文件的路径,否则会出现找不到`libtlib.dylib`库。
# 可以设置多个路径,在后面直接添加即可,比如再添加`/usr/local/lib`
set_property(
	TARGET ${PROJECT_NAME}
	PROPERTY
	INSTALL_RPATH
	"@loader_path/../lib"
	"/usr/local/lib"
)
# 设置CPACK的生成器类型为`DragNDrop`
set(CPACK_GENERATOR DragNDrop)
#设置CPack的项目名
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack的项目版本号
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack
include(CPack)

执行CPack打包命令后,会生成t-0.1.0-Darwin.dmg安装包,在MacOS系统中双击加载后好即可拖曳安装了,与其它DMG安装包一样。
在这里插入图片描述

拖曳安装好后,执行:

在这里插入图片描述

使用otool -L ./t命令来查看依赖:

在这里插入图片描述

使用otool -l ./t命令来查看rpath

在这里插入图片描述

完整的CMakeLists.txt

cmake_minimum_required(VERSION 3.25.0)
project(t VERSION 0.1.0)

add_subdirectory(lib)

aux_source_directory(. SRC)

add_executable(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} PUBLIC lib)
target_link_libraries(${PROJECT_NAME} tlib)

#设置安装规则
install(TARGETS ${PROJECT_NAME} tlib tlibs
	ARCHIVE DESTINATION ${PROJECT_NAME}/lib #静态库,macOS中标记为“FRAMEWORK”除外
	LIBRARY DESTINATION ${PROJECT_NAME}/lib #动态库,macOS中标记为“FRAMEWORK”除外
	RUNTIME DESTINATION ${PROJECT_NAME}/bin #可执行文件,macOS中标记为“MACOSX_BUNDLE”除外
	FRAMEWORK DESTINATION ${PROJECT_NAME}/framework # 在 macOS上,标有“FRAMEWORK”属性的静态库和共享库都被视为“FRAMEWORK”目标。
	PUBLIC_HEADER DESTINATION ${PROJECT_NAME}/include # 与库关联的任何公开头文件
	PRIVATE_HEADER DESTINATION ${PROJECT_NAME}/include/priv # 与库关联的任何私有头文件
)
# 需要设置`${PROJECT_NAME}`目标的属性`INSTALL_RPATH`为`@loader_path/../lib`,
# 即安装后的运行时路径为`@loader_path/../lib`,@loader_path是重点,
# 是相对于可执行文件的路径,否则会出现找不到`libtlib.dylib`库。
# 可以设置多个路径,在后面直接添加即可,比如再添加`/usr/local/lib`
set_property(
	TARGET ${PROJECT_NAME}
	PROPERTY
	INSTALL_RPATH
	"@loader_path/../lib"
	"/usr/local/lib"
)

# 设置CPACK的生成器类型为`DragNDrop`
set(CPACK_GENERATOR DragNDrop)
#设置CPack的项目名
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack的项目版本号
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack
include(CPack)

2. pkg

CPack生成器productbuild可以生成macOS下的pkg安装包。
将前面的CPACK_GENERATOR 改为productbuild,再使用cpack_add_component(${PROJECT_NAME} REQUIRED)添加进组件即可生成一个简单的安装包,如果没有使用cpack_add_component(${PROJECT_NAME} REQUIRED)添加组件,只会生成一个空包。

完整的CMakeLists.txt

cmake_minimum_required(VERSION 3.25.0)
project(t VERSION 0.1.0)

add_subdirectory(lib)

aux_source_directory(. SRC)

add_executable(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} PUBLIC lib)
target_link_libraries(${PROJECT_NAME} tlib)

#设置安装规则
install(TARGETS ${PROJECT_NAME} tlib tlibs
	ARCHIVE DESTINATION ${PROJECT_NAME}/lib #静态库,macOS中标记为“FRAMEWORK”除外
	LIBRARY DESTINATION ${PROJECT_NAME}/lib #动态库,macOS中标记为“FRAMEWORK”除外
	RUNTIME DESTINATION ${PROJECT_NAME}/bin #可执行文件,macOS中标记为“MACOSX_BUNDLE”除外
	FRAMEWORK DESTINATION ${PROJECT_NAME}/framework # 在 macOS上,标有“FRAMEWORK”属性的静态库和共享库都被视为“FRAMEWORK”目标。
	PUBLIC_HEADER DESTINATION ${PROJECT_NAME}/include # 与库关联的任何公开头文件
	PRIVATE_HEADER DESTINATION ${PROJECT_NAME}/include/priv # 与库关联的任何私有头文件
)
# 需要设置`${PROJECT_NAME}`目标的属性`INSTALL_RPATH`为`@loader_path/../lib`,
# 即安装后的运行时路径为`@loader_path/../lib`,@loader_path是重点,
# 是相对于可执行文件的路径,否则会出现找不到`libtlib.dylib`库。
# 可以设置多个路径,在后面直接添加即可,比如再添加`/usr/local/lib`
set_property(
	TARGET ${PROJECT_NAME}
	PROPERTY
	INSTALL_RPATH
	"@loader_path/../lib"
	"/usr/local/lib"
)

# 设置CPACK的生成器类型为`productbuild`
set(CPACK_GENERATOR productbuild)
#设置CPack的项目名
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack的项目版本号
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack
include(CPack)
#这里必须使用cpack_add_component命令添加组件,否则只会生成一个空包
cpack_add_component(${PROJECT_NAME} REQUIRED)

安装情况如下所示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

默认情况下,介绍请先阅读许可三个页面的内容是CPack的模板内容,是三个txt文本文件,需要自定义一下,可以使用html文件。在项目根目录添加一个res目录,把所有自定义的CPack安装模板放在里面,现在目录树:

在这里插入图片描述

welcome.html

<html>
	<head>
		<meta charset="utf-8">
	</head>
	<body>
		<h1>欢迎使用macOS软件t</h1>
	</body>
</html>

readme.html

<html>
	<head>
		<meta charset="utf-8">
	</head>
	<body>
		<h1>读一下我吧!</h1>
	</body>
</html>

license.html

<html>
	<head>
		<meta charset="utf-8">
	</head>
	<body>
		<h1>版权所有(C),Witton Bell</h1>
	</body>
</html>

CMakeLists.txt设置:

set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_SOURCE_DIR}/res/welcome.html")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/res/license.html")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/res/readMe.html")

# 设置CPACK的生成器类型为`productbuild`
set(CPACK_GENERATOR productbuild)
#设置CPack的项目名
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack的项目版本号
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack
include(CPack)
cpack_add_component(${PROJECT_NAME} REQUIRED)

现在生成的安装包,运行如下所示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、Windows

Window下介绍一下NSIS/NSIS64,它是一个开源的安装包制作工具。
使用nsis软件来制作安装包,需要先下载nsis并安装。
设置CPack的生成器为NSIS或者NSIS64,该安装程序的许可证是UTF16编码格式的文本。

CMakeLists.txt设置:

set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/res/license.txt")

# 设置CPACK的生成器类型为`NSIS64`
set(CPACK_GENERATOR NSIS64)
#设置CPack的项目名
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack的项目版本号
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
#必须包含CPack
include(CPack)

它有一些特殊的变量,可以自定义安装界面的一些显示,具体参见:https://cmake-doc.readthedocs.io/zh-cn/latest/cpack_gen/nsis.html

在这里插入图片描述

NSIS软件可以做很多定制,感兴趣的读者可以继续深究!

四、自解压安装包

自解压安装包是使用的shell脚本作为安装程序的,它的CMakeLists.txt设置:

#设置许可证文本的文件,而不是内容
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/res/license.txt")
# 设置CPACK的生成器类型
set(CPACK_GENERATOR "STGZ")
#设置CPack的项目名
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
#设置CPack的项目版本号
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_VENDOR "Witton Bell")
#必须包含CPack
include(CPack)

实际上它是根据CMake安装目录下share/cmake/Modules/Internal/CPack/CPack.STGZ_Header.sh.in的模板文件来生成的。

它主要可以定制以下变量:

  • CPACK_PACKAGE_NAME:包名
  • CPACK_PACKAGE_VERSION:包版本
  • CPACK_PACKAGE_VENDOR:包所有者
  • CPACK_RESOURCE_FILE_LICENSE_CONTENT:直接设置许可证内容

在这里插入图片描述

如果想要定制更多内容,可以修改CPack.STGZ_Header.sh.in模板文件的内容。

如果对你有帮助,欢迎点赞收藏!

<think>好的,我需要帮用户解决在Ubuntu下使用Qt和CMake开发程序后的打包问题。首先,用户的需求是将他们的应用程序打包,以便于分发和部署。我需要考虑Qt程序打包时常见的依赖问题,尤其是动态链接库的处理。 首先,我应该回忆一下Qt程序在Linux下的打包方法。通常,直接编译后的可执行文件依赖系统的Qt库,如果目标机器没有安装相应版本的库,程序就无法运行。因此,打包的关键在于包含所有必要的依赖项。 用户使用的是CMake,这可能意味着他们的项目结构比较规范,但CMake本身并不处理打包,所以需要借助其他工具。常见的工具有linuxdeployqt,这是一个将Qt程序打包成AppImage的工具,或者生成包含所有依赖的目录。这可能是主要的解决方案。 接下来,我需要分步骤说明如何使用linuxdeployqt。首先,用户需要确保程序已经编译成功,并且有一个可执行文件。然后下载linuxdeployqt,可能需要从GitHub获取。然后运行这个工具,指定可执行文件,它会自动收集所需的Qt库、插件和其他依赖项,生成一个AppImage或者包含所有文件的目录。 另外,可能需要提到使用CMake的install命令来整理安装目录结构,方便打包。比如设置安装路径,把头文件、可执行文件、资源文件等组织好,这样在打包时可以直接处理这个安装目录。 还有,可能需要考虑其他打包方式,比如制作DEB包。这时候需要工具如dpkg-buildpackage或者CPack。用户如果希望生成DEB包,可以配置CPack来简化流程。需要写一些CMake的配置,包括设置包信息、依赖等,然后运行CPack生成DEB文件。 另外,可能存在的注意事项,比如处理非Qt的依赖库,可能需要手动添加这些库到打包目录中。或者使用ldd检查可执行文件的依赖,确保所有动态库都被包含进去。 还有需要提到的是,测试打包后的程序是否可以在其他机器上运行。用户可能需要一个没有安装开发环境的系统来测试,或者使用Docker容器进行验证。 总结一下,步骤大致如下: 1. 使用CMake构建项目,生成可执行文件。 2. 安装linuxdeployqt工具。 3. 运行linuxdeployqt处理可执行文件,生成AppImage或目录。 4. 或者配置CPack生成DEB包。 5. 处理额外的依赖库。 6. 测试打包结果。 需要注意的常见问题:Qt插件如平台插件(platforms)、图像格式插件(imageformats)等是否被正确打包,否则程序可能无法启动。此外,确保翻译文件(qm)和资源文件也被包含进去。 可能用户对步骤的具体命令不太清楚,所以需要给出示例命令,比如如何运行linuxdeployqt,如何配置CPack等。同时提醒环境变量可能需要设置,比如QT_PLUGIN_PATH等。 最后,检查是否有遗漏的步骤,比如调试符号的处理,或者是否需要剥离符号以减小体积,但用户可能更关注基本的打包流程,这些高级步骤可以暂时不提,除非用户有特别需求。</think>在Ubuntu下使用Qt和CMake开发程序后,打包需解决依赖和分发问题。以下是分步指南: --- ### **一、基础打包方法(生成独立目录)** #### 1. 编译Release版本 ```bash mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j4 ``` #### 2. 安装linuxdeployqt工具 ```bash wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage chmod +x linuxdeployqt-continuous-x86_64.AppImage sudo mv linuxdeployqt-continuous-x86_64.AppImage /usr/local/bin/linuxdeployqt ``` #### 3. 收集依赖并打包 假设可执行文件为`myapp`: ```bash # 创建打包目录 mkdir -p pkg/usr/bin cp myapp pkg/usr/bin/ # 运行linuxdeployqt(自动复制Qt库和插件) linuxdeployqt pkg/usr/bin/myapp -appimage -always-overwrite # 生成独立目录(位于当前目录) mv pkg myapp_dir ``` --- ### **二、生成AppImage** #### 1. 添加.desktop文件 在`pkg/usr/share/applications`目录下创建`myapp.desktop`: ```ini [Desktop Entry] Name=MyApp Exec=myapp Icon=myapp-icon Type=Application Categories=Utility; ``` #### 2. 运行打包命令 ```bash linuxdeployqt pkg/usr/bin/myapp -appimage ``` --- ### **三、使用CPack生成DEB包** #### 1. 配置CMakeLists.txt 添加以下内容到`CMakeLists.txt`: ```cmake # 设置安装路径 install(TARGETS myapp DESTINATION bin) install(FILES myapp.desktop DESTINATION share/applications) install(FILES myapp.png DESTINATION share/icons/hicolor/48x48/apps) # 配置CPack set(CPACK_PACKAGE_NAME "myapp") set(CPACK_PACKAGE_VERSION "1.0.0") set(CPACK_PACKAGE_CONTACT "your-email@example.com") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5core5a, libqt5gui5, libqt5widgets5") include(CPack) ``` #### 2. 生成DEB包 ```bash cd build cpack -G DEB ``` --- ### **四、手动处理依赖** 若工具未自动包含某些库: 1. 使用`ldd`检查依赖: ```bash ldd myapp | grep "not found" ``` 2. 手动复制缺失的库到打包目录的`lib`子目录。 --- ### **注意事项** 1. **Qt插件**:确保`platforms`、`imageformats`等插件被复制到`plugins`目录。 2. **测试**:在纯净虚拟机或Docker中测试打包结果。 3. **图标问题**:提供`.desktop`文件和图标,确保桌面环境正常显示。 --- 通过上述步骤,可生成独立运行的目录、AppImage或DEB安装包,适应不同分发需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值