Appearance
CMake入门速查手册
文件类型
CMakelists.txt
:CMake工作的主要文件,名称大小写不敏感。*.cmake
:模块文件,用于定义可重用的代码片段,可以被多个项目或多个 CMakeLists.txt 文件包含。模块文件通常用于定义宏、设置查找外部库的逻辑、定义测试、配置文件等。模块文件可以通过include()
命令被包含在 CMakeLists.txt 文件中。*Config.cmake
:配置文件,当使用install(EXPORT ...)
命令安装目标时,CMake 可以生成配置文件,这些文件包含了安装的目标和它们的位置信息。这些文件通常用于在安装后的项目中查找和使用安装的目标。*ConfigVersion.cmake
:版本文件,这些文件包含了关于包版本的信息,它们与配置文件一起使用,以确保找到正确版本的库。Find*.cmake
:用于查找库。例如,FindBoost.cmake
模块用于查找 Boost 库。
静态库和动态库
静态库:预先已经编译,使用时只需要链接到你自己的工程中。由于静态库会全部链接到工程中,所以最终生成的工程会比较大。
动态库:运行时加载,运行的多个程序可以使用同一个加载到内存中的动态库。
静态库使用条件
开发:需要头文件 + 静态库。
发布:因为静态库最终链接到你的工程中,所以最终生成的exe可以直接运行,无需静态库和头文件了。
动态库使用条件
开发:需要头文件 + 导出库(和静态库同样的后缀名)+ 动态库
发布:只需要你自己工程生成的exe和制作的dll文件即可。
常用预定义变量
宏名称 | 描述 |
---|---|
CMAKE_SOURCE_DIR | cmake后面紧跟的工程目录,或CMake-GUI的source code |
CMAKE_CURRENT_LIST_DIR | 当前处理的CMakeLists.txt文件的所在目录 |
CMAKE_CURRENT_SOURCE_DIR | 指向当前正在定义的目标的源代码目录 |
CMAKE_INSTALL_PREFIX | 安装目录的路径,比如:${CMAKE_SOURCE_DIR}/install |
CMAKE_MODULE_PATH | 指定查找CMake模块的路径 |
CMAKE_BINARY_DIR | 运行cmake命令的目录 |
CMAKE_C_COMPILER | C 编译器的路径 |
CMAKE_CXX_COMPILER | C++ 编译器的路径 |
CMAKE_C_FLAGS | C 编译器的编译选项 |
CMAKE_CXX_FLAGS | C++ 编译器的编译选项 |
CMAKE_BUILD_TYPE | 构建类型,如 Debug 或 Release |
CMAKE_LIBRARY_OUTPUT_DIRECTORY | 共享库(如.dll和.so)的输出路径 |
CMAKE_ARCHIVE_OUTPUT_DIRECTORY | 静态库(如.lib和.a)的输出路径 |
CMAKE_RUNTIME_OUTPUT_DIRECTORY | 运行时目标文件(如.exe和.dll)的输出路径 |
PROJECT_NAME | 获取最近调用project()命令指定的名称 |
PROJECT_SOURCE_DIR | 通过 project() 命令定义的项目顶层源代码目录,每个项目可以有自己的PROJECT_SOURCE_DIR |
CMAKE_INSTALL_PREFIX | 执行install命令时安装根目录 |
CMAKE_INSTALL_INCLUDEDIR | 头文件的安装路径,相对于CMAKE_INSTALL_PREFIX的相对路径,下同 |
CMAKE_INSTALL_LIBDIR | 动态库和静态库的安装路径 |
CMAKE_INSTALL_BINDIR | 可执行文件的安装路径 |
说明:
CMAKE_SOURCE_DIR 和 PROJECT_SOURCE_DIR 的区别:
CMAKE_SOURCE_DIR 永远指向整个解决方案的顶层目录,PROJECT_SOURCE_DIR 较为复杂,见下图(为简化描述,example03目录用root代替):CMAKE_SOURCE_DIR的值永远都是/root/
CMAKE_CURRENT_LIST_DIR 和 CMAKE_CURRENT_SOURCE_DIR 区别:
project/ ├── CMakeLists.txt └── src/ ├── CMakeLists.txt └── main.cpp
在 src/CMakeLists.txt 文件中,CMAKE_CURRENT_LIST_DIR 将指向 src 目录,而 CMAKE_CURRENT_SOURCE_DIR 也将指向 src 目录,因为这是定义目标的地方。但是,如果你在 project/CMakeLists.txt 中包含 src/some_other_file.cmake,那么在处理 some_other_file.cmake 时,CMAKE_CURRENT_LIST_DIR 将指向 src 目录,而 CMAKE_CURRENT_SOURCE_DIR 将指向 project 目录。
常用命令
说明:
- 以下有些命令省略了部分可选参数,完整参数请查看官方网站。
- 测试平台:Windows系统
add_dependencies
添加依赖关系,影响构建顺序,即在构建指定目标前必须先构建它所依赖的其他目标。注意:这不影响链接过程。
示例:
cmake
add_library(libcalc STATIC calc.cpp)
add_executable(app main.cpp)
# 构建app目标之前必须先构建静态库libcalc
add_dependencies(app libcalc)
add_definitions
宏定义,同gcc/g++在命令中指定宏:
shell
gcc test.c -DDEBUG -o app
# 使用-D参数定义了一个名为DEBUG的宏
cmake中定义宏:
CMAKE
add_definitions(-DDEBUG)
add_executable
定义工程生成一个可执行程序。
add_executable(可执行文件名 源文件)
- 可执行文件名:和项目名称没有关系,你可以任意取名字,但是通常会使用项目名来作为可执行文件名。
- 源文件:支持多个源文件。如果有头文件,那么基于VS的工程打开会有一个专门的Header Files筛选器放这些头文件,否则源文件依赖的头文件将在
外部依赖项
中。如果你有自己定义的头文件,建议加入到此处。
示例:
cmake
add_executable(${PROJECT_NAME} main.c add.c)
add_library
生成静态库:
cmake
add_library(库名 STATIC 源文件)
示例:
cmake
file(GLOB OPERATION_SOURCES
${CMAKE_CURRENT_LIST_DIR}/add.c
${CMAKE_CURRENT_LIST_DIR}/sub.c
)
# 指定静态库和动态库的生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(operation STATIC ${OPERATION_SOURCES})
这里将生成的operation.lib放到lib目录下,查看 link_libraries 如何链接生成的静态库。
生成动态库:
cmake
add_library(库名 SHARED 源文件)
示例:
cmake
## Windows下使用VS生成dll需要同时生成导出库(即导出符号,与静态库一样使用.lib作为后缀)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
add_library(operation SHARED ${OPERATION_SOURCES})
关于动态库的输出目录:
优先级:CMAKE_RUNTIME_OUTPUT_DIRECTORY > LIBRARY_OUTPUT_PATH
在Linux系统下,EXECUTABLE_OUTPUT_PATH 可设置动态库的生成路径,但在Windows下只能设置生成的exe文件的目录。
add_subdirectory
建立CMake中的父子节点关系,添加子节点,并将父节点的变量传递到子节点中,也就是说通过add_subdirectory添加的目录下的CMakelists.txt文件中可以使用父节点的变量,使用include添加CMakelists.txt子节点则不能使用父节点的变量。
cmake
add_subdirectory(libs)
通常用于添加第三方库。
aux_source_directory
查找某个路径下的所有源文件(指.cpp
、.c
、.c++
等C相关的源文件),并将结果保存到变量(注意不会添加.h
或.hpp
头文件,但这不影响运行,只是没加到这个变量中)。
示例:
cmake
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src MY_SOURCES)
# 搜索当前CMakelists.txt目录下的src文件夹内的源文件并放到变量MY_SOURCES
add_executable(${PROJECT_NAME} MY_SOURCES)
configure_file
用于将一个源文件的内容复制到另一个文件中,并在复制过程中进行变量替换,这些变量通常来自CMake中的变量或特定的配置文件等,多用于生成头文件。有点模板的味道。
configure_file(<input_file> <output_file> [COPYONLY] [ESCAPE_QUOTES] [@ONLY])
<input_file>
:指定输入文件的路径,这是要复制并替换变量的源文件,后缀通常为.h.in
。示例 version.h.in
c++#ifndef __APP_version_h #define __APP_version_h #include <string> namespace myapp { constexpr int APP_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@; constexpr int APP_VERSION_MINOR = @PROJECT_VERSION_MINOR@; constexpr int APP_VERSION_PATCH = @PROJECT_VERSION_PATCH@; const std::string APP_VERSION { "v@PROJECT_VERSION@" }; } #endif
<output_file>
:指定输出文件的路径,这是复制并替换后内容的目标文件。COPYONLY
:如果指定了这个选项,那么输入文件的内容将被直接复制到输出文件中,而不进行任何变量替换。ESCAPE_QUOTES
:如果指定了这个选项,那么在输出文件中,所有的双引号("
)将被转义。@ONLY
:如果指定了这个选项,那么只有@
包围的变量会被替换,这在处理包含 CMake 变量的文件时非常有用。
示例:
cmake
configure_file(
${CMAKE_CURRENT_LIST_DIR}/cmake/version.h.in
${CMAKE_CURRENT_LIST_DIR}/sources/myapp/APPVersion.h
@ONLY
)
file
搜索文件:
cmake
file(GLOB|GLOB_RECURSE <variable> <globbing-expressions>)
- GLOB|GLOB_RECURSE:
- variable:将搜索到的文件列表保存到此变量中
- globbing-expressions:要搜索的文件路径和文件类型,多个路径可以空格或换行分割
示例:
cmake
file(GLOB SOURCES
${CMAKE_CURRENT_LIST_DIR}/*.h
${CMAKE_CURRENT_LIST_DIR}/*.cpp)
file还可以创建、移动、复制、删除文件或目录等等,详细见官网。
find_package
查找软件包,常用的两种搜索模式:
配置
<PackageName>Config.cmake
文件或<lowercasePackageName>-config.cmake
文件。如果提供了版本,则是
<PackageName>ConfigVersion.cmake
或
<lowercasePackageName>-config-version.cmake
,搜索路径比较复杂,通常将配置文件路径添加到 CMAKE_PREFIX_PATH变量中。模块
搜索 CMAKE_MODBLE_PATH 路径下的
Find<PackageName>.cmake
文件。
include
从给定文件中加载并运行CMake代码,文件可以是 CMakelists.txt
或 *.cmake
模块。
指定模块的搜索顺序:先搜索用户定义的模块路径(设置CMAKE_MODULE_PATH
),再搜索CMake内置的系统模块路径。特例:如果调用include()
的文件本身位于 CMake 内置模块目录中,则首先搜索 CMake 内置模块目录,然后搜索CMAKE_MODULE_PATH
。
CMake自带很多内置模块,各个模块内定义了一些function或macro,使用include加载后就可以直接使用这些函数或宏了。比如:CheckIncludeFile.cmake
通常用于检测系统是否提供了特定的库或功能。
cmake
include(CheckIncludeFile)
# 检查stdlib.h,并将结果赋给变量HAVE_STDLIB_H
check_include_file("stdlib.h" HAVE_STDLIB_H)
if(HAVE_STDLIB_H)
message("stdlib.h is available.")
add_definitions(-DHAVE_STDLIB_H)
else()
message("stdlib.h is not available.")
endif()
include_directories
指定要包含的头文件路径。如果源文件和头文件在同一个目录层级,那通常不需要额外指定头文件路径;但如果头文件在另外的目录,那么生成的工程可能就找不到头文件了,这时就需要使用include_directories来指定路径。
全局命令,影响之后所有的目标,即之后的目标都会加上这个头文件路径。基本上有前缀target_
的就是针对给定的目标进行设置,必须是 add_executable()
或 add_library()
等命令创建的目标。
这里需要注意的是,指定的目录层级和源文件中include头文件的路径。假设有如下目录层级:
└─sources
├─include
│ └─oper
| └─ head.h
└─operation
└─ add.c
└─ CMakelists.txt
cmake
include_directories(${CMAKE_CURRENT_LIST_DIR}/../include)
# 指定到了include这一层,那么源文件add.c应该这样加入头文件:#include "oper/head.h"
# 即需要加上oper这个目录,否则就找不到头文件了
# 通常使用一些第三方库时都是指定到include这一层级,源文件中使用 "库名/*.h"
install
通常用于生成SDK,生成头文件、静态库或动态库等到一个干净的文件夹中,别人拷贝这个文件夹到自己的项目中就能使用了。
TARGETS
通过 add_library 或 add_executable 命令添加的目标,即可执行文件、静态库、动态库。
- CONFIGURATIONS
<config>
:指定安装规则所应用的生成配置的列表,如Debug或Release等 - DESTINATION
<dir>
:推荐使用相对路径,绝对路径不适用于--prefix参数 - EXPORT:这里主要是定义一个导出名称,方便使用 EXPORT 进行导出。
示例:
cmake
install(TARGETS ${EXAMPPLE01_TARGETS} EXPORT Example01Targets
RUNTIME DESTINATION bin/$<CONFIG>
LIBRARY DESTINATION lib/$<CONFIG>
ARCHIVE DESTINATION lib/$<CONFIG>
)
EXPORT
生成一个*.cmake文件,该文件包含从安装目录树导入安装命令中列出的所有目标的代码,这样其他项目就可以通过 find_package()
命令来发现和使用这些目标。
示例:生成并重命名为Example01Targets.cmake文件,放到lib/cmake/目录下
cmake
install(EXPORT Example01Targets FILE Example01Targets.cmake DESTINATION lib/cmake/)
DIRECTORY
安装目录
FILES
安装文件
link_directories
添加链接所需要的库文件路径。全局命令,它指定的路径将被所有后续创建的目标所使用。
link_libraries
用于设置全局链接库,这些库会链接到之后定义的所有目标(即通过add_executable或add_library创建的名称目标)。除非你确定需要为所有目标链接,否则推荐使用 target_link_libraries
示例:
cmake
project(Operation)
file(GLOB OPERATION_SOURCES
${CMAKE_CURRENT_LIST_DIR}/main.c
${CMAKE_CURRENT_LIST_DIR}/../include/**/*.h
)
link_directories(${PROJECT_SOURCE_DIR}/lib)
link_libraries(operation)
add_executable(${PROJECT_NAME} ${OPERATION_SOURCES})
# 如果后面还有目标,那么这些目标也会链接operation
可在VS的项目右键 属性->配置属性->链接器->输入->附加依赖项
中看到链接的静态库。
list
操作列表变量。比如通过file命令搜索的结果保存到变量中,这个变量就是一个列表,其内部是由分号分割的字符串。使用方法见官网
message
打印日志消息
project
设置项目名称
set
常用方式:
设置CMake预先定义的变量
cmakeset(CMAKE_INSTALL_INCLUDEDIR "includes")
生成一个列表类型的变量
cmakeset(EXAMPPLE01_TARGETS operation operation2 Demo) # EXAMPPLE01_TARGETS=operation;operation2;Demo
set_property
设置多个对象的一个属性。
对象如下:
- GLOBAL:全局变量
- DIRECTORY:目录
- TARGET:目标,见set_target_properties,可以设置一个目标的多个属性,注意区别。
- SOURCE
- INSTALL
- TEST
- CACHE
set_target_properties
设置目标的一个或多个属性。
string
对字符串进行操作
source_group
主要是在VS的IDE环境中组织代码结构,如创建筛选器等,不影响编译过程。
target_include_directories
针对给定的目标添加头文件路径。
cmake
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
target_link_directories
区别于link_directories,target_link_directories是针对给定的目标
target_link_libraries
区别于link_libraries,target_link_libraries是针对给定的目标链接静态库或动态库。
cmake
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
注意:在cmake中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后。
示例:
cmake
target_link_libraries(${PROJECT_NAME} PUBLIC MyLib)
应用
C#
cmake
cmake_minimum_required(VERSION 3.16)
project(Demo LANGUAGES CSharp)
# Include CMake utilities for CSharp, for WinForm and WPF application support.
include(CSharpUtilities)
file(GLOB_RECURSE CODE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.*)
# 生成库文件
# add_library(${PROJECT_NAME} SHARED ${CODE_SOURCES})
# 生成可执行文件
add_executable(${PROJECT_NAME} ${CODE_SOURCE})
# Set the .NET Framework version for the executable.
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.8")
# Set the executable to be 32-bit.
set_property(TARGET ${PROJECT_NAME} PROPERTY WIN32_EXECUTABLE TRUE)
# Set the C# language version (defaults to 3.0).
set(CMAKE_CSharp_FLAGS "/langversion:latest")
set(CMAKE_CSharp_FLAGS "/platform:x64")
# Add in the .NET reference libraries.
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_REFERENCES
"Microsoft.CSharp"
"System"
"System.Core"
)