target_include_directories() vs headers in target_sources()

Looking for advice on how to think about when to add directories with headers in them as target_include_directories() vs adding every header file by hand using target_sources().

One thing I’ve noted is that I struggle to tell the linker which one to use if different headers with the same name (say utils.h) exists on different levels in a code base. Is target_sources() the most practical solution in that case?
Would appreciate some rules of thumb type advice on this, thanks.

1 Like

target_include_directories is relevant to the preprocessor (which is usually part of the compiler), since it allows to resolve #include preprocessor directives.

Adding headers with target_sources is relevant to IDEs (Visual Studio, Xcode, etc.) so they can display these header files, even if they are not directly compiled.

So these 2 functions are doing quite different things, and I can’t think of any situation where you would have to choose between them.

I hope this helps.

1 Like

I was asking myself the same question.

The documentation of target_sources says :

Specifies sources to use when building a target and/or its dependents.

There is no mention of display in the documentation of this function.

Are you not confused with the command source_group ?

The situation has changed with the advent of file sets. Now you can define a HEADERS file set with target_sources(), and it does affect the header search path in a way that you probably don’t need target_include_directories() any more. See the target_sources documentation for details.

3 Likes

Yes, I discovered file sets today wy reading one of your post on your web site (so I bought your book some hours ago…).
But I don’t really understand how to use them.

Especially FILE_SET <set> : what is this set name used for ?

In the Professional CMake book, there’s a section on file sets in “Chapter 35: Installing”. The next edition will split things out to earlier sections for more logical flow and to make file set details easier to find.

Especially FILE_SET <set> : what is this set name used for ?

The set name is primarily useful with install(TARGETS ... FILE_SET ...). This is a very convenient way to install headers that are in PUBLIC and INTERFACE header sets. You also need the set name when you are adding files to a file set progressively, such as when you have them spread across multiple directories and you want to add each directory’s files in its own target_sources() call.

In my case HEADERS file set could not replace target_include_directories(). My project structure is highly atomized, with lots of deeply nested subdirectories and private headers kept distinct from sources. target_include_directories() allows me to use #include without any paths preceding the header’s filename. With HEADERS file set using BASE_DIR the best I could do was to - as the argument says - set the base directory from which my paths had to be built in #includes. Adding to the BASE_DIR list any subdirectory of already added catalog leads to an error, and setting the FILES list doesn’t help the preprocessor.
I feel like file sets are currently focused on less complex project structure and helping the install() process.
If there is any way to satisfy such needs with target_sources() I would be glad to acknowledge it.

You’re not limited to just one header set named HEADERS - you can create multiple header sets that have overlapping BASE_DIRS.

Right. Somehow it didn’t work for me previously.

Now, I’m confused if there are any advantages of adding headers to file set’s FILES argument in my case, which is I’m not sharing the headers and I don’t need them to be available for installation. The software I develop isn’t destined to be included or linked anywhere.