下面的代码是怎么回事?
#include <cmath>
int
main(int argc, char *argv[])
{
}
当在最近安装的 Arch Linux 上使用 GCC 6.1.1 和标志 -isystem/usr/include
进行编译时,它会生成:
$ g++ -isystem /usr/include math.cc
In file included from math.cc:1:0:
/usr/include/c++/6.1.1/cmath:45:23: fatal error: math.h: No such file or directory
#include_next <math.h>
^
compilation terminated.
这是一个非常简单的例子;原来的命令行是:
$ g++ ... -isystem `llvm-config -includedir` ...
用于使用 LLVM 的程序的一部分。在 Arch Linux 上,LLVM 包安装时其头目录位于 /usr/include
中,这是 llvm-config
报告的目录。 ...包括 -Wextra
和 -Wconversion
,它们会在 LLVM header 中引起警告。与 -I
不同,-isystem
标志通过将 LLVM 目录视为“系统头”来防止出现警告。请参阅GNU C preprocessor documentation了解更多信息。
但是升级到 GCC 6.1.1 后,构建中会出现上述错误。
最佳答案
除了考虑目录包含“系统头文件”之外,-isystem
更改 header 搜索列表,将目录参数放在系统 header 目录的顶部。如果该目录已存在于搜索列表中,则会将其从当前位置删除。
(至少)从 GCC 6.1.1 开始,一些 C++ 头文件,例如 cmath
使用#include_next
为标准 C 头文件提供猴子补丁 C++ 支持。请参阅Why < cstdlib > is more complicated than you might think了解更多信息。例如,cmath
有行:
#include_next <math.h>
#include_next
,与正常的 #include
不同语句,在包含目录搜索路径的下一个条目开始搜索文件,而不是在搜索路径的顶部。自 -isystem /usr/include
移动/usr/include
在包含 cmath
的目录之前的搜索路径中, math.h
找不到。
详细来说,命令g++ -I /usr/include
的搜索路径是
/usr/include/c++/6.1.1
/usr/include/c++/6.1.1/x86_64-pc-linux-gnu
/usr/include/c++/6.1.1/backward
/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/include
/usr/local/include
/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/include-fixed
/usr/include
( /usr/include
是系统目录; -I
参数不执行任何操作。)
cmath
位于路径 /usr/include/c++/6.1.1/cmath
,这是搜索路径的第一个元素。 math.h
可以在
/usr/include/math.h
/usr/include/c++/6.1.1/math.h
使用#include_next <math.h>
在cmath
确保 math.h
的副本在/usr/include/c++/6.1.1
被跳过并且使用的副本是 /usr/include/math.h
.
与 g++ -isystem /usr/include
,搜索路径为
/usr/include
/usr/include/c++/6.1.1
/usr/include/c++/6.1.1/x86_64-pc-linux-gnu
/usr/include/c++/6.1.1/backward
/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/include
/usr/local/include
/usr/lib/gcc/x86_64-pc-linux-gnu/6.1.1/include-fixed
使用#include_next <math.h>
现在跳过/usr/include/c++/6.1.1
还有/usr/include
,它位于搜索路径的上方。因此,编译器无法找到 math.h
的任何副本。 .
总而言之,请谨慎使用 -isystem
因为它有消除错误的副作用;如果包含的目录已经在搜索路径中,则路径的顺序可能会被修改,GCC 可能会报告错误。
类似于下面的Makefile
解决方法应该足够了:
llvm.include.dir := $(shell $(LLVM_CONFIG) --includedir)
include.paths := $(shell echo | cc -v -E - 2>&1)
ifeq (,$(findstring $(llvm.include.dir),$(include.paths)))
# LLVM include directory is not in the existing paths;
# put it at the top of the system list
llvm.include := -isystem $(llvm.include.dir)
else
# LLVM include directory is already on the existing paths;
# do nothing
llvm.include :=
endif
这设置了 make
变量llvm.include
为 -isystem <dir>
或者什么都不做,取决于是否确实需要。
关于gcc - 系统上的 -isystem 包含目录会导致错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46129786/