CMake include_directories 和 target_include_directories,头文件的搜索顺序

业务中遇到个问题,引用了 jsoncpp 这个库之后,需要引用其头文件,但其头文件进一步引用了 jsoncpp 安装位置的其他头文件,但如果一台 Linux 机器上在不同的路径安装了两个不同版本的 jsoncpp 库,并且这两个路径都是该项目的头文件包含路径,那么,编译器会先找哪个路径下的头文件呢?带着这个问题,我开始翻阅 camke 的官方文档,由于是全英文版,读起来还是很费劲,因此先翻译出来。
翻译完之后发现,include 的顺序跟翻译关系不大,因此,又将文档翻译写到了后半段,而把实验写到了前半段。

头文件搜索顺序

先说结论,编译器会按照 CMake 脚本给出的 include 路径顺序从头到尾(从左往右)依次搜索,如果在前面找到了相应的头文件,则不再去后面搜索。

实验

目录结构

CMake include_directories 和 target_include_directories,头文件的搜索顺序_第1张图片

文件内容

// include1/includea.h
int i = 1;
// include2/includea.h
int i = 2;
// src/src.cpp
#include "includea.h"
#include 
using namespace std;
int main() {
	cout << "Hello, " << a << endl;
}
cmake_minimum_required(VERSION 3.10.0)
project(includeorder)
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)
add_executable(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include2 ${PROJECT_SOURCE_DIR}/include1)

编译后执行结果:Hello, 2

cmake_minimum_required(VERSION 3.10.0)
project(includeorder)
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)
add_executable(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include1 ${PROJECT_SOURCE_DIR}/include2)

编译后执行结果:Hello, 1

文档翻译

include_directories

添加包含路径到构建任务。
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...]
给编译器添加给出的路径,用来搜索包含文件。相对路径将被翻译成相对于当前脚本所在目录的相对路径。
这些包含目录将被添加到当前 CMakeLists 文件的 INCLUDE_DIRECTORIES 目录属性中。他们同时也会被添加到当前 CMakeLists 文件中的每一个构建目标的目标属性中。目标属性的值是被生成器使用的。
这些路径会被默认添加到当前的包含路径列表的尾部,这种默认行为也可以通过修改参数 CMAKE_INCLUDE_DIRECTORIES_BEFOREON 来修改。显式使用 AFTERBEFORE,你可以选择添加到包含列表的头部或者尾部,与默认行为无关。
如果使用 SYSTEM 参数,在某些平台上将会告知编译器这些路径是系统包含路径。使用该参数可以实现一些效果,例如让编译器跳过警告,或者那些固定安装的系统文件不会被认为是依赖项——参考编译器文档。
include_directories 的参数也可以借助语法 “$<…>” 使用 “生成器表达式”,参考 cmake-generator-expressions(7)指南来获取可用表达式。参考 cmake-buildsystem(7) 指南来获取关于定义构建系统属性的更多知识。
**注意:**建议使用 target_include_directores() 指令给单独的构建目标添加包含目录,并可选地传播(propagate)或导出他们给相关依赖(dependents)。

target_include_directories

给构建目标添加包函目录。

target_include_directories(<target> [system] [AFTER|BEFORE]
							<INTERFACE|PUBLIC|PRIVATE> [items1...]
							[<INTERFACE|PUBLIC|PRIVATE> [items2...]...])

针对给定的构建目标(target)定义编译时使用的包含路径。参数 必须是已被类似于 add_executable() 或者 add_library() 命令创建出来的,并且不能是一个 ALIAS TARGET。
明确使用参数AFTERBEFORE,你可以选择添加到尾部或者头部,不受默认行为影响。
参数 INTERFACEPUBLICPRIVATE 被用来指定接下来的参数的作用范围。
PRIVATEPUBLIC 会将 INCLUDE_DIRECOTRIES 属性添加给目标
PUBLICINTERFACE 参数会将 INTERFACE_INCLUDE_DIRECTORIES 属性添加给目标
关于 INCLUDE_DIRECTORIES 和 INTERFACE_INCLUDE_DIRECTORIES 的差异请点击链接。
接下来的参数定义了包含目录。
3.11 版本的新功能:允许设置 INTERFACE 参数来影响 导入的目标.。
对同一目标重复调用命令将添加按调用顺序相关参数到列表尾部。
在某些平台,使用 SYSTEM 参数可告知编译器这些目录是系统包含目录,这会让编译器忽略警告或者跳过包含相关头文件。此外,系统包含目录会在普通包含目录之后被搜索,无论在列表中的顺序如何。
如果 SYSTEM 参数和 PUBLIC 参数或者 INTERFACE 参数同时使用,这些包含目录会被添加到INTERFACE_SYSTEM_INCLUDE_DIRECTORIES 目标属性。
给到 target_include_directories 的参数可以是使用 $<...> 语法的生成器表达式。参考 cmake-generator-expressions(7)指南来获取可用表达式。参考 cmake-buildsystem(7) 指南来获取关于定义构建系统属性的更多知识。
定义的包含目录可以使绝对路径或相对路径,相对路径将被翻译为相对于当前脚本所在目录(CMAKE_CURRENT_SOURCE_DIR)的路径,转换成一个绝对路径存储在在相关目标属性中。如果路径是以生成器表达式开头的,该路径将被认为是一个绝对路径(有一个例外,下面注明),并且不做任何修改地使用。
以下内容由于跟主题关系不大。不再翻译,暂时贴上原文,有空再翻。
Include directories usage requirements commonly differ between the build-tree and the install-tree. The BUILD_INTERFACE and INSTALL_INTERFACE generator expressions can be used to describe separate usage requirements based on the usage location. Relative paths are allowed within the INSTALL_INTERFACE expression and are interpreted as relative to the installation prefix. Relative paths should not be used in BUILD_INTERFACE expressions because they will not be converted to absolute. For example

target_include_directories(mylib PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
  $<INSTALL_INTERFACE:include/mylib>  # /include/mylib
)

你可能感兴趣的:(C++,Tools,c++,CMake,compiler)