target_link_libraries adds files to vcproj

When I used target_link_libraries in cmake 3.21.0-rc2 on a test application in a solution that has a static lib, all public headers are being added to the vcproject.

I expect the directories, flags, etc, but I do want files to be added to the project!
I did have at one point some public sources and was really surprised when cpp sources from the lib were added to MY target.

I cannot think of a valid use case (in c++) for adding actual .h and .cpp files from another target into mine.

This indicates that the library you are linking has target_sources(PUBLIC). This means, to CMake, that these sources should be added to consumers as well. I would fix whatever the target exporting headers as public sources to not do that.

Headers? Yeah, they don’t make much sense. But there are use cases for sources. The main one is Qt5::QtMain adding a source containing the QtMain forwarding logic to executables linking to Qt.

I am glad we agree on headers, no need to add them to my project. Especially sense the compiler is completely going to ignore them. It uses the #include and the include paths, The are added to a project as a convenience for you to edit headers in the project they are associated with.

The entire point of a static lib is the code is built once. The users of this code “Link” using the linker and the public header iff it is included in the cpp. By injecting sources into my project, the compiler is forced to compile it. I cannot provide a varient or modified version of that main for example without modifing the upstream library that has crossed the line.

My god. think of something like a graphics engine that has directx9,10,11, vulcan, opengl and openglES backends. ALL these headers must be PUBLIC but I may only want one. What about a Widget library that has 100’s or 1000’s of widgets spread across 20-50 headers. All the headers must be public, but I may only want 2 or 3 of them.

I still cannot see the use case for the sources.

  1. it is MY project, I would want to control it. What if I needed a modified main in that example. I am stuck, I now need to go thru contortions or have the upstream modify.

  2. What if upstream comes up with more than one possible but mutually exclusive implmentations. This policy breaks that.

  3. It is a code snip-it.
    Now maybe that is an Idea? a snipit library but lets call that one target_import_snipit or something.

  4. I think we are violating a c/c++ principal. Static libs exist to build the code once. then let each user of the code “Link” to it with the linker and an include in the path placed in my cpp file. We should not try to piggy back this on c/c++'s well established Linker. A new Project feature and not a hack may be powerful for includeing snipits, but these are by definitioin NOT compiled into the library the are just there to pull into your project and CMake can provide a separate feature for that.

Public sources have uses (mentioned above) and they will not be going away.

Mixing oil and water is always going to need something extra to make them play well together. I don’t find this surprising.

I’m not sure what principle you’re talking about here. Static libraries, shared libraries, the linker, and how they are organized and used are outside the scope of the language specifications because this is more of a platform decision than a language decision.

The problem you have is that a project is exporting sources improperly. This is no different than a target handing you a flag like -Wnot-a-warning-flag or -fbreak-the-world as part of its usage requirements. Fix the target that is broken. Maybe there’s something to be improved with header installation logic based on the source listing, but that’s what should be fixed (@kyle.edwards).

I understand what is going on. PUBLIC means I can include not that they get automatically added to the project. I admit I do not understand the use mentioned above. A bit more detail, I have not played with QT in years. I need some convincing that the QT use case is valid.

In this case I removed my PUBLIC sources, I control the lib, no problem. But it seems wrong or at least counter intuitive. on a command target_LINK_libraries. L I N K

I will forget we said QT needs this, if you will forget I said c++ :slight_smile:

Speaking purely from an abstract meta-project perspective
target_sources controls the sources in my target
target_link_libraries adds the -L and -l linker option, it should never add files to projects.

Event the header one is a mess,
I am currently working on use of a sha1 from a lib that has 10 or 15 different algorithms you may use. These headers MUST be Public, Right? I need 1, My project is now littered with all of them. (and I think am having a problem because there is a c++ and C mixed and the __cplusplus is being enabled and appears to change the include and not the .c. Maybe that is just Visual Studio Intellinonsense. )

“Linking” in CMake brings in usage requirements. This may include sources, compile flags, linker flags, include directories, language features, and more. It hasn’t been “just -l flags” since 2.8.12 or so.

