[cmake-developers] transitive -I and -D usage requirements (was: Policy for INTERFACE_LINK_LIBRARIES)

Stephen Kelly steveire at gmail.com
Thu Dec 13 10:22:55 EST 2012


Brad King wrote:

> On 12/09/2012 08:48 AM, Stephen Kelly wrote:
>> I think the 'Setting include directories via target_link_libraries() ?'
>> thread needs to be concluded first (with reasoning), as that affects
>> everything else.
> 
> While thinking about my next response to that discussion I read your
> wip-target-interface in more detail to look at handling of include dirs
> and compile defs. 

Thanks for that. I had wondered how much of the stuff in there you were 
aware of. Have you read it all?

> Can you explain what the commit
> 
>  "Handle INTERFACE properties transitively for includes and defines"
> 
> is doing please?  What is "transitive" in that implementation?

It means that for the whitelisted properties:

 add_library(foo ...)
 set_property(TARGET foo PROPERTY INTERFACE_INCLUDE_DIRECTORIES /foo/hdrs)

 add_library(bar ...)
 set_property(TARGET bar PROPERTY 
   INTERFACE_INCLUDE_DIRECTORIES 
     /bar/hdrs $<TARGET_PROPERTY:foo,INTERFACE_INCLUDE_DIRECTORIES> 
 )

 add_library(bat ...)
 set_property(TARGET bat PROPERTY 
   INCLUDE_DIRECTORIES 
     /bat/hdrs $<TARGET_PROPERTY:bar,INTERFACE_INCLUDE_DIRECTORIES> 
 )

So, the INCLUDE_DIRECTORIES will be evaluated for for bat, and when the 
INTERFACE_INCLUDE_DIRECTORIES is encountered for bar, the content of it is 
evaluated as a generator expression, and therefore we get the foo INTERFACE 
headers too.

It has been implemented like that for some time (not via the link closure), 
but mostly back then we werer discussing the INTERFACE_LINK_LIBRARIES stuff:

 http://thread.gmane.org/gmane.comp.programming.tools.cmake.devel/3615/focus=5247

 http://thread.gmane.org/gmane.comp.programming.tools.cmake.devel/3615/focus=5115

That is the cmGeneratorExpressionEvaluator part of the patch. The cmExport* 
parts of the patch copy the INTERFACE_* properties from the target to be 
exported into the generated IMPORTED target for it.

> Later in the topic
> 
>  "Add includes and compile definitions with target_link_libraries"
> 
> I see propagation of INTERFACE_INCLUDE_DIRECTORIES into the implementation
> INCLUDE_DIRECTORIES of the target.  However, it is not transitive through
> the link interface closure.

Correct. I think at the time I implemented it, I didn't see the need to 
implement it via the link closure (which is more complex).

I think these properties are different to the INTERFACE_LINK_LIBRARIES 
property for several reasons.

> 
> Since the link interface closure isn't known until generate time when the
> full link interface generator expressions can be evaluated, I do not think
> we can compute the full include directories or compile definitions until
> then either.  When a dependency is put in the link interface of a shared
> library that means that clients including headers from the shared library
> may get headers from the dependency too.  

You mean s/may/need to/ ?

> Otherwise there is no way the
> client could get direct references to symbols from the dependency and it
> would not need to be in the link interface.  

Yes, exactly.

> Once a dependency is in the
> link interface, even transitively, clients need the -I and -D flags for
> it.  

Yes.

> Therefore we need to get them from the link interface closure

I'm not sure this conclusion follows from the above. In my branch, I assume 
that they are set at the same time (with either tll() or a new command). 
That does mean that if someone uses 

 set_property(TARGET foo LINK_LIBRARIES bar)

they don't get the interface include directories, but they also bypass a lot 
of other useful stuff from tll(), so it's probably a bad idea anyway.

By doing it the way I currently do it in the branch, I think the order of 
the list of resulting include directories is more natural.

 set_property(TARGET foo INTERFACE_INCLUDE_DIRECTORIES 
    /include/foo
    $<TARGET_PROPERTY:bar,INTERFACE_INCLUDE_DIRECTORIES>
    $<TARGET_PROPERTY:bat,INTERFACE_INCLUDE_DIRECTORIES>
 )

The consumer will get the foo include followed by the bar include followed 
by the bat include. Due to the way tll() resolves and orders dependencies, 
that would not be the same.

I'm not completely tied to separating the includes and defines from the 
linking transitivity, but I'd like to investigate what is wrong with it, and 
why using the link closure instead is better, and what that prevents (it 
prevents using $<TARGET_PROPERTY:bar:INTERFACE_INCLUDE_DIRECTORIES> in the 
(INTERFACE_)?INCLUDE_DIRECTORIES of any foo at least).

Thanks,

Steve.





More information about the cmake-developers mailing list