[CMake] Subdirectories and FIND_LIBRARY routines
Michael Wild
themiwi at gmail.com
Mon Jul 26 12:46:28 EDT 2010
On 26. Jul, 2010, at 17:59 , Clifford Yapp wrote:
> Hi! I have a rather oddball question, and I'm not quite sure if I'm
> even asking the right question, but I'll describe the circumstances
> and see if it makes sense...
>
> I am trying to set up a CMake project which includes as subdirectories
> other CMake projects, but it does so only conditionally. I.e., I
> have:
>
> toplevel/
> toplevel/libz
> toplevel/libpng
>
> and want to provide the user with the options to either:
>
> a) Use system libraries (disable the compilation and use of the local
> libz and libpng)
> b) Use local libraries (compile and use libz and libpng from local
> tree - libpng should also use the local libz in this situation)
> c) Use a mix of the two (system libz but local libpng, in this case
> libpng would use the system libz)
>
> I am getting the hang of setting variables using CACHE and FORCE
> options, and the VTK CMake files offer valuable hints, but I'm getting
> stuck on a detail. FindZLIB sets ZLIB_LIBRARY, and if I alter the
> configuration from local to system selection FindZLIB is run. All
> well and good, but if I switch back to local, the ZLIB_LIBRARY remains
> and remains set to the system lib. This raises two questions - if
> ZLIB_LIBRARY is set by FindZLIB, does it also need to be set when
> building the local libz (and if so, how, since libz is not built at
> configure time...) - also, since ZLIB_LIBRARY is now set in the cache
> by FIND_LIBRARY, what mechanism can I use to override that value with
> the local settings? Presumably I can't point FIND_LIBRARY at libz
> when it's not built yet and expect ZLIB_LIBRARY to get set, and even
> if I could would FIND_LIBRARY override and reset the cache settings?
>
> I've tried poking around various websites and wikis, and I apologize
> if I missed a tutorial covering these points. (For those of you
> thinking it, I've already lost the argument against any sort of local
> libz copy... the policy is that the option needs to be there).
>
> Autotools allows subconfigures, but the process has never been all
> that robust as most projects don't write their files with the notion
> of being a subconfigure in mind. I'm hoping CMake might provide a
> more robust solution to this issue?
>
> Thanks,
> CY
Here's what I do:
# third-party library handling. if <optional>, generates an option
# ENABLE_<upper_name> (defaulting to <enable>) and if that is TRUE
# (or the package is not optional), generates an option
# BUILD_PRIVATE_<upper_name> whose default is determined by whether
# Find<pkgname>.cmake was successful. if that option is TRUE, the
# included package will be built, otherwise the system installation
# will be used.
macro(thirdparty_option name description
pkgname optional enable)
set(_tpo_name "${name}")
set(_tpo_description "${description}")
set(_tpo_pkgname "${pkgname}")
set(_tpo_optional "${optional}")
set(_tpo_enable "${enable}")
string(TOUPPER ${_tpo_name} _tpo_upper_name)
string(TOUPPER ${_tpo_pkgname} _tpo_upper_pkgname)
if(_tpo_optional)
option(ENABLE_${_tpo_upper_name}
"${_tpo_description}" ${_tpo_enable})
mark_as_advanced(ENABLE_${_tpo_upper_name})
endif()
if(ENABLE_${_tpo_upper_name} OR NOT _tpo_optional)
find_package(${_tpo_pkgname} QUIET)
if(${_tpo_upper_pkgname}_FOUND)
set(_tpo_private OFF)
else()
set(_tpo_private ON)
endif()
option(BUILD_PRIVATE_${_tpo_upper_name}
"Download and compile ${_tpo_name} instead of searching in on the system"
${_tpo_private})
if(BUILD_PRIVATE_${_tpo_upper_name})
build_thirdparty(${_tpo_upper_name})
elseif(NOT ${_tpo_upper_pkgname}_FOUND)
# just to get the not-found-message on the screen
find_package(${_tpo_pkgname})
set(_tpo_msg
" If you have ${_tpo_name} installed, edit the variables beginning"
" with ${_tpo_upper_name}_ to refer to the installation or enable the"
" setting BUILD_PRIVATE_${_tpo_upper_name} in the cache.\n"
)
if(_tpo_optional)
message(SEND_ERROR
"ENABLE_${_tpo_upper_name} is TRUE, but ${_tpo_name} cannot be"
" found." ${_tpo_msg}
)
else()
message(SEND_ERROR
"${_tpo_name} is required but cannot be found.\n"
${_tpo_msg}
)
endif()
else()
# just to get the found-message on the screen
find_package(${_tpo_pkgname})
endif()
mark_as_advanced(BUILD_PRIVATE_${_tpo_upper_name})
endif()
endmacro()
Where build_thirdparty() is a macro that builds the named package (in my case through ExternalProject_Add) and sets compatibility variables (i.e. the XXX_INCLUDE_DIR and XXX_LIBRARIES variables). This is possible since you can always override the cached values of a variable (the reverse is undefined, however. so be careful). Above code is intentionally a macro, since otherwise it would be cumbersome to propagate the variables XXX_INCLUDE_DIR and XXX_LIBRARIES (and similar ones) to the caller scope.
HTH
Michael
More information about the CMake
mailing list