【C/C++编译】CMake定义头文件路径、链接库路径及链接选项:PRIVATE,INTERFACE,PUBLIC

文章目录

一、CMake常用变量

在 CMake 中,有许多常见的变量可用于配置和管理构建过程。以下是一些常见的变量,包括当前源码路径:

  • CMAKE_SOURCE_DIR当前 CMakeLists.txt 所在的源码目录的根路径
  • CMAKE_BINARY_DIR:构建目录的根路径,即构建生成的可执行文件、库和其他构建输出的存放位置
  • CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的源码目录的路径。
  • CMAKE_CURRENT_BINARY_DIR:当前处理的 CMakeLists.txt 所在的构建目录的路径。
  • CMAKE_CURRENT_LIST_DIR:当前处理的 CMakeLists.txt 所在的路径(源码目录或构建目录)。
  • CMAKE_CURRENT_LIST_LINE:当前正在处理的 CMakeLists.txt 的行号。
  • CMAKE_MODULE_PATH:一个用于指定额外的 CMake 模块(.cmake 文件)的搜索路径的列表
  • CMAKE_INCLUDE_CURRENT_DIR如果设置为 ON,则在构建过程中自动将当前处理的 CMakeLists.txt 所在的目录添加到包含路径中
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY库文件的输出目录
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY可执行文件的输出目录

以上是一些常用的 CMake 变量,其中包含了当前源码路径相关的变量。您可以在 CMakeLists.txt 文件中使用这些变量来设置路径、配置目录结构以及管理构建过程中的输出位置

二、自定义头文件、链接库路径的方法

自定义头文件路径

可以使用 include_directories 命令来指定自定义的头文件路径。该命令会将指定的路径添加到编译器的头文件搜索路径中

powershell 复制代码
include_directories(path/to/include/dir)

你可以多次调用 include_directories 命令,以添加多个头文件路径。

自定义链接库路径

可以使用 link_directories 命令来指定自定义的链接库路径。该命令会将指定的路径添加到链接器的库搜索路径中

powershell 复制代码
link_directories(path/to/library/dir)

你可以多次调用 link_directories 命令,以添加多个链接库路径

需要注意的是,尽量避免在 CMake 中使用 include_directories 和 link_directories 命令来处理第三方库的头文件和链接库路径。更好的做法是使用 find_package 命令或编写 Find 模块来查找和链接第三方库,这样可以更好地管理依赖关系和跨平台兼容性

另一种实现的命令

另外,对于特定的库 ,你还可以使用 target_include_directories 命令和 target_link_directories 命令,将自定义的头文件路径和链接库路径应用于特定的目标

powershell 复制代码
target_include_directories(target_name PUBLIC path/to/include/dir)
target_link_directories(target_name PUBLIC path/to/library/dir)

这样可以确保自定义路径只应用于特定的目标,并使得相关路径不会泄漏到其他目标中

通过以上方式,你可以在 CMake 中自定义头文件路径和链接库路径,以满足项目的需求。

两种命令的区别介绍

target_include_directories() 的功能完全可以使用 include_directories() 实现。但是我还是建议使用 target_include_directories()。为什么?保持清晰!

include_directories(header-dir) 是一个全局包含,向下传递。什么意思呢?就是说如果某个目录的 CMakeLists.txt 中使用了该指令,其下所有的子目录默认也包含了header-dir 目录

上述例子中,如果在顶层的 cmake-test/CMakeLists.txt 中加入:

powershell 复制代码
include_directories(hello-world)
include_directories(hello-world/hello)
include_directories(hello-world/world)

那么整个工程的源文件在编译时都会增加:

powershell 复制代码
-I hello-world -I hello-world/hello -I hello-world/world ...

各级子目录中无需使用 target_include_directories() 或者 include_directories()了。如果此时查看详细的编译过程(make VERBOSE=1)就会发现编译过程是一大坨,很不舒服。

当然了,在最终子目录的 CMakeLists.txt 文件中,使用 include_directories() 和 target_include_directories() 的效果是相同的

而使用 target_include/link_directories() 每一个目录都是一个模块,目录内部应将对外和对内的头文件进行区分,由模块的调用者决定模块是否被传递(PRIVATE,INTERFACE,PUBLIC)。

测试工程目录结构:

