Need correct CMake “$<TARGET_NAME:” usage to enable cross-export set INTERFACE_LINK_LIBRARY dependencies

I have a target in an export set—plugins::support_macros, say—that has an interface link dependence on a target in a different export set in the same package (e.g. libs::main_lib). I understand that I need to express the dependency using $<TARGET_NAME:...>, but it keeps getting interpreted too early and I get, “The following imported targets are referenced, but missing:…”

Attempts to delay interpretation of the generator expression result either in raw and unhelpful \ characters in the Targets.cmake file, or in CMake not recognizing the target at build time. My closest attempt so far is to express the dependence as:

target_link_libraries(support_macros INTERFACE $<1:$<TARGET_NAME:libs::main_lib>> ...)

This ends up in the Targets.cmake as:

...INTERFACE_LINK_LIBRARIES "\$<1:libs::main_lib>;..."

and the subsequent check fails:

foreach(_target "lib::main_lib" )
  if(NOT TARGET "${_target}" )

I’m at my wits’ end here: the documentation for $<TARGET_NAME:...> on CMake’s generator expressions page is terse, and finding search terms that don’t just return links to it from all over the internet has been impossible for me thus far.

Any help appreciated!

[PS] I have seen, but it does not appear to address my problem.
[PPS] This is with CMake 3.18.2

Your problem is not that $<TARGET_NAME> is helping or hurting. The problem is that your export sets must be included in their dependency order. So the export containing libs::main_lib must be included from your config.cmake file before the export containing plugins::support_macros.

Thanks for this. Given the somewhat cryptic explanation for $<TARGET_NAME:...> in the documentation, I’d allow myself to believe that it could be used as a placeholder to allow the order of inclusion of the targets files to not matter. Given the logical, rather than physical nature of export sets, this seemed like a reasonable feature to expect.

I’m incorporating modern CMake paradigms into a package that allows non-expert scientists to code their own software without becoming CMake experts, and a large part of that task is autogenerating the config and target files to do (almost) everything our scientists want without requiring hand-rolling. It’s relatively straightforward to detect a dependency from another export set and wrap it; less so to ensure that the contents of the export sets (as specified by non-experts) follow a strict dependency hierarchy.

Oh well, time to look for a different approach.

Thanks for taking the time to give me the bad news rather than letting me dig on in ignorance.

I wouldn’t give too much power over export sets to developers if you’re trying to do something like this. On the other hand, for the size of the task you’re looking for, VTK’s module system is on the order of the complication you’re going to end up with. Note that you can probably rip out various parts of it, but things like vtk_module_find_package and vtk_module_scan/vtk_module_build are immensely helpful with gathering the required information from the project.

Great, thank you for the extra information.

@ben.boeckel Can you suggest some improved wording for the $<TARGET_NAME:...> generator expression documentation? It currently says “This is required if exporting targets to multiple dependent export sets”, but it doesn’t say why. Some sort of example would really help, but I don’t really understand the use of this even after looking at its C++ implementation (which does nothing but unconditionally return the target name anyway, so I don’t know what it is affecting). Having arrived here myself after another export-related problem in a different project but with similar error message, it is apparent that we need to make it clearer what problems $<TARGET_NAME:...> does and does not solve.

My understanding is that $<TARGET_NAME> is there so that CMake can pull a target name out of an arbitrary genex without evaluating it. I really don’t know the provenance of that statement in the documentation. It looks to come from the commit adding the expression, so it’s been there from the beginning (b0c8f73eb6 (Add the TARGET_NAME generator expression., 2012-12-10)). I don’t know if things got confused or what, but the commit message mentions its use as a preprocessing marker as well.