目录
0. CMake常用的命令或函数:
1. 定义项目 - project
2.多个目录 - add_subdirectory
3.常用命令 - add_executable、add_library
4.常用命令 - 改变最终目标文件输出位置
5.makefile 中输入 log
6.自定义编译选项,为源代码生成宏 configure_file
6'.自定义编译选项,为源代码生成宏 add_definitions
6''.自定义编译选项,为源代码生成宏 target_compile_definitions
6''.自定义编译选项,为源代码生成宏 target_compile_options
7. 处理 CMake 对源码的设置 - configure_file
8. 为指定项目添加 include 路径
9. 安装和测试
10. 指定 C++ 标准
11. 搜索源文件:
参考文献:
定义项目:project(myProject C CXX)
:该命令会影响 PROJECT_SOURCE_DIR
、PROJECT_BINARY_DIR
、PROJECT_NAME
等变量。另外要注意的是,对于多个project嵌套的情况,CMAKE_PROJECT_NAME
是当前CMakeLists.txt文件回溯至最顶层CMakeLists.txt文件中所在位置之前所定义的最后一个project的名字。cmake_minimum_required(VERSION 3.0)
:出进行编译所需要的CMake最低版本,如果不指定的话系统会自己指定一个,但是也会扔出一个warning
。
搜索源文件:file(
:按照正则表达式搜索路径下的文件,比如:file(GLOB SRC_LIST "./src/*.cpp")
。aux_source_directory(
:搜索文件内所有的源文件。
添加编译目标:add_library(mylib [STATIC|SHARED] ${SRC_LIST})
add_executable(myexe ${SRC_LIST})
添加头文件目录:include_directories(
:为该位置之后的target链接头文件目录(不推荐)。target_include_directories(
:为特定的目标链接头文件目录。
添加依赖库:link_libraries(
:为该位置之后的target链接依赖库。target_link_libraries(
:为特定的目标链接依赖库。
这里,常见的依赖库可能是以下几种情况:
find_package()
等命令搜索到的。对于find_package(XXX)
,该命令本身并不直接去进行搜索,而是通过特定路径下的FindXXX.cmake或XXXConfig.cmake文件来定位头文件和库文件的位置,分别被称为Module模式和Config模式。该命令会定义一个XXX_FOUND
变量,如果成功找到,该变量为真,同时会定义XXX_INCLUDE_DIR
和XXX_LIBRARIES
两个变量,用于link和include。add_dependencies
(
使顶层
依赖于其他顶层目标,以确保它们在
之前构建。顶级目标是由 add_executable() , add_library() 或 add_custom_target() 命令之一创建的目标(但不是由CMake生成的目标,如 install
)。
添加子目录:add_subdirectories(
:子目录中要有CMakeLists.txt文件,否则会报错。
包含其他cmake文件:include(./path/to/tool.cmake)
或set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ./path/to)
,随后include(tool)
。
该命令相当于将tool.cmake的内容直接包含进来。
定义变量:set(
set(
其中CACHE
会将变量定义在缓存文件CMakeCache.txt
里,可以在下次编译的时候读取。
作用域:add_subdirectories(
会创建一个子作用域,里面可以使用父作用域里定义的变量,但里面定义的变量在父作用域不可见,同样,在子作用域修改父作用域里的变量不会影响父作用域。function()
同样会产生一个子作用域。若想让子作用域里的定义或者修改在父作用域可见,需要使用PARENT_SCOPE
标记。
相对地,macro()
和include()
不会产生子作用域。
选项:add_option(MY_OPTION
:会定义一个选项。在使用cmake
命令时,可以通过-D
改变选项的值。比如cmake .. -DMY_OPTION=ON
。
编译选项:add_compile_options(-std=c++11)
如果想要指定具体的编译器的选项,可以使用make_cxx_flags()
或cmake_c_flags()
。
与源文件的交互:configure_file(XXX.in XXX.XX)
会读入一个文件,处理后输入到新的位置。一方面,会替换掉#XXX
或者@XXX@
定义的内容。另一方面,会将文件里的#cmakedefine VAR …
替换为#define VAR …
或者/* #undef VAR */
。
字符串操作、循环、判断、文件/变量存在判断等
这些命令同样有用,请参考网络资料。
project(
将Name存到PROJECT_NAME,同时设置变量
例如:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(CalculateSqrt)
INCLUDE_DIRECTORIES(${CalculateSqrt_SOURCE_DIR}/Hello)
# add the executable
add_executable(Calculate hello.cxx)
如上图,project 名字:CalculateSqrt;可执行程序的名字:Calculate。
通过 project(CalculateSqrt) 生成两个宏:
// main 文件的目录
CalculateSqrt_SOURCE_DIR:STATIC=/home/liu/Project/CMake-test
// 可执行文件的目录
CalculateSqrt_BINARY_DIR:STATIC=/home/liu/Project/CMake-test/build
之后,就可以在下文中使用这两个宏,如:
INCLUDE_DIRECTORIES(${CalculateSqrt_SOURCE_DIR}/Hello)
这个语句的作用是增加编译子目录。其基本语法格式是:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
一共有三个参数,后两个是可选参数。
source_dir 源代码目录
指定一个包含 CMakeLists.txt 和代码文件所在的目录,该目录可以是绝对路径,也可以是相对路径,对于后者相对路径的起点是 CMAKE_CURRENT_SOURCE_DIR。
此外,子目录 必须 再次包含的 CMakeLists.txt,否则会报错。此时将继续处理里层的CMakeLists.txt,而不是继续处理当前源代码。
add_executable(
[WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [source1] [source2 ...])
例:
# 指定生成目标
add_executable(Demo main.cc MathFunctions.cc)
add_library(
[STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [
例:
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
add_executable(DEMO EXCLUDE_FROM_ALL src/main.cpp)
当在函数中加入 EXCLUDE_FROM_ALL
之后,这个 target(${PROJECT_NAME}) 就不会在 make
的时候做任何操作(因为 make
只对 all target 生效)。
但是这个 target 依然存在的,需要 make
来主动执行:
【注】
手动编译时,make 后边名称,是 add_executable 中的项目名称,即 DEMO
我们想把 make 最终生成目标库输出至 lib 中,在 CMakeLists.txt 增加以下语句:
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
当然如果是输出可执行到另一个地方,那么增加以下语句:
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
message("output-dir:${PROJECT_NAME}")
CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON
,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。
├── CMakeLists.txt
├── config.h.in
├── main.cc
└── math
├── CMakeLists.txt
├── MathFunctions.cc
└── MathFunctions.h
我们要做的第一步是在顶层的 CMakeLists.txt 文件中添加该选项:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
set (Demo_VERSION_MAJOR 1)
set (Demo_VERSION_MINOR 0)
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
"Use provided math implementation" ON)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
add_definitions(-D USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
【注意】:
configure_file 语句中的文件 config.h.in 定义了 USE_MYMATH
该变量是在 option (USE_MYMATH "Use provided math implementation" ON) 定义的
设为 ON 时,会在生成的文件 config.h 中进行 define 定义,即: #define USE_MYMATH
设为 OFF 时,会在生成的文件 config.h 中进行 undefine 定义,即: /* #undef USE_MYMATH */
其中:
configure_file
命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。option
命令添加了一个 USE_MYMATH
选项,并且默认值为 ON
。USE_MYMATH
变量的值来决定是否使用我们自己编写的 MathFunctions 库。上面的程序值得注意的是第2行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH
的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:
#define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@
#define Demo_VERSION_MINOR @Demo_VERSION_MINOR@#cmakedefine USE_MYMATH
这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。
其中,cmakedefine 是检查 CMakeLists.txt 是否定义了 USE_MYMATH,即:option (USE_MYMATH "Use provided math implementation" ON)
设为 ON 时,会在生成的文件 config.h 中进行 define 定义,即: #define USE_MYMATH
设为 OFF 时,会在生成的文件 config.h 中进行 undefine 定义,即: /* #undef USE_MYMATH */
其中,@Demo_VERSION_MAJOR@ 值是从 CMakeLists.txt 中获取的
set (Demo_VERSION_MAJOR 1)
set (Demo_VERSION_MINOR 0)
之后修改 main.cc 文件,让其根据 USE_MYMATH
的预定义值来决定是否调用标准库还是 MathFunctions 库:
#include
#include
#include "config.h"
#ifdef USE_MYMATH
#include "math/MathFunctions.h"
#else
#include
#endif
int main(int argc, char *argv[])
{
if (argc < 3){
printf("Usage: %s base exponent \n", argv[0]);
return 1;
}
double base = atof(argv[1]);
int exponent = atoi(argv[2]);
#ifdef USE_MYMATH
printf("Now we use our own Math library. \n");
double result = power(base, exponent);
#else
printf("Now we use the standard library. \n");
double result = pow(base, exponent);
#endif
printf("%g ^ %d is %g\n", base, exponent, result);
return 0;
}
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 指定生成 MathFunctions 链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
/**
* power - Calculate the power of number.
* @param base: Base value.
* @param exponent: Exponent value.
*
* @return base raised to the power exponent.
*/
double power(double base, int exponent)
{
int result = base;
int i;
if (exponent == 0) {
return 1;
}
for(i = 1; i < exponent; ++i){
result = result * base;
}
return result;
}
#ifndef POWER_H
#define POWER_H
extern double power(double base, int exponent);
#endif
cmake -D USE_MYMATH=ON ..
make
可以通过编译参数来控制 CMakeLists 中的 option 参数的开关。
或者
cmake ..
make
在上述的 CMakeLists.txt 中去除 config.h.in 文件,增加 add_definitions 参数
CMakeLists.txt 如下:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
"Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
add_definitions(-D USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
add_definitions:添加预处理器定义
为目标添加编译定义。
target_compile_definitions(
[items1...] [
[items2...] ...])
指定在编译给定的
命名的
需要 INTERFACE , PUBLIC 和 PRIVATE 关键字来指定以下参数的范围。
PRIVATE 和 PUBLIC 项目将填充
PUBLIC 和 INTERFACE 项目将填充
以下参数指定编译定义。重复调用相同的
项目上的任何前导 -D
将被删除。空项目将被忽略。例如,以下各项均等效:
target_compile_definitions(foo PUBLIC FOO)
target_compile_definitions(foo PUBLIC -DFOO) #-D已删除
target_compile_definitions(foo PUBLIC "" FOO) #“”被忽略
target_compile_definitions(foo PUBLIC -D FOO) #-D变成“”,然后忽略
定义可以选择有值。
target_compile_definitions(foo PUBLIC FOO=1)
请注意,许多编译器将 -DFOO
等同于 -DFOO=1
,但是其他工具可能无法在所有情况下都识别出此情况(例如IntelliSense)。
target_compile_options(
[BEFORE]
[items1...] [
[items2...] ...])
将选项添加到 COMPILE_OPTIONS 或 INTERFACE_COMPILE_OPTIONS 目标属性。
在编译给定的
如果指定 BEFORE
,则内容将被添加到该属性的前面,而不是被附加。
需要 INTERFACE
, PUBLIC
和 PRIVATE
关键字来指定以下参数的范围。
PRIVATE
和 PUBLIC
项目将填充
的 COMPILE_OPTIONS 属性。
PUBLIC
和 INTERFACE
项目将填充
的 INTERFACE_COMPILE_OPTIONS 属性。以下参数指定编译选项。重复调用相同的
会按调用顺序追加项目。
如:
target_compile_options (Demo PUBLIC -DUSE_MYMATH)
生成代码:
CMakeFiles/Demo.dir/flags.make:9:CXX_FLAGS = -DUSE_MYMATH
此命令可用于添加任何选项。
但是,要添加预处理器定义和包含目录,建议使用更具体的命令 target_compile_definitions() 和 target_include_directories() 。
configure_file:
将一份文件拷贝到另一个位置并修改它的内容,从而在代码中使用 CMake 中定义的变量
configure_file(
详情:configure_file — CMake 3.24.0-rc4 Documentation
target_include_directories(CalculateSqrt PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
指定项目编译的时候需要 include 的文件路径
PROJECT_BINARY_DIR 为编译发生的目录, make 执行的目录,Makefile 所在目录
PROJECT_SOURCE_DIR 为工程所在的目录
install 规则非常简单,对于MathFunctions库来说,我们在MathFunctions/CMakeLists.txt中添加如下两行来实现安装库和头文件
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
对于应用来说,我们在上层的CMakeLists.txt中添加如下两行来实现安装目标文件和可配置头文件
install(TARGETS CalculateSqrt DESTINATION ${PROJECT_SOURCE_DIR}/bin)
install(FILES "${PROJECT_BINARY_DIR}/CalculateSqrtConfig.h"
DESTINATION ${PROJECT_SOURCE_DIR}/include
)
这样我们在执行cmake..&&make install之后,指定的库、头文件和目标文件就安装到指定的目录中了,具体如下
使用标志位CMAKE_CXX_STANDARD_REQUIRED 和 CMAKE_CXX_STANDARD指定编译器的使用版本。
如果 CMAKE_CXX_STANDARD_REQUIRED 设置为 True,则必须使用 CMAKE_CXX_STANDARD 指定的版本;
如果 CMAKE_CXX_STANDARD_REQUIRED 设置为 OFF,则 CMAKE_CXX_STANDARD 指定版本的为首选版本,如果没有会使用上一版本。
# 设置指定的C++编译器版本是必须的,如果不设置,或者为OFF,则指定版本不可用时,会使用上一版本。
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 指定为C++11 版本
set(CMAKE_CXX_STANDARD 11)
该方法直接指定 CMAKE_CXX_FLAGS 标志位进行设置,具体使用方式如下面的代码例子:
# 设置cmake的最低版本
cmake_minimum_required(VERSION 3.10)
# 设置工程名称 和版本
project(tutorial VERSION 1.0)
# 指定版本号的配置文件
configure_file(include/TutorialConfig.h.in TutorialConfig.h)
# 设置指定C++编译器版本。
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") # set C++ 11
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
message( STATUS "The comipler ${CMAKE_CXX_COMIPLER} has no C++ 11 suport. Please use a different C++ comipler.")
endif()
# 增加生成可执行文件,生成的程序名称为:tutorial_first
add_executable(tutorial src/tutorial.cpp)
# 为指定项目添加 include 路径
target_include_directories(tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
【注意】:
必须先 include(CheckCXXCompilerFlag)
原因:
因为 CHECK_CXX_COMPILER_FLAG 是临时设置 CMAKE_REQUIRED_DEFINITIONS
变量,并从 CheckCXXSourceCompiles 模块调用 check_cxx_source_compiles
宏。
而 include(CheckCXXCompilerFlag) 是搜索名称为
的文件(即:<
CheckCXXCompilerFlag>.cmake
),然后加载.
file(
)
按照正则表达式搜索路径下的文件,比如file(GLOB SRC_LIST "./src/*.cpp")
。
aux_source_directory(
)
搜索文件内所有的源文件。
CMake 3.21 中文