[CMake] Apply FIND_PACKAGE_HANDLE_STANDARD_ARGS() on COMPONENTS

Michael Hertling mhertling at online.de
Sun Apr 25 05:48:19 EDT 2010


On 04/22/2010 09:55 PM, Alexander Neundorf wrote:
> On Thursday 22 April 2010, Michael Hertling wrote:
>> On 04/21/2010 09:13 PM, Alexander Neundorf wrote:
>>> On Tuesday 20 April 2010, Michael Hertling wrote:
>>>> Dear CMake community, dear CMake developers,
>>>
>>> ...
>>>
>>>> There's another aspect related to this I'd like to comment on: During
>>>> the abovementioned considerations on the bug tracker and the mailing
>>>> list, the question has arisen if it's reasonable to set XXX_FOUND to
>>>> FALSE if any of the requested components aren't found. As for myself,
>>>> I'd say: No, it isn't. Let's have a look at the following scenario:
>>>>
>>>> Package XXX normally provides components YY1 and YY2, but for some
>>>> reason, only YY1 is installed. Moreover, XXX provides a config file
>>>> XXXConfig.cmake. Now, a project's CMake script requests both YY1/2 by
>>>> FIND_PACKAGE(XXX COMPONENTS YY1 YY2). As Brad King has pointed out in
>>>> <http://www.mail-archive.com/cmake@cmake.org/msg15952.html>, finding a
>>>> config file results in XXX_FOUND to be set to TRUE automatically. Thus,
>>>> the absence of YY2 does not mean the absence of XXX as a whole in any
>>>> case, and, notwithstanding, the requesting CMake script should have a
>>>> chance to proceed even if YY2 isn't available, i.e. the following seems
>>>> reasonable: XXX_YY1_FOUND=TRUE, XXX_YY2_FOUND=FALSE *but*
>>>> XXX_FOUND=TRUE.
>>>
>>> I think I don't agree here.
>>> If I say
>>> find_package(XXX COMPONENTS YY1 YY2 REQUIRED)
>>> I think it makes a lot of sense to interpret this as "search package XXX,
>>> and I need YY1 and YY2 from it".
>>
>> OK, the presence of the REQUIRED option makes everything clear: A find
>> module or a config file bails out if anything is missing - no worry
>> about the final value of XXX_FOUND. The actual questions arise if
>> REQUIRED is absent.
>>
>>> What other reason would I have to give YY1 and YY2 there otherwise ?
>>
>> Perhaps, because YY2 is optional and I do not want FIND_PACKAGE() to
>> look for unrequested components, but that's merely my personal habit.
>>
>>> If it still succeeds if they are not found, why should I list them then ?
>>
>> It is possible for FIND_PACKAGE() - w/o REQUIRED - to succeed although
>> listed components aren't found, and *this* is what I'm worrying about.
>> As Brad King has pointed out in the abovementioned post, FIND_PACKAGE()
>> in config mode sets XXX_FOUND to TRUE if the config file is found and
>> processed without errors, and there's nothing one can do against it -
> 
> 
> Well, find_package() could be enhanced to set this considering the specified 
> COMPONENTS. [...] 

I'm in doubt if this is the right way, and you, Alex, once have been in
doubt, too: <http://www.cmake.org/Bug/view.php?id=5920>, Note #0010405:
"[...] this means FOO_FOUND is only set to TRUE if all components have
been found, right ? Is this the expected behaviour ? I'm not sure."

I'm not, too: For a single-component package, you've an all-or-nothing
situation which can be perfectly summed up by a sole boolean variable.
For a multi-component package, you've the states "all", "nothing" and
intermediate "partials", and a boolean variable is, IMHO, neither
sufficient nor appropriate to comprise all possible outcomes of
FIND_PACKAGE() in such a case. Therefore, the interpretation of
XXX_FOUND should be restricted, roughly like the following:

FALSE: Nothing of XXX has been found at all - don't try to use it; in
particular, don't rely on any other variable related to XXX to have a
defined value. [The latter is config mode not finding a config file.]
TRUE: Basically, XXX has been found, but for any information about the
available components, refer to their specific variables, in particular
XXX_YY_FOUND, as provided by the find module or the config file.

This interpretation is compliant to the behaviour of FIND_PACKAGE() in
config mode and preserves the possibility to reliably request optional
components from a package as I've pointed out in this thread recently.
Obviously, it implies that XXX_FOUND could be TRUE although not all
requested components have been found.

> [..] There hasn't been much discussion about how the COMPONENTS 
> arguments should be interpreted until now.

OK, let me try to contribute. From a module designer's perspective,
things are quite clear: XXX_FIND_QUIETLY and XXX_FIND_REQUIRED get the
QUIET and REQUIRED options, and components go into XXX_FIND_COMPONENTS.
What we'd need is a guideline how the results of FIND_PACKAGE() should
be set up and interpreted when components come into play. My suggestion:

1) Recommend to interpret XXX_FOUND in then above-mentioned manner.
2) Urge the module designers to document which components can be
   requested with FIND_PACKAGE() and if they must be requested
   explicitly or if they are searched automatically.
3) To support the handling of components, provide a function like:

