[cmake-developers] Generator expressisons in target properties (was Re: conditionals in generator expressions)

Stephen Kelly steveire at gmail.com
Tue Oct 23 09:13:30 EDT 2012


Brad King wrote:

> On 10/22/2012 05:42 PM, Stephen Kelly wrote:
>> Ah, I was wondering about that when I wrote that patch, but forgot to put
>> in the email. I thought they would be able to have per-config
>> dependencies.
>> 
>>  set_property(TARGET foo LINK_LIBRARIES $<$<Config:Debug>:debughelpers>
>> 
>> will never work with IDE generators? That's not a limitation of the
>> generators but of the IDEs?
> 
> That can work because the IDEs support per-config link libraries.
> It's the logical build ordering dependencies among targets that are
> not per-config.  That means in a Release build the IDE will still
> want to build debughelpers before foo, but will not actually have
> to link to it.

Ok, I see. Thanks.

> 
>>  target_link_libraries(bar LINK_PUBLIC foo)
>>  # bar LINK_LIBRARIES is now:
>>  #   "foo;$<TARGET_PROPERTY:foo,INTERFACE_LINK_LIBRARIES>"
>>  # bar INTERFACE_LINK_LIBRARIES is now:
>>  #   "foo;$<TARGET_PROPERTY:foo,INTERFACE_LINK_LIBRARIES>"
> 
> Why does "$<TARGET_PROPERTY:foo,INTERFACE_LINK_LIBRARIES>"
> belong in either one of them?  The transitive closure should be
> computed by link-aware code that knows it must follow foo's link
> interface, not just by generator expression evaluation.  The
> purpose of GetOriginalLinkLibraries is to get the directly linked
> libraries without any transitive dependencies.

This is why I was uncertain about this section :). I wasn't sure why or 
whether it would be important for GetOriginalLinkLibraries to not contain 
transitive dependencies.

What is 'link-aware code'? A part of the cmake implementation that has rules 
specific to linking where the difference between the direct and transitive 
dependencies is important?

The "$<TARGET_PROPERTY:foo,INTERFACE_LINK_LIBRARIES>" belongs in the 
INTERFACE_LINK_LIBRARIES of bar because it links to foo with LINK_PUBLIC. If 
it used LINK_PRIVATE it would not appear there. The expression belongs in 
LINK_LIBRARIES of bar because it links to foo and therefore must link to 
things foo says it must link to. This means that by evaluating the 
LINK_LIBRARIES expression for a target, we get all of its dependencies 
(probably with duplication, something else I haven't thought through yet if 
that's  a problem). Because we have all dependencies, it should be easier to 
get the INCLUDE_DIRECTORIES and COMPILE_DEFINITIONS etc too. It also made 
the code I had to write much less complex (only one place to add the use of 
generator expressions).

However, if the direct and transitive dependencies really must be kept 
separate, that will have to be rethought anyway.

> 
>>  # In my branch WIN32_EXECUTABLE would be defaulted to:
>>  #
>>  # $<EACH:$<TARGET_PROPERTY:LINK_LIBRARIES>,"
>>  #           "$<OR:$<EACH_ACCUMULATED:0>,"
>>  #                "$<BOOL:$<TARGET_PROPERTY:$<EACH_ARG>,"
>>  #                "INTERFACE_WIN32_EXECUTABLE>>>"
>>  #
>>  # That is, check if any of the link libraries say WIN32_EXECUTABLE
>>  # should be ON.
> 
> What is the use case for that default?  I think enabling the
> WIN32_EXECUTABLE option should remain an explicit choice.

Sure, for that property maybe so. That's not future-proof though. There may 
be other properties we want to add in the future where it does make sense. 
For example, it makes sense for POSITION_INDEPENDENT_CODE, so Qt5Core should 
have INTERFACE_POSITION_INDEPENDENT_CODE ON and the 
POSITION_INDEPENDENT_CODE property should be evaluated with EACH as I 
pseudo-implemented in my patch. 

I can't think of a reason to link a library based on whether 
POSITION_INDEPENDENT_CODE is true or false for a target. However, you can 
see that the dependency from target property to link libraries is in the 
opposite direction to the direction for WIN32_EXECUTABLE. That means that 
there may be a property for which it could make sense in either direction 
(ie, that it could make sense to link a library based on a FOO property, or 
it might make sense to set the FOO property based on what is linked), and we 
can't control that choice. If we release CMake 2.8.11 with the FOO property, 
we can't add something like this to CMake 2.8.12:

  setPropertyDefault("FOO", "$<EACH:$<TARGET_PROPERTY:LINK_LIBRARIES>,"
           "$<OR:$<EACH_ACCUMULATED:0>,"
                "$<BOOL:$<TARGET_PROPERTY:$<EACH_ARG>,"
                "INTERFACE_FOO>>>")

because user code might already be doing this:

  target_link_libraries(exe $<$<TARGET_PROPERTY:FOO>:bar>)

and that would become a cycle.

In somewhat more concrete terms, maybe if the STD_CXX11 property ever 
becomes a reality, a library built with c++11 might require that downstreams 
also use c++11 because of binary compatibility concerns in the stdlib 
implementation. (See http://gcc.gnu.org/ml/gcc/2012-06/msg00239.html for 
example, but even if all issues with GCC are fixed, there are many other 
compilers out there). Similarly, a cxx98helpers library might be linked if 
$<NOT$<TARGET_PROPERTY:STD_CXX11>>.

So, I don't think we can ignore the issue of cycles completely, but we might 
be able to simply choose up front the direction of dependence of properties 
that are there when this feature lands, and when future properties land, how 
they're going to work and then stick with that choice.

>> Can you give more information about the case you mention? I don't see any
>> mention in the documentation of using find_package() with a directory.
> 
> Projects may do this:
> 
>  if(NOT USE_SYSTEM_FOO)
>    add_subdirectory(Foo)
>    set(Foo_DIR ${CMAKE_CURRENT_BINARY_DIR}/Foo)
>  endif()
>  find_package(Foo)
>  ...
>  target_link_libraries(MyExe ${Foo_LIBRARIES})
> 
> such that Foo_LIBRARIES ends up with namespaced imported targets
> that refer to the in-tree real targets.

I see. Thanks.

> 
>>> We need to consider how to handle compatibility here,
>>> but only with projects not using the new features enabled by delaying
>>> export() until generate time.  Can you explain this in more detail?
>> 
>> This may only be doable by duplicating a lot more code and making
>> cmExportBuildFileGenerator not inherit from cmExportFileGenerator. The
>> latter uses GetLinkInterface, GetSoName and HasSoName.
>> 
>> HasSoName depends on the link languages, and therefore the libraries and
>> therefore has to move to cmGeneratorTarget. If export() is to remain a
>> configure-time construct, then HasSoName would have to remain in some
>> form in cmTarget. That could mean a lot of duplication.
> 
> We may need a policy implemented with minimum duplication to provide
> the capabilities that export() used to.

Would the policy be used to determine whether to execute the export() at 
configure-time (and strip out any generator expressions found) or at 
generate-time (and process the generator expressions)?

It still looks like a lot of duplication to me (my commit that moves 
HasSOName and all previous commits may need to be copied). Maybe it's not as 
bad as it seems though.

Thanks,

Steve.





More information about the cmake-developers mailing list