-isystem on a system include directory causes errors

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.

Leave a Comment