Skip to content

Commit bc2ee9c

Browse files
authored
Merge pull request #10 from rsps/add-required-args-macro
Add required args macro
2 parents f31201e + 12792af commit bc2ee9c

File tree

8 files changed

+126
-104
lines changed

8 files changed

+126
-104
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
* Project's root `CMakeLists.txt`.
1313
* `dependencies.cmake` and `dev-dependencies.cmake` scripts.
1414
* `CPM.cmake` script that downloads specified version of [CPM](https://github.com/cpm-cmake/CPM.cmake).
15-
* `dump()`, `dd()`, `fail_in_source_build()`, `extract_value()` and `safeguard_properties()` utils functions, in `helpers.cmake`.
15+
* `fail_in_source_build()`, `extract_value()`, `requires_arguments()` and `safeguard_properties()` utils functions, in `helpers.cmake`.
16+
* `dump()` and `dd()` in `debug.cmake`.
1617
* `semver_parse()`, `write_version_file` and `version_from_file()` utils, in `version.cmake`.
1718
* `git_find_version_tag()` util, in `git.cmake`.
1819
* `VERSION` file.

cmake/rsp/cache.cmake

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ include_guard(GLOBAL)
77
# Debug
88
message(VERBOSE "rsp/cache module included")
99

10+
include("rsp/helpers")
11+
1012
if (NOT DEFINED RSP_CACHE_EXPIRES_AT_KEY_AFFIX)
1113
set(RSP_CACHE_EXPIRES_AT_KEY_AFFIX "[rsp@expires_at]")
1214
endif ()
@@ -37,14 +39,7 @@ if (NOT COMMAND "cache_set")
3739
set(multiValueArgs "") # N/A
3840

3941
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
40-
41-
# Ensure required arguments are defined
42-
set(requiredArgs "KEY;VALUE")
43-
foreach (name ${requiredArgs})
44-
if (NOT DEFINED INPUT_${name})
45-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
46-
endif ()
47-
endforeach ()
42+
requires_arguments("KEY;VALUE" INPUT)
4843

4944
# Resolve optional arguments
5045
if (NOT DEFINED INPUT_TYPE)
@@ -104,14 +99,7 @@ if (NOT COMMAND "cache_get")
10499
set(multiValueArgs "") # N/A
105100

106101
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
107-
108-
# Ensure required arguments are defined
109-
set(requiredArgs "KEY")
110-
foreach (name ${requiredArgs})
111-
if (NOT DEFINED INPUT_${name})
112-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
113-
endif ()
114-
endforeach ()
102+
requires_arguments("KEY" INPUT)
115103

116104
# Resolve optional arguments
117105
if (NOT DEFINED INPUT_DEFAULT)
@@ -160,14 +148,7 @@ if (NOT COMMAND "cache_has")
160148
set(multiValueArgs "") # N/A
161149

162150
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
163-
164-
# Ensure required arguments are defined
165-
set(requiredArgs "KEY;OUTPUT")
166-
foreach (name ${requiredArgs})
167-
if (NOT DEFINED INPUT_${name})
168-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
169-
endif ()
170-
endforeach ()
151+
requires_arguments("KEY;OUTPUT" INPUT)
171152

172153
# Determine if entry exists in cache
173154
if (DEFINED CACHE{${INPUT_KEY}})
@@ -227,14 +208,7 @@ if (NOT COMMAND "cache_forget")
227208
set(multiValueArgs "") # N/A
228209

229210
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
230-
231-
# Ensure required arguments are defined
232-
set(requiredArgs "KEY")
233-
foreach (name ${requiredArgs})
234-
if (NOT DEFINED INPUT_${name})
235-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
236-
endif ()
237-
endforeach ()
211+
requires_arguments("KEY" INPUT)
238212

239213
# Remove entry if it exists
240214
if (DEFINED CACHE{${INPUT_KEY}})
@@ -305,14 +279,7 @@ if (NOT COMMAND "cache_remember")
305279
set(multiValueArgs "") # N/A
306280

307281
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
308-
309-
# Ensure required arguments are defined
310-
set(requiredArgs "KEY;CALLBACK")
311-
foreach (name ${requiredArgs})
312-
if (NOT DEFINED INPUT_${name})
313-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
314-
endif ()
315-
endforeach ()
282+
requires_arguments("KEY;CALLBACK" INPUT)
316283

317284
# Ensure that callback exists
318285
if (NOT COMMAND ${INPUT_CALLBACK})
@@ -389,14 +356,7 @@ if (NOT COMMAND "cache_has_expired")
389356
set(multiValueArgs "") # N/A
390357

391358
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
392-
393-
# Ensure required arguments are defined
394-
set(requiredArgs "KEY;OUTPUT")
395-
foreach (name ${requiredArgs})
396-
if (NOT DEFINED INPUT_${name})
397-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
398-
endif ()
399-
endforeach ()
359+
requires_arguments("KEY;OUTPUT" INPUT)
400360

401361
# Make expires at key...
402362
cache_make_expires_at_key(EXPIRES_AT_KEY INPUT_KEY)

cmake/rsp/git.cmake

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ include_guard(GLOBAL)
77
# Debug
88
message(VERBOSE "rsp/git module included")
99

10+
include("rsp/helpers")
11+
1012
# Ensure that git is available or this module will not work
1113
find_package(Git REQUIRED)
1214

@@ -40,14 +42,7 @@ if (NOT COMMAND "git_find_version_tag")
4042
set(multiValueArgs "") # N/A
4143

4244
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
43-
44-
# Ensure required arguments are defined
45-
set(requiredArgs "OUTPUT;WORKING_DIRECTORY")
46-
foreach (name ${requiredArgs})
47-
if (NOT DEFINED INPUT_${name})
48-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
49-
endif ()
50-
endforeach ()
45+
requires_arguments("OUTPUT;WORKING_DIRECTORY" INPUT)
5146

5247
# Resolve optional arguments
5348
if (NOT DEFINED INPUT_MATCH_PATTERN)

cmake/rsp/helpers.cmake

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,42 @@ if (NOT COMMAND "extract_value")
4848
endfunction()
4949
endif ()
5050

51+
if (NOT COMMAND "requires_arguments")
52+
53+
#! requires_arguments : Ensures that specified arguments have been defined, fails otherwise
54+
#
55+
# Macro is intended to be used within a custom function, after `cmake_parse_arguments()` has
56+
# been used.
57+
#
58+
# @see https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html#cmake-parse-arguments
59+
# @see https://cmake.org/cmake/help/latest/command/if.html#defined
60+
#
61+
# @param <list> required List of required arguments
62+
# @param <variable|string> prefix The parsed input arguments prefix
63+
# used in your cmake_parse_arguments() call.
64+
#
65+
# @throws If required arguments are not defined
66+
#
67+
macro(requires_arguments required prefix)
68+
# Note: the "prefix" parameter cannot be made optional for this macro.
69+
# It is unsafe to rely on any ${ARGV} in this context, ...
70+
# @see https://cmake.org/cmake/help/latest/command/macro.html#argument-caveats
71+
72+
# Append "_" to the prefix, if given.
73+
# @see https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html#cmake-parse-arguments
74+
set(resolved_prefix "${prefix}")
75+
if(NOT ${prefix} EQUAL "")
76+
set(resolved_prefix "${prefix}_")
77+
endif ()
78+
79+
foreach (arg ${required})
80+
if (NOT DEFINED "${resolved_prefix}${arg}")
81+
message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
82+
endif ()
83+
endforeach ()
84+
endmacro()
85+
endif ()
86+
5187
if (NOT COMMAND "safeguard_properties")
5288

5389
#! safeguard_properties : Invoke a "risky" callback whilst "safeguarding" properties
@@ -76,14 +112,7 @@ if (NOT COMMAND "safeguard_properties")
76112
set(multiValueArgs PROPERTIES)
77113

78114
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
79-
80-
# Ensure required arguments are defined
81-
set(requiredArgs "CALLBACK;PROPERTIES")
82-
foreach (arg ${requiredArgs})
83-
if (NOT DEFINED INPUT_${arg})
84-
message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
85-
endif ()
86-
endforeach ()
115+
requires_arguments("CALLBACK;PROPERTIES" INPUT)
87116

88117
# ---------------------------------------------------------------------------------------------- #
89118

cmake/rsp/testing.cmake

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ include_guard(GLOBAL)
77
# Debug
88
message(VERBOSE "rsp/testing module included")
99

10+
include("rsp/helpers")
1011
include("rsp/cache")
1112
include("rsp/testing/asserts")
1213

@@ -90,14 +91,7 @@ if (NOT COMMAND "define_test_suite")
9091
set(multiValueArgs "") # N/A
9192

9293
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
93-
94-
# Ensure required arguments are defined
95-
set(requiredArgs "DIRECTORY")
96-
foreach (arg ${requiredArgs})
97-
if (NOT DEFINED INPUT_${arg})
98-
message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
99-
endif ()
100-
endforeach ()
94+
requires_arguments("DIRECTORY" INPUT)
10195

10296
# ---------------------------------------------------------------------------------------------- #
10397

@@ -450,14 +444,7 @@ if (NOT COMMAND "add_ctest_using_executor")
450444
set(multiValueArgs LABELS CALLBACK_ARG) # N/A
451445

452446
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
453-
454-
# Ensure required arguments are defined
455-
set(requiredArgs "NAME;CALLBACK;TEST_CASE")
456-
foreach (arg ${requiredArgs})
457-
if (NOT DEFINED INPUT_${arg})
458-
message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
459-
endif ()
460-
endforeach ()
447+
requires_arguments("NAME;CALLBACK;TEST_CASE" INPUT)
461448

462449
# ---------------------------------------------------------------------------------------------- #
463450
# Resolve optional arguments

cmake/rsp/version.cmake

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ include_guard(GLOBAL)
77
# Debug
88
message(VERBOSE "rsp/version module included")
99

10+
include("rsp/helpers")
1011
include("rsp/git")
1112
include("rsp/version/semver")
1213

@@ -20,6 +21,7 @@ if (NOT COMMAND "write_version_file")
2021
# @example
2122
# # Defaults to version obtained via git
2223
# write_version_file(FILE "version.txt")
24+
#
2325
# # Or, use custom version
2426
# write_version_file(FILE "version.txt" VERSION "v1.4.3")
2527
#
@@ -43,14 +45,7 @@ if (NOT COMMAND "write_version_file")
4345
set(multiValueArgs "") # N/A
4446

4547
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
46-
47-
# Ensure required arguments are defined
48-
set(requiredArgs "FILE")
49-
foreach (name ${requiredArgs})
50-
if (NOT DEFINED INPUT_${name})
51-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
52-
endif ()
53-
endforeach ()
48+
requires_arguments("FILE" INPUT)
5449

5550
# Resolve version, if none specified
5651
if (NOT DEFINED INPUT_VERSION)
@@ -123,14 +118,7 @@ if (NOT COMMAND "version_from_file")
123118
set(multiValueArgs "") # N/A
124119

125120
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
126-
127-
# Ensure required arguments are defined
128-
set(requiredArgs "FILE;OUTPUT")
129-
foreach (name ${requiredArgs})
130-
if (NOT DEFINED INPUT_${name})
131-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
132-
endif ()
133-
endforeach ()
121+
requires_arguments("FILE;OUTPUT" INPUT)
134122

135123
# Resolve optional arguments
136124
if (NOT DEFINED INPUT_DEFAULT)

cmake/rsp/version/semver.cmake

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ include_guard(GLOBAL)
77
# Debug
88
message(VERBOSE "rsp/version/semver module included")
99

10+
include("rsp/helpers")
11+
1012
if (NOT COMMAND "semver_parse")
1113

1214
#! semver_parse : Parses a semantic version string
@@ -38,13 +40,7 @@ if (NOT COMMAND "semver_parse")
3840
set(multiValueArgs "") # N/A
3941

4042
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
41-
42-
# Ensure required arguments are defined
43-
foreach (name ${oneValueArgs})
44-
if (NOT DEFINED INPUT_${name})
45-
message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()")
46-
endif ()
47-
endforeach ()
43+
requires_arguments("VERSION;OUTPUT" INPUT)
4844

4945
# Remove eventual "v" prefix from given version string
5046
string(REGEX REPLACE "^[v]" "" cleanVersion "${INPUT_VERSION}")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
include("rsp/testing")
2+
include("rsp/helpers")
3+
4+
define_test_case(
5+
"Requires Arguments Test"
6+
LABELS "requires;args;helpers"
7+
)
8+
9+
# -------------------------------------------------------------------------------------------------------------- #
10+
# Helpers
11+
# -------------------------------------------------------------------------------------------------------------- #
12+
13+
function(fn_with_named_args)
14+
set(options "") # N/A
15+
set(oneValueArgs NAME DESCRIPTION)
16+
set(multiValueArgs "") # N/A
17+
18+
cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
19+
requires_arguments("NAME;DESCRIPTION" INPUT)
20+
endfunction()
21+
22+
function(fn_with_args name)
23+
if (${ARGC} EQUAL 2)
24+
set(description "${ARGV1}")
25+
endif ()
26+
27+
# This may seem a bit strange to declare "description" as required
28+
# here, however, this is fully intended (for the test...)
29+
requires_arguments("name;description" "")
30+
endfunction()
31+
32+
# -------------------------------------------------------------------------------------------------------------- #
33+
# Actual Tests
34+
# -------------------------------------------------------------------------------------------------------------- #
35+
36+
define_test("ensures required named args are defined" "ensures_required_named_args_defined")
37+
function(ensures_required_named_args_defined)
38+
39+
# If invoking method does not cause a fatal error, test passes.
40+
fn_with_named_args(NAME "foo" DESCRIPTION "bar")
41+
42+
endfunction()
43+
44+
define_test("ensures required args are defined" "ensures_required_args_defined")
45+
function(ensures_required_args_defined)
46+
47+
# If invoking method does not cause a fatal error, test passes.
48+
fn_with_args("foo" "bar")
49+
50+
endfunction()
51+
52+
define_test("fails when required named args are not defined" "fails_when_required_named_arg_not_defined" EXPECT_FAILURE)
53+
function(fails_when_required_named_arg_not_defined)
54+
55+
# This should cause a failure because DESCRIPTION is required
56+
fn_with_named_args(NAME "foo")
57+
58+
endfunction()
59+
60+
define_test("fails when required args are not defined" "fails_when_required_arg_not_defined" EXPECT_FAILURE)
61+
function(fails_when_required_arg_not_defined)
62+
63+
# This should cause a failure because second argument is required
64+
fn_with_args("foo")
65+
66+
endfunction()

0 commit comments

Comments
 (0)