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

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

名称 文件类型 平台及说明
STGZ STGZ(.sh) 自解压文件,支持shell脚本的平台
7Z 7zip(.7z) 跨平台
ZIP ZIP(.zip) 跨平台
TGZ TGZ(.tar.gz) 跨平台
TXZ TXZ(.tar.xz) 跨平台
TBZ2 TBZ2(.tar.bz2) 跨平台
TZ TZ(.tar) 跨平台
TZST TZST(.tar.zst) 跨平台
RPM RPM(.rpm) Linux,用于redhat系产品
DEB DEB(.deb) Linux,用于Debian, ubuntu系列产品
DragNDrop DMG(.dmg) macOS
productbuild PKG(.pkg) macOS
Bundle Bundle(.bundle) macOS
NSIS Binary(.exe) Windows
INNOSETUP Binary(.exe) Windows
NuGet NuGet(.nupkg) Windows
WIX MSI(.msi) Windows
IFW Binary Linux, Windows, macOS,使用QtIFW编译器生成
External JSON(.json) 与外部打包工具集成
FreeBSD PKG(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下使用下面的命令安装:

1sudo apt install rpm

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

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

1 yum install rpm-build

CMakeLists.txt设置

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

2. DEB

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

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

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

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

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

1set(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

1#include <iostream>
2using namespace std;
3
4void foo() {
5    cout << "foo" << endl;
6}

lib/lib.h

1#ifndef __LIB__H__
2#define __LIB__H__
3
4#include "priv/priv.h"
5
6void foo();
7
8#endif //__LIB__H__

lib/priv/priv.h

1#pragma once
2
3#ifndef __LIB__H__
4#error 私有头文件不能直接包含,需要include `lib.h`
5#endif
6
7// 这是私有头文件

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

lib/CMakeLists.txt

 1cmake_minimum_required(VERSION 3.25.0)
 2project(tlib VERSION 0.1.0)
 3
 4add_library(${PROJECT_NAME} SHARED lib.cc)
 5add_library(${PROJECT_NAME}s lib.cc)
 6
 7#设置库的公开头文件,注意,这里一定要加上${CMAKE_CURRENT_SOURCE_DIR},不然安装时会找不到文件
 8set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/lib.h)
 9#设置库的私有头文件
10set_target_properties(${PROJECT_NAME} PROPERTIES PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/priv/priv.h)

main.cc

1#include <stdio.h>
2#include <stdlib.h>
3#include "lib.h"
4
5int main(int argc, char *argv[])
6{
7    foo();
8    return 0;
9}

CMakeLists.txt

 1cmake_minimum_required(VERSION 3.25.0)
 2project(t VERSION 0.1.0)
 3
 4add_subdirectory(lib)
 5aux_source_directory(. SRC)
 6
 7add_executable(${PROJECT_NAME} ${SRC})
 8target_include_directories(${PROJECT_NAME} PUBLIC lib)
 9target_link_libraries(${PROJECT_NAME} tlib)
10
11#设置安装规则
12install(TARGETS ${PROJECT_NAME} tlib tlibs
13	ARCHIVE DESTINATION ${PROJECT_NAME}/lib #静态库,macOS中标记为“FRAMEWORK”除外
14	LIBRARY DESTINATION ${PROJECT_NAME}/lib #动态库,macOS中标记为“FRAMEWORK”除外
15	RUNTIME DESTINATION ${PROJECT_NAME}/bin #可执行文件,macOS中标记为“MACOSX_BUNDLE”除外
16	FRAMEWORK DESTINATION ${PROJECT_NAME}/framework # 在 macOS上,标有“FRAMEWORK”属性的静态库和共享库都被视为“FRAMEWORK”目标。
17	PUBLIC_HEADER DESTINATION ${PROJECT_NAME}/include # 与库关联的任何公开头文件
18	PRIVATE_HEADER DESTINATION ${PROJECT_NAME}/include/priv # 与库关联的任何私有头文件
19)

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

在这里插入图片描述

1. dmg

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

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

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

拖曳安装好后,执行:

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

完整的CMakeLists.txt

 1cmake_minimum_required(VERSION 3.25.0)
 2project(t VERSION 0.1.0)
 3
 4add_subdirectory(lib)
 5
 6aux_source_directory(. SRC)
 7
 8add_executable(${PROJECT_NAME} ${SRC})
 9target_include_directories(${PROJECT_NAME} PUBLIC lib)
10target_link_libraries(${PROJECT_NAME} tlib)
11
12#设置安装规则
13install(TARGETS ${PROJECT_NAME} tlib tlibs
14	ARCHIVE DESTINATION ${PROJECT_NAME}/lib #静态库,macOS中标记为“FRAMEWORK”除外
15	LIBRARY DESTINATION ${PROJECT_NAME}/lib #动态库,macOS中标记为“FRAMEWORK”除外
16	RUNTIME DESTINATION ${PROJECT_NAME}/bin #可执行文件,macOS中标记为“MACOSX_BUNDLE”除外
17	FRAMEWORK DESTINATION ${PROJECT_NAME}/framework # 在 macOS上,标有“FRAMEWORK”属性的静态库和共享库都被视为“FRAMEWORK”目标。
18	PUBLIC_HEADER DESTINATION ${PROJECT_NAME}/include # 与库关联的任何公开头文件
19	PRIVATE_HEADER DESTINATION ${PROJECT_NAME}/include/priv # 与库关联的任何私有头文件
20)
21# 需要设置`${PROJECT_NAME}`目标的属性`INSTALL_RPATH`为`@loader_path/../lib`,
22# 即安装后的运行时路径为`@loader_path/../lib`,@loader_path是重点,
23# 是相对于可执行文件的路径,否则会出现找不到`libtlib.dylib`库。
24# 可以设置多个路径,在后面直接添加即可,比如再添加`/usr/local/lib`
25set_property(
26	TARGET ${PROJECT_NAME}
27	PROPERTY
28	INSTALL_RPATH
29	"@loader_path/../lib"
30	"/usr/local/lib"
31)
32
33# 设置CPACK的生成器类型为`DragNDrop`
34set(CPACK_GENERATOR DragNDrop)
35#设置CPack的项目名
36set(CPACK_PROJECT_NAME ${PROJECT_NAME})
37#设置CPack的项目版本号
38set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
39#必须包含CPack
40include(CPack)

2. pkg

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

完整的CMakeLists.txt

 1cmake_minimum_required(VERSION 3.25.0)
 2project(t VERSION 0.1.0)
 3
 4add_subdirectory(lib)
 5
 6aux_source_directory(. SRC)
 7
 8add_executable(${PROJECT_NAME} ${SRC})
 9target_include_directories(${PROJECT_NAME} PUBLIC lib)
10target_link_libraries(${PROJECT_NAME} tlib)
11
12#设置安装规则
13install(TARGETS ${PROJECT_NAME} tlib tlibs
14	ARCHIVE DESTINATION ${PROJECT_NAME}/lib #静态库,macOS中标记为“FRAMEWORK”除外
15	LIBRARY DESTINATION ${PROJECT_NAME}/lib #动态库,macOS中标记为“FRAMEWORK”除外
16	RUNTIME DESTINATION ${PROJECT_NAME}/bin #可执行文件,macOS中标记为“MACOSX_BUNDLE”除外
17	FRAMEWORK DESTINATION ${PROJECT_NAME}/framework # 在 macOS上,标有“FRAMEWORK”属性的静态库和共享库都被视为“FRAMEWORK”目标。
18	PUBLIC_HEADER DESTINATION ${PROJECT_NAME}/include # 与库关联的任何公开头文件
19	PRIVATE_HEADER DESTINATION ${PROJECT_NAME}/include/priv # 与库关联的任何私有头文件
20)
21# 需要设置`${PROJECT_NAME}`目标的属性`INSTALL_RPATH`为`@loader_path/../lib`,
22# 即安装后的运行时路径为`@loader_path/../lib`,@loader_path是重点,
23# 是相对于可执行文件的路径,否则会出现找不到`libtlib.dylib`库。
24# 可以设置多个路径,在后面直接添加即可,比如再添加`/usr/local/lib`
25set_property(
26	TARGET ${PROJECT_NAME}
27	PROPERTY
28	INSTALL_RPATH
29	"@loader_path/../lib"
30	"/usr/local/lib"
31)
32
33# 设置CPACK的生成器类型为`productbuild`
34set(CPACK_GENERATOR productbuild)
35#设置CPack的项目名
36set(CPACK_PROJECT_NAME ${PROJECT_NAME})
37#设置CPack的项目版本号
38set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
39#必须包含CPack
40include(CPack)
41#这里必须使用cpack_add_component命令添加组件,否则只会生成一个空包
42cpack_add_component(${PROJECT_NAME} REQUIRED)

安装情况如下所示:

在这里插入图片描述

在这里插入图片描述

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

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

在这里插入图片描述

welcome.html

1<html>
2	<head>
3		<meta charset="utf-8">
4	</head>
5	<body>
6		<h1>欢迎使用macOS软件t</h1>
7	</body>
8</html>

readme.html

1<html>
2	<head>
3		<meta charset="utf-8">
4	</head>
5	<body>
6		<h1>读一下我吧!</h1>
7	</body>
8</html>

license.html

1<html>
2	<head>
3		<meta charset="utf-8">
4	</head>
5	<body>
6		<h1>版权所有(C),Witton Bell</h1>
7	</body>
8</html>

CMakeLists.txt设置:

 1set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_SOURCE_DIR}/res/welcome.html")
 2set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/res/license.html")
 3set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/res/readMe.html")
 4
 5# 设置CPACK的生成器类型为`productbuild`
 6set(CPACK_GENERATOR productbuild)
 7#设置CPack的项目名
 8set(CPACK_PROJECT_NAME ${PROJECT_NAME})
 9#设置CPack的项目版本号
10set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
11#必须包含CPack
12include(CPack)
13cpack_add_component(${PROJECT_NAME} REQUIRED)

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、Windows

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

CMakeLists.txt设置:

 1set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/res/license.txt")
 2
 3# 设置CPACK的生成器类型为`NSIS64`
 4set(CPACK_GENERATOR NSIS64)
 5#设置CPack的项目名
 6set(CPACK_PROJECT_NAME ${PROJECT_NAME})
 7#设置CPack的项目版本号
 8set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
 9#必须包含CPack
10include(CPack)

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

在这里插入图片描述

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

四、自解压安装包

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

 1#设置许可证文本的文件,而不是内容
 2set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/res/license.txt")
 3# 设置CPACK的生成器类型
 4set(CPACK_GENERATOR "STGZ")
 5#设置CPack的项目名
 6set(CPACK_PROJECT_NAME ${PROJECT_NAME})
 7#设置CPack的项目版本号
 8set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
 9set(CPACK_PACKAGE_VENDOR "Witton Bell")
10#必须包含CPack
11include(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模板文件的内容。

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