[CMake] What does find_package(COMPONENTS) do? and

Michael Hertling mhertling at online.de
Mon Oct 11 00:50:57 EDT 2010


On 10/06/2010 10:47 PM, Stephen Kelly wrote:
> Hi,
> 
> The documentation says
> 
>> A package-specific list of components may be listed after the REQUIRED 
>> option or after the COMPONENTS option if no REQUIRED option is given. 
> 
> http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package
> 
> But it doesn't say why you would want to do that. At first I thought it was 
> to specify that only the components specified would be used to fill cmake 
> variables. However, find_package(Qt COMPONENTS QtCore) also results in 
> ${QT_QTGUI_LIBRARY} being defined for example. [...]

Which variables do you aim at? The cached ones, e.g. XXX_YY_LIBRARY, or
the non-cached ones, e.g. XXX_LIBRARIES? IMO, it is alright to fill the
formers regardless if the corresponding component has been requested or
not, although there could be reasons not to do so. Anyway, the latters
should be provided with values for the explicitly requested components
only; otherwise, FIND_PACKAGE()'s COMPONENTS option doesn't make sense.
When including the QT_USE_FILE, the find module of Qt4 behaves in this
manner, but only the QT_LIBRARIES variable is provided; the component-
specific definitions like QT_GUI_LIB and the include directories are
just enabled without being made accessible via variables, cf. [1].

> [...] So then I thought maybe the 
> semantic is that if one of the components can not be found, the find_package 
> fails. So I tried find_package(Qt REQUIRED QtCore QtDoesNotExist) which 
> works without error.

In my understanding, this behaviour is wrong, indeed, since it makes
the REQUIRED flag useless, or in other words: After having requested
the components as REQUIRED, one strongly dislikes the need to check
whether they've actually been found. However, there's a fundamental
limitation of FIND_PACKAGE() w.r.t. optional components: They can't
be requested along with required ones as a find module has no means
to distinguish them, cf. [2].

> What am I missing?

Basically nothing, but the recommended behaviour of components-aware
find modules should be elaborated better, and the Qt ones are even a
bit special. Questions which may need a clarification are, e.g.:

- Lookup of components that aren't requested by the user?
- Meaning of the REQUIRED flag in regard to missing components?
- Standardization of component lookup results? FPHSA() doesn't suit.
- Do multi-component FIND_PACKAGE() calls work accumulatively, i.e. do
  FIND_PACKAGE(XXX COMPONENTS YY1) and FIND_PACKAGE(XXX COMPONENTS YY2)
  end up with XXX_LIBRARIES=="${XXX_YY1_LIBRARY} ${XXX_YY2_LIBRARY}",
  or are the results of the former overwritten by the latter?
- Guidelines concerning "use files" like QT_USE_FILES.

> The reason I ask is that I finally figured out why the tests in my library 
> needed to be linked to QtGui, even though it only uses QtCore.
> 
> The reason is that find_package(Qt) causes QT_GUI_LIB to be defined. Which 
> in turn causes QTEST_MAIN to be defined to expand to use QApplication 
> instead of QCoreApplication as it is if QT_GUI_LIB is not defined.
> 
> http://qt.gitorious.org/qt/qt/blobs/4.7/src/testlib/qtest.h
> 
> find_package(Qt REQUIRED QtCore QtScript) does not cause QT_GUI_LIB to be 
> defined. However, some of my targets do need QtGui, so I should specify it, 
> right? 

Not necessarily: You could try to issue, e.g., FIND_PACKAGE(Qt4) without
including the QT_USE_FILE, rely on FindQt4.cmake to detect all available
Qt components and use the latters' specific variables explicitly, i.e.:

ADD_DEFINITIONS(-DQT_GUI_LIB)
INCLUDE_DIRECTORIES(${QT_QTGUI_INCLUDE_DIR})
...
TARGET_LINK_LIBRARIES(... ${QT_QTGUI_LIBRARY})

So, you can control if, when and for what QT_GUI_LIB is defined since
it's the QT_USE_FILE which enables QT_GUI_LIB generally if QtGui is
mentioned among the components of the FIND_PACKAGE(Qt4 ...) call.

> So if I do specify it I'll end up having QT_GUI_LIB defined when building my 
> unit tests. I could remove_definitions(-DQT_GUI_LIB), but apart from being a 
> BadHack(tm), it causes my unit tests which *do* require QtGui to fail at 
> runtime because they create QWidgets and by undeffing QT_GUI_LIB I build 
> them to use QCoreApplication instead of QApplication (Not allowed in Qt), so 
> the tests fail at runtime.
> 
> http://gitorious.org/grantlee/grantlee/blobs/0.1/tests/CMakeLists.txt
> 
> Is there a solution to all this? What is the point of COMPONENTS if it has 
> no effect on what I can include or link to? Is it possible to link some of 
> my tests to QtGui but not all of them and still have them all pass? Do I 
> need to just link my core tests to QtGui and use QApplication and quit my 
> complaining?

As I can see from your CMakeLists.txt, you do not use QT_USE_FILE's
results, particularly QT_LIBRARIES, so it should be possible to omit
its inclusion and, instead, define QT_GUI_LIB et al. per source file
or target. As an alternative, you can follow Andreas' suggestion and
divide your targets into GUI-dependent and GUI-independent ones in
order to enable include directories and definitions per directory.

Concerning the point of COMPONENTS: Technically, AFAIK, it sets up the
values of XXX_FIND_COMPONENTS and XXX_FIND_REQUIRED_YYY, but anything
else is left to the find module, and that's why we should have some
guiding recommendations for its design when it is components-aware.

> The only way I can see to satisfy all requirements is attached. Is that 
> acceptable or is there a better way?

Looks good to me, and if you remove the INCLUDE(${QT_USE_FILE}) you
shouldn't need to REMOVE_DEFINITIONS(-DQT_GUI_LIB), but don't forget

ADD_DEFINITIONS(${QT_DEFINITIONS})
INCLUDE_DIRECTORIES(${QT_INCLUDE_DIR})

and

INCLUDE_DIRECTORIES(${QT_QT<MODULE>_INCLUDE_DIR})

as well as defining QT_<MODULE>_LIB - not just QT_GUI_LIB - where the
appropriate modules are used. Finally, you should perhaps take a look
at the QT_DEBUG/QT_NO_DEBUG definitions processed by the QT_USE_FILE
aka Modules/UseQt4.cmake in case you're relying on them somewhere.

Regards,

Michael

[1] http://www.mail-archive.com/cmake@cmake.org/msg29020.html
[2] http://www.mail-archive.com/cmake@cmake.org/msg28536.html


More information about the CMake mailing list