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

Johannes Zarl Johannes.Zarl at jku.at
Mon Nov 29 08:28:13 EST 2010


Sorry for the late response, but your mail was simply to long for a
swift response...

On 11/26/2010 at 05:47, Michael Hertling <mhertling at online.de> wrote: 
> On 11/24/2010 04:51 PM, Johannes Zarl wrote:
>> 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 this mean that XXX_LIBRARIES etc. should normally incorporate the
settings for the components as well? IMO this can't work if you call
find_package several times with different components. Also, this would 
make it impossible to link to the "base library" alone, i.e. without
the components...

> - 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. 

Thanks, I hadn't thought of this. So the code-snippet above should 
contain a "set ( XXX_${component}_FOUND ${component}-NOTFOUND )".

> Besides, it's a good style to refer to any
> component-related variables only if the particular component has been
> requested explicitly.

Ok, so a better style would be to only set the XXX_HAS_* variables at 
first (maybe unsetting them at the end of the config file?), and then
to conditionally set the XXX_YYY_INCLUDE_DIRS etc. if XXX_YYY_FOUND
is true.

> - 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.

I don't really see this as a problem. Although the code at hand lacks 
support for this kind of dependencies, in most cases you won't need it.
And if you as the config file writer need it, you obviously know you
have dependencies and that you have to write code supporting those
dependencies.

Update/FYI: I just tried tried to write generic code to deal with 
dependencies, and its not as straightforward as I thought it is. Assuming
that there are no circular dependencies, you could recursively add the 
dependencies to the XXX_FIND_COMPONENTS variable and remove the duplicates
afterwards. Then you can use the above code to set XXX_*_FOUND, and
afterwards, you have to recursively add the libraries/defines/include_dirs
of the depended upon components to each component's includes (this
is the tricky part IMO). I guess it is far easier to write this in a
non-generic way...

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

Generally speaking, I would say: yes. At least this is the way of the
least surprise for the user, as in sufficiently complex projects a
package may be included at different places with different arguments.

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

Ah, this is how it's done. I was wondering why a call like

find_package(XXX COMPONENTS ZZZ REQUIRED YYY)

sets both XXX_YYY_REQUIRED and XXX_ZZZ_REQUIRED.

> 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.

This isn't that complicated. I have written such a FindLibrary.cmake module
(although without dependencies) and the REQUIRED and QUIET options are
quite easy to handle. If you split the process into 2 parts, you lose much
of the complexity: first, search for any components you can find, second
check for the required ones, giving diagnostic/error messages as is
appropriate.

> - 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.

As I have written above, I don't think that the components should alter
the values of the package-wide variables (XXX_LIBRARIES etc.). The same
applies to the XXX_FOUND variable. If you search for library XXX and 
component YYY, XXX is still found even if it lacks the requested component.
If you want to know if XXX was found, you use XXX_FOUND, if you want the 
same for component YYY, you use XXX_YYY_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. 

I definitely agree. Multi-component packages (and package-finding in 
general) are often a complex matter, and the documentation on this topic
is not easy to read.

> See [1] for a former discussion of
> several aspects regarding multi-component packages.
> [...]
> [1] http://www.mail-archive.com/cmake@cmake.org/msg28431.html

Looks interesting. I will have to read that discussion...

Cheers,
  Johannes




-- 
Johannes Zarl
Virtual Reality Services

Johannes Kepler University
Informationsmanagement

Altenbergerstrasze 69
4040 Linz, Austria
Phone: +43 732 2468-8321
johannes.zarl at jku.at
http://vrc.zid.jku.at











More information about the CMake mailing list