target_source defaults with FILE_SET too intrusive

I have a simple tree with sources like this:

Sources
--Instruments
----Instrument.h
----CMakeList.txt
--Source.cpp

I require the code using any headers from subfolders to include them explicitly in the path, so #include "Instruments/Instrument.h" is allowed while #include "Instrument.h" is not.

Now, if I’m being “old fashioned” I have the following code in CMakeList.txt:
target_sources(MyTarget PRIVATE Instrument.h) and everything works fine, my requirement is enforced by default. But I read about the new better way to use target_source and rewrite my code as follows: target_sources(MyTarget PRIVATE FILE_SET HEADERS FILES Instrument.h) and suddenly my rule is no longer enforced. Without my asking anywhere CMake just added ${CMAKE_CURRENT_SOURCE_DIR} to the include search dirs for my target.

First of all, it is counter-intutive: this command does what target_include_directories does w/o advertising it anywhere but the documentation.
Second, I don’t see any elegant way to actually prevent it adding the directory at all. Ofc, I could write: BASE_DIRS Nonsense and I would have ${CMAKE_CURRENT_SOURCE_DIR}/Nonsense added as the include which, technically, would prevent my rules violation but it is just a hack.

So, my question: why is this default there at all? Shouldn’t users set the BASE_DIRS explicitly instead?

For now, I stay away from this new feature but since with modules I will have to use FILE_SET I will have no choice but to somehow circumvent this default.

In other words, you want the base dir of your file set to be Sources. So just tell CMake that is what you want:

target_sources(MyTarget PRIVATE FILE_SET HEADERS
  FILES Instrument.h
  BASE_DIRS ..
)

Thanks for the immediate solution (or a better hack) to the cmake-code problem in the question but no, I actually don’t want any base dirs because I don’t even know what it means. I accidentally know what behavior I get from that command parameter but that’s all. Nowhere in the docs it actually says why I want to set that parameter. And since I don’t see why I want to set it, I just don’t.

Now about the solution with ... By using BASE_DIRS .. I make the current file know its place in the hierarchy which, in my opinion, is just wrong. You can have arbitrary long nesting and every time you need to calculate how many dots you need to write. That’s simply not good. The nested entities are managed by the entity at the top of the hierarchy, they don’t need to know what place they are in.

And given all that, imagine a new dev coming to the project with this line: target_sources(MyTarget PRIVATE FILE_SET HEADERS FILES Instrument.h BASE_DIRS ..)
He doesn’t know what BASE_DIRS is, doesn’t know that it behaves like target_include_directories so he just removes it, sees that nothing really broke and leaves it at that.

And all of that would be unnecessary if there was no default which is forcing particular (and undocumented) behavior on the user.

It does. Quoting the docs:

Target properties related to include directories are also modified by target_sources(FILE_SET) as follows:

File sets are more than just a “dumb” collection of files. That is simply one of their properties: a file set of headers ensures that the target containing it defines consumption of those headers appropriately.

If you’re only looking for a straightforward list of files with no extra properties, use the file-based signature of target_sources. A file set is more than that, by design.

1 Like

Ok, thank you. That part I definitely missed. So it doesn’t affect modules and I can continue to ignore FILE_SET in other places, good to know. The default is still questionable, though.