[cmake-developers] Generator expressisons in target properties

Stephen Kelly steveire at gmail.com
Sat Oct 27 14:00:59 EDT 2012


On Tuesday, October 23, 2012 09:47:49 Brad King wrote:
> On 10/23/2012 09:13 AM, Stephen Kelly wrote:
> > However, if the direct and transitive dependencies really must be kept
> > separate, that will have to be rethought anyway.
> 
> They do.  A few years ago I went through major pain to drop uses of the
> old GetLinkLibraries and use either GetOriginalLinkLibraries or results
> from cmComputeLinkDepends wherever possible.  It is necessary to keep
> them separate because cmComputeLinkDepends needs to know exactly what
> the user originally specified as direct dependencies in order to compute
> the closure properly.

I think in order to finish this feature, I'll need to know more about why 
the direct and indirect dependencies need to be kept separate. Can you give 
more information?

> > I can't think of a reason to link a library based on whether
> > POSITION_INDEPENDENT_CODE is true or false for a target.
> 
> A package could provide a PIC and non-PIC version of a library and
> pick one based on whether the target is PIC or not.

Something like this?


 add_library(foo-pic SHARED ${foo_pic_srcs})
 add_library(foo-nopic SHARED ${foo_nopic_srcs})

 add_library(foo INTERFACE)
 set_property(TARGET foo INTERFACE_LINK_LIBRARIES
  $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:foo-pic>
  $<$<NOT:$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>>:foo-nopic>
 )


Or this?

 add_library(foo-pic SHARED IMPORTED)
 set_property(foo-pic IMPORTED_LOCATION ...)
 add_library(foo-nopic SHARED IMPORTED)
 set_property(foo-nopic IMPORTED_LOCATION ...)

 add_library(foo INTERFACE)
 set_property(TARGET foo INTERFACE_LINK_LIBRARIES
  $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:foo-pic>
  $<$<NOT:$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>>:foo-nopic>
 )

> > 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
> IMO that should be a detect-and-reject case rather than propagated:
> 
>  CMake Error ...:
>   Target foo has STD_CXX11=OFF but links bar with STD_CXX11=ON.
> 
> Just like PIC and WIN32 a package can provide multiple libraries and
> select them conditionally on the needs of the final target.

I thought more about this, and I realized that it's counter to my goals for 
Qt. My goal is for this to work on all platforms and all Qt configurations:

 find_pacakge(Qt5Widgets REQUIRED)
 add_executable(foo main.cpp)
 target_link_libraries(foo Qt5::Widgets)


So, to satisfy the 'all platforms' part of the goal, Qt5::Widgets needs to 
know to link to qtmain.lib on Windows. That is not counter to what you said.

However, the default configuration of Qt requires that 
POSITION_INDEPENDENT_CODE is ON. I would want that to be part of the 
interface of Qt5::Widgets too in the above example. This part is counter to 
what you said. It is the opposite of detect-and-reject.

Another related thing I would like to be able to do is this:
 
 try_compile(_compile_result ${CMAKE_BINARY_DIR} 
    "#include <QGlobal>\n int main(int,char**) { return 0; }" 
    TARGETS Qt5::Core
    OUTPUT_VARIABLE _compile_output_var)


That is, add a TARGETS component to the try_compile signature to specify the 
contents of the target_link_libraries line in the code generated by try 
compile. (As a side note, I'd also like to extend try_compile so that it 
really only tries to compile with a COMPILE_ONLY option or so).

In KDE, a try_compile like that is used to determine if Qt is compiled with 
hidden visibility.

Currently, we have to specify CMAKE_POSITION_INDEPENDENT_CODE ON globally to 
be able to use try_compile with anything Qt 5 related. That's not 'target-
orientated', so I'd prefer to be able to remove the global use of 
CMAKE_POSITION_INDEPENDENT_CODE and make target-orientation part of the 
design goals here.

That would require something like 

 set_property(TARGET Qt5::Core INTERFACE_POSITION_INDEPENDENT_CODE ON)

> I think the general philosophy is that the final target should specify
> how it wants to build and the libraries it links evaluate their exprs
> to meet its requirements.  If they can't then it is an error.

I propose a different algorithm. Something similar to this:

For each target to build:
    compute the link closure
    populate a container of target properties which \
        appeared in generator expressions while \ 
        computing the closure. [1]

    For each property that has an INTERFACE_ variant:
        #  eg, WIN32, CXX11, PIC
        If the property is set:
            try to evaluate it as a generator expression
            if a property from [1] is encountered:
                raise an error.
        else:
            for each target in the link closure:
                evaluate the INTERFACE_ variant of the property as a genex        
                if uses $<TARGET_PROPERTY:LINK_LIBRARIES>:
                    raise an error.
            The end-value is property-specific. Some may use \
                ANY_OF while others may use ALL_OF.
    For each build-property that has an INTERFACE_ variant:
        # eg COMPILE_DEFINITIONS, INCLUDE_DIRECTORIES
        for each target in the link closure:
            evaluate the INTERFACE_ variant of the property as a genex        
            if uses $<TARGET_PROPERTY:LINK_LIBRARIES>:
                raise an error.


This would mean that either the link libraries may depend on the property, 
or the property may depend on the link libraries, but not both. 

That is, either:

 set_property(TARGET foo INTERFACE_LINK_LIBRARIES
  $<$<BOOL:$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>>:foo-pic>)

or

 set_property(TARGET Qt5::Core INTERFACE_POSITION_INDEPENDENT_CODE ON)

could be used, but an error is raised if both are used in the same link 
closure (a cycle).

This would mean that we could not use a default-configured Qt5::Core with 
foo-nopic. This is true at build-time anyway, but now we catch it earlier at 
generate-time.

We could also not use Qt5::Core with foo-pic because the cycle would be 
encountered. This should be allowed though, and the algorithm would allow it 
by simply setting the property explicitly, thus breaking the cycle:

 add_executable(exe ${exe_srcs}) 
 target_link_libraries(exe Qt5::Core foo)
 set_property(TARGET exe POSITION_INDEPENDENT_CODE ON)

I think this is the best compromise. 



Any thoughts? 

Do current generators compute the link closure before or after the 
INCLUDE_DIRECTORIES or COMPILE_DEFINITIONS? It needs to be one of the first 
things to determine with this algorithm. Maybe it can be determined in a 
loop by cmGlobalGenerator::Generate and cached for later.

Thanks,

Steve.





More information about the cmake-developers mailing list