[CMake] providing library information, what's the cmake way

Michael Hertling mhertling at online.de
Thu Nov 25 23:47:16 EST 2010


On 11/24/2010 04:51 PM, Johannes Zarl wrote:
> Hi,
> 
> On 11/23/2010 at 21:25, Alexander Neundorf <a.neundorf-work at gmx.net> wrote: 
>> CMAKE_MODULE_PATH is only for finding Find-modules, not for finding the 
>> actual packages or Config-files.
>> For that you can set CMAKE_PREFIX_PATH. CMake uses each directory contained 
>> in CMAKE_PREFIX_PATH for all its find_program/file/path/library/package() 
>> calls to search in the respective subdirectories for the 
>> executables/files/libraries/Config-files.
> 
> Thanks for this information! Knowing this greatly simplifies the whole 
> find_package stuff...
> 
>>> And is there any documentation on how to create a LibraryConfig.cmake file
>>> with support for components?
>>
>> In KDE we have a macro MACRO_WRITE_BASIC_CMAKE_VERSION_FILE() which helps 
>> with creating a basic version-info file which should be installed along 
>> with the Config-file.
>> It consists of MacroWriteBasicCMakeVersionFile.cmake and 
>> BasicFindPackageVersion.cmake.in which you can find in 
>> http://websvn.kde.org/trunk/KDE/kdelibs/cmake/modules/ .
> 
> Nice to have some reusable code for versioning...
> 
> About the components question again: I played around a bit, and I think I 
> now more or less have comprehended this. I guess for a package XXX with 
> components YYY and ZZZ, one could set variables XXX_HAS_YYY and XXX_HAS_ZZZ
> and then use a loop like this one in the XXXConfig.cmake file:
> 
> foreach( component ${XXX_FIND_COMPONENTS} )
>     if ( XXX_HAS_${component})
>         set ( XXX_${component}_FOUND TRUE )
>     else( XXX_HAS_${component})
>         if ( ${XXX_FIND_REQUIRED})
>             message(FATAL_ERROR "Required component ${component} not found!")
>         elseif ( NOT XXX_FIND_QUIETLY)
>             message(STATUS "Component ${component} not found!")
>         endif ( ${XXX_FIND_REQUIRED})
>     endif ( XXX_HAS_${component})
> endforeach( component )
> 
> Correct?

While that's a possible approach it lacks the invocation-specific
variables, i.e. XXX_{INCLUDE_DIRS,LIBRARIES,DEFINITIONS}, and in
some cases, these can't be assembled in a simple component-wise
manner, see below. Moreover, there are further questions w.r.t.
multi-component packages and their config files:

- Does the config file provide the component-specific variables like
XXX_YYY_FOUND for each available component or for the requested ones
only, i.e. can you rely on XXX_ZZZ_FOUND to have a definite value if
you just said FIND_PACKAGE(XXX COMPONENTS YYY)? With your foregoing
approach, you can't. That's alright, but should be mentioned in the
package's documentation. Imagine the following scenario: There's one
installation of XXX for the native system and another one for cross
compiling purposes. The latter has YYY and ZZZ while the former has
YYY only. Due to FIND_PACKAGE()'s ability to search for config files
in various locations, such a coexistence is easily possible. Now, a
FIND_PACKAGE(XXX COMPONENTS YYY ZZZ) for the cross compilation XXX
returns with XXX_ZZZ_FOUND=TRUE, but does a subsequent invocation of
FIND_PACKAGE(XXX COMPONENTS YYY) within the same scope for the native
XXX return with XXX_ZZZ_FOUND=FALSE, or does XXX_ZZZ_FOUND=TRUE still
hold from the previous invocation? Both alternatives are fine, but the
user should know the score. Besides, it's a good style to refer to any
component-related variables only if the particular component has been
requested explicitly.

- Handling of inter-component dependencies: Imagine the user just says
FIND_PACKAGE(XXX COMPONENTS ZZZ), but ZZZ needs YYY. If YYY is to be
enabled automatically the config file must know about the dependency
and cannot simply add the ZZZ-specific variables to the invocation-
specific ones; the YYY-specific variables must be mentioned, too.

- Do multiple consecutive FIND_PACKAGE(XXX ...) invocations act in an
accumulative manner on the invocation-specific variables? E.g., does

FIND_PACKAGE(XXX COMPONENTS YYY)
FIND_PACKAGE(XXX COMPONENTS ZZZ)

result in XXX_LIBRARIES="${XXX_YYY_LIBRARY} ${XXX_ZZZ_LIBRARY}", or
does the second invocation overwrite the results of the first? Again,
both alternatives are fine, but should be documented. That's important
when there are required and optional components to handle: Imagine YYY
is required while ZZZ is optional for the user; one might tend to say

FIND_PACKAGE(XXX REQUIRED YYY)
FIND_PACKAGE(XXX COMPONENTS ZZZ)

instead of

FIND_PACKAGE(XXX COMPONENTS YYY ZZZ)
IF(NOT XXX_YYY_FOUND)
    MESSAGE(FATAL_ERROR ...)
ENDIF()

When turning towards find modules, the situation becomes even more
complicated. From a user's perspective, find modules and config files
should behave the same, but the formers can't know which components are
available, so they must look for them; this gives rise to questions like:

- Meaning of the REQUIRED and QUIET options: When a find module looks
for any component the effects of REQUIRED and QUIET depend on whether
the component has been requested explicitly or not. If it hasn't, the
find module mustn't bail out if the component isn't found even though
REQUIRED was specified; that's the opposite of what is expected for an
explicitly requested component. E.g., if FIND_PACKAGE(XXX REQUIRED YYY)
also looks for ZZZ on its own behalf the find module is not expected to
throw a fatal error if ZZZ is absent whereas the absence of YYY should
result in such a behaviour right away. A similar view may be taken for
the QUIET option: Even if it wasn't specified the user wouldn't expect
any messages related to components that weren't requested explicitly.

- Interpretation of XXX_FOUND: Config files can't set the value of this
variable, but find modules can, so one should think about what it means
for XXX_FOUND if a component - requested or not - hasn't been found.

None of these questions arises for single-component packages, and while
there are reasonable answers to all of them, IMO, CMake's accompanying
documentation could perhaps be enhanced a bit w.r.t. multi-component
packages; particularly, a simple but yet complete example how to do
things would be quite helpful. See [1] for a former discussion of
several aspects regarding multi-component packages.

Regards,

Michael

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


More information about the CMake mailing list