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 _______________________________________________ Powered by www.kitware.com Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ Follow this link to subscribe/unsubscribe: http://www.cmake.org/mailman/listinfo/cmake