Do not unnecessarily constrain values in verifyPropType()
[hiphop-php.git] / third-party / watchman / src / build / fbcode_builder / CMake / FBCMakeParseArgs.cmake
blob933180189d0765743266b672aeaabe88ae8f307f
2 # Copyright (c) Facebook, Inc. and its affiliates.
4 # Helper function for parsing arguments to a CMake function.
6 # This function is very similar to CMake's built-in cmake_parse_arguments()
7 # function, with some improvements:
8 # - This function correctly handles empty arguments.  (cmake_parse_arguments()
9 #   ignores empty arguments.)
10 # - If a multi-value argument is specified more than once, the subsequent
11 #   arguments are appended to the original list rather than replacing it.  e.g.
12 #   if "SOURCES" is a multi-value argument, and the argument list contains
13 #   "SOURCES a b c SOURCES x y z" then the resulting value for SOURCES will be
14 #   "a;b;c;x;y;z" rather than "x;y;z"
15 # - This function errors out by default on unrecognized arguments.  You can
16 #   pass in an extra "ALLOW_UNPARSED_ARGS" argument to make it behave like
17 #   cmake_parse_arguments(), and return the unparsed arguments in a
18 #   <prefix>_UNPARSED_ARGUMENTS variable instead.
20 # It does look like cmake_parse_arguments() handled empty arguments correctly
21 # from CMake 3.0 through 3.3, but it seems like this was probably broken when
22 # it was turned into a built-in function in CMake 3.4.  Here is discussion and
23 # patches that fixed this behavior prior to CMake 3.0:
24 # https://cmake.org/pipermail/cmake-developers/2013-November/020607.html
26 # The one downside to this function over the built-in cmake_parse_arguments()
27 # is that I don't think we can achieve the PARSE_ARGV behavior in a non-builtin
28 # function, so we can't properly handle arguments that contain ";".  CMake will
29 # treat the ";" characters as list element separators, and treat it as multiple
30 # separate arguments.
32 function(fb_cmake_parse_args PREFIX OPTIONS ONE_VALUE_ARGS MULTI_VALUE_ARGS ARGS)
33   foreach(option IN LISTS ARGN)
34     if ("${option}" STREQUAL "ALLOW_UNPARSED_ARGS")
35       set(ALLOW_UNPARSED_ARGS TRUE)
36     else()
37       message(
38         FATAL_ERROR
39         "unknown optional argument for fb_cmake_parse_args(): ${option}"
40       )
41     endif()
42   endforeach()
44   # Define all options as FALSE in the parent scope to start with
45   foreach(var_name IN LISTS OPTIONS)
46     set("${PREFIX}_${var_name}" "FALSE" PARENT_SCOPE)
47   endforeach()
49   # TODO: We aren't extremely strict about error checking for one-value
50   # arguments here.  e.g., we don't complain if a one-value argument is
51   # followed by another option/one-value/multi-value name rather than an
52   # argument.  We also don't complain if a one-value argument is the last
53   # argument and isn't followed by a value.
55   list(APPEND all_args ${ONE_VALUE_ARGS})
56   list(APPEND all_args ${MULTI_VALUE_ARGS})
57   set(current_variable)
58   set(unparsed_args)
59   foreach(arg IN LISTS ARGS)
60     list(FIND OPTIONS "${arg}" opt_index)
61     if("${opt_index}" EQUAL -1)
62       list(FIND all_args "${arg}" arg_index)
63       if("${arg_index}" EQUAL -1)
64         # This argument does not match an argument name,
65         # must be an argument value
66         if("${current_variable}" STREQUAL "")
67           list(APPEND unparsed_args "${arg}")
68         else()
69           # Ugh, CMake lists have a pretty fundamental flaw: they cannot
70           # distinguish between an empty list and a list with a single empty
71           # element.  We track our own SEEN_VALUES_arg setting to help
72           # distinguish this and behave properly here.
73           if ("${SEEN_${current_variable}}" AND "${${current_variable}}" STREQUAL "")
74             set("${current_variable}" ";${arg}")
75           else()
76             list(APPEND "${current_variable}" "${arg}")
77           endif()
78           set("SEEN_${current_variable}" TRUE)
79         endif()
80       else()
81         # We found a single- or multi-value argument name
82         set(current_variable "VALUES_${arg}")
83         set("SEEN_${arg}" TRUE)
84       endif()
85     else()
86       # We found an option variable
87       set("${PREFIX}_${arg}" "TRUE" PARENT_SCOPE)
88       set(current_variable)
89     endif()
90   endforeach()
92   foreach(arg_name IN LISTS ONE_VALUE_ARGS)
93     if(NOT "${SEEN_${arg_name}}")
94       unset("${PREFIX}_${arg_name}" PARENT_SCOPE)
95     elseif(NOT "${SEEN_VALUES_${arg_name}}")
96       # If the argument was seen but a value wasn't specified, error out.
97       # We require exactly one value to be specified.
98       message(
99         FATAL_ERROR "argument ${arg_name} was specified without a value"
100       )
101     else()
102       list(LENGTH "VALUES_${arg_name}" num_args)
103       if("${num_args}" EQUAL 0)
104         # We know an argument was specified and that we called list(APPEND).
105         # If CMake thinks the list is empty that means there is really a single
106         # empty element in the list.
107         set("${PREFIX}_${arg_name}" "" PARENT_SCOPE)
108       elseif("${num_args}" EQUAL 1)
109         list(GET "VALUES_${arg_name}" 0 arg_value)
110         set("${PREFIX}_${arg_name}" "${arg_value}" PARENT_SCOPE)
111       else()
112         message(
113           FATAL_ERROR "too many arguments specified for ${arg_name}: "
114           "${VALUES_${arg_name}}"
115         )
116       endif()
117     endif()
118   endforeach()
120   foreach(arg_name IN LISTS MULTI_VALUE_ARGS)
121     # If this argument name was never seen, then unset the parent scope
122     if (NOT "${SEEN_${arg_name}}")
123       unset("${PREFIX}_${arg_name}" PARENT_SCOPE)
124     else()
125       # TODO: Our caller still won't be able to distinguish between an empty
126       # list and a list with a single empty element.  We can tell which is
127       # which, but CMake lists don't make it easy to show this to our caller.
128       set("${PREFIX}_${arg_name}" "${VALUES_${arg_name}}" PARENT_SCOPE)
129     endif()
130   endforeach()
132   # By default we fatal out on unparsed arguments, but return them to the
133   # caller if ALLOW_UNPARSED_ARGS was specified.
134   if (DEFINED unparsed_args)
135     if ("${ALLOW_UNPARSED_ARGS}")
136       set("${PREFIX}_UNPARSED_ARGUMENTS" "${unparsed_args}" PARENT_SCOPE)
137     else()
138       message(FATAL_ERROR "unrecognized arguments: ${unparsed_args}")
139     endif()
140   endif()
141 endfunction()