CMake学习:CMake在静态库工程场景上应用

前面我们已经学习CMake在编译二进制程序上的应用了,但是在工程场景中不可避免要用到很多库,因此只会编译普通的二进制程序是没有意义的,必须要学会如何链接库。

本期先学习CMake如何用在静态库上。

相关代码代码上传至作者gitee:CMake 学习: CMake工具开发介绍仓库,虽然标记的是C++

目录

前言

示例

顶层

MyLib

include

src

app

编译链接

CMake的核心属性

目标(target)

核心目标类型详解

创建命令参数详解

总结

属性(Properties)

CMake常用API

[创建目标的 API](#创建目标的 API)

[按功能分类的目标属性设置 API(专用命令)](#按功能分类的目标属性设置 API(专用命令))

[通用目标属性操作 API](#通用目标属性操作 API)

[通用属性操作 API(适用于任意作用域)](#通用属性操作 API(适用于任意作用域))

[查询与调试类 API](#查询与调试类 API)

属性传递性相关概念(作用域说明)

总结

[目标操作 API](#目标操作 API)

[属性操作 API(通用,适用于多种作用域)](#属性操作 API(通用,适用于多种作用域))

CMake支持的类型

add_subdirectory

Install静态库

find_packge查找静态库

export

[export 与 install(EXPORT) 的区别](#export 与 install(EXPORT) 的区别)


前言

静态库的不经过链接过程,本质上只是一个目标归档文件。

打包为静态库并且使用的过程:

  1. 编译源文件,生成目标文件(.o
bash 复制代码
g++ -c file1.cpp file2.cpp file3.cpp

-c 选项告诉编译器只编译不链接。

执行后你会得到 file1.ofile2.ofile3.o 三个目标文件。

  1. 使用 ar 工具创建静态库
bash 复制代码
ar rcs libmylib.a file1.o file2.o file3.o

libmylib.a 是你指定的静态库名称,必须以 lib 开头,以 .a 结尾

r:插入或替换文件;c:创建库(若不存在);s:建立索引,加速链接。

  1. (可选)验证库内容
bash 复制代码
ar t libmylib.a

列出库中包含的目标文件,确认无误。

  1. 使用静态库编译主程序

假设你有一个主程序文件 main.cpp,它调用了库中的函数。编译命令如下:

bash 复制代码
g++ main.cpp -L. -lmylib -o myprogram

-L.:告诉链接器在当前目录(.)查找库文件。

-lmylib:指定要链接的库名为 mylib(编译器会自动补全为 libmylib.a)。

-o myprogram:指定输出的可执行文件名。

  1. 运行程序
bash 复制代码
./myprogram

示例

我们以一个简单地数学加减函数,编辑为库,使用静态库来编辑

项目目录结构是这样的

我们来逐步拆解

顶层

顶层CMakeLists.txt

bash 复制代码
#设置最低版本号
cmake_minimum_required(VERSION 3.18)
#项目名称
project(MathTest)
#目录层级添加
add_subdirectory(MyLib)#先执行MyLib目录的

add_subdirectory(app)#然后执行app目录的

因为app搜索不到头文件,因此我们还需要修改C/C++设置。在includePath中添加:${workspaceFolder}/MyMath/MyLib/include

或者也可以按CTRL +shift+P,选择"C/C++编辑配置",在下图的位置修改

MyLib

CMakeLists.txt

bash 复制代码
#1、收集库源代码
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
#2、添加构建目标
add_library(MyMath STATIC ${SRC_LIST})
#3、库使用要求
target_include_directories(MyMath
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include  
)

include

math.h

bash 复制代码
int add(int a,int b);
int sub(int a, int b);

src

add.cpp

cpp 复制代码
#include"math.h"
int add(int a ,int b)
{
    return a+b;
}

sub.cpp

cpp 复制代码
#include"math.h"
int sub(int a ,int b)
{
    return a-b;
}

app

CMakeLists.txt

bash 复制代码
#搜索文件列表
file(GLOB SRC_LIST "*.cpp")

#添加依赖目标
add_executable(main ${SRC_LIST})
#添加依赖库列表
target_link_libraries(main PRIVATE MyMath)

main.cpp

cpp 复制代码
#include<iostream>
#include"math.h"

int main()
{
    std::cout<<"3+4="<<add(3,4)<<std::endl;
    std::cout<<"3-4="<<sub(3,4)<<std::endl;
    return 0;
}

编译链接

随后创建build目录编译链接运行

这里运行的时候大家会发现可执行文件跑到了build目录下的app目录中

源代码目录顶层有一个顶层CMakeLists.txt,构建目录build对应有一个顶层的CMakeCache.txt与之对应。而它们的app、MyLib等也会一一对应

因此我们就会发现,目标定义在那里,二进制执行文件就会相对应地放在哪里。

我们同样也可以修改默认输出路径,只需要在CMakeLists.txt里添加即可。

bash 复制代码
#app目录下的CMakeLists.txt
#搜索文件列表
file(GLOB SRC_LIST "*.cpp")

#添加依赖目标
add_executable(main ${SRC_LIST})
#添加依赖库列表
target_link_libraries(main PRIVATE MyMath)
# 指定可执行文件(main)输出到源码顶层目录
set_target_properties(main PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
bash 复制代码
#MyLib目录下的CMakeLists.txt
#1、收集库源代码
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
#2、添加构建目标
add_library(MyMath STATIC ${SRC_LIST})
#3、库使用要求
target_include_directories(MyMath
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include  
)

# 指定静态库(MyMath)输出到源码顶层目录
set_target_properties(MyMath PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)

重新测试

我们会在build目录下发现可执行文件和库文件已经放到build下的bin和lib下

CMake的核心属性

前面我们学习过,CMake是基于目标的属性传递的现代化构建系统。拥有属于自己的核心属性:目标、属性和API,和CMake的属性传递机制一起,组成了CMake现代构建系统的和核心

目标(target)

核心目标类型详解

1. EXECUTABLE

创建命令add_executable(<name> [source1 source2 ...])

作用 :生成一个可执行文件。这是最基础的目标类型,用于构建应用程序或命令行工具。产物后缀根据平台自动确定(Windows 下为 .exe,Unix-like 下无后缀或自定义)。

典型产物maincurlmyapp.exe

2. STATIC

创建命令add_library(<name> STATIC [source1 source2 ...])

作用 :构建静态库。静态库是目标文件的归档文件,链接时会被完整复制到可执行文件中。产物后缀通常为 .a(Unix-like)或 .lib(Windows)。

典型产物libfoo.afoo.lib

3. SHARED

创建命令add_library(<name> SHARED [source1 source2 ...])

作用 :构建动态链接库(共享库)。动态库在运行时被加载,多个程序可共享同一份代码。产物后缀因平台而异:.so(Linux)、.dylib(macOS)、.dll(Windows,同时可能生成导入库 .lib)。

典型产物libfoo.sofoo.dll(及 foo.lib 导入库)

4. MODULE

创建命令add_library(<name> MODULE [source1 source2 ...])

作用 :构建插件模块。与共享库类似,但不会被自动链接到其他目标,通常用于通过 dlopenLoadLibrary 在运行时动态加载。产物后缀与共享库相同(如 .so.dll)。

典型产物libplugin.soplugin.dll

5. OBJECT

创建命令add_library(<name> OBJECT [source1 source2 ...])

作用 :编译源文件生成目标文件(.o/.obj),但不归档或链接。这些目标文件可以稍后被其他库或可执行文件引用,实现编译结果的重用。目标文件本身不生成独立的库文件,仅存在于 CMake 内部的对象库概念中。

产物说明 :没有直接的库文件,但可通过 $<TARGET_OBJECTS:objlib> 在其他目标中引用其编译后的目标文件。

6. INTERFACE

创建命令add_library(<name> INTERFACE)

作用 :不编译任何源文件,仅用于传递使用要求 (如头文件搜索路径、编译定义、链接选项)。常用于纯头文件库或仅提供接口的库。它可以链接其他目标,并将自身的 INTERFACE_* 属性传播给使用者。

产物说明:无任何构建产物,仅作为 CMake 中的逻辑目标存在。

7. IMPORTED

创建命令add_library(<name> <type> IMPORTED)<type> 可为 STATICSHAREDMODULEUNKNOWN

作用 :引用磁盘上已存在的外部构建产物 (如预编译的第三方库),而不在项目中重新构建。通过设置其 IMPORTED_LOCATION 等属性,可以像普通目标一样使用该库,便于依赖管理。

产物说明:不生成新文件,仅提供对已有文件的 CMake 目标封装。

8. ALIAS

创建命令add_library(<alias> ALIAS <target>)

作用 :为同项目内的现有目标(通常是库目标)创建一个别名,方便在不同目录或命名空间中引用。别名目标不可修改属性,也不产生新的构建,仅用于简化引用。

产物说明:无独立产物,完全指向原始目标。

创建命令参数详解

1. EXECUTABLE

  • 命令add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [source1 source2 ...])

  • 参数含义

    • <name>必需,可执行文件的逻辑目标名称,也是构建产物的基本名称(平台自动添加后缀)。

    • WIN32可选 ,在 Windows 上创建 GUI 应用程序(无控制台窗口),对应 WinMain 入口点。

    • MACOSX_BUNDLE可选 ,在 macOS 上创建应用程序包(.app 目录结构)。

    • EXCLUDE_FROM_ALL可选 ,将该目标排除在默认的 ALL 构建目标之外,需要显式构建。

    • source1 source2 ...可选,构建该可执行文件所需的源文件列表(支持生成器表达式)。

2. STATIC / SHARED / MODULE / OBJECT

命令add_library(<name> <type> [EXCLUDE_FROM_ALL] [source1 source2 ...])

  • 参数含义

    • <name>必需,库的逻辑目标名称,产物名称默认基于此(可自定义)。

    • <type>必需 ,库类型,取值为 STATICSHAREDMODULEOBJECT 之一。

    • EXCLUDE_FROM_ALL可选,同上,排除默认构建。

    • source1 source2 ...可选 ,构成该库的源文件列表。对于 OBJECT 库,源文件被编译为目标文件但不归档。

3. INTERFACE

命令add_library(<name> INTERFACE [EXCLUDE_FROM_ALL])

  • 参数含义

    • <name>必需,接口库的逻辑名称,用于传递使用要求。

    • INTERFACE必需,指明这是一个接口库(无源文件)。

    • EXCLUDE_FROM_ALL可选,同上。

4. IMPORTED

命令add_library(<name> <type> IMPORTED [GLOBAL])

  • 参数含义

    • <name>必需,为外部库在 CMake 中创建的别名。

    • <type>必需 ,外部库的实际类型,如 STATICSHAREDMODULEUNKNOWN(当类型不确定时)。

    • IMPORTED必需,关键字,表示这是一个导入目标。

    • GLOBAL可选,使目标在全局可见(默认仅在创建它的目录及子目录可见)。

5. ALIAS

命令add_library(<alias> ALIAS <target>)

  • 参数含义

    • <alias>必需,为现有目标创建的别名。

    • ALIAS必需,关键字。

    • <target>必需 ,同一项目中已存在的目标名称(必须是库目标,且不能是 ALIAS 本身)。

总结

类型 创建命令 作用说明
EXECUTABLE add_executable(<name> [sources...]) 生成可执行文件(应用程序/命令行工具)
STATIC add_library(<name> STATIC [sources...]) 构建静态库(.a / .lib),链接时被完整复制到可执行文件
SHARED add_library(<name> SHARED [sources...]) 构建动态链接库(.so / .dylib / .dll),运行时加载
MODULE add_library(<name> MODULE [sources...]) 构建插件模块(.so / .dll),用于 dlopen 等动态加载,不自动链接
OBJECT add_library(<name> OBJECT [sources...]) 编译源文件为目标文件(.o / .obj),不归档,可被其他目标引用
INTERFACE add_library(<name> INTERFACE) 纯头文件库或仅传递使用要求的目标,无编译产物
IMPORTED add_library(<name> <type> IMPORTED) 引用磁盘上已有的预编译库,不参与构建,仅提供目标接口
ALIAS add_library(<alias> ALIAS <target>) 为现有目标创建别名,方便引用,不产生新构建

属性(Properties)

属性类别 作用域 常用设置命令 常用获取命令 常用示例属性
全局属性 整个 CMake 运行环境 set_property(GLOBAL PROPERTY <name> <value>) get_property(<VAR> GLOBAL PROPERTY <name>) CMAKE_CXX_KNOWN_FEATURESIN_TRY_COMPILE
目录属性 当前目录及子目录 set_directory_properties(PROPERTIES <name> <value>) get_directory_property(<VAR> [DIRECTORY dir] <name>) INCLUDE_DIRECTORIESCOMPILE_DEFINITIONS
目标属性 单个目标(可执行文件/库) set_target_properties(<target> PROPERTIES <name> <value>) get_target_property(<VAR> <target> <name>) COMPILE_DEFINITIONSLINK_LIBRARIESOUTPUT_NAME
源文件属性 单个源文件 set_source_files_properties(<file> PROPERTIES <name> <value>) get_source_file_property(<VAR> <file> <name>) COMPILE_FLAGSLANGUAGEHEADER_FILE_ONLY
测试属性 单个测试(由 add_test 定义) set_tests_properties(<test> PROPERTIES <name> <value>) get_test_property(<test> <VAR> <name>) TIMEOUTWORKING_DIRECTORYENVIRONMENT
缓存变量属性 单个缓存变量 set_property(CACHE <varname> PROPERTY <name> <value>) get_property(<VAR> CACHE <varname> PROPERTY <name>) TYPEADVANCEDHELPSTRING
安装文件属性 install 命令生成的文件 set_property(INSTALL <file> PROPERTY <name> <value>) get_property(<VAR> INSTALL <file> PROPERTY <name>)

属性作用域与传播

属性类别 关键字 (若适用) 构建影响 传播范围 对目标的影响 核心作用
目标私有属性 PRIVATE 仅影响当前目标 不传播 直接影响当前目标的编译或链接行为。例如 PRIVATE 链接的库仅用于当前目标。 封装目标内部实现,隐藏依赖。
目标接口属性 INTERFACE 不影响当前目标 传播给所有依赖者 自身不产生构建,但为使用者提供要求。例如 INTERFACE_INCLUDE_DIRECTORIES 告知使用者所需的头文件路径。 定义目标的使用接口,便于依赖管理。
目标公有属性 PUBLIC 影响当前目标 传播给所有依赖者 既是自身构建的一部分,也成为其公共API的一部分。例如 PUBLIC 的编译定义会影响库和其使用者。 共享公共构建要求,保持依赖链一致。
目录作用域属性 影响目录下所有目标 向下传播给子目录 为目录及其子目录内的所有目标设置默认值。例如在根目录设置 CMAKE_CXX_STANDARD 实现项目范围的统一配置。
全局作用域属性 影响整个项目 全局可见 设置影响整个CMake运行环境的参数。例如查询编译器支持的C++特性。 存储全局状态和系统级信息。
继承属性 INHERITED 取决于原始设置 向上作用域链查找 当属性在当前作用域未设置时,允许其从父目录乃至全局继承值。 实现配置的层级覆盖与复用。

CMake常用API

创建目标的 API

add_executable

  • 作用:定义一个可执行文件目标。

  • 表达式

bash 复制代码
add_executable(<name> [WIN32] [MACOSX_BUNDLE] 
            [EXCLUDE_FROM_ALL] [source1] [source2...])

参数解析

  • <name>:目标名称,在项目中全局唯一,后续通过该名称引用。

  • WIN32:可选,Windows 平台下构建为 GUI 应用(不打开控制台窗口)。

  • MACOSX_BUNDLE:可选,macOS 下构建为应用程序 Bundle。

  • EXCLUDE_FROM_ALL:可选,该目标不会加入默认的 all 构建规则,需显式构建。

  • [source...]:源文件列表,可以是 .c/.cpp 等,也支持生成器表达式。

  • 效果 :创建一个名为 <name> 的目标,并可选地指定源文件;该目标后续可通过 target_... 系列命令配置编译、链接等属性。

add_library

  • 作用:定义一个库目标(静态、动态、对象库、接口库等)。

  • 表达式

bash 复制代码
add_library(<name> [STATIC | SHARED | MODULE | OBJECT | INTERFACE] 
                [source1...])

参数解析

  • <name>:目标名称。

  • STATIC:静态库(.a/.lib)。

  • SHARED:动态库(.so/.dll/.dylib)。

  • MODULE:插件模块,运行时动态加载,不链接到其他目标。

  • OBJECT:对象库,只编译源文件生成 .o 文件,不打包为库,可供其他目标链接。

  • INTERFACE:接口库,不编译源文件,仅用于传递依赖(如头文件库)。

  • EXCLUDE_FROM_ALL:同 add_executable

  • [source...]:源文件列表(接口库不能包含源文件,只能通过 target_sources 添加 INTERFACE 源文件)。

  • 效果 :创建一个库目标,类型由参数决定;接口库(INTERFACE)不产生实际构建输出,仅用于传递依赖。

add_custom_target

  • 作用:定义一个自定义目标,不产生实际构建产物,常用于执行命令或作为依赖聚合点。

  • 表达式

bash 复制代码
add_custom_target(<name> [ALL] [COMMAND ...] [DEPENDS ...])

参数解析

  • <name>:目标名称。

  • ALL:将该目标加入默认 all 构建。

  • COMMAND:指定要执行的命令(可多个),支持生成器表达式。

  • DEPENDS:依赖的其他目标或文件。

  • BYPRODUCTS:声明该命令会生成的副产品(如生成的文件),用于依赖链。

  • WORKING_DIRECTORY:执行命令的工作目录。

  • COMMENT:构建时输出的提示信息。

  • VERBATIM:保证参数正确转义,推荐始终使用。

  • USES_TERMINAL:在构建时开启终端交互(如需要输入密码时)。

  • SOURCES:添加源文件,仅用于 IDE 展示。

  • 效果:生成一个总是被视为"未构建"的目标,可用于触发自定义命令或管理构建顺序。

按功能分类的目标属性设置 API(专用命令)

这些命令分别设置目标的某类具体属性,底层实际修改的是目标的相应属性变量。

target_include_directories

  • 作用:为目标添加头文件搜索路径。

  • 表达式

bash 复制代码
​target_include_directories(<target> [SYSTEM] [AFTER|BEFORE] 
            <INTERFACE|PUBLIC|PRIVATE> [items...])

参数解析

  • <target>:目标名。

  • SYSTEM:可选,将路径视为系统目录(编译器会忽略该目录中的警告)。

  • AFTER|BEFORE:控制路径在包含列表中的顺序,默认 AFTER

  • <INTERFACE|PUBLIC|PRIVATE>:指定传播范围。

    • PRIVATE:仅当前目标使用,依赖者不继承。

    • PUBLIC:当前目标和依赖者都使用。

    • INTERFACE:仅依赖者使用,当前目标不编译源文件时使用(如头文件库)。

  • [items...]:路径列表(绝对路径、相对路径、生成器表达式)。

效果 :设置目标的 INCLUDE_DIRECTORIES 属性,并影响依赖传递。

  • 效果 :将路径添加到目标的 INCLUDE_DIRECTORIES 属性中,并根据作用域(PUBLIC/INTERFACE)传播给依赖者。

target_compile_definitions

  • 作用:为目标添加预处理器定义。

  • 表达式

bash 复制代码
target_compile_definitions(<target> <INTERFACE|PUBLIC|PRIVATE> [items...])

参数解析

  • <target>:目标名。

  • <INTERFACE|PUBLIC|PRIVATE>:传播范围。

  • [items...]:宏定义,格式 MACROMACRO=VALUE(无需 -D 前缀,CMake 自动处理)。

  • 效果 :将定义添加到目标的 COMPILE_DEFINITIONS 属性,并根据作用域传播。

target_compile_options

  • 作用:为目标添加编译选项。

  • 表达式

bash 复制代码
target_compile_options(<target> [BEFORE] 
        <INTERFACE|PUBLIC|PRIVATE> [items...])

参数解析

  • <target>:目标名。

  • BEFORE:可选,将选项添加到现有选项之前。

  • <INTERFACE|PUBLIC|PRIVATE>:传播范围。

  • [items...]:编译器标志,如 -Wall/W4,支持生成器表达式。

  • 效果 :将选项添加到目标的 COMPILE_OPTIONS 属性,并依作用域传播。

target_link_libraries

  • 作用:指定目标要链接的库或目标。

  • 表达式

bash 复制代码
target_link_libraries(<target> <INTERFACE|PUBLIC|PRIVATE> [items...])

参数解析

  • <target>:目标名。

  • <INTERFACE|PUBLIC|PRIVATE>:传播范围。

    • PRIVATE:仅当前目标链接,依赖者不继承。

    • PUBLIC:当前目标和依赖者都链接,且传递使用要求(如头文件路径、编译定义)。

    • INTERFACE:仅依赖者链接(用于接口库)。

  • [items...]:可以是另一个目标名、库文件路径、链接器标志、生成器表达式。

  • 效果 :设置目标的 LINK_LIBRARIES 属性,并处理传递性依赖(接口库、链接库路径等)。

target_link_options

  • 作用:为目标添加链接器选项(CMake 3.13+)。

  • 表达式

bash 复制代码
target_link_options(<target> [BEFORE] <INTERFACE|PUBLIC|PRIVATE> [items...])

参数解析

  • 参数含义与 target_compile_options 类似,区别是作用于链接阶段。

  • [items...]:链接器标志,如 -L/path-Wl,--rpath,支持生成器表达式。

  • 效果 :将选项添加到目标的 LINK_OPTIONS 属性,并依作用域传播。

target_sources

  • 作用:为目标追加源文件。

  • 表达式

bash 复制代码
target_sources(<target> <INTERFACE|PUBLIC|PRIVATE> [items...])

参数解析

  • <target>:目标名。

  • <INTERFACE|PUBLIC|PRIVATE>:传播范围。

    • PRIVATE:源文件仅用于当前目标编译。

    • PUBLIC:源文件用于当前目标和依赖者(很少见,主要用于对象库)。

    • INTERFACE:源文件仅由依赖者编译(常用于头文件库的 .h 文件,以便 IDE 显示)。

  • [items...]:源文件列表(支持生成器表达式)。

  • 效果 :将源文件添加到目标的 SOURCES 属性,并处理传递性(接口库可传递源文件)。

target_compile_features

  • 作用 :指定目标所需的编译特性(如 cxx_std_11)。

  • 表达式

bash 复制代码
target_compile_features(<target> <INTERFACE|PUBLIC|PRIVATE> [features...])

参数解析

  • <target>:目标名。

  • <INTERFACE|PUBLIC|PRIVATE>:传播范围。

  • [features...]:语言特性名称,如 cxx_std_11cxx_std_14c_std_99 等。CMake 会根据特性自动设置 CXX_STANDARD 等属性。

  • 效果 :设置 COMPILE_FEATURES 属性,CMake 会自动添加相应的编译选项(如 -std=c++11)。

target_precompile_headers

  • 作用:为目标指定预编译头文件(CMake 3.16+)。

  • 表达式

bash 复制代码
target_precompile_headers(<target> <INTERFACE|PUBLIC|PRIVATE> [header...])

参数解析

  • <target>:目标名。

  • <INTERFACE|PUBLIC|PRIVATE>:传播范围。

  • [header...]:头文件列表(<header.h>"header.h")。

  • REUSE_FROM <other_target>:复用另一个目标的预编译头文件,避免重复生成。

  • 效果 :将头文件标记为预编译头,生成对应的 PRECOMPILE_HEADERS 属性,提升编译速度。

通用目标属性操作 API

set_target_properties

  • 作用:直接设置一个或多个目标的一个或多个属性。

  • 表达式

bash 复制代码
set_target_properties(<target1> <target2> ... PROPERTIES 
            <prop1> <value1> <prop2> <value2> ...)

参数解析

  • <target1> <target2> ...:一个或多个目标名。

  • PROPERTIES:固定关键字。

  • <prop1> <value1> <prop2> <value2> ...:属性名和值对。

  • 效果 :将指定属性赋值为对应值,覆盖原有值。常用于设置 CXX_STANDARDPOSITION_INDEPENDENT_CODEFOLDER 等非传播性属性。

get_target_property

  • 作用:获取目标的某个属性值。

  • 表达式

bash 复制代码
get_target_property(<var> <target> <property>)

参数解析

  • <var>:用于存储结果的变量名。

  • <target>:目标名。

  • <property>:属性名。

  • 效果 :将属性值存入变量 <var>;若属性不存在,则 <var><property>-NOTFOUND

get_target_properties

  • 作用:一次性获取目标的多个属性。

  • 表达式

bash 复制代码
get_target_properties(<target> <prop1> <prop2> ...)

参数解析

  • <var>:存储结果的变量名,结果是一个分号分隔的列表。

  • <target>:目标名。

  • <prop1> <prop2> ...:多个属性名。

  • 效果 :将属性值依次存入以 <prop>_<target> 命名的变量(如 CXX_STANDARD_myexe)。

通用属性操作 API(适用于任意作用域)

CMake 中的属性可附加于多种对象(目录、目标、源文件、测试等)。以下命令适用于任何作用域,包括目标。

set_property

  • 作用:在指定作用域上设置属性(可追加、可覆盖)。

  • 表达式

bash 复制代码
set_property(<GLOBAL | DIRECTORY [dir] | TARGET [target...] 
            | SOURCE [src...] | TEST [test...] | INSTALL [file...] 
            | CACHE [entry...]>
            [APPEND] [APPEND_STRING] PROPERTY <name> [value...])

参数解析

  • <GLOBAL | DIRECTORY ...>:指定作用域。

    • GLOBAL:全局属性。

    • DIRECTORY [dir]:当前目录或指定目录的属性。

    • TARGET [target...]:一个或多个目标的属性。

    • SOURCE [src...]:一个或多个源文件的属性。

    • TEST [test...]:一个或多个测试的属性。

    • INSTALL [file...]:安装文件的属性。

    • CACHE [entry...]:缓存条目的属性。

  • APPEND:追加值(列表形式)。

  • APPEND_STRING:将值作为字符串追加(不添加分号)。

  • PROPERTY <name>:属性名。

  • [value...]:属性值。

  • 效果 :在指定对象上设置属性。APPEND 追加值,APPEND_STRING 拼接字符串。

get_property

  • 作用:获取指定作用域上属性的值,并提供灵活的输出控制。

  • 表达式

bash 复制代码
get_property(<var> <GLOBAL | DIRECTORY [dir] | TARGET <target> 
        | SOURCE <source> | TEST <test> | INSTALL <file> | CACHE <entry>>
        PROPERTY <name> [SET | DEFINED | BRIEF_DOCS | FULL_DOCS])

参数解析

  • <var>:存储结果的变量名。

  • 作用域选择器(同上,但只能指定单一对象)。

  • PROPERTY <name>:属性名。

  • 可选标志:

    • SET:返回布尔值,表示属性是否被显式设置。

    • DEFINED:类似 SET,但某些内部属性即使未显式设置也返回真。

    • BRIEF_DOCS:获取属性的简要文档。

    • FULL_DOCS:获取属性的完整文档。

  • 效果 :将属性值存入 <var>,可选查询属性是否存在(SET)或获取文档字符串。

define_property

  • 作用:定义自定义属性的文档和行为(是否可继承等)。

  • 表达式

bash 复制代码
define_property(<GLOBAL | DIRECTORY | TARGET | SOURCE 
                | TEST | VARIABLE | CACHE>PROPERTY <name> 
                [INHERITED] [BRIEF_DOCS <brief>] [FULL_DOCS <full>])

参数解析

  • <GLOBAL | DIRECTORY ...>:属性适用的作用域类型。

  • PROPERTY <name>:自定义属性名。

  • INHERITED:可选,表示该属性可以继承(如目录属性可继承给子目录)。

  • BRIEF_DOCS <brief>:简短文档字符串。

  • FULL_DOCS <full>:详细文档字符串。

  • 效果 :声明一个自定义属性,使其在 get_propertyBRIEF_DOCSFULL_DOCS 时有说明,并可指定是否自动继承。

查询与调试类 API

get_cmake_property

  • 作用:获取 CMake 内部的全局属性(如变量列表、目标列表等)。

  • 表达式

bash 复制代码
get_cmake_property(<var> <property>)

参数解析

  • <var>:存储结果的变量名。

  • <property>:CMake 内置属性名,如 VARIABLESCACHE_VARIABLESCOMMANDSMACROSTARGETS 等。

  • 效果 :将全局属性(如 TARGETSCACHE_VARIABLES)存入 <var>

get_directory_property

  • 作用:获取目录属性,也可间接获取目标属性(通过作用域)。

  • 表达式

bash 复制代码
get_directory_property(<var> [DIRECTORY <dir>] <prop>)

参数解析

  • <var>:存储结果的变量名。

  • DIRECTORY <dir>:可选,指定目录(绝对或相对路径)。

  • <prop>:目录属性名,如 INCLUDE_DIRECTORIESCOMPILE_DEFINITIONS 等。

  • 效果:获取当前目录或指定目录的属性值。

get_source_file_property / set_source_file_properties

  • 作用 :操作源文件级属性(如 LANGUAGECOMPILE_FLAGS 等)。

  • 表达式

bash 复制代码
get_source_file_property(<var> <file> <property>)

set_source_files_properties(<files...> PROPERTIES <prop1> <value1> ...)

参数解析

  • get_source_file_property

    • <var>:存储结果的变量名。

    • <file>:源文件路径(相对或绝对)。

    • <property>:属性名,如 LANGUAGECOMPILE_FLAGS 等。

  • set_source_files_properties

    • <files...>:一个或多个源文件。

    • PROPERTIES:关键字。

    • <prop1> <value1> ...:属性名值对。

  • 效果:获取或设置单个源文件的属性,这些属性可能影响编译命令。

属性传递性相关概念(作用域说明)

在上述专用命令(target_...)中,PUBLICPRIVATEINTERFACE 三个关键字决定了属性在目标依赖链中的传播方式:

作用域 效果
PRIVATE 属性仅对当前目标生效,不传播给依赖者。
INTERFACE 属性不对当前目标生效,但会传播给依赖者。
PUBLIC 属性对当前目标生效,并且也传播给依赖者。

这种设计使得依赖管理(如头文件路径、编译定义)可以在整个依赖图中自动传递,无需手动重复设置

总结

目标操作 API
分类 API 作用 表达式(核心语法) 效果
创建目标 add_executable 定义可执行文件目标 add_executable(<name> [source...]) 创建可执行目标,可指定源文件
add_library 定义库目标(静态/动态/接口等) `add_library(<name> [STATIC SHARED
add_custom_target 定义自定义目标(无产物) add_custom_target(<name> [COMMAND...] [DEPENDS...]) 生成一个"总是未构建"的目标,用于执行命令或聚合依赖
专用配置命令 target_include_directories 添加头文件搜索路径 `target_include_directories(<target> [PUBLIC PRIVATE
target_compile_definitions 添加预处理器定义 `target_compile_definitions(<target> [PUBLIC PRIVATE
target_compile_options 添加编译选项 `target_compile_options(<target> [PUBLIC PRIVATE
target_link_libraries 指定链接的库或目标 `target_link_libraries(<target> [PUBLIC PRIVATE
target_link_options 添加链接器选项 `target_link_options(<target> [PUBLIC PRIVATE
target_sources 追加源文件 `target_sources(<target> [PUBLIC PRIVATE
target_compile_features 指定所需编译特性 `target_compile_features(<target> [PUBLIC PRIVATE
target_precompile_headers 指定预编译头文件 `target_precompile_headers(<target> [PUBLIC PRIVATE
通用目标属性操作 set_target_properties 直接设置目标的一个或多个属性 set_target_properties(<targets...> PROPERTIES <prop1> <val1> <prop2> <val2> ...) 批量设置目标属性,如 CXX_STANDARDFOLDER
get_target_property 获取目标的单个属性值 get_target_property(<var> <target> <property>) 将属性值存入变量,若不存在则变量为 ...-NOTFOUND
get_target_properties 获取目标的多个属性值 get_target_properties(<target> <prop1> <prop2> ...) 将各属性值存入以 <prop>_<target> 命名的变量
查询与调试 get_cmake_property 获取全局 CMake 属性(如所有目标列表) get_cmake_property(<var> <property>) 将全局属性(如 TARGETSCACHE_VARIABLES)存入变量
属性操作 API(通用,适用于多种作用域)
分类 API 作用 表达式(核心语法) 效果
通用属性读写 set_property 在指定作用域(目标、目录、源文件等)上设置属性 set_property(<作用域> [APPEND] PROPERTY <name> [values...]) 为指定对象设置属性值,APPEND 追加值,APPEND_STRING 拼接字符串
get_property 获取指定作用域上属性的值 `get_property(<var> <作用域> PROPERTY <name> [SET DEFINED
define_property 定义自定义属性的文档及继承行为 define_property(<作用域> PROPERTY <name> [INHERITED] [BRIEF_DOCS <text>] [FULL_DOCS <text>]) 声明一个自定义属性,使其在 get_property 中可显示帮助信息,并可选自动继承
目录/源文件属性 get_directory_property 获取目录属性 get_directory_property(<var> [DIRECTORY <dir>] <prop>) 获取当前目录或指定目录的属性值
get_source_file_property 获取源文件属性(如 LANGUAGE get_source_file_property(<var> <file> <property>) 将源文件的指定属性存入变量
set_source_files_properties 设置源文件属性 set_source_files_properties(<files...> PROPERTIES <prop1> <val1> ...) 为源文件设置属性(如 COMPILE_FLAGS
全局属性查询 get_cmake_property 获取 CMake 全局属性 get_cmake_property(<var> <property>) 查询如 TARGETSDIRECTORIES 等全局信息
变量/缓存属性 set_property (CACHE) 设置缓存条目属性 set_property(CACHE <entry> PROPERTY <name> <value>) 修改缓存条目的属性(如 ADVANCEDTYPE
get_property (CACHE) 获取缓存条目属性 get_property(<var> CACHE <entry> PROPERTY <name>) 获取缓存条目的属性值

说明<作用域> 可以是 GLOBALDIRECTORYTARGETSOURCETESTINSTALLCACHE 之一,具体取决于操作对象。

CMake支持的类型

类型 创建命令 产物示例/说明
EXECUTABLE add_executable(<name> [source...]) 可执行文件 。这是最终的用户应用程序或工具。在不同平台上产物不同,例如 Linux 下为 main,Windows 下为 main.exe
STATIC add_library(<name> STATIC [source...]) 静态库 。它是目标文件(.o/.obj)的归档文件。在链接时,其内容会被复制到最终的可执行文件中。产物例如 Linux 下的 libfoo.a,Windows 下的 foo.lib
SHARED add_library(<name> SHARED [source...]) 动态链接库(共享库) 。它在运行时被加载,可以被多个程序共享。产物例如 Linux 下的 libfoo.so,macOS 下的 libfoo.dylib,Windows 下的 foo.dll(通常还会伴随生成一个用于链接的导入库 foo.lib)。
MODULE add_library(<name> MODULE [source...]) 模块库 。它是一种特殊的动态库,不用于链接 (即不能出现在 target_link_libraries() 的右侧),而是设计用于在运行时通过 dlopenLoadLibrary 等机制动态加载的插件 。产物例如 libplugin.soplugin.dll
OBJECT add_library(<name> OBJECT [source...]) 对象库 。它编译源文件,但不归档或链接成库文件 ,只生成中间的目标文件(.o/.obj)。这些目标文件集合可以通过 $<TARGET_OBJECTS:name> 在其他目标中引用,从而实现编译结果的重用,并避免重复编译 。
INTERFACE add_library(<name> INTERFACE) 接口库 。它不编译任何源文件,也不生成任何库文件 。它纯粹是一个逻辑目标,用于传递使用要求(如头文件搜索路径、编译定义等)。最常见的用途是表示仅头文件(header-only)库
IMPORTED add_library(<name> <type> IMPORTED) 导入库 。它用于引用磁盘上已存在的外部构建产物 (如第三方预编译库),而不在当前项目中重新构建它。通过设置其属性(如 IMPORTED_LOCATION),可以像普通目标一样使用它 。<type> 可以是 STATICSHAREDMODULEUNKNOWN

add_subdirectory

作用 :向当前构建中添加一个子目录(即子项目)。CMake 会进入该子目录,处理其中的 CMakeLists.txt 文件,并将其构建目标整合到当前构建树中。

表达式

cpp 复制代码
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])
复制代码

参数解析

  • source_dir:必需,子目录的路径(相对于当前 CMakeLists.txt 所在目录,也可以是绝对路径)。

  • binary_dir:可选,指定子目录对应的构建输出目录。若不指定,则默认使用 source_dir 相对于当前二进制目录的相对路径。

  • EXCLUDE_FROM_ALL:可选,子目录中的目标不会加入默认的 all 构建规则,除非显式依赖。

  • SYSTEM:可选(CMake 3.25+),将子目录视为系统目录,其包含路径将被视为系统路径,编译器会抑制其中的警告。

效果

  • CMake 会在处理当前 CMakeLists.txt 的过程中,暂停当前目录的解析,转而进入 source_dir 并执行其中的 CMakeLists.txt

  • 子目录中定义的所有目标(add_executableadd_library 等)都会被添加到当前构建树中,可以被父目录中的目标引用(如通过 target_link_libraries 直接使用目标名)。

  • 若指定了 binary_dir,子目录的构建产物会存放在该目录下,否则存放在默认位置。

典型使用场景

  • 将大型项目拆分为多个模块,每个模块放在独立子目录中,各自拥有独立的 CMakeLists.txt

  • 引用第三方库(如 add_subdirectory(third_party/foo))来集成其源码。

Install静态库

顶层CMakeLists.txt

cpp 复制代码
cmake_minimum_required(VERSION 3.18)

project(InstallMyMath
    LANGUAGES CXX
)

add_subdirectory(MyMath)

底层CMakeLists.txt

cpp 复制代码
# 1 收集源代码
file(GLOB SRC_LISTS "src/*.cpp")

# 2 添加构建目标
add_library(MyMath STATIC ${SRC_LISTS})

# 3 设置库的使用要求,也就是下游消费者必须包含的头文件搜索路径
target_include_directories(MyMath INTERFACE
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" # include
)

# 4 设置库的默认输出路径
set_target_properties(MyMath PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)

# 5 安装我们的静态库
include(GNUInstallDirs)
install(TARGETS MyMath
    EXPORT MyMathTargets
    DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib
)

# 6 安装头文件
install(DIRECTORY include/
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/math # /usr/local/include/math/math.h
    FILES_MATCHING PATTERN "*.h"
)

# 7 安装导出目标集合 到  构建树
export(EXPORT MyMathTargets
    FILE ${CMAKE_CURRENT_BINARY_DIR}/MyMathTargets.cmake
)

# 8 安装导出目标集合 到  安装树
install(EXPORT MyMathTargets
    FILE MyMathTargets.cmake
    NAMESPACE MyMath:: # MyMath::MyMath
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyMath # /usr/local/lib/cmake/MyMath/MyMathTargets.cmake
)

# 9 生成find_package 需要的 配置文件
include(CMakePackageConfigHelpers)
configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake
    INSTALL_DESTINATION "lib/cmake/MyMath"
)

# 10 安装配置文件到cmake 标准的安装路径
install(FILES 
    ${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake
    DESTINATION "lib/cmake/MyMath"
)

配置文件

bash 复制代码
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/MyMathTargets.cmake)

运行结果:

find_packge查找静态库

目录结构:

CMakeLists.txt:

cpp 复制代码
cmake_minimum_required(VERSION 3.10)

project(MyMathApp)
# 查找 MyMath 库
find_package(MyMath REQUIRED CONFIG)

# 添加可执行文件
add_executable(main main.cpp)

# 添加依赖关系
target_link_libraries(main PRIVATE MyMath::MyMath)

export

作用 :从当前构建树中导出一个或多个目标,并生成一个 CMake 配置文件(.cmake),描述这些目标的接口(包含目录、编译定义、链接库等)。其他项目可以通过 include 这个文件来导入这些目标,而无需先执行安装。

表达式

cpp 复制代码
export(TARGETS <target>... [NAMESPACE <namespace>] [APPEND] 
        FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES])

export(PACKAGE <PackageName>)

export(TARGETS ...) 形式

参数解析

  • TARGETS <target>...:要导出的目标列表(如库、可执行文件等)。

  • NAMESPACE <namespace>:可选,为目标名称添加前缀命名空间,导入时目标名会变为 <namespace><target>

  • APPEND:可选,如果 FILE 指定的文件已存在,则追加内容而非覆盖。

  • FILE <filename>:输出文件的路径(通常放在构建目录中,如 MyMathTargets.cmake)。

  • EXPORT_LINK_INTERFACE_LIBRARIES:可选(旧版兼容),用于控制是否导出链接接口库(现代 CMake 已通过 INTERFACE 属性自动处理)。

效果

  • 生成一个 CMake 脚本文件,其中包含 add_library(... IMPORTED) 等命令,用于在外部项目中导入这些目标。

  • 导出的目标会保留其所有 PUBLICINTERFACE 属性(如 INTERFACE_INCLUDE_DIRECTORIESINTERFACE_COMPILE_DEFINITIONS 等),因此导入后可直接使用。

  • 通常与 export(PACKAGE) 配合,使 find_package 能够在构建树中找到该包。

export(PACKAGE <PackageName>) 形式

作用 :将当前构建树注册到 CMake 的用户包注册表中,使 find_package 能在构建目录中直接找到该包(无需安装)。

参数

  • <PackageName>:包名称,例如 MyMath

效果

  • 在 CMake 的用户包注册表(~/.cmake/packages)中记录该包的构建目录位置。

  • 当其他项目调用 find_package(MyMath) 时,CMake 会先搜索注册表,从而找到当前构建树中的导出文件。

exportinstall(EXPORT) 的区别

特性 export install(EXPORT)
使用场景 构建树内共享(不安装) 安装后共享(部署到系统或用户目录)
生成文件位置 构建目录(如 ${CMAKE_BINARY_DIR} 安装目录(如 /usr/local/lib/cmake/
导入方式 include(...)find_package(配合 export(PACKAGE) find_package(依赖安装路径或 CMAKE_PREFIX_PATH)
目标名称 可直接使用(带命名空间) 安装后通常带有 IMPORTED 标记
依赖管理 导出文件依赖构建树中的其他目标 导出文件依赖安装后的文件路径

本期关于CMake在静态库上的应用方面的介绍到这里就结束了,后续我们还会更新动态库相关的知识点。敬请期待

封面图自取:

相关推荐
Evand J3 小时前
【MATLAB例程】多无人机协同巡逻仿真:基于长机-僚机模型的编队保持与串级PID控制
开发语言·matlab·无人机·控制·pid·串级pid
yoothey3 小时前
我对Java Web开发中多线程的困惑
java·开发语言·前端
周淳APP3 小时前
【React之Hooks原理、组件、状态管理浅谈】
开发语言·前端·javascript
森G3 小时前
28、视图基类 QAbstractItemView---------Model/View模型视图
c++·qt
不想看见4043 小时前
C++/Qt 使用 Tushare 获取股票信息
c++·qt·信息可视化
csbysj20203 小时前
SVG 渐变 - 线性
开发语言
迷藏4943 小时前
**发散创新:用 Rust实现高效共识算法——从 Raft到自研轻量级协议的实战演进**
java·开发语言·rust·共识算法
wuqingshun3141593 小时前
说说你对spring MVC的理解
java·开发语言·jvm