业务中遇到个问题,引用了 jsoncpp 这个库之后,需要引用其头文件,但其头文件进一步引用了 jsoncpp 安装位置的其他头文件,但如果一台 Linux 机器上在不同的路径安装了两个不同版本的 jsoncpp 库,并且这两个路径都是该项目的头文件包含路径,那么,编译器会先找哪个路径下的头文件呢?带着这个问题,我开始翻阅 camke 的官方文档,由于是全英文版,读起来还是很费劲,因此先翻译出来。 翻译完之后发现,include 的顺序跟翻译关系不大,因此,又将文档翻译写到了后半段,而把实验写到了前半段。
头文件搜索顺序
先说结论,编译器会按照 CMake 脚本给出的 include 路径顺序从头到尾(从左往右)依次搜索,如果在前面找到了相应的头文件,则不再去后面搜索。
实验
目录结构
文件内容
int i = 1;
int i = 2;
#include "includea.h"
#include <iostream>
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_BEFORE 为 ON 来修改。显式使用 AFTER 或 BEFORE ,你可以选择添加到包含列表的头部或者尾部,与默认行为无关。 如果使用 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)定义编译时使用的包含路径。参数 <target> 必须是已被类似于 add_executable() 或者 add_library() 命令创建出来的,并且不能是一个 ALIAS TARGET。 明确使用参数AFTER 或 BEFORE ,你可以选择添加到尾部或者头部,不受默认行为影响。 参数 INTERFACE ,PUBLIC 和 PRIVATE 被用来指定接下来的参数的作用范围。 PRIVATE 和 PUBLIC 会将 INCLUDE_DIRECOTRIES 属性添加给目标 <target> 。 PUBLIC 和 INTERFACE 参数会将 INTERFACE_INCLUDE_DIRECTORIES 属性添加给目标 <target> 。 关于 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>
)
|