FUNCTION(FIND_COMPONENT_HANDLE_STANDARD_ARGS PKG CMPNT MSG VAR0)
    # Prepare forwarding variables:
    SET(${PKG}_${CMPNT}_FIND_QUIETLY ${${PKG}_FIND_QUIETLY})
    SET(${PKG}_${CMPNT}_FIND_REQUIRED ${${PKG}_FIND_REQUIRED})
    # Delegate the actual work:
    FIND_PACKAGE_HANDLE_STANDARD_ARGS(
        ${PKG}_${CMPNT} ${MSG} ${VAR0} ${ARGN}
    )
    # Propagate the result:
    SET(${PKG}_${CMPNT}_FOUND ${${PKG}_${CMPNT}_FOUND} PARENT_SCOPE)
ENDFUNCTION()

This wrapper around FIND_PACKAGE_HANDLE_STANDARD_ARGS() handles a
single component in the same way the latter handles a package; in
particular, it respects the package's QUIET and REQUIRED options,
and finally, it sets up the component's XXX_YY_FOUND variable as
demanded in Modules/readme.txt for a package XXX's "part" YY.

4) Slightly edit Modules/readme.txt to reflect the changes.

Further measures, e.g. concerning FIND_PACKAGE(), aren't needed.

Of course, I would be especially interested in your opinion, Alex,
since the related issue #5920 is still open and marked as urgent.

[...]

>> Finally, an example: I have endangered my Qt installation by saying
>> "# mv libQtXml.so{,.bak}" for testing the following CMakeLists.txt:
>>
>> CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
>> PROJECT(QT_COMPONENT_TEST)
>> FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui QtXml REQUIRED)
>> MESSAGE("QT4_FOUND: <${QT4_FOUND}>")
>> MESSAGE("QT_QTCORE_FOUND: <${QT_QTCORE_FOUND}>")
>> MESSAGE("QT_QTGUI_FOUND: <${QT_QTGUI_FOUND}>")
>> MESSAGE("QT_QTXML_FOUND: <${QT_QTXML_FOUND}>")
>>
>> When run from a clean directory - no cache and the like - it yields:
>>
>> [...]
>> QT4_FOUND: <YES>
>> QT_QTCORE_FOUND: <1>
>> QT_QTGUI_FOUND: <1>
>> QT_QTXML_FOUND: <>
>> [...]
> 
> Hmm, not for me ( and I also didn't remember such behaviour):
> ammer:~/src/tests/findqt4/b262$ cat ../CMakeLists.txt
> cmake_minimum_required(VERSION 2.6)
> 
> find_package(Qt4 COMPONENTS QtXML)
> 
> message(STATUS "QT_QTXML_FOUND ${QT_QTXML_FOUND}")
> message(STATUS "QT_QTXML_LIBRARY ${QT_QTXML_LIBRARY}")
> hammer:~/src/tests/findqt4/b262$ /opt/cmake-2.6.2-Linux-i386/bin/cmake ..
> -- The C compiler identification is GNU
> -- The CXX compiler identification is GNU
> -- Check for working C compiler: /usr/bin/gcc
> -- Check for working C compiler: /usr/bin/gcc -- works
> -- Detecting C compiler ABI info
> -- Detecting C compiler ABI info - done
> -- Check for working CXX compiler: /usr/bin/c++
> -- Check for working CXX compiler: /usr/bin/c++ -- works
> -- Detecting CXX compiler ABI info
> -- Detecting CXX compiler ABI info - done
> -- Looking for Q_WS_X11
> -- Looking for Q_WS_X11 - found
> -- Looking for Q_WS_WIN
> -- Looking for Q_WS_WIN - not found.
> -- Looking for Q_WS_QWS
> -- Looking for Q_WS_QWS - not found.
> -- Looking for Q_WS_MAC
> -- Looking for Q_WS_MAC - not found.
> -- Found Qt-Version 4.6.0
> -- Found OpenSSL: /usr/lib/libssl.so
> -- Looking for _POSIX_TIMERS
> -- Looking for _POSIX_TIMERS - found
> -- QT_QTXML_FOUND 1
> -- QT_QTXML_LIBRARY /opt/kde-qt/lib/libQtXml.so
> -- Configuring done
> -- Generating done
> -- Build files have been written to: /home/alex/src/tests/findqt4/b262

Here, I'm not sure if I got your point. You requested QtXml from Qt4 as
non-required and observed that FindQt4.cmake reports this component as
found: Nothing unusual, isn't it? What I have done, in contrast, is:

- I moved libQtXml.so out of the way, so QtXml cannot be found anymore.
- Nonetheless, I requested QtXml as a REQUIRED component from Qt4.

Findings are:

- FIND_PACKAGE() does not bail out, but returns regularly, yielding
- QT_QTXML_FOUND=FALSE - correct, but somewhat perky - and
- QT4_FOUND=TRUE,

and it's this latter result I'd like to mention as an example for my
concern: The absence of a requested component does *not* prevent the
whole package from being considered as found, and, IMHO, this is the
way things should be done. Nevertheless, FindQt4 is convicted of not
honouring the REQUIRED option. ;)

Best regards,

Michael


More information about the CMake mailing list