CMake简单入门
新建项目
新建项目t4,目录结构如下:
1 | |
该程序引入了自建的hello.h程序库包含了函数func()。main.c的内容如下所示:
1 | |
CMake变量
一般变量
CMake变量引用的方式
使用${}进行变量的引用。例如:
1 | |
在 IF 等语句中,是直接使用变量名而不通过${}取值。
cmake自定义变量的方式
cmake变量定义的方式有两种:隐式定义和显式定义。
隐式定义
前面举了一个隐式定义的例子,就是PROJECT指令,他会隐式的定义_BINARY_DIR和_SOURCE_DIR两个变量。
显示定义
显式定义的例子我们前面也提到了,使用 SET 指令,就可以构建一个自定义变量了。比如:
1 | |
就可以通过${HELLO_SRC}来引用这个自定义变量(main.c)了.
环境变量
调用环境变量的方式
使用 $ENV{NAME}指令就可以调用系统的环境变量了。比如
1 | |
设置环境变量的方式
1 | |
CMake常用变量
使用cmake --help-variable-list可以查看cmake中默认变量。
| 变量名 | 变量说明 |
|---|---|
| PROJECT_NAME | 返回通过PROJECT指令定义的项目名称 |
| PROJECT_SOURCE_DIR | CMake源码地址,即cmake命令后指定的地址 |
| PROJECT_BINARY_DIR | 运行cmake命令的目录,通常是PROJECT_SOURCE_DIR下的build目录 |
| CMAKE_MODULE_PATH | 定义自己的cmake模块所在的路径 |
| CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt所在的路径 |
| CMAKE_CURRENT_LIST_DIR | 当前文件夹路径 |
| CMAKE_CURRENT_LIST_FILE | 输出调用这个变量的CMakeLists.txt的完整路径 |
| CMAKE_CURRENT_LIST_LINE | 输出这个变量所在的行 |
| CMAKE_RUNTIME_OUTPUT_DIRECTORY | 生成可执行文件路径 |
| CMAKE_LIBRARY_OUTPUT_DIRECTORY | 生成库的文件夹路径 |
| CMAKE_BUILD_TYPE | 指定基于make的产生器的构建类型(Release,Debug) |
| CMAKE_C_FLAGS | .C文件编译选项,如 -std=c99 -O3 -march=native* |
| CMAKE_CXX_FLAGS | .CPP文件编译选项,如 -std=c++11 -O3 -march=native* |
| CMAKE_CURRENT_BINARY_DIR | target编译目录 |
| CMAKE_INCLUDE_PATH | 环境变量,非cmake变量 |
| CMAKE_LIBRARY_PATH | 环境变量 |
| CMAKE_STATIC_LIBRARY_PREFIX | 静态库前缀, Linux下默认为lib |
| CMAKE_STATIC_LIBRARY_SUFFIX | 静态库后缀,Linux下默认为.a |
| CMAKE_SHARED_LIBRARY_PREFIX | 动态库前缀,Linux下默认为lib |
| CMAKE_SHARED_LIBRARY_SUFFIX | 动态库后缀,Linux下默认为.so |
| BUILD_SHARED_LIBS | 如果为ON,则add_library默认创建共享库 |
| CMAKE_INSTALL_PREFIX | 配置安装路径,默认为/usr/local |
| CMAKE_ABSOLUTE_DESTINATION_FILES | 安装文件列表时使用ABSOLUTE DESTINATION 路径 |
| CMAKE_AUTOMOC_RELAXED_MODE | 在严格和宽松的automoc模式间切换 |
| CMAKE_BACKWARDS_COMPATIBILITY | 构建工程所需要的CMake版本 |
| CMAKE_COLOR_MAKEFILE | 开启时,使用Makefile产生器会产生彩色输出 |
| CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS | 用来控制IF ELSE语句的书写方式 |
使用cmake --help-variable <cmake变量名> 可以查看该变量的默认值和使用场景,简单使用就可以不用再去查cmake手册了。
- 运行CMake,并使用
cmakeGUI工具查看缓存。然后,您将获得所有变量。 - 或者使用
-LH.运行CMake,然后将在配置后打印所有变量。
主要的开关选项:
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用来控制 IF ELSE 语句的书写方式,在下一节语法部分会讲到。BUILD_SHARED_LIBS这个开关用来控制默认的库编译方式,如果不进行设置,使用 ADD_LIBRARY 并没有指定库类型的情况下,默认编译生成的库都是静态库。如果SET(BUILD_SHARED_LIBS ON)后,默认生成的为动态库。CMAKE_C_FLAGS设置 C 编译选项,也可以通过指令ADD_DEFINITIONS()添加。CMAKE_CXX_FLAGS设置 C++ 编译选项,也可以通过指令ADD_DEFINITIONS()添加。
系统信息
CMAKE_MAJOR_VERSION, CMAKE 主版本号,比如 2.4.6 中的 2CMAKE_MINOR_VERSION, CMAKE 次版本号,比如 2.4.6 中的 4CMAKE_PATCH_VERSION, CMAKE 补丁等级,比如 2.4.6 中的 6CMAKE_SYSTEM,系统名称,比如 Linux-2.6.22CMAKE_SYSTEM_NAME,不包含版本的系统名,比如 LinuxCMAKE_SYSTEM_VERSION,系统版本,比如 2.6.22CMAKE_SYSTEM_PROCESSOR,处理器名称,比如 i686.UNIX,在所有的类 UNIX 平台为 TRUE ,包括 OS X 和 cygwinWIN32,在所有的 win32 平台为 TRUE ,包括 cygwin
指定C++标准
1 | |
Modern CMake propose an interface for this purpose target_compile_features. Documentation is here: Requiring Language Standards. Use it like this:
1 | |
In CMake, PUBLIC (for everyone) = INTERFACE (for the other) + PRIVATE (for me)
引入第三方头文件
hello.h 位于/root/cpp_test/backup/cmake_test/t4/include/hello目录中,并没有位于系统标准的头文件路径,为了让我们的工程能够找到 hello.h 头文件,我们需要引入一个新的指令INCLUDE_DIRECTORIES,其完整语法为:
1 | |
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面,你可以通过两种方式来进行控制搜索路径添加的方式:
现在我们在 src/CMakeLists.txt 中添加一个头文件搜索路径,方式很简单,加入:
1 | |
进入 build 目录,重新进行构建,这是找不到 hello.h 的错误已经消失,但是出现了一个新的错误:
1 | |
因为我们并没有 link 到共享库 libhello 上。
为 target 添加共享库
我们现在需要完成的任务是将目标文件链接到 libhello,这里我们需要引入两个新的指令 LINK_DIRECTORIES 和 TARGET_LINK_LIBRARIES
add_library
该指令的主要作用就是将指定的源文件生成链接文件,然后添加到工程中去。该指令常用的语法如下:
1 | |
其中<name>表示库文件的名字,该库文件会根据命令里列出的源文件来创建。而STATIC、SHARED和MODULE的作用是指定生成的库文件的类型:
STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接(动态链接库),在运行时会被加载。MODULE库是一种不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数。
默认状态下,库文件将会在于源文件目录树的构建目录树的位置被创建,该命令也会在这里被调用。
而语法中的source1 source2分别表示各个源文件。
link_directories
该指令的作用主要是指定要链接的库文件的路径,该指令有时候不一定需要。因为find_package和find_library指令可以得到库文件的绝对路径。不过你自己写的动态库文件放在自己新建的目录下时,可以用该指令指定该目录的路径以便工程能够找到。
LINK_DIRECTORIES 的全部语法是:
1 | |
这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可 执行二进制,在编译时就需要指定一下这些共享库的路径。对应tasks的 "-L"参数 .dll等。
注意:LINK_DIRECTORIES放在ADD_EXECUTABLE之前
官网不推荐使用LINK_DIRECTORIES,原文如下:
Note that this command link_directories is rarely necessary. Library locations returned by find_package() and find_library() are absolute paths. Pass these absolute library file paths directly to the target_link_libraries() command. CMake will ensure the linker finds them.
target_link_libraries
这个例子中我们没有用到这个指令而是使用TARGET_LINK_LIBRARIES 。 TARGET_LINK_LIBRARIES 的全部语法是:
1 | |
上述指令中的<target>是指通过add_executable()和add_library()指令生成已经创建的目标文件。而[item]表示库文件没有后缀的名字。默认情况下,库依赖项是传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的连接线上。这个传递的接口存储在interface_link_libraries的目标属性中,可以通过设置该属性直接重写传递接口。
这个指令可以用来为 target 添加需要链接的共享库,本例中是一个可执行文件,但是同样可以用于为自己编写的共享库添加共享库链接,libhello.so.1.2共享库的路径为/root/cpp_test/backup/cmake_test/t4/thirdPath/libhello.so.1.2。 为了解决我们前面遇到的 func 未定义错误,我们需要作的是向src/CMakeLists.txt 中添加如下指令:
1 | |
注意:
target_link_libraries要在add_executable之后link_libraries要在add_executable之前,对应tasks的 "-l"参数 .lib。目前文档中说link_libraries已经被废弃了Deprecated. Use the target_link_libraries() command instead.
链接库综合例子
1 | |
如上所述,我们接下来进行编译:
1 | |
这是我们就得到了一个连接到 libhello 的可执行程序 main,位于 build/bin目录,运行 main 的结果是输出:
1 | |
查看执行文链接库的情况
让我们来检查一下 main 的链接情况,输入命令:
1 | |
得到的输出如下:

可以清楚的看到 main 确实链接了共享库 libhello,而且链接的是动态库 libhello.so.1
链接到静态库
将 TARGET_LINK_LIBRRARIES指令修改为:
1 | |
重新构建后再来看一下 main 的链接情况 ldd src/main

说明,main 确实链接到了静态库 libhello.a
总结
CMakeLists.txt所有指令的基本操作如下:
1 | |
举个简单的例子,项目learn-c++目录结构如下:
1 | |
CmakeLists.txt文件如下:
1 | |
注:file的一个作用是生成目录列表。
- 添加当前目录下的所有c文件列表到
lib_srcs变量中
1 | |
- 添加当前目录及其子目录下的所有c文件列表到
lib_srcs变量中
1 | |