IDEs and header files

Sample project:
add_library( a )
target_sources( a PRIVATE a.c PUBLIC a.h )

add_library( b )
target_sources( b PRIVATE b.c PUBLIC b.h )
target_link_libraries( b PUBLIC a )

When I bring this up in my IDE, target a has headers ‘a.h’, but target b has headers ‘a.h’, ‘b.h’. It seems that all the public sources in a are being imported into the visible space of b.

On the one hand, this is useful if I want to be able to easily view all the headers. But it also means that (in a large project) the headers that belong to b directly get lost among all the headers of all b’s dependencies.

Am I doing something wrong here?

Note that all of a’s headers are already available in my project as source files from a. But CMake makes them appear a second time as source files of b.

target_sources adds source files to a target, just like target_compile_options adds compilation options to it etc.

With all these commands, PRIVATE means “this applies only to the target itself” and PUBLIC means “this applies both to the target itself and to anyone who links to it.”(1)

In other words, marking a source files as PUBLIC means “add this source file to this target and also to all targets which link to it.” In particular, it does not mean “this header is intended for public consumption.”

So, it’s very rarely useful to mark a source file as PUBLIC, and I can’t think of a situation where it would make sense to do so for a header. In particular, note that it does not give the consuming target any include-path access to the file.

As a rule of thumb, don’t mark source files as anything other than PRIVATE. If you do encounter a situation where you need to deviate from that, you will probably know it by just how extraordinary it is.

If you wanted to signal "a.h is a public API header of the library," you’d actually do that by setting the library’s target_include_directories(a PUBLIC ...) so that whoever links against a gets the correct paths to be able to do #include "a.h".


(1) And for completeness, INTERFACE means "this applies only to those who link against the target. So PUBLIC is the union of PRIVATE and INTERFACE.

As an example of a PUBLIC source use is the source for many RTOSes. You’d prefer it to be in the library, but it needs to be compiled with the same headers and options as the application, since many of the lightweight rtoses configure the available resources and options with #defines in a config,h file to be included by the RTOS and application.

1 Like