[cmake-developers] Setting include directories via target_link_libraries() ?

Alexander Neundorf neundorf at kde.org
Sun Dec 30 11:33:38 EST 2012


Hi,

On Saturday 29 December 2012, Stephen Kelly wrote:
> Stephen Kelly wrote:
> > I'd like to defer the details of the
> > porcelain API for now and focus instead on the plumbing API and
> > implementation.
> 
> I have several quirks with the new command proposal, The new command can be
> emulated with my branch and this macro:
> 
>  include(CMakeParseArguments)
> 
>  macro(target_use_interfaces target)
>    cmake_parse_arguments(TUI "" "" "INTERFACE;PUBLIC;PRIVATE" ${ARGN})
>    if(TUI_UNPARSED_ARGUMENTS)
>      message(FATAL_ERROR "Unknown keywords given to
> target_use_interfaces(): \"${TUI_UNPARSED_ARGUMENTS}\"")
>    endif()
> 
>    foreach(lib ${TUI_INTERFACE} ${TUI_PRIVATE} ${TUI_PUBLIC})
>      if (NOT TARGET ${lib})
>        message(FATAL_ERROR "This macro only accepts targets")
>      endif()
>    endforeach()
> 
>    if (TUI_INTERFACE)
>      target_link_libraries(${target}
>                     INTERFACE_LINK_LIBRARIES ${TUI_INTERFACE})
>    endif()
>    if (TUI_PRIVATE)
>      target_link_libraries(${target} LINK_PRIVATE ${TUI_PRIVATE})
>    endif()
>    if (TUI_PUBLIC)
>      target_link_libraries(${target} LINK_PUBLIC ${TUI_PUBLIC})
>    endif()
>  endmacro()
> 
> The quirks:
> 
> 1) Restrictiveness
> 
> Only targets are allowed, which means you likely can't use ${Foo_LIBRARIES}
> at all anymore, even for porting, unless you know exactly what it contains.
> Even if you do know exactly what it contains, that doesn't necessarily
> help.
> 
> Consider that Bar maybe doesn't provide 'rich' imported targets yet. Then
> Foo will have this:
> 
>  set(Foo_LIBRARIES Foo::importedlib ${Bar_LIBRARIES})

This shouldn't be necessary, should it ?
If Foo::importedlib is an exported target created by cmake, there should be no 
need to add additional libraries manually to it, AFAICT.

> Or worse, FooConfig.cmake will create IMPORTED targets for Bar (Bad idea,
> see 5 below)
> 
> This is solvable by letting target_use_interfaces also accept library names
> and library paths, but then the new command is less 'exact' and more
> redundant.


It could warn if it detects a normal library...
 
> 2) Inconvenience
> 
> Assuming target_use_interfaces only allows targets, you'll have a lot of
> this:
> 
>  tll(foo lib1)
>  target_use_interfaces(foo lib2)
>  tll(foo lib3)
> 
> instead of just
> 
>  tll(foo lib1 lib2 lib3)

Shouldn't 

target_link_libraries(foo lib1 lib3)
target_use_interfaces(foo lib2)

work too ?
The imported target lib2 should know all its dependencies, right ?

 
> 3) Incompleteness
> 
> link-libraries, INTERFACE_POSITION_INDEPENDENT_CODE,
> INTERFACE_WIN32_EXECUTABLE, INTERFACE_STD_CXX_11,
> INTERFACE_CXX_RUNTIME_LIBCXX, INTERFACE_CXX_RUNTIME_LIBSTDCXX
> 
> will all handled by tll in both proposals (as they depend on the
> LINK_LIBRARIES, which is populated by tll), but defines and includes alone
> will be the INTERFACE properties which do not get populated with tll, and
> need the new command instead.
>
> 4) Redundancy into the future
> 
> Some Bar packages will not create IMPORTED targets with appropriate
> INTERFACE properties.
> 
> tll will not 'go away'. The extent to which a project can achieve a 'fully
> ported' state depends on its upstreams. The new command may drive some (but
> not most) people to read the help entry for it for a couple of releases.
> Depending on how fast their cmake dependency moves, how fast they move
> their upstream dependency versions and how fast their upstream
> dependencies move their cmake version, the new command could be very old
> news by the time they get to use it.

Yes.
 
> And after that, there will always be two commands for very similar things.

Well, depends on whether you view those two things as "very similar" ;-)


> Not every good idea is adopted (particularly not in a reasonable time), no
> matter how good the idea is. Consider the amount and rate of adoption of
> Config files to date.
> 
> 5) Greater incentive to make future changes accidentally harder.
> 
> People will create their own IMPORTED targets to 'port fully' to the new
> command and push themselves over the line (both in their own CMakeLists
> files, and in their own Config files).
> 
> The same thing could happen even if tll does includes and defines too, but
> a greater incentive is there if there's a new command and a 'port fully to
> the new command' goal.
> 
> That means that if upstream introduces IMPORTED targets where they didn't
> before there will be breakage.
> 
> 6) API non-argument
> 
> One of the suggested benefits of a new command is a more restrictive
> signature to be more typo safe.
> 
>  http://thread.gmane.org/gmane.comp.programming.tools.cmake.devel/5526/focu
> s=5569
> 
> If that is really such a problem for tll that it needs a solution (I don't
> believe it is) we can just deprecate
> 
>  tll(foo lib1 lib2)
> 
> with a policy and require people to use
> 
>  tll(foo LINK_INTERFACE_LIBRARIES lib1 lib2)
>  tll(foo LINK_PRIVATE lib1 lib2)
>  tll(foo LINK_PUBLIC lib1 lib2)
> 
> instead if the policy is NEW. (and also
> 
>  tll(foo INTERFACE_LINK_LIBRARIES lib1 lib2)
> 
> later, when that exists of course - The deprecation policy can be
> implemented immediately for the next CMake release).
> 
> 7) Complexity non-argument
> 
> The implementation complexity argument leveled against using tll to set
> includes and defines is just not true. The patch to add it is very simple
> ('Add includes and compile definitions with target_link_libraries') where
> it touches the tll command implementation.
> 
> Implementing a new command would probably be more complex as tll may have
> to be refactored for code sharing.
> 
> 8) Non-discoverability
> 
> No one will use/benefit from/discover the new feature (with regard to
> includes and defines) without first explicitly using the new command. No
> one will discover it accidentally. Maybe we can add a note about it to the
> tll documentation, but if people are going to read that, then it is just
> as easy for us to write 'tll also populates includes and defines from its
> dependencies'.

Here I disagree.
You talk about the discoverability of somebody who wants to use this new 
feature.
For me the reading and understanding of an existing CMakeLists.txt is more 
important, i.e. maintaining and debugging, not writing.
From this POV, seeing a different command is obvious, while using tll() is 
not.
 
> 9) No benefits, only burden

It surely is not completely black and white only ;-)

Alex



More information about the cmake-developers mailing list