[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