In addition to considering the directory to contain "system headers", -isystem
alters the header search list, putting the directory argument at the top of the system header directories. If the directory already exists in the search list, it is removed from its current location.
As of (at least) GCC 6.1.1, some C++ headers such as cmath
use #include_next
to monkey-patch C++ support for the standard C headers. See Why < cstdlib > is more complicated than you might think for more information. For example, cmath
has the line:
#include_next <math.h>
#include_next
, unlike the normal #include
statement, starts the search for the file at the next entry in the include directory search path, rather than at the top of the search path. Since -isystem /usr/include
moves /usr/include
in the search path before the directory containing cmath
, math.h
cannot be found.
In detail, the search path for the command g++ -I /usr/include
is
/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
is a system directory; the -I
argument does nothing.)
cmath
is at the path /usr/include/c++/6.1.1/cmath
, which is the first element of the search path. math.h
can be found in
/usr/include/math.h
/usr/include/c++/6.1.1/math.h
The use of #include_next <math.h>
in cmath
ensures that the copy of math.h
in /usr/include/c++/6.1.1
is skipped and that the copy used is /usr/include/math.h
.
With g++ -isystem /usr/include
, the search path is
/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
The use of #include_next <math.h>
now skips /usr/include/c++/6.1.1
but also /usr/include
, which is above it in the search path. As a result, the compiler cannot find any copy of math.h
.
To summarize, be cautious about using -isystem
for its error-silencing side-effects; if the directory being included is already on the search path, the order of the path may be modified and GCC may report errors.
Something like the following Makefile
work-around should suffice:
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
This sets the make
variable llvm.include
to be either -isystem <dir>
or nothing, depending on if it is actually needed or not.