From b173b879f8c2a3035d826e1cfa1407380b6eb2ee Mon Sep 17 00:00:00 2001 From: Alex Neundorf Date: Sat, 14 Aug 2010 22:06:49 +0200 Subject: [PATCH] Add macro CMakeParseArguments() and use it in FPHSA() This adds a macro cmake_parse_arguments() (as discussed on cmake-devel) which can be used in macros or functions to help with parsing its arguments. Detailled docs are included. find_package_handle_standard_args() is the first user of this new macro. Alex --- Modules/CMakeParseArguments.cmake | 137 ++++++++++++++++++++ Modules/FindPackageHandleStandardArgs.cmake | 100 +++++--------- 2 files changed, 166 insertions(+), 71 deletions(-) create mode 100644 Modules/CMakeParseArguments.cmake diff --git a/Modules/CMakeParseArguments.cmake b/Modules/CMakeParseArguments.cmake new file mode 100644 index 000000000..c7148aec4 --- /dev/null +++ b/Modules/CMakeParseArguments.cmake @@ -0,0 +1,137 @@ +# CMAKE_PARSE_ARGUMENTS( args...) +# +# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for +# parsing the arguments given to that macro or function. +# It processes the arguments and defines a set of variables which hold the +# values of the respective options. +# +# The argument contains all options for the respective macro, +# i.e. keywords which can be used when calling the macro without any value +# following, like e.g. the OPTIONAL keyword of the install() command. +# +# The argument contains all keywords for this macro +# which are followed by one value, like e.g. DESTINATION keyword of the +# install() command. +# +# The argument contains all keywords for this macro +# which can be followed by more than one value, like e.g. the TARGETS or +# FILES keywords of the install() command. +# +# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the +# keywords listed in , and +# a variable composed of the given +# followed by "_" and the name of the respective keyword. +# These variables will then hold the respective value from the argument list. +# For the keywords this will be TRUE or FALSE. +# +# All remaining arguments are collected in a variable +# _UNPARSED_ARGUMENTS, this can be checked afterwards to see whether +# your macro was called with unrecognized parameters. +# +# As an example here a my_install() macro, which takes similar arguments as the +# real install() command: +# +# function(MY_INSTALL) +# set(options OPTIONAL FAST) +# set(oneValueArgs DESTINATION RENAME) +# set(multiValueArgs TARGETS CONFIGURATIONS) +# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) +# ... +# +# Assume my_install() has been called like this: +# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) +# +# After the cmake_parse_arguments() call the macro will have set the following +# variables: +# MY_INSTALL_OPTIONAL = TRUE +# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() +# MY_INSTALL_DESTINATION = "bin" +# MY_INSTALL_RENAME = "" (was not used) +# MY_INSTALL_TARGETS = "foo;bar" +# MY_INSTALL_CONFIGURATIONS = "" (was not used) +# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" +# +# You can the continue and process these variables. +# +# Keywords terminate lists of values, e.g. if directly after a one_value_keyword +# another recognized keyword follows, this is interpreted as the beginning of +# the new option. +# E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in +# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would +# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. + +# Copyright (c) 2010, Alexander Neundorf, +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + + +if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) + return() +endif() +set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) + + +function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) + # first set all result variables to empty/FALSE + foreach(arg_name ${_singleArgNames} ${_multiArgNames}) + set(${prefix}_${arg_name}) + endforeach(arg_name) + + foreach(option ${_optionNames}) + set(${prefix}_${option} FALSE) + endforeach(option) + + set(${prefix}_UNPARSED_ARGUMENTS) + + set(insideValues FALSE) + set(currentArgName) + + # now iterate over all arguments and fill the result variables + foreach(currentArg ${ARGN}) + list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword + list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword + list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword + + if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) + if(insideValues) + if("${insideValues}" STREQUAL "SINGLE") + set(${prefix}_${currentArgName} ${currentArg}) + set(insideValues FALSE) + elseif("${insideValues}" STREQUAL "MULTI") + list(APPEND ${prefix}_${currentArgName} ${currentArg}) + endif() + else(insideValues) + list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) + endif(insideValues) + else() + if(NOT ${optionIndex} EQUAL -1) + set(${prefix}_${currentArg} TRUE) + set(insideValues FALSE) + elseif(NOT ${singleArgIndex} EQUAL -1) + set(currentArgName ${currentArg}) + set(${prefix}_${currentArgName}) + set(insideValues "SINGLE") + elseif(NOT ${multiArgIndex} EQUAL -1) + set(currentArgName ${currentArg}) + set(${prefix}_${currentArgName}) + set(insideValues "MULTI") + endif() + endif() + + endforeach(currentArg) + + # propagate the result variables to the caller: + foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) + set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) + endforeach(arg_name) + set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) + +endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs) diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake index 37a55b357..c698480be 100644 --- a/Modules/FindPackageHandleStandardArgs.cmake +++ b/Modules/FindPackageHandleStandardArgs.cmake @@ -68,89 +68,47 @@ # License text for the above reference.) INCLUDE(FindPackageMessage) - -# 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) +INCLUDE(CMakeParseArguments) FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG _VAR1) - SET(_KEYWORDS_FOR_EXTENDED_MODE FAIL_MESSAGE REQUIRED_VARS VERSION_VAR ) +# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in +# new extended or in the "old" mode: + SET(options) # none + SET(oneValueArgs FAIL_MESSAGE VERSION_VAR) + SET(multiValueArgs REQUIRED_VARS) + SET(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) 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) + SET(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + SET(FPHSA_REQUIRED_VARS ${_VAR1} ${ARGN}) + SET(FPHSA_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) + CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${_VAR1} ${ARGN}) - IF(ALL_ARGS) - MESSAGE(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${ALL_ARGS}\"") - ENDIF(ALL_ARGS) + IF(FPHSA_UNPARSED_ARGUMENTS) + MESSAGE(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + ENDIF(FPHSA_UNPARSED_ARGUMENTS) - IF(NOT _FAIL_MESSAGE) - SET(_FAIL_MESSAGE "DEFAULT_MSG") - ENDIF(NOT _FAIL_MESSAGE) + IF(NOT FPHSA_FAIL_MESSAGE) + SET(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + ENDIF(NOT FPHSA_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}") - ENDIF("${_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") + IF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") + SET(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + ENDIF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") - IF(NOT _REQUIRED_VARS) + IF(NOT FPHSA_REQUIRED_VARS) MESSAGE(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") - ENDIF(NOT _REQUIRED_VARS) + ENDIF(NOT FPHSA_REQUIRED_VARS) - LIST(GET _REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + LIST(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) STRING(TOUPPER ${_NAME} _NAME_UPPER) @@ -160,7 +118,7 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG _VAR1) SET(DETAILS "") SET(${_NAME_UPPER}_FOUND TRUE) # check if all passed variables are valid - FOREACH(_CURRENT_VAR ${_REQUIRED_VARS}) + FOREACH(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) IF(NOT ${_CURRENT_VAR}) SET(${_NAME_UPPER}_FOUND FALSE) SET(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}") @@ -177,7 +135,7 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG _VAR1) # if the package was found, check for the version using _FIND_VERSION IF (${_NAME_UPPER}_FOUND) - SET(VERSION ${${_VERSION_VAR}} ) + SET(VERSION ${${FPHSA_VERSION_VAR}} ) IF(VERSION) @@ -227,20 +185,20 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG _VAR1) IF(NOT VERSION_OK) IF (${_NAME}_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "${_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") + MESSAGE(FATAL_ERROR "${FPHSA_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}})") + MESSAGE(STATUS "${FPHSA_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}") + MESSAGE(FATAL_ERROR "${FPHSA_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}") + MESSAGE(STATUS "${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") ENDIF (NOT ${_NAME}_FIND_QUIETLY) ENDIF (${_NAME}_FIND_REQUIRED) ENDIF(NOT VERSION_OK)