[CMake] SuperBuild whoes
Michael Wild
themiwi at gmail.com
Tue Apr 26 09:19:15 EDT 2011
On 04/26/2011 02:48 PM, Michael Hertling wrote:
> On 04/25/2011 05:15 PM, Michael Wild wrote:
>> On 04/25/2011 04:51 PM, Michael Hertling wrote: [...]
>>>> [...] The only thing that required some thinking was writing a
>>>> relocatable XXXConfig.cmake file. I think I will update my
>>>> tutorial on the WIKI to use a un-configured, relocatable
>>>> XXXConfig.cmake file.
>>>
>>> Just a hint for that tutorial, though off-topic: Targets may
>>> usually not be defined multiple times, i.e. the export file
>>> generated by INSTALL(EXPORT ...) may not be included more than
>>> once, so the
>>>
>>> include("@FOOBAR_CMAKE_DIR@/FooBarLibraryDepends.cmake")
>>>
>>> should possibly be guarded by IF(NOT TARGET ...)/ENDIF()
>>> constructs. Otherwise, the resulting FooBarConfig.cmake file must
>>> not be loaded twice or more - unless one is aware of the imported
>>> targets' feature of being, say, "half-scoped", cf. [1]. This
>>> might be an uncomfortable limitation, in particular w.r.t.
>>> multi-component packages. Regrettably, such IF(NOT TARGET
>>> ...)/ENDIF() constructs can hardly be automated, so one could
>>> perhaps consider to allow redefinitions for imported targets. Due
>>> to the absence of sources, this should be much easier to
>>> implement than for non-imported targets.
>>
>> Good point. I will do something like this:
>>
>> get_property(FOOBAR_INCLUDED GLOBAL PROPERTY FOOBAR_INCLUDED
>> DEFINED) if(NOT FOOBAR_INCLUDED) # include
>> FooBarLibraryDepends.cmake here set_property(GLOBAL PROPERTY
>> FOOBAR_INCLUDED TRUE) endif()
>
> Don't do it in this way. First, the DEFINED clause of GET_PROPERTY()
> queries if the property has been *defined* by DEFINE_PROPERTY(); if
> it is *set* by SET_PROPERTY() et al. doesn't matter in this regard.
> See the following CMakeLists.txt:
>
> CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) PROJECT(PROPERTIES
> NONE) GET_PROPERTY(d GLOBAL PROPERTY P DEFINED) GET_PROPERTY(s GLOBAL
> PROPERTY P SET) MESSAGE("P initially: defined=${d}, set=${s}")
> SET_PROPERTY(GLOBAL PROPERTY P TRUE) GET_PROPERTY(d GLOBAL PROPERTY P
> DEFINED) GET_PROPERTY(s GLOBAL PROPERTY P SET) MESSAGE("P after
> setting: defined=${d}, set=${s}") DEFINE_PROPERTY(GLOBAL PROPERTY P
> BRIEF_DOCS "P" FULL_DOCS "P") GET_PROPERTY(d GLOBAL PROPERTY P
> DEFINED) GET_PROPERTY(s GLOBAL PROPERTY P SET) MESSAGE("P after
> defining: defined=${d}, set=${s}")
>
> Thus, you must use GET_PROPERTY()'s SET clause to achieve your aim.
Argh, sorry. I meant SET, of course.
>
> Moreover, imported targets - in contrast to traditional ones - are,
> as I said, "half-scoped", i.e. they've a scope like variables, but
> cannot be overwritten/redefined. As a consequence, you can define an
> imported target in a subdirectory, and subsequently, i.e. after
> returning from ADD_SUBDIRECTORY(), you can define it again the
> current scope. If you guard an export file's inclusion by a global
> property, the inclusion will fail if it has taken place earlier in a
> subdirectory, so the imported targets will be undefined in the
> current scope. See the following CMakeLists.txt files:
>
> # CMakeLists.txt: CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
> PROJECT(FOOBAR NONE) ADD_SUBDIRECTORY(subdir)
> GET_PROPERTY(FOOBAR_DEFINED GLOBAL PROPERTY FOOBAR_DEFINED SET)
> IF(NOT FOOBAR_DEFINED) ADD_EXECUTABLE(foobar IMPORTED)
> SET_PROPERTY(GLOBAL PROPERTY FOOBAR_DEFINED TRUE) ENDIF() IF(TARGET
> foobar) MESSAGE("Target foobar defined at toplevel") ELSE()
> MESSAGE("Target foobar NOT defined at toplevel") ENDIF()
>
> # subdir/CMakeLists.txt: GET_PROPERTY(FOOBAR_DEFINED GLOBAL PROPERTY
> FOOBAR_DEFINED SET) IF(NOT FOOBAR_DEFINED) ADD_EXECUTABLE(foobar
> IMPORTED) SET_PROPERTY(GLOBAL PROPERTY FOOBAR_DEFINED TRUE) ENDIF()
> IF(TARGET foobar) MESSAGE("Target foobar defined in subdir") ELSE()
> MESSAGE("Target foobar NOT defined in subdir") ENDIF()
>
> Instead, you should use either directory properties to protect
> imported targets from being redefined - appropriate to their scoping
> - or just ordinary variables, though none of these solutions is
> bullet-proof.
This is truly infuriating... Inherited directory properties seem to be
the way to go then... The reason I like to use properties is that they
are much less used than variables, reducing the potential for naming
conflicts.
>
> Alternatively, one might consider to provide a function specifically
> designed for the accident-free inclusion of export files, e.g. like:
>
> FUNCTION(INCLUDE_EXPORT EXPORTFILE) SET(TARGETSDEFINED FALSE)
> FOREACH(i IN LISTS ARGN) IF(TARGET "${i}") SET(TARGETSDEFINED TRUE)
> ENDIF() ENDFOREACH() IF(NOT TARGETSDEFINED) INCLUDE(${EXPORTFILE})
> ENDIF() ENDFUNCTION()
>
> Apparently, a function's scope does not shield an imported target
> from the surrounding scope as a directory's scope does. However, one
> needs to provide the targets when invoking INCLUDE_EXPORT(), but
> probably it's quite possible to retrieve them automatically by
> scanning the export file, e.g. with an ADD_.+\(([^ ]+)IMPORTED\) or
> the like.
Nah, too roundabout...
>
> IMO, the interdiction of - equivalently - redefining imported
> targets is currently the latters' main disadvantage since this means
> exactly the above-mentioned trouble when dealing with them in config
> files.
True. IMHO the automatically generated export files should actually
guard against double inclusion. Perhaps something for the next release? ;-)
Michael
More information about the CMake
mailing list