瀏覽代碼

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
Alex Neundorf 15 年之前
父節點
當前提交
b173b879f8
共有 2 個文件被更改,包括 166 次插入71 次删除
  1. 137 0
      Modules/CMakeParseArguments.cmake
  2. 29 71
      Modules/FindPackageHandleStandardArgs.cmake

+ 137 - 0
Modules/CMakeParseArguments.cmake

@@ -0,0 +1,137 @@
+# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> <multi_value_keywords> 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 <options> 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 <one_value_keywords> argument contains all keywords for this macro
+# which are followed by one value, like e.g. DESTINATION keyword of the
+# install() command.
+#
+# The <multi_value_keywords> 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 <options>, <one_value_keywords> and
+# <multi_value_keywords> a variable composed of the given <prefix>
+# followed by "_" and the name of the respective keyword.
+# These variables will then hold the respective value from the argument list.
+# For the <options> keywords this will be TRUE or FALSE.
+#
+# All remaining arguments are collected in a variable
+# <prefix>_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, <[email protected]>
+#
+# 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)

+ 29 - 71
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 <NAME>_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)