Skip to content

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_DIRcmake后面紧跟的工程目录,或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_COMPILERC 编译器的路径
CMAKE_CXX_COMPILERC++ 编译器的路径
CMAKE_C_FLAGSC 编译器的编译选项
CMAKE_CXX_FLAGSC++ 编译器的编译选项
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

查找软件包,常用的两种搜索模式:

  1. 配置

    <PackageName>Config.cmake文件或<lowercasePackageName>-config.cmake文件。

    如果提供了版本,则是<PackageName>ConfigVersion.cmake

    <lowercasePackageName>-config-version.cmake搜索路径比较复杂,通常将配置文件路径添加到 CMAKE_PREFIX_PATH变量中。

  2. 模块

    搜索 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

安装文件

添加链接所需要的库文件路径。全局命令,它指定的路径将被所有后续创建的目标所使用。

用于设置全局链接库,这些库会链接到之后定义的所有目标(即通过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

常用方式:

  1. 设置CMake预先定义的变量

    cmake
    set(CMAKE_INSTALL_INCLUDEDIR "includes")
  2. 生成一个列表类型的变量

    cmake
    set(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...] ...])

区别于link_directories,target_link_directories是针对给定的目标

区别于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"
)

Last updated: