How does add_subdirectory(... SYSTEM) work?

Hi all, fairly new to cmake.

I have a library that I want to use in my project but want its files to be treated as system header files when included in my own code.

Here’s my directory structure:


In directory libs I have a CMakeLists.txt that has

add_subdirectory(blah/some_lib SYSTEM)

some_lib itself uses its own cmake setup to build. I expected all the include directories from some_lib to be using -isystem but they still use -I. I even tried adding

add_subdirectory(libs SYSTEM)

to my top level CMakeLists.txt but still no go.

Is this the right way to do what I want to do? Looking at the cmake documentation it’s not clear if the SYSTEM property will propagate to all subdirectories.


I don’t see the docs mentioning that it is recursive, so I’d assume that it is not.

But in that case I’d be forced to edit the third-party library’s CMakeList.txt files, which is rather undesirable.


Yeah. Maybe there’s another way around this though. @craig.scott ?

I was trying to follow discussions like this but they did not make it clearer what to do.

If I recall correctly, when you set the SYSTEM directory property to true, it propagates into any subdirectories that directory creates too. In other words, it should be acting recursively. It wouldn’t really make sense for it to work any other way, as already highlighted in comments above.

The behavior probably relates to details of the project not provided. You mentioned that the subdirectory uses its own cmake setup, but what does that mean? It is most likely related to that, but without any details about precisely how that is done and how you’re adding things from there into the main build, it’s hard to offer any further insight.

Could be. Is there a way to debug how the -I/-isystem paths are added to consumers?

I tried --debug-output/–log-level/–trace-source but it did not seem to have any info about that.

Depends what you mean by “how the -I/-isystem paths are added to consumers”. If you just want to see the compiler command lines so you can tell what flags are used, the method for that depends on what CMake generator you’re using. If you’re using Ninja, add --verbose to the ninja command line. If using Makefiles, add VERBOSE=1 to the make command line (I think this works, it’s been so long since I had to do that with Makefiles). If you’re using something else, you can probably find the details in the IDE settings somewhere.

I can see that the command lines don’t have the -isystem that I expected.

What I meant is to find where those command lines are created. Perhaps checking the SYSTEM property at that point can shed some light and I can somehow trace it back.

Can you show the contents of the file at libs/blah/some_lib/CMakeLists.txt? The details of that will probably shed some light on what’s really going on.


or-tools/CMakeLists.txt at stable · google/or-tools · GitHub

but when compilation lines for the consumers are created I see


instead of the expected


I’ll be honest, that’s a lot more detail than I have time to go through. I suggest you reduce your real world case down to a minimal project which demonstrates the problem. That exercise very often uncovers the underlying cause. If you can still reproduce the problem with a very minimal example, that will be more likely to be investigated further.

I can definitely understand :slight_smile: I have a decent workaround. As I get more comfortable with cmake, if I find the time, I’ll try to distill it and report it here.