powershell 复制代码
cmake-test/                 工程主目录,main.c 调用 libhello-world.so
├── CMakeLists.txt
├── hello-world             生成 libhello-world.so,调用 libhello.so 和 libworld.so
│   ├── CMakeLists.txt
│   ├── hello               生成 libhello.so 
│   │   ├── CMakeLists.txt
│   │   ├── hello.c
│   │   └── hello.h         libhello.so 对外的头文件
│   ├── hello_world.c
│   ├── hello_world.h       libhello-world.so 对外的头文件
│   └── world               生成 libworld.so
│       ├── CMakeLists.txt
│       ├── world.c
│       └── world.h         libworld.so 对外的头文件
└── main.c

调用关系:

powershell 复制代码
                                ├────libhello.so
可执行文件────libhello-world.so
                                ├────libworld.so

关键字用法说明:

PRIVATE:私有的。生成 libhello-world.so时,只在 hello_world.c 中包含了 hello.h,libhello-world.so 对外的头文件------hello_world.h 中不包含 hello.h。而且 main.c 不会调用 hello.c 中的函数,或者说 main.c 不知道 hello.c 的存在,那么在 hello-world/CMakeLists.txt 中应该写入:

powershell 复制代码
target_link_libraries(hello-world PRIVATE hello)
target_include_directories(hello-world PRIVATE hello)

INTERFACE:接口。生成 libhello-world.so 时,只在libhello-world.so 对外的头文件------hello_world.h 中包含 了 hello.h, hello_world.c 中不包含 hello.h,即 libhello-world.so 不使用 libhello.so 提供的功能,只使用 hello.h 中的某些信息,比如结构体。但是 main.c 需要使用 libhello.so 中的功能。那么在 hello-world/CMakeLists.txt 中应该写入:

powershell 复制代码
target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello)

PUBLIC:公开的。PUBLIC = PRIVATE + INTERFACE。生成 libhello-world.so 时,在 hello_world.c 和 hello_world.h 中都包含了 hello.h。并且 main.c 中也需要使用 libhello.so 提供的功能。那么在 hello-world/CMakeLists.txt 中应该写入:

powershell 复制代码
target_link_libraries(hello-world PUBLIC hello)
target_include_directories(hello-world PUBLIC hello)

实际上,这三个关键字指定的是目标文件依赖项的使用范围(scope)或者一种传递(propagate)官方说明

可执行文件依赖 libhello-world.solibhello-world.so 依赖 libhello.solibworld.so

  1. main.c 不使用 libhello.so 的任何功能,因此 libhello-world.so 不需要将其依赖 libhello.so 传递给 main.c ,hello-world/CMakeLists.txt 中使用 PRIVATE 关键字;
  2. main.c 使用 libhello.so 的功能,但是libhello-world.so 不使用 ,hello-world/CMakeLists.txt 中使用 INTERFACE 关键字;
  3. main.c 和 libhello-world.so 都使用 libhello.so 的功能 ,hello-world/CMakeLists.txt 中使用 PUBLIC 关键字;

补充阅读:
CMake的链接选项:PRIVATE,INTERFACE,PUBLIC

参考链接:

https://zhuanlan.zhihu.com/p/631823872

https://zhuanlan.zhihu.com/p/82244559

仅作学习记录

相关推荐
起名字真南5 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
少年负剑去5 分钟前
第十五届蓝桥杯C/C++B组题解——数字接龙
c语言·c++·蓝桥杯
cleveryuoyuo5 分钟前
AVL树的旋转
c++
神仙别闹28 分钟前
基于MFC实现的赛车游戏
c++·游戏·mfc
小c君tt35 分钟前
MFC中 error C2440错误分析及解决方法
c++·mfc
木向1 小时前
leetcode92:反转链表||
数据结构·c++·算法·leetcode·链表
阿阿越1 小时前
算法每日练 -- 双指针篇(持续更新中)
数据结构·c++·算法
hunandede1 小时前
FFmpeg存放压缩后的音视频数据的结构体:AVPacket简介,结构体,函数
c++
skaiuijing1 小时前
Sparrow系列拓展篇:对调度层进行抽象并引入IPC机制信号量
c语言·算法·操作系统·调度算法·操作系统内核
hunandede2 小时前
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三:将AVFrame转换成AVPacket。视频编码,AVPacket 重要函数,结构体成员学习
c++·ffmpeg·音视频