为了介绍如何使用CMake的CPack工具进行项目打包,这里使用了前文 CMake项目使用ctest+gtest进行单元测试中的示例。

为了更接近实际开发中项目的情况,自行下载gtest并进行源码编译来模拟实际项目中的依赖项;在实际的开发中,可能会有各种各样的配置文件需要在打包的时候一起发布出去,这里在根目录下创建一个bin目录(将所有项目生成的文件全部指定生成到此目录下的各个项目目录中),下面再创建一个demo目录(根据项目来取名),在demo目录中创建一个config.ini文件。

根目录的CMakeLists.txt如下:

 1cmake_minimum_required(VERSION 3.12.0)
 2project(demo VERSION 0.1.0)
 3
 4# 这里不管系统有没安装GTest都从网络上下载源码进行编译,使用自己编译的GTest
 5cmake_policy(SET CMP0135 NEW)
 6message("download GTest...")
 7include(FetchContent)
 8FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/heads/main.zip)
 9FetchContent_MakeAvailable(googletest)
10
11include(CTest)
12
13enable_testing()
14#设置编译后的可执行文件存放路径
15set(BIN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin)
16
17add_subdirectory(test)
18
19# 根据项目存放到以项目名命名的目录中
20set(EXECUTABLE_OUTPUT_PATH ${BIN_DIR}/${PROJECT_NAME})
21add_executable(${PROJECT_NAME} main.cpp)
22
23# 设置需要安装的文件
24install(DIRECTORY ${BIN_DIR}
25	DESTINATION ./
26	FILES_MATCHING
27	PATTERN "*.exe"
28	PATTERN "*.ini"
29)
30
31# 设置包的模式,可以是7Z、ZIP等等,参见cpack --help
32set(CPACK_GENERATOR 7Z)
33include(CPack)

为了让各个项目都将编译后的可执行文件按指定要求存放,需要在每个子项目中(project后)添加如下指令:set(EXECUTABLE_OUTPUT_PATH ${BIN_DIR}/${PROJECT_NAME})

本例中需要在test目录中修改CMakeLists.txt

1project(t)
2
3set(EXECUTABLE_OUTPUT_PATH ${BIN_DIR}/${PROJECT_NAME})
4
5add_executable(${PROJECT_NAME} test.cpp ../func.cpp)
6target_link_libraries(${PROJECT_NAME} PRIVATE gtest gtest_main)
7
8include(GoogleTest)
9gtest_discover_tests(t)

然后设置需要安装的文件,根据前面的设定,这里使用目录的方式设定安装文件:

1install(DIRECTORY ${BIN_DIR}
2	DESTINATION ./
3	FILES_MATCHING
4	PATTERN "*.exe"
5	PATTERN "*.ini"
6)

即将所有输出目录中的exe文件以及ini文件打包。

此时编译后的目录结构如下:

在build目录(有CMakeCache.txt的目录),运行cpack,即可得到打包后的文件,笔者的为demo-0.1.0-win64.7z

可以在build_CPack_Packages\win64\7Z\demo-0.1.0-win64查看打包的情况,这里有额外的include与lib,并不是想要的,是因为使用的GTest为自行编译的库,如果不想让它们出现在包中,可以在引入GTest之前设置INSTALL_GTESTOFF

现在很多项目都是使用的Git来管理项目,可以在打包的时候以Git的分支名时期时间,SHA组合的方式来命名包。

在根CMakeLists.txt中的install指令前添加如下指令:

 1string(TIMESTAMP vTimeStamp "%Y%m%d%H%M%S")
 2execute_process(
 3	COMMAND git log -1 --format=%h
 4	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 5	OUTPUT_VARIABLE vGitCommit
 6	OUTPUT_STRIP_TRAILING_WHITESPACE
 7)
 8
 9execute_process(
10	COMMAND git rev-parse --abbrev-ref HEAD
11	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
12	OUTPUT_VARIABLE vGitBranch
13	OUTPUT_STRIP_TRAILING_WHITESPACE
14)

include(CPack)前加上set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${vGitBranch}-${vTimeStamp}-${vGitCommit}")即可。

附上最终根CMakeLists.txt

 1cmake_minimum_required(VERSION 3.12.0)
 2project(demo VERSION 0.1.0)
 3
 4set(INSTALL_GTEST OFF)
 5cmake_policy(SET CMP0135 NEW)
 6message("GTest not found, download it...")
 7include(FetchContent)
 8FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/heads/main.zip)
 9FetchContent_MakeAvailable(googletest)
10include(CTest)
11
12enable_testing()
13
14set(BIN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin)
15
16add_subdirectory(test)
17
18set(EXECUTABLE_OUTPUT_PATH ${BIN_DIR}/${PROJECT_NAME})
19add_executable(${PROJECT_NAME} main.cpp)
20
21string(TIMESTAMP vTimeStamp "%Y%m%d%H%M%S")
22execute_process(
23	COMMAND git log -1 --format=%h
24	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
25	OUTPUT_VARIABLE vGitCommit
26	OUTPUT_STRIP_TRAILING_WHITESPACE
27)
28
29execute_process(
30	COMMAND git rev-parse --abbrev-ref HEAD
31	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
32	OUTPUT_VARIABLE vGitBranch
33	OUTPUT_STRIP_TRAILING_WHITESPACE
34)
35
36install(DIRECTORY ${BIN_DIR}
37	DESTINATION ./
38	FILES_MATCHING
39	PATTERN "*.exe"
40	PATTERN "*.ini"
41)
42
43set(CPACK_GENERATOR 7Z)
44set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${vGitBranch}-${vTimeStamp}-${vGitCommit}")
45include(CPack)