OK, Ok, Someone thought it was a good idea, I think it is a bit twisted :frowning: ( Confessing I started with CMake before 2.8 so a bit of re-learning is at hand, I would of not piggy backed on link)

Sources must be really really needed to be made public by the lib author and be pushed into peoples project files, and while I still disagree, I can live with it, because it should be very rare.

But how do I take back control and prevent unwanted headers being added to my project?
A lib provider is forced to make public all those that may possibly be used by all possible users, but most projects will only need a much smaller subset.

boost has it’s own way and does not cause this.
wxWidgets would add 401 headers.
Diligent Engine, god too many to count.
ffmpeg at least 100

I see no benefit to bloating the projects with this stuff and inviting devs to confuse them with headers they need to edit. Not to mention the collision danger usually avoided with good #include <> and project paths.

I can’t think of a scenario where I would want to use PUBLIC for target_sources(). For most cases, PRIVATE is what should be used. There are scenarios where INTERFACE makes sense though. A few that come to mind:

  • A header-only library. Specifying the headers as INTERFACE allows the headers to show up somewhere in the IDE. Nothing is (usually) going to compile or process those libraries (I can think of cases where this won’t be true, but it’s not a scenario likely to matter to this discussion). This practice has been very common in the CMake community for years now. Starting with CMake 3.21, you can add PRIVATE sources to an interface library though, so it is no longer necessary to use that long-standing approach if 3.21 can be your minimum CMake version.
  • In a static build, if your project uses plugins that are also going to be static, there may be a need to add code that calls certain initialization functions to force the linker to include that plugin’s code in the final executable. That added initialization code would have to be given to the linker as an object file to force it to include it in the binary (there are other ways, but they are linker-specific). One way to do that is to use target_sources() and INTERFACE to add that code to whatever consumes the plugin. That makes the consumer have the initialization code appear as one of its own sources, which means it is an object file given to the linker and it doesn’t get discarded. I’m glossing over a lot of details and limitations and alternatives, but the point is that there are scenarios where indeed it is useful to inject a source file into a consumer.
  • The source in question might be resources rather than actual code. You may need to have the consuming target compile and add those resources themselves. Using INTERFACE might be one way to achieve this.

Thanks. That is a bit above my head. A real < 1% use case.

I am ok with the sources implementation for that very reason. Very rare, only when needed.

Back to the main question. What possible use case for adding all public headers to a users project? Do you agree this is wrong. aka a bug.?

Don’t confuse the PUBLIC keyword with meaning “part of the public API that consuming targets will want to include”. Those are two very different things. The PUBLIC keyword in target_sources() is about which targets those sources should be added to directly, not which headers should consumers be allowed to #include.

As I stated, I can’t think of a scenario where I would use PUBLIC for target_sources(). There might be one, but I am not aware of it. You would have to look at what the project is trying to do, but if it uses PUBLIC with target_sources(), it is very likely to be incorrect.

If your question is whether adding headers as INTERFACE with target_sources() is valid, I touched on that in the first dot point of my previous comment. It has been used for a long time with header-only libraries (which are relatively common). It’s not wrong to do this, but it might not be what everyone would want.

I think my education is getting there.

The docs do not do a clear job of explaining this, I need to read more but I did find after it says PUBLIC get Added to INTERFACE_SOURCES a blurb on the linked page.

" When target dependencies are specified using target_link_libraries(), CMake will read this property from all target dependencies to determine the sources of the consumer."

So many sites that give more detailed “how to do CMake right” all just use something overly simple like

add_library(JSONUtils src/json_utils.cpp)

I assume PRIVATE is default?

Anyway thanks for your patience in teaching me, it is indeed a strange line to cross pushing code into peoples projects. Gets the creative juices flowing how you could make a self assembling snippet lib with configuration from the cmakegui LOL

Yes. When you list sources in a call to add_library() or add_executable(), they are added as private sources. The following are equivalent:

add_library(mything src.cpp)
target_sources(mything PRIVATE src.cpp)