1 概述
- 本文基于前文环境
本节目标:target_include_directories 用法
2 作用
- target_include_directories 的作用, 用于给固定目标指定头文件搜索路径。
- modern cmake之前,与之对应的是 include_directories。include_directories也是用于设置头文件搜索路径。
- 它俩对比
名称 | 区别 |
---|---|
target_include_directories | 为指定的目标设置头文件搜索路径设置 |
include_directories | 为当前CMakeLists.txt中的项目设定头文件搜索路径设置 |
- 也就是说,CMakeLists.txt脚本中,可创建多个项目
- 当使用target_include_directories时,只能为某一个项目设置头文件设置
- 当使用include_directories时,当前脚本文件中的所有项目都会使用include_directories参数中的路径。 具有传递性(下面说)
3 一个例子说明
- 目录结构:
│ CMakeLists.txt
│
├─Common
│ CommonOutput.cmake
│
├─include
│ Typedef.h
│
└─src
main.cc
- 有这样一个 CMakeLists.txt 脚本文件, 当前脚本中创建了项目A、B和C, 而这三个项目中,main.cc 又需要 ${CMAKE_CURRENT_SOURCE_DIR}/include 目录下的头文件typedef.h
# 指定CMake脚本解析的最低版本,
cmake_minimum_required(VERSION 3.18)
# 指定项目
project(HelloCMake)
# 引入脚本:参数为脚本文件的全路径
include(${CMAKE_CURRENT_SOURCE_DIR}/Common/CommonOutput.cmake)
# -------------------------------------------
# 项目A
add_executable(ProjectA
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# -------------------------------------------
# 项目B
add_executable(ProjectB
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
# -------------------------------------------
# 项目C
add_executable(ProjectC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
上面这段脚本构建没有问题。不过项目编译却无法通过编译, 因为没有指定typedef.h文件的搜索路径。
3.1 如果通过项目编译?
- 设置项目的头文件搜索路径, 下面是解决方法
3.2 使用include_directories
- include_directories用法简单,括号内写路径即可。
- 创建项目A之前,增加下一脚本
# 参数为: 当前脚本目录/include目录所在绝对路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
- 此时,CMakeLists.txt完整脚本为
# 指定CMake脚本解析的最低版本,
cmake_minimum_required(VERSION 3.18)
# 指定项目
project(HelloCMake)
# 引入脚本:参数为脚本文件的全路径
include(${CMAKE_CURRENT_SOURCE_DIR}/Common/CommonOutput.cmake)
# 参数为: 当前脚本目录/include目录所在绝对路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# -------------------------------------------
# 项目A
add_executable(ProjectA
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# -------------------------------------------
# 项目B
add_executable(ProjectB
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# -------------------------------------------
# 项目C
add_executable(ProjectC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
- 执行项目编译, 可见编译结果为通过:
[build] ProjectC.vcxproj -> C:\A\publish\x64\bin\debug64\ProjectC.exe
[build] ProjectB.vcxproj -> C:\A\publish\x64\bin\debug64\ProjectB.exe
[build] ProjectA.vcxproj -> C:\A\publish\x64\bin\debug64\ProjectA.exe
3.3 使用target_include_directories
- 需要为 每个 项目设置头文件搜寻路径设置
# 为每个项目指定头文件搜索路径
target_include_directories( ProjectA PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories( ProjectB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories( ProjectC PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
- 此时完整CmakeLists.txt脚本
# 指定CMake脚本解析的最低版本,
cmake_minimum_required(VERSION 3.18)
# 指定项目
project(HelloCMake)
# 引入脚本:参数为脚本文件的全路径
include(${CMAKE_CURRENT_SOURCE_DIR}/Common/CommonOutput.cmake)
# -------------------------------------------
# 项目A
add_executable(ProjectA
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# -------------------------------------------
# 项目B
add_executable(ProjectB
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# -------------------------------------------
# 项目C
add_executable(ProjectC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# 为每个项目指定头文件搜索路径
target_include_directories( ProjectA PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories( ProjectB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories( ProjectC PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
- 编译项目,通过。
[build] ProjectB.vcxproj -> C:\A\publish\x64\bin\debug64\ProjectB.exe
[build] ProjectA.vcxproj -> C:\A\publish\x64\bin\debug64\ProjectA.exe
[build] ProjectC.vcxproj -> C:\A\publish\x64\bin\debug64\ProjectC.exe
5 使用注意
- 如果你的CMakeLists.txt脚本中只有单个项目, 那么, target_include_directories 和 include_directories的效果是一样的
- 如果CMakeLists.txt脚本中存在多个项目,建议使用target_include_directories,避免 某些项目引用到其他用不到的目录而引发的问题。
- Modern CMake中 支持 target_include_directories 用法。
- 简明扼要
- include_directories: 1 VS 多
- target_include_directories: 1 VS 1
5.1 include_directories具有传递性
- 什么意思呢? include_directories 为会 子目录中的项目增加头文件路径的设置。
- 还是上面的那个目录结构,此时多了一个文件夹demo, demo目录下也有一个CMakeLists.txt脚本,demo目录下CMakeLists.txt脚本中创建了一个名为 ProjectDemo的项目。
- 目录
.
│ CMakeLists.txt
│
├─Common
│ CommonOutput.cmake
│
├─demo
│ CMakeLists.txt
│ main.cc
│
├─include
│ Typedef.h
│
└─src
main.cc
- 其中,demo目录下CMake脚本内容仅仅为
cmake_minimum_required(VERSION 3.18)
# 指定项目
project(ProjectDemo)
# -------------------------------------------
# 项目A
add_executable(ProjectDemo
${CMAKE_CURRENT_SOURCE_DIR}/main.cc
)
- 而demo目录下的main.cc中的代码 没有 引用 顶层目录/include 目录下的任何文件
#include <iostream>
int main(int argc, char*argv[])
{
std::cout << "hello" << std::endl;
return 0;
}
- 回到顶层的CMakeLists.txt中,代码略微的不一样, 增加 add_subdirectory(demo) 用于创建子项目。
# 指定CMake脚本解析的最低版本,
cmake_minimum_required(VERSION 3.18)
# 指定项目
project(HelloCMake)
# 引入脚本:参数为脚本文件的全路径
include(${CMAKE_CURRENT_SOURCE_DIR}/Common/CommonOutput.cmake)
# 引入头文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# -------------------------------------------
# 项目A
add_executable(ProjectA
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc
${CMAKE_CURRENT_SOURCE_DIR}/include/typedef.h
)
# 子项目 Demo
add_subdirectory(demo)
请允许我再总结下: ProjectDemo没有引用 typedef.h文件。 ProjectA项目引用了 Typedef.h文件。 typedef.h位于顶层CMakeLists.txt文件所在目录/include目录下
- 5.2 构建, 查看传递性
- 构建 [5.1] 中的脚本,使用VS2019打开,查看 ProjectDemo项目属性:
- ProjectDemo 明明没有引用,而项目设置中却出现了 对目录的引用设置。
- 这就是 include_directories 的传递性。使用需要注意
6 target_include_directories 使用
- target_include_directories 的作用是为具体的目标设置 INCLUDE_DIRECTORIES 和 INTERFACE_INCLUDE_DIRECTORIES 属性。
- PRIVATE 和 PUBLIC 关键字是用来设定目标的 INCLUDE_DIRECTORIES属性
- INTERFACE 和 PUBLIC 关键字是用来设定目标的 INTERFACE_INCLUDE_DIRECTORIES属性
- 区别:
- PRIVATE: 仅当前目标使用,不会将 PRIVATE后 紧跟的参数进行传递
- PUBLIC: PUBLIC 后面紧跟的参数, 自己使用外, 也传递给给别人使用
- INTERFACE: INTERFACE 后面紧跟的参数,自己不用,传递给别人使用
这三个关键字都是用来限定 其后紧跟的参数的范围。 可见: PUBLIC 就是 PRIVATE 和 INTERFACE 两者的结合 。
7 我的使用经验
- 模块或者项目内,尽量使用 PRIVATE 达到范围使用最小化原则,避免一些不必要的麻烦。