【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 命令来指定自定义的头文件路径。该命令会将指定的路径添加到编译器的头文件搜索路径中

include_directories(path/to/include/dir)

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

自定义链接库路径

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

link_directories(path/to/library/dir)

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

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

另一种实现的命令

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

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 中加入:

include_directories(hello-world)
include_directories(hello-world/hello)
include_directories(hello-world/world)

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

-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)。

测试工程目录结构:

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

调用关系:

                                ├────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 中应该写入:

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 中应该写入:

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 中应该写入:

target_link_libraries(hello-world PUBLIC hello)
target_include_directories(hello-world PUBLIC hello)

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

可执行文件依赖 libhello-world.so, libhello-world.so 依赖 libhello.so 和 libworld.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
仅作学习记录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值