[CMake] Boost macro behavior with respect to version

Michael Hertling mhertling at online.de
Sat Feb 12 12:28:15 EST 2011


On 02/10/2011 09:17 PM, Adams, Brian M wrote:
> I'm curious if the behavior I'm seeing with respect to FindBoost.cmake is expected.  (I realize I'm using these macros in a convoluted way, so understand if I can't make it work more cleanly.)
> 
> What I'd like to be able to do is something like the following to probe for a system-provided or user-specified Boost, but then fall back on a distributed one that comes with our software as needed (probably in config instead of module mode, but that's for another day):
> 
> find_package(Boost 1.40)
> if (NOT Boost_FOUND)
>   set(BOOST_ROOT /path/to/boost/in/our/tree)
>   find_package(Boost 1.40 REQUIRED)
> endif()
> 
> I get problems with this, e.g., on RHEL5, which has Boost 1.33 installed.  The first find fails due to version too low, then the second find doesn't probe because it detects Boost settings in the cache.  After the first probe (and in the end) Boost_FOUND is false, AND the include and lib dirs for Boost are set to the 1.33 install location, which is misleading.  It seems odd to me that these variables get set even though the probe failed.
> 
> I can perform a workaround where I trick FindBoost.cmake to not use the cache:
> 
> find_package(Boost 1.40)
> if (NOT Boost_FOUND)
>   unset(Boost_INCLUDE_DIR CACHE)
>   # these not needed, but could do to be safe:
>   #unset(Boost_LIBRARY_DIRS CACHE)
>   #unset(Boost_LIB_VERSION CACHE)
>   #unset(Boost_VERSION CACHE)
>   set(BOOST_ROOT /path/to/boost/in/our/tree)
>   find_package(Boost 1.40)
> endif()
> 
> Then I end up with the 1.40 that I'd expect.  This seems likely to bite me down the road.  How (besides requiring the user to have sufficiently new Boost on their system), should I handle this?
> 
> Thanks,
> Brian

Without having inspected FindBoost.cmake in detail, I think what you
report here is a fundamental problem of find modules in general: They
don't clean up if they're about to return with a negative result, i.e.
XXX_FOUND==FALSE. Particularly, they leave variables in the cache that
stem from diverse find function calls, and if some of these calls have
succeeded, a following invocation of the same module can't perform the
search again because the find functions are prevented to do so by the
positive results in the cache.

Even quite simple find modules like FindJPEG.cmake show that behaviour:
Suppose you've the runtime component, i.e. the library only, installed
from the distribution and another full installation in, say, /opt/jpeg.
If you say FIND_PACKAGE(JPEG) without hinting CMake at the latter path,
you would end up with JPEG_FOUND==FALSE as well as JPEG_INCLUDE_DIR==
JPEG_INCLUDE_DIR-NOTFOUND but JPEG_LIBRARY==/usr/lib/libjpeg.so, so a
following FIND_PACKAGE(JPEG) with, e.g., CMAKE_PREFIX_PATH==/opt/jpeg
set before will result in JPEG_FOUND==TRUE with

JPEG_INCLUDE_DIR==/opt/jpeg/include
JPEG_LIBRARY==/usr/lib/libjpeg.so

which means a non-matching combination of header and library.

While one hardly calls FindJPEG.cmake more than once in this manner,
there're quite good occasions to do so with more complex find modules
like FindBoost.cmake, especially if they support the specification of
a version via FIND_PACKAGE() as your scenario clearly shows, cf. [1].

An obvious solution would be to unset the concerned variables in the
cache if the module is going to return with a negative result so that
a subsequent invocation of the find module can start all over again.
OTOH, if you configure with -DXXX_LIBRARY=... on the command line or
a GUI, you'd probably expect this to be respected by all invocations
of FindXXX.cmake throughout the entire project, so the module would
need to keep track of which variables are passed in from the outside
and which ones are actually set by the module. Of course, this would
complicate things a lot when writing find modules.

IMO, the best approach to address this issue in your case is the one
you presented above: Explicitly unset the concerned variables in the
cache before performing the search again with a modified BOOST_ROOT.
Alternatively, you might simply bail out if Boost 1.40 has not been
found and instruct the user to throw away the cache and reconfigure
with BOOST_ROOT set appropriately - or provide a dedicated option,
e.g. USE_INTERNAL_BOOST, to handle this in your CMakeLists.txt.

Note that there's no such issue with configuration files: A negative
result from FIND_PACKAGE() in config mode means that no config file
has been found or all version files have declared their package as
incompatibe. In either case, no config file has been processed, so
there can't be any legacies in the cache.

Regards,

Michael

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


More information about the CMake mailing list