From d358cf5cc4d70f730873646ae95bdb0c637db262 Mon Sep 17 00:00:00 2001 From: Alex Neundorf Date: Thu, 29 Jul 2010 00:09:14 +0200 Subject: [PATCH] add 2nd, more powerful mode to find_package_handle_standard_args() This additional mode also supports version checking and should be easily extendible, e.g. for COMPONENT stuff. Updated FindBISON.cmake as first user of this new mode. Docs updated. Alex --- Modules/FindBISON.cmake | 6 +- Modules/FindPackageHandleStandardArgs.cmake | 242 +++++++++++++++++--- 2 files changed, 211 insertions(+), 37 deletions(-) diff --git a/Modules/FindBISON.cmake b/Modules/FindBISON.cmake index b604d0ba6..b1b501019 100644 --- a/Modules/FindBISON.cmake +++ b/Modules/FindBISON.cmake @@ -5,6 +5,9 @@ # BISON_VERSION - version of bison # BISON_FOUND - true if the program was found # +# The minimum required version of bison can be specified using the +# standard CMake syntax, e.g. find_package(BISON 2.1.3) +# # If bison is found, the module defines the macros: # BISON_TARGET( [VERBOSE ] # [COMPILE_FLAGS ]) @@ -149,6 +152,7 @@ IF(BISON_EXECUTABLE) ENDIF(BISON_EXECUTABLE) INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON DEFAULT_MSG BISON_EXECUTABLE) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE + VERSION_VAR BISON_VERSION) # FindBISON.cmake ends here diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake index e17105c3e..21facd456 100644 --- a/Modules/FindPackageHandleStandardArgs.cmake +++ b/Modules/FindPackageHandleStandardArgs.cmake @@ -1,21 +1,56 @@ -# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME (DEFAULT_MSG|"Custom failure message") VAR1 ... ) -# This macro is intended to be used in FindXXX.cmake modules files. -# It handles the REQUIRED and QUIET argument to FIND_PACKAGE() and -# it also sets the _FOUND variable. -# The package is found if all variables listed are TRUE. -# Example: +# FIND_PACKAGE_HANDLE_STANDARD_ARGS( ... ) +# This function is intended to be used in FindXXX.cmake modules files. +# It handles the REQUIRED, QUIET and version-related arguments to FIND_PACKAGE(). +# It also sets the _FOUND variable. +# The package is considered found if all variables ... listed contain +# valid results, e.g. valid filepaths. # -# FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARIES LIBXML2_INCLUDE_DIR) +# There are two modes of this function. The first argument in both modes is +# the name of the Find-module where it is called (in original casing). # -# LibXml2 is considered to be found, if both LIBXML2_LIBRARIES and +# The first simple mode looks like this: +# FIND_PACKAGE_HANDLE_STANDARD_ARGS( (DEFAULT_MSG|"Custom failure message") ... ) +# If the variables to are all valid, then _FOUND +# will be set to TRUE. +# If DEFAULT_MSG is given as second argument, then the function will generate +# itself useful success and error messages. You can also supply a custom error message +# for the failure case. This is not recommended. +# +# The second mode is more powerful and also supports version checking: +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME [REQUIRED_VARS ...] +# [VERSION_VAR +# [FAIL_MESSAGE "Custom failure message"] ) +# +# As above, if through are all valid, _FOUND +# will be set to TRUE. +# Via FAIL_MESSAGE a custom failure message can be specified, if this is not +# used, the default message will be displayed. +# Following VERSION_VAR the name of the variable can be specified which holds +# the version of the package which has been found. If this is done, this version +# will be checked against the (potentially) specified required version used +# in the find_package() call. The EXACT keyword is also handled. The default +# messages include information about the required version and the version +# which has been actually found, both if the version is ok or not. +# Example for mode 1: +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) +# +# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and # LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to TRUE. -# If it is not found and REQUIRED was used, it fails with FATAL_ERROR, +# If it is not found and REQUIRED was used, it fails with FATAL_ERROR, # independent whether QUIET was used or not. -# If it is found, the location is reported using the VAR1 argument, so -# here a message "Found LibXml2: /usr/lib/libxml2.so" will be printed out. -# If the second argument is DEFAULT_MSG, the message in the failure case will -# be "Could NOT find LibXml2", if you don't like this message you can specify -# your own custom failure message there. +# If it is found, success will be reported, including the content of . +# On repeated Cmake runs, the same message won't be printed again. +# +# Example for mode 2: +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE +# VERSION_VAR BISON_VERSION) +# In this case, BISON is considered to be found if the variable(s) listed +# after REQUIRED_VAR are all valid, i.e. BISON_EXECUTABLE in this case. +# Also the version of BISON will be checked by using the version contained +# in BISON_VERSION. +# Since no FAIL_MESSAGE is given, the default messages will be printed. #============================================================================= # Copyright 2007-2009 Kitware, Inc. @@ -31,13 +66,89 @@ # License text for the above reference.) INCLUDE(FindPackageMessage) -FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FAIL_MSG _VAR1 ) - IF("${_FAIL_MSG}" STREQUAL "DEFAULT_MSG") +# Checks a list of strings (typically function/macro parameters) whether it contains a keyword +# (_keyword) and return the value(s) following this keyword in _outputVar. +# If _multipleValues is true, this can be more than one value. +# Then end of the values for a keyword is then determined by checking whether the +# next argument is contained in _allKeywordsList. +FUNCTION(FPHSA_GET_OPTION_VALUE _keyword _outputVar _allArgsList _allKeywordsList _multipleValues) + UNSET(${_outputVar}) + UNSET(_removeIndices) + + SET(_insideValues FALSE) + SET(_counter 0) + FOREACH(_currentArg ${${_allArgsList}}) + IF(NOT _insideValues) # first check that we find the keyword we are currently interested in... + IF("${_currentArg}" STREQUAL "${_keyword}") + SET(_insideValues TRUE) + LIST(APPEND _removeIndices ${_counter}) + ENDIF("${_currentArg}" STREQUAL "${_keyword}") + ELSE(NOT _insideValues) + + LIST(FIND ${_allKeywordsList} "${_currentArg}" _index) # ... then this marks the end of the arguments belonging to this keyword + IF(${_index} EQUAL -1) + LIST(APPEND _result ${_currentArg}) + LIST(APPEND _removeIndices ${_counter}) # collect the indices of the strings we found to remove them from the list afterwards + IF(NOT _multipleValues) + BREAK() + ENDIF(NOT _multipleValues) + ELSE(${_index} EQUAL -1) + SET(_insideValues FALSE) + BREAK() + ENDIF(${_index} EQUAL -1) + ENDIF(NOT _insideValues) + + MATH(EXPR _counter "${_counter} + 1") + + ENDFOREACH(_currentArg ${${_allArgsList}}) + + IF(DEFINED _removeIndices) + LIST(REMOVE_AT ${_allArgsList} ${_removeIndices}) + ENDIF(DEFINED _removeIndices) + + SET(${_outputVar} ${_result} PARENT_SCOPE) + SET(${_allArgsList} ${${_allArgsList}} PARENT_SCOPE) + +ENDFUNCTION(FPHSA_GET_OPTION_VALUE _keyword _outputVar _allArgsList _allKeywordsList _multipleValues) + + +FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG _VAR1) + + SET(_KEYWORDS_FOR_EXTENDED_MODE FAIL_MESSAGE REQUIRED_VARS VERSION_VAR ) + LIST(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + IF(${INDEX} EQUAL -1) + SET(_FAIL_MESSAGE ${_FIRST_ARG}) + SET(_REQUIRED_VARS ${_VAR1} ${ARGN}) + SET(_VERSION_VAR) + ELSE(${INDEX} EQUAL -1) + SET(ALL_ARGS ${_FIRST_ARG} ${_VAR1} ${ARGN}) + + FPHSA_GET_OPTION_VALUE("FAIL_MESSAGE" _FAIL_MESSAGE ALL_ARGS _KEYWORDS_FOR_EXTENDED_MODE FALSE) + FPHSA_GET_OPTION_VALUE("REQUIRED_VARS" _REQUIRED_VARS ALL_ARGS _KEYWORDS_FOR_EXTENDED_MODE TRUE) + FPHSA_GET_OPTION_VALUE("VERSION_VAR" _VERSION_VAR ALL_ARGS _KEYWORDS_FOR_EXTENDED_MODE FALSE) + + IF(ALL_ARGS) + MESSAGE(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${ALL_ARGS}\"") + ENDIF(ALL_ARGS) + + IF(NOT _FAIL_MESSAGE) + SET(_FAIL_MESSAGE "DEFAULT_MSG") + ENDIF(NOT _FAIL_MESSAGE) + ENDIF(${INDEX} EQUAL -1) + +# now that we collected all arguments, process them + + IF("${_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") SET(_FAIL_MESSAGE "Could NOT find ${_NAME}") - ELSE("${_FAIL_MSG}" STREQUAL "DEFAULT_MSG") - SET(_FAIL_MESSAGE "${_FAIL_MSG}") - ENDIF("${_FAIL_MSG}" STREQUAL "DEFAULT_MSG") + ENDIF("${_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") + + IF(NOT _REQUIRED_VARS) + MESSAGE(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + ENDIF(NOT _REQUIRED_VARS) + + LIST(GET _REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) STRING(TOUPPER ${_NAME} _NAME_UPPER) @@ -46,15 +157,8 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FAIL_MSG _VAR1 ) SET(MISSING_VARS "") SET(DETAILS "") SET(${_NAME_UPPER}_FOUND TRUE) - IF(NOT ${_VAR1}) - SET(${_NAME_UPPER}_FOUND FALSE) - SET(MISSING_VARS " ${_VAR1}") - ELSE(NOT ${_VAR1}) - SET(DETAILS "${DETAILS}[${${_VAR1}}]") - ENDIF(NOT ${_VAR1}) - # check if all passed variables are valid - FOREACH(_CURRENT_VAR ${ARGN}) + FOREACH(_CURRENT_VAR ${_REQUIRED_VARS}) IF(NOT ${_CURRENT_VAR}) SET(${_NAME_UPPER}_FOUND FALSE) SET(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}") @@ -63,18 +167,84 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FAIL_MSG _VAR1 ) ENDIF(NOT ${_CURRENT_VAR}) ENDFOREACH(_CURRENT_VAR) + + # version handling: + SET(VERSION_MSG "") + SET(VERSION_OK TRUE) + IF (${_NAME}_FIND_VERSION) + + # if the package was found, check for the version using _FIND_VERSION + IF (${_NAME_UPPER}_FOUND) + SET(VERSION ${${_VERSION_VAR}} ) + + IF(VERSION) + + IF(${_NAME}_FIND_VERSION_EXACT) # exact version required + IF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + SET(VERSION_MSG " Found version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + SET(VERSION_OK FALSE) + ELSE (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + SET(VERSION_MSG " (found exact version \"${VERSION}\")") + ENDIF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + + ELSE(${_NAME}_FIND_VERSION_EXACT) # minimum version specified: + IF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + SET(VERSION_MSG " Found version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") + SET(VERSION_OK FALSE) + ELSE ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + SET(VERSION_MSG " (found version \"${VERSION}\", required is \"${${_NAME}_FIND_VERSION}\")") + ENDIF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + ENDIF(${_NAME}_FIND_VERSION_EXACT) + +# Uncomment the following two lines to see to which Find-modules the VERSION_VAR keywords still need to be added: +# ELSE(VERSION) +# SET(VERSION_MSG " (WARNING: Required version is \"${${_NAME}_FIND_VERSION}\", but version of ${_NAME} is unknown)") + ENDIF(VERSION) + + # if the package was not found, but a version was given, add that to the output: + ELSE (${_NAME_UPPER}_FOUND) + IF(${_NAME}_FIND_VERSION_EXACT) + SET(VERSION_MSG " (Required is exact version \"${${_NAME}_FIND_VERSION}\")") + ELSE(${_NAME}_FIND_VERSION_EXACT) + SET(VERSION_MSG " (Required is at least version \"${${_NAME}_FIND_VERSION}\")") + ENDIF(${_NAME}_FIND_VERSION_EXACT) + ENDIF (${_NAME_UPPER}_FOUND) + ENDIF (${_NAME}_FIND_VERSION) + + IF(VERSION_OK) + SET(DETAILS "${DETAILS}[v${${VERSION}}]") + ELSE(VERSION_OK) + SET(${_NAME_UPPER}_FOUND FALSE) + ENDIF(VERSION_OK) + + + # print the result: IF (${_NAME_UPPER}_FOUND) - FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_VAR1}}" "${DETAILS}") + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG}" "${DETAILS}") ELSE (${_NAME_UPPER}_FOUND) - IF (${_NAME}_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "${_FAIL_MESSAGE} (missing: ${MISSING_VARS})") - ELSE (${_NAME}_FIND_REQUIRED) - IF (NOT ${_NAME}_FIND_QUIETLY) - MESSAGE(STATUS "${_FAIL_MESSAGE} (missing: ${MISSING_VARS})") - ENDIF (NOT ${_NAME}_FIND_QUIETLY) - ENDIF (${_NAME}_FIND_REQUIRED) + IF(NOT VERSION_OK) + + IF (${_NAME}_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "${_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") + ELSE (${_NAME}_FIND_REQUIRED) + IF (NOT ${_NAME}_FIND_QUIETLY) + MESSAGE(STATUS "${_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") + ENDIF (NOT ${_NAME}_FIND_QUIETLY) + ENDIF (${_NAME}_FIND_REQUIRED) + + ELSE(NOT VERSION_OK) + + IF (${_NAME}_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "${_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") + ELSE (${_NAME}_FIND_REQUIRED) + IF (NOT ${_NAME}_FIND_QUIETLY) + MESSAGE(STATUS "${_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") + ENDIF (NOT ${_NAME}_FIND_QUIETLY) + ENDIF (${_NAME}_FIND_REQUIRED) + ENDIF(NOT VERSION_OK) + ENDIF (${_NAME_UPPER}_FOUND) SET(${_NAME_UPPER}_FOUND ${${_NAME_UPPER}_FOUND} PARENT_SCOPE) -ENDFUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS) +ENDFUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _FIRST_ARG)