-I dir vs. -isystem dir

So here's the difference I've found by running some experiments. Imagine the following setup:

my_std_lib/stdio.h

#ifndef _CUSTOM_STDIO_H

void test() {}

#endif

#include_next <stdio.h>
#include_next <custom.h>

my_user_lib/custom.h

#ifndef _CUSTOM_HEADER_H

void custom_func() {}

#endif

main.cpp

#include "stdio.h"

int main() {
  test();
  custom_func();
  printf("Hello world!");
  return 0;
}

If you compile using g++ -isystem my_std_lib -isystem my_user_lib main.cpp everything will work fine.

However, g++ -isystem my_std_lib -I my_user_lib main.cpp will result into an error

In file included from main.cpp:1:
my_std_lib/stdio.h:10:15: fatal error: 'custom.h' file not found
#include_next <custom.h>
              ^~~~~~~~~~
1 error generated.

So what is going on?

To my understanding, when I write #include "stdio.h", GCC will start traversing the list of available header files until it finds my_std_lib/stdio.h. Directive #include_next <custom.h> at the end of this file tells the compiler to search for a custom.h by traversing include directories from its current position onwards.

When I add my_user_lib to the list of directories using -I flag, it appears before all the system directories in the directory list. Therefore, it appears in the list before my_std_lib directory and the #include_next fails.

The same would happen if I were to compile using g++ -isystem my_user_lib -isystem my_std_lib main.cpp. Apparently, directories are added to the list in the same order the flags are specified, so, again, my_user_lib will come before my_std_lib.

So in a nutshell, -I and -isystem differ in a way they add their target to the list of include directories.


One way to view this is to use headers that you control with -I and the ones you don't (system, 3rd party libs) with -isystem. The practical difference comes when warnings are enabled in that warnings which come from -isystem headers will be suppressed.


You should use -I to specify the location of your headers.

The files you specify with -isystem are searched after -I is processed and receive a special treatment by gcc (the same as standard system headers).


From the gcc documentation for -I:

Add the directory dir to the head of the list of directories to be searched for header files. This can be used to override a system header file, substituting your own version, since these directories are searched before the system header file directories. However, you should not use this option to add directories that contain vendor-supplied system header files (use -isystem for that). If you use more than one -I option, the directories are scanned in left-to-right order; the standard system directories come after.

If a standard system include directory, or a directory specified with -isystem, is also specified with -I, the -I option will be ignored. The directory will still be searched but as a system directory at its normal position in the system include chain. This is to ensure that GCC's procedure to fix buggy system headers and the ordering for the include_next directive are not inadvertently changed. If you really need to change the search order for system directories, use the -nostdinc and/or -isystem options.

So -I is probably the preferred option to specify the location of your header files, except for special cases such as vendor-supplied system headers.

Tags:

C

Gcc