|
| 1 | +# This file defines helpers for common operations with cmake_parse_arguments. |
| 2 | + |
| 3 | +include_guard(GLOBAL) |
1 | 4 | include(CMakeParseArguments) |
2 | 5 |
|
3 | | -# A nicer wrapper around cmake_parse_arguments: |
| 6 | +# Routine error checks for argument parsing. |
4 | 7 | # |
5 | | -# cccl_parse_arguments( |
6 | | -# <parent_name> # Required; function / macro calling this helper. Must be first. |
7 | | -# [OPTIONS <option1> <option2> ...] # Optional; If specified, passed to cmake_parse_arguments. |
8 | | -# [ONE_VALUE_ARGS <arg1> <arg2> ...] # Optional; If specified, passed to cmake_parse_arguments. |
9 | | -# [MULTI_VALUE_ARGS <arg1> <arg2> ...] # Optional; If specified, passed to cmake_parse_arguments. |
10 | | -# [ERROR_IF_UNPARSED] # Optional; If specified, unparsed args cause fatal error. |
11 | | -# [PREFIX <prefix>] # Optional; default: "self" |
12 | | -# [PARENT_NAME <parent_scope_name>] # Optional; function / macro for nicer error messages. |
13 | | -# PARENT_ARGN <parent_argn> # Required, must be last. |
14 | | -# ) |
15 | | - |
16 | | -function(cccl_parse_arguments) |
17 | | - # Provided def in .gersemi/ext/cccl.py: |
18 | | - # gersemi: ignore |
19 | | - set(options ERROR_IF_UNPARSED) |
20 | | - set(oneValueArgs PREFIX PARENT_NAME) |
21 | | - set(multiValueArgs PARENT_ARGN OPTIONS ONE_VALUE_ARGS MULTI_VALUE_ARGS) |
| 8 | +# Required positional arguments: |
| 9 | +# caller_name: Name of the calling function (for error messages). |
| 10 | +# caller_prefix: Prefix used for the caller's cmake_parse_arguments. |
| 11 | +# caller_opts: List of option keywords used by the caller. |
| 12 | +# caller_svas: List of single-value argument keywords used by the caller. |
| 13 | +# caller_mvas: List of multi-value argument keywords used by the caller. |
| 14 | +# |
| 15 | +# Checks: |
| 16 | +# [ERROR_UNPARSED] |
| 17 | +# Checks that there were no unparsed arguments. |
| 18 | +# [REQUIRED_KEYWORDS kw1 [kw2]...] |
| 19 | +# Check that the listed keywords were provided (even if with empty values). |
| 20 | +# [REQUIRED_VALUES kw1 [kw2]...] |
| 21 | +# Check that the listed keywords were provided with non-empty values. |
| 22 | +# [DEFAULT_VALUES kw1 val1 [kw2 val2]...] |
| 23 | +# For any of the listed keywords not provided, set them to the given default |
| 24 | +# values. |
| 25 | +function( |
| 26 | + cccl_parse_arguments_error_checks |
| 27 | + caller_name |
| 28 | + caller_prefix |
| 29 | + caller_opts |
| 30 | + caller_svas |
| 31 | + caller_mvas |
| 32 | +) |
| 33 | + set(options ERROR_UNPARSED) |
| 34 | + set(singleValueArgs "") |
| 35 | + set(multiValueArgs REQUIRED_KEYWORDS REQUIRED_VALUES DEFAULT_VALUES) |
| 36 | + # gersemi: hints { DEFAULT_VALUES: pairs } |
| 37 | + # gersemi: hints { REQUIRED_KEYWORDS: sort+unique } |
| 38 | + # gersemi: hints { REQUIRED_VALUES: sort+unique } |
22 | 39 | cmake_parse_arguments( |
23 | | - "__self_" |
| 40 | + __self |
24 | 41 | "${options}" |
25 | | - "${oneValueArgs}" |
| 42 | + "${singleValueArgs}" |
26 | 43 | "${multiValueArgs}" |
27 | 44 | ${ARGN} |
28 | 45 | ) |
29 | 46 |
|
30 | | - if (NOT DEFINED "__self_PARENT_ARGN") |
31 | | - message(FATAL_ERROR "cccl_parse_arguments requires PARENT_ARGN to be defined.") |
32 | | - endif() |
33 | | - |
34 | | - if (NOT DEFINED "__self_PREFIX") |
35 | | - set(__self_PREFIX "self") |
36 | | - endif() |
37 | | - |
38 | | - if (NOT DEFINED "__self_PARENT_NAME") |
39 | | - set(__self_PARENT_NAME "cccl_parse_arguments's parent function") |
40 | | - endif() |
41 | | - |
42 | | - # Reset args for cmake_parse_arguments call: |
43 | | - set(options) |
44 | | - if (DEFINED "__self_OPTIONS") |
45 | | - set(options "${__self_OPTIONS}") |
46 | | - endif() |
47 | | - |
48 | | - set(one_value_args) |
49 | | - if (DEFINED "__self_ONE_VALUE_ARGS") |
50 | | - set(one_value_args "${__self_ONE_VALUE_ARGS}") |
51 | | - endif() |
52 | | - |
53 | | - set(multi_value_args) |
54 | | - if (DEFINED "__self_MULTI_VALUE_ARGS") |
55 | | - set(multi_value_args "${__self_MULTI_VALUE_ARGS}") |
| 47 | + if (__self_ERROR_UNPARSED AND DEFINED ${caller_prefix}_UNPARSED_ARGUMENTS) |
| 48 | + message( |
| 49 | + FATAL_ERROR |
| 50 | + "${caller_name}: Unrecognized arguments: ${${caller_prefix}_UNPARSED_ARGUMENTS}" |
| 51 | + ) |
56 | 52 | endif() |
57 | 53 |
|
58 | | - cmake_parse_arguments( |
59 | | - "${__self_PREFIX}" |
60 | | - "${options}" |
61 | | - "${one_value_args}" |
62 | | - "${multi_value_args}" |
63 | | - ${__self_PARENT_ARGN} |
64 | | - ) |
65 | | - |
| 54 | + # Check that required keywords were detected, include those with empty values: |
| 55 | + foreach (kw IN LISTS __self_REQUIRED_KEYWORDS __self_REQUIRED_VALUES) |
| 56 | + set(caller_kw "${caller_prefix}_${kw}") |
| 57 | + # gersemi: off |
| 58 | + if (kw IN_LIST caller_opts AND DEFINED ${caller_kw}) |
| 59 | + message(FATAL_ERROR "${caller_name}: Internal error: Options cannot be required keywords: '${kw}'.") |
| 60 | + elseif(kw IN_LIST caller_svas OR kw IN_LIST caller_mvas) |
| 61 | + if (DEFINED ${caller_kw} OR ${kw} IN_LIST ${caller_prefix}_KEYWORDS_MISSING_VALUES) |
| 62 | + continue() |
| 63 | + endif() |
| 64 | + else() |
| 65 | + message(FATAL_ERROR "${caller_name}: Internal error: Unrecognized required keyword: '${kw}'.") |
| 66 | + endif() |
| 67 | + message(FATAL_ERROR "${caller_name}: Required argument '${kw}' not provided.") |
| 68 | + # gersemi: on |
| 69 | + endforeach() |
66 | 70 |
|
67 | | - if (DEFINED "${__self_PREFIX}_UNPARSED_ARGUMENTS") |
68 | | - if (DEFINED "__self_NO_UNPARSED") |
69 | | - message(FATAL_ERROR "${${__self_PREFIX}_PARENT_NAME} given invalid arguments: ${${__self_PREFIX}_UNPARSED_ARGUMENTS}") |
| 71 | + # Check that required values were provided, failing for keywords with empty values: |
| 72 | + foreach (kw IN LISTS __self_REQUIRED_VALUES) |
| 73 | + set(caller_kw "${caller_prefix}_${kw}") |
| 74 | + # gersemi: off |
| 75 | + if (kw IN_LIST caller_opts) |
| 76 | + message(FATAL_ERROR "${caller_name}: Internal error: Options cannot have values: '${kw}'.") |
| 77 | + elseif(kw IN_LIST caller_svas OR kw IN_LIST caller_mvas) |
| 78 | + if (DEFINED ${caller_kw}) |
| 79 | + continue() |
| 80 | + endif() |
70 | 81 | else() |
71 | | - set("${__self_PREFIX}_UNPARSED_ARGUMENTS" "${${__self_PREFIX}_UNPARSED_ARGUMENTS}" PARENT_SCOPE) |
| 82 | + message(FATAL_ERROR "${caller_name}: Internal error: Unrecognized required value keyword: '${kw}'.") |
72 | 83 | endif() |
73 | | - else() |
74 | | - unset("${__self_PREFIX}_UNPARSED_ARGUMENTS" PARENT_SCOPE) |
75 | | - endif() |
| 84 | + message(FATAL_ERROR "${caller_name}: Required argument '${kw}' not provided or missing required value.") |
| 85 | + # gersemi: on |
| 86 | + endforeach() |
| 87 | + |
| 88 | + # Set defaults if not defined: |
| 89 | + set(kw) |
| 90 | + foreach (iter IN LISTS __self_DEFAULT_VALUES) |
| 91 | + # Alternates key / values. Only single values allowed. |
| 92 | + if (NOT kw) |
| 93 | + set(kw "${iter}") |
| 94 | + continue() |
| 95 | + endif() |
| 96 | + set(caller_kw "${caller_prefix}_${kw}") |
| 97 | + set(value "${iter}") |
76 | 98 |
|
77 | | - foreach(arg IN LISTS options one_value_args multi_value_args) |
78 | | - if (DEFINED "${__self_PREFIX}_${arg}") |
79 | | - set("${__self_PREFIX}_${arg}" "${${__self_PREFIX}_${arg}}" PARENT_SCOPE) |
| 99 | + # gersemi: off |
| 100 | + if (kw IN_LIST caller_opts) |
| 101 | + message(FATAL_ERROR "${caller_name}: Internal error: Options cannot have default values: '${kw}'.") |
| 102 | + elseif (kw IN_LIST caller_svas OR kw IN_LIST caller_mvas) |
| 103 | + if (NOT DEFINED ${caller_kw} AND NOT ${kw} IN_LIST ${caller}_KEYWORDS_MISSING_VALUES) |
| 104 | + set(${caller_kw} "${value}" PARENT_SCOPE) |
| 105 | + endif() |
80 | 106 | else() |
81 | | - unset("${__self_PREFIX}_${arg}" PARENT_SCOPE) |
| 107 | + message(FATAL_ERROR "${caller_name}: Internal error: Unrecognized default value keyword: '${kw}'.") |
82 | 108 | endif() |
| 109 | + set(kw) # Clear to signal next pair |
| 110 | + # gersemi: on |
83 | 111 | endforeach() |
84 | 112 | endfunction() |
0 commit comments