文章目录
- 安装与配置
- 编译单个源文件
- 编译前的准备
- 开始编译
- 编译多个源文件
- 多个源文件在同一目录下
- 多个源文件在不同目录下
- math 目录下的 CMakeLists.txt
- 根目录的 CMakeLists.txt
- option 选项
- 导入外部库
- 本地导入(find_package)
- 外部导入(FetchContent)
- 安装与测试
- 安装
- 测试
- 生成安装包
安装与配置
- 进入 官网下载
dmg
文件:
(在红圈中根据自己的Mac版本任选一个 dmg
下载)
-
安装完成后,从菜单栏选择:Tools–How to Install For Command Line Use
-
在终端命令行中输入弹出弹窗中第二项
to install symlinks to '/usr/local/bin', run:
的指令:
-
检查是否配置成功:
cmake --version
可以正常识别,配置成功。
编译单个源文件
编译前的准备
使用 CMake 进行编译之前需要具备三大内容:
他们的作用及内容如下:
源文件
main.cpp
#include <iostream>int main(int argc, const char * argv[]) {// insert code here...std::cout << "Hello, World!\n";return 0;
}
cmake 构建规则
CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
# cmake最低版本需求,不加入此行会受到警告信息PROJECT(HELLO)
# 项目名称,同时会自动生成 PROJECT_NAME 变量,
# 使用${PROJECT_NAME} 即可访问到 hello_cmake。AUX_SOURCE_DIRECTORY(. SRC_LIST)
# 把当前目录(.)下所有源代码文件和头文件加入变量SRC_LISTADD_EXECUTABLE(hello ${SRC_LIST}) # 生成应用程序 hello
在编译单个文件时,也可以不添加第三句规则,而是直接在第四句规则中指定编译的文件,例如,删除第三句,并将第四句改为如下内容,本编译规则依然有效:
ADD_EXECUTABLE(hello main.cpp) #生成应用程序 hello
AUX_SOURCE_DIRECTORY
:
- 第一个参数是目录的路径
- 第二个参数是变量名。当我们使用这个命令时,就会将指定目录下的所有源文件保存到指定的变量名中。
- 这里将名为
main.cpp
的源文件编译成一个名称为hello
的可执行文件。
保存临时构建文件和目标文件的文件夹
build
。
- 本示例中,该步骤本质上是创建一个可以位于文件系统上任何位置的构建文件夹(这里只是选择了在当前文件夹中创建)。 所有临时构建和目标文件都位于此目录中,以保持源代码树的整洁。该种构建方式为外部构建。
- 其实也可以没有这个文件夹,而是以内部构建的形式直接在源文件目录构建项目,只是会导致临时文件和源代码放在一起,不好清理。
开始编译
- 通过
cmake ..
命令开始进行编译:
- 使用
cmake
生成的makefile
编译得到可执行文件
- 此时在当前目录下,就会生成可执行文件
hello
。
- 将其运行查看是否成功编译:
编译多个源文件
多个源文件在同一目录下
与编译单个源文件相比,难点主要是对 AUX_SOURCE_DIRECTORY 命令的使用。
如果不想使用 AUX_SOURCE_DIRECTORY 命令,也可以使用 set
来手动将多个源文件保存到变量名中:
set(SRC_LISTother.cppmain.cpp
)
多个源文件在不同目录下
.
├── CMakeLists.txt
├── main.cc
└── math├── CMakeLists.txt├── MathFunctions.cc└── MathFunctions.h
在多个目录下有着多个源文件的情况下,需要在每个目录中都编写一个 CMakeLists.txt
。这里为了方便,我们可以将 math
里的文件编译为一个静态库再通过 main
函数调用。
math 目录下的 CMakeLists.txt
math
目录下的 CMakeLists.txt
主要做的事是将当前目录下的文件编译为一个静态库:
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)# 指定生成 MathFunctions 链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
- add_library:用于从某些源文件创建一个库,默认生成在构建文件夹。
- 第一个参数为库名(不需要 lib 前缀,会自动添加)
- 第二个参数用于指定 SHARED(动态库)、STATIC(静态库)(如果不写,则通过全局的
BUILD_SHARED_LIBS
的FALSE
或TRUE
来指定。上例中就没有写) - 第三个参数即为源文件列表。
根目录的 CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)# 项目信息
project (Demo3)# 查找目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)# 添加 math 子目录
add_subdirectory(math)# 指定生成目标
add_executable(Demo ${DIR_SRCS})# 添加链接库
target_link_libraries(Demo MathFunctions)
- add_subdirectory:用于表示该项目包含一个子目录,此时会去处理子目录下的
CMakeLists.txt
与源文件。 - target_link_libraries:该命令用于指明可执行文件
Demo
需要链接MathFunctions
库。- 第一个参数为可执行文件名
- 第二个参数为访问权限(
PUBLIC
、PRIVATE
、INTERFACE
,默认为PUBLIC
) - 第三个参数为库名(这里库名使用了
math
目录下的CMakeLists.txt
中生成的MathFunctions
链接库) - 【后两个参数可以为多个】
option 选项
面对 库文件很大 或者 库文件依赖第三方库 的时候,我们可能希望将 该库文件作为一个可选项(而不是非得链接不可)。
- 这里仍以
MathFunctions
为例进行实现。首先在顶层CMakeLists.txt
文件添加一个选项option
,其基本格式如下:
option (USE_MYMATH "should we use our own math functions?" ON)
- 第一个参数定义选项名称。
- 第二个参数说明选项的含义。
- 第三个参数定义选项默认状态,一般是
OFF
或者ON
,除去ON
之外,其他所有值都被认为是OFF
。
- 接下来就是将构建和连接
MathFunctions
设置为可选项。修改顶层CMakeLists.txt
文件如下所示:
if (USE_MYMATH)include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")add_subdirectory (math)# 将 ${EXTRA_LIBS} 变量中旧有的内容和 MathFunctions # 都加入到EXTRA_LIBS变量中set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)# add the executable
add_executable(Demo ${DIR_SRCS})
target_link_libraries(Demo ${EXTRA_LIBS})
PS: 这里我们使用了 USE_MYMATH
这个宏,倘若在源码中也想使用它,那么需要由 CMake
通过在配置文件 Config.h
添加以下代码:
#cmakedefine USE_MYMATH
之后源码 include
该头文件(Config.h)来实现。
导入外部库
本地导入(find_package)
本节示例的目录结构如下:
.
├── CMakeLists.txt
├── main.cpp
└── README.adoc
以 boost
为例,演示如何导入一个本地的第三方库, MakeLists.txt
内容如下:
cmake_minimum_required(VERSION 3.5)# Set the project name
project (third_party_include)
# 使用库文件系统和系统查找 boost install
# 注意这是第三方库,而不是自己生成的静态动态库
find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
if(Boost_FOUND)message ("boost found")
else()message (FATAL_ERROR "Cannot find Boost")
endif()# Add an executable
add_executable(third_party_include main.cpp)# link against the boost libraries
target_link_libraries( third_party_includePRIVATEBoost::filesystem
)
使用 find_package
命令来在本地搜索对应的第三方库,它的参数的含义如下:
Boost
代表需要查询的库名称;1.46.1
代表需要库的最低版本;REQUIRED
表示该库是必须的,如果找不到会报错;COMPONENTS
用于检测该库的对应组件是否存在,如果不存在则认为找到的库不满足条件。filesystem
和system
代表搜索的位置,分别是 库文件系统 和 文件系统。
外部导入(FetchContent)
FetchContent
是 3.11.0
版本开始提供的功能,只需要一个 URL
或者 Git
仓库即可引入一个库,这里以 GoogleTest
库为例:
cmake_minimum_required(VERSION 3.14)
project(my_project)# GoogleTest requires at least C++11
set(CMAKE_CXX_STANDARD 11)include(FetchContent)
FetchContent_Declare(googletestURL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# 对于Windows:防止重写父项目的编译器/链接器设置
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
include(FetchContent)
:表示引入FetchContent
。FetchContent_Declare(第三方库)
:获取第三方库,可以是一个URL
或者一个Git
仓库。FetchContent_MakeAvailable(第三方库)
:将这个第三方库引入项目。target_link_libraries(主项目 PRIVATE 子模块::子模块)
:链接这个第三方库。
安装与测试
安装
示例的目录结构如下:
.
├── CMakeLists.txt
├── config.h.in
├── License.txt
├── main.cc
└── math├── CMakeLists.txt├── MathFunctions.cc└── MathFunctions.h
math
目录下的CMakeLists.txt
:
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)# 指定生成 MathFunctions 链接库
add_library (MathFunctions ${DIR_LIB_SRCS})# 指定 MathFunctions 库的安装路径
install (TARGETS MathFunctions DESTINATION lib)
install (FILES MathFunctions.h DESTINATION include)
通过 install
命令:
- 将静态库
MathFunctions
安装到/usr/local/lib
目录下 - 将头文件
MathFunctions.h
安装到/usr/local/include
目录下。
根目录
下的CMakeLists.txt
中的install
内容:
# 指定安装路径
install (TARGETS Demo DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h"DESTINATION include)
- 将可执行程序
Demo
安装到了/usr/local/lib
目录下; - 将头文件
config.h
安装到/usr/local/lib
目录下。
PS:/usr/local/
是默认安装的根目录,可以通过修改 CMAKE_INSTALL_PREFIX
变量来指定文件安装文件的根目录。
测试
CMake
提供了一个名为 CTest
的测试工具。它通过 add_test
命令来进行相关测试,项目根目录的 CMakeLists.txt
文件中的 add_test
命令内容如下:
# 启用测试
enable_testing()# 测试程序是否成功运行
add_test (test_run Demo 5 2)# 测试帮助信息是否可以正常提示
add_test (test_usage Demo)
set_tests_properties (test_usagePROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent")# 测试 5 的平方
# add_test (test_5_2 Demo 5 2)# set_tests_properties (test_5_2
# PROPERTIES PASS_REGULAR_EXPRESSION "is 25")# 测试 10 的 5 次方
# add_test (test_10_5 Demo 10 5)# set_tests_properties (test_10_5
# PROPERTIES PASS_REGULAR_EXPRESSION "is 100000")# 测试 2 的 10 次方
# add_test (test_2_10 Demo 2 10)# set_tests_properties (test_2_10
# PROPERTIES PASS_REGULAR_EXPRESSION "is 1024")# 如果觉得上述过程过于复杂
# 也可以定义一个宏,用来简化测试工作
macro (do_test arg1 arg2 result)add_test (test_${arg1}_${arg2} Demo ${arg1} ${arg2})set_tests_properties (test_${arg1}_${arg2}PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro (do_test)# 利用 do_test 宏,测试一系列数据
do_test (5 2 "is 25")
do_test (10 5 "is 100000")
do_test (2 10 "is 1024")
enable_testing
:用于启动测试。add_test
:用于添加测试- 第一个参数为测试名;
- 第二个参数为可执行程序;
- 剩下的为可执行程序的参数。
set_tests_properties
:测试的提示信息。PASS_REGULAR_EXPRESSION
:测试属性(正则表达式)来验证输出中是否包含了特定的字符串。这里验证开方是否正确并且在计算错误时输出输出对应信息。macro
:宏,用于编写一个重复性操作来简化测试用例的编写。do_test
:编写的测试宏。
生成安装包
如果想要生成安装包,则需要使用 CMake
提供的打包工具 CPack
。此时需要在 CMakeLists.txt
中添加以下内容:
# 构建一个 CPack 安装包
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE"${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}")
include (CPack)
include (InstallRequiredSystemLibraries)
:导入InstallRequiredSystemLibraries
模块。- 设置一些
CPack
相关变量。 include (CPack)
:导入CPack
模块。
接着执行 cmake
和 make
构建工程,此时再执行 cpack
命令即可生成安装包:
#生成二进制安装包
cpack -C CPackConfig.cmake#生成源码安装包
cpack -C CPackSourceConfig.cmake
当命令执行成功后,就会在当前目录下生成 *.sh
、*.tar.gz
、*.tar.Z
这三个格式的安装包。