From 350eecf3bc1675086b8dc6d7ceb46b8e0f882c16 Mon Sep 17 00:00:00 2001 From: alin Date: Tue, 28 Jan 2025 15:59:01 +0100 Subject: [PATCH 1/9] Add requires_arguments() util --- cmake/rsp/helpers.cmake | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cmake/rsp/helpers.cmake b/cmake/rsp/helpers.cmake index 05d2325..ebf05ac 100644 --- a/cmake/rsp/helpers.cmake +++ b/cmake/rsp/helpers.cmake @@ -48,6 +48,30 @@ if (NOT COMMAND "extract_value") endfunction() endif () +if (NOT COMMAND "requires_arguments") + + #! requires_arguments : Verifies that specified arguments have been defined, fails otherwise + # + # Macro is intended to be used within a custom function, after `cmake_parse_arguments()` has + # been used. + # + # @see https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html#cmake-parse-arguments + # @see https://cmake.org/cmake/help/latest/command/if.html#defined + # + # @param input The parsed arguments prefix + # @param required List of required arguments + # + # @throws If required arguments are not defined + # + macro(requires_arguments input required) + foreach (arg ${required}) + if (NOT DEFINED "${input}_${arg}") + message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () + endmacro() +endif () + if (NOT COMMAND "safeguard_properties") #! safeguard_properties : Invoke a "risky" callback whilst "safeguarding" properties From 02d79095a5d7594d9dc3875d0a8bf9367a76d3a1 Mon Sep 17 00:00:00 2001 From: alin Date: Tue, 28 Jan 2025 15:59:09 +0100 Subject: [PATCH 2/9] Add test for requires_arguments() --- .../helpers/requires_arguments_test.cmake | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/unit/helpers/requires_arguments_test.cmake diff --git a/tests/unit/helpers/requires_arguments_test.cmake b/tests/unit/helpers/requires_arguments_test.cmake new file mode 100644 index 0000000..d09739e --- /dev/null +++ b/tests/unit/helpers/requires_arguments_test.cmake @@ -0,0 +1,40 @@ +include("rsp/testing") +include("rsp/helpers") + +define_test_case( + "Requires Arguments Test" + LABELS "requires;args;helpers" +) + +# -------------------------------------------------------------------------------------------------------------- # +# Helpers +# -------------------------------------------------------------------------------------------------------------- # + +function(fn_with_required_args) + set(options "") # N/A + set(oneValueArgs NAME DESCRIPTION) + set(multiValueArgs "") # N/A + + cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + requires_arguments(INPUT "NAME;DESCRIPTION") +endfunction() + +# -------------------------------------------------------------------------------------------------------------- # +# Actual Tests +# -------------------------------------------------------------------------------------------------------------- # + +define_test("ensures required args are defined" "ensures_required_args_defined") +function(ensures_required_args_defined) + + # If invoking method does not cause a fatal error, test passes. + fn_with_required_args(NAME "foo" DESCRIPTION "bar") + +endfunction() + +define_test("fails when required args are not defined" "fails_when_required_arg_not_defined" EXPECT_FAILURE) +function(fails_when_required_arg_not_defined) + + # This should cause a failure because DESCRIPTION is required + fn_with_required_args(NAME "foo") + +endfunction() \ No newline at end of file From b3c200bd6b11bc206b7297aefe053744eb8650c3 Mon Sep 17 00:00:00 2001 From: alin Date: Tue, 28 Jan 2025 16:08:03 +0100 Subject: [PATCH 3/9] Refactor, use requires_arguments() for ensuring args are defined --- cmake/rsp/cache.cmake | 56 ++++++----------------------------------- cmake/rsp/git.cmake | 11 +++----- cmake/rsp/helpers.cmake | 9 +------ cmake/rsp/testing.cmake | 19 +++----------- 4 files changed, 15 insertions(+), 80 deletions(-) diff --git a/cmake/rsp/cache.cmake b/cmake/rsp/cache.cmake index cabdde0..7752dd4 100644 --- a/cmake/rsp/cache.cmake +++ b/cmake/rsp/cache.cmake @@ -7,6 +7,8 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/cache module included") +include("rsp/helpers") + if (NOT DEFINED RSP_CACHE_EXPIRES_AT_KEY_AFFIX) set(RSP_CACHE_EXPIRES_AT_KEY_AFFIX "[rsp@expires_at]") endif () @@ -37,14 +39,7 @@ if (NOT COMMAND "cache_set") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;VALUE") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "KEY;VALUE") # Resolve optional arguments if (NOT DEFINED INPUT_TYPE) @@ -104,14 +99,7 @@ if (NOT COMMAND "cache_get") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "KEY") # Resolve optional arguments if (NOT DEFINED INPUT_DEFAULT) @@ -160,14 +148,7 @@ if (NOT COMMAND "cache_has") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;OUTPUT") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "KEY;OUTPUT") # Determine if entry exists in cache if (DEFINED CACHE{${INPUT_KEY}}) @@ -227,14 +208,7 @@ if (NOT COMMAND "cache_forget") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "KEY") # Remove entry if it exists if (DEFINED CACHE{${INPUT_KEY}}) @@ -305,14 +279,7 @@ if (NOT COMMAND "cache_remember") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;CALLBACK") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "KEY;CALLBACK") # Ensure that callback exists if (NOT COMMAND ${INPUT_CALLBACK}) @@ -389,14 +356,7 @@ if (NOT COMMAND "cache_has_expired") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;OUTPUT") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "KEY;OUTPUT") # Make expires at key... cache_make_expires_at_key(EXPIRES_AT_KEY INPUT_KEY) diff --git a/cmake/rsp/git.cmake b/cmake/rsp/git.cmake index da32d6e..dd88fd2 100644 --- a/cmake/rsp/git.cmake +++ b/cmake/rsp/git.cmake @@ -7,6 +7,8 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/git module included") +include("rsp/helpers") + # Ensure that git is available or this module will not work find_package(Git REQUIRED) @@ -40,14 +42,7 @@ if (NOT COMMAND "git_find_version_tag") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "OUTPUT;WORKING_DIRECTORY") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "OUTPUT;WORKING_DIRECTORY") # Resolve optional arguments if (NOT DEFINED INPUT_MATCH_PATTERN) diff --git a/cmake/rsp/helpers.cmake b/cmake/rsp/helpers.cmake index ebf05ac..ad83583 100644 --- a/cmake/rsp/helpers.cmake +++ b/cmake/rsp/helpers.cmake @@ -100,14 +100,7 @@ if (NOT COMMAND "safeguard_properties") set(multiValueArgs PROPERTIES) cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "CALLBACK;PROPERTIES") - foreach (arg ${requiredArgs}) - if (NOT DEFINED INPUT_${arg}) - message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "CALLBACK;PROPERTIES") # ---------------------------------------------------------------------------------------------- # diff --git a/cmake/rsp/testing.cmake b/cmake/rsp/testing.cmake index 9a7af8a..8307fd5 100644 --- a/cmake/rsp/testing.cmake +++ b/cmake/rsp/testing.cmake @@ -7,6 +7,7 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/testing module included") +include("rsp/helpers") include("rsp/cache") include("rsp/testing/asserts") @@ -90,14 +91,7 @@ if (NOT COMMAND "define_test_suite") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "DIRECTORY") - foreach (arg ${requiredArgs}) - if (NOT DEFINED INPUT_${arg}) - message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "DIRECTORY") # ---------------------------------------------------------------------------------------------- # @@ -450,14 +444,7 @@ if (NOT COMMAND "add_ctest_using_executor") set(multiValueArgs LABELS CALLBACK_ARG) # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "NAME;CALLBACK;TEST_CASE") - foreach (arg ${requiredArgs}) - if (NOT DEFINED INPUT_${arg}) - message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments(INPUT "NAME;CALLBACK;TEST_CASE") # ---------------------------------------------------------------------------------------------- # # Resolve optional arguments From 17fe398a270941935248cf49b9e9faffb39fcdf7 Mon Sep 17 00:00:00 2001 From: alin Date: Tue, 28 Jan 2025 16:08:20 +0100 Subject: [PATCH 4/9] Fix description --- cmake/rsp/helpers.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/rsp/helpers.cmake b/cmake/rsp/helpers.cmake index ad83583..141ef83 100644 --- a/cmake/rsp/helpers.cmake +++ b/cmake/rsp/helpers.cmake @@ -50,7 +50,7 @@ endif () if (NOT COMMAND "requires_arguments") - #! requires_arguments : Verifies that specified arguments have been defined, fails otherwise + #! requires_arguments : Ensures that specified arguments have been defined, fails otherwise # # Macro is intended to be used within a custom function, after `cmake_parse_arguments()` has # been used. From 0ddb6559b4b41d5685e1277337e820ebfd89cc59 Mon Sep 17 00:00:00 2001 From: alin Date: Wed, 29 Jan 2025 08:37:53 +0100 Subject: [PATCH 5/9] Revert "Refactor, use requires_arguments() for ensuring ..." This reverts commit b3c200bd6b11bc206b7297aefe053744eb8650c3. This was a bit too fast - the macro still needs a bit of work. --- cmake/rsp/cache.cmake | 56 +++++++++++++++++++++++++++++++++++------ cmake/rsp/git.cmake | 11 +++++--- cmake/rsp/helpers.cmake | 9 ++++++- cmake/rsp/testing.cmake | 19 +++++++++++--- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/cmake/rsp/cache.cmake b/cmake/rsp/cache.cmake index 7752dd4..cabdde0 100644 --- a/cmake/rsp/cache.cmake +++ b/cmake/rsp/cache.cmake @@ -7,8 +7,6 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/cache module included") -include("rsp/helpers") - if (NOT DEFINED RSP_CACHE_EXPIRES_AT_KEY_AFFIX) set(RSP_CACHE_EXPIRES_AT_KEY_AFFIX "[rsp@expires_at]") endif () @@ -39,7 +37,14 @@ if (NOT COMMAND "cache_set") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "KEY;VALUE") + + # Ensure required arguments are defined + set(requiredArgs "KEY;VALUE") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Resolve optional arguments if (NOT DEFINED INPUT_TYPE) @@ -99,7 +104,14 @@ if (NOT COMMAND "cache_get") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "KEY") + + # Ensure required arguments are defined + set(requiredArgs "KEY") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Resolve optional arguments if (NOT DEFINED INPUT_DEFAULT) @@ -148,7 +160,14 @@ if (NOT COMMAND "cache_has") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "KEY;OUTPUT") + + # Ensure required arguments are defined + set(requiredArgs "KEY;OUTPUT") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Determine if entry exists in cache if (DEFINED CACHE{${INPUT_KEY}}) @@ -208,7 +227,14 @@ if (NOT COMMAND "cache_forget") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "KEY") + + # Ensure required arguments are defined + set(requiredArgs "KEY") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Remove entry if it exists if (DEFINED CACHE{${INPUT_KEY}}) @@ -279,7 +305,14 @@ if (NOT COMMAND "cache_remember") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "KEY;CALLBACK") + + # Ensure required arguments are defined + set(requiredArgs "KEY;CALLBACK") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Ensure that callback exists if (NOT COMMAND ${INPUT_CALLBACK}) @@ -356,7 +389,14 @@ if (NOT COMMAND "cache_has_expired") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "KEY;OUTPUT") + + # Ensure required arguments are defined + set(requiredArgs "KEY;OUTPUT") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Make expires at key... cache_make_expires_at_key(EXPIRES_AT_KEY INPUT_KEY) diff --git a/cmake/rsp/git.cmake b/cmake/rsp/git.cmake index dd88fd2..da32d6e 100644 --- a/cmake/rsp/git.cmake +++ b/cmake/rsp/git.cmake @@ -7,8 +7,6 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/git module included") -include("rsp/helpers") - # Ensure that git is available or this module will not work find_package(Git REQUIRED) @@ -42,7 +40,14 @@ if (NOT COMMAND "git_find_version_tag") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "OUTPUT;WORKING_DIRECTORY") + + # Ensure required arguments are defined + set(requiredArgs "OUTPUT;WORKING_DIRECTORY") + foreach (name ${requiredArgs}) + if (NOT DEFINED INPUT_${name}) + message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # Resolve optional arguments if (NOT DEFINED INPUT_MATCH_PATTERN) diff --git a/cmake/rsp/helpers.cmake b/cmake/rsp/helpers.cmake index 141ef83..6e1f5de 100644 --- a/cmake/rsp/helpers.cmake +++ b/cmake/rsp/helpers.cmake @@ -100,7 +100,14 @@ if (NOT COMMAND "safeguard_properties") set(multiValueArgs PROPERTIES) cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "CALLBACK;PROPERTIES") + + # Ensure required arguments are defined + set(requiredArgs "CALLBACK;PROPERTIES") + foreach (arg ${requiredArgs}) + if (NOT DEFINED INPUT_${arg}) + message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # ---------------------------------------------------------------------------------------------- # diff --git a/cmake/rsp/testing.cmake b/cmake/rsp/testing.cmake index 8307fd5..9a7af8a 100644 --- a/cmake/rsp/testing.cmake +++ b/cmake/rsp/testing.cmake @@ -7,7 +7,6 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/testing module included") -include("rsp/helpers") include("rsp/cache") include("rsp/testing/asserts") @@ -91,7 +90,14 @@ if (NOT COMMAND "define_test_suite") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "DIRECTORY") + + # Ensure required arguments are defined + set(requiredArgs "DIRECTORY") + foreach (arg ${requiredArgs}) + if (NOT DEFINED INPUT_${arg}) + message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # ---------------------------------------------------------------------------------------------- # @@ -444,7 +450,14 @@ if (NOT COMMAND "add_ctest_using_executor") set(multiValueArgs LABELS CALLBACK_ARG) # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "NAME;CALLBACK;TEST_CASE") + + # Ensure required arguments are defined + set(requiredArgs "NAME;CALLBACK;TEST_CASE") + foreach (arg ${requiredArgs}) + if (NOT DEFINED INPUT_${arg}) + message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") + endif () + endforeach () # ---------------------------------------------------------------------------------------------- # # Resolve optional arguments From fe70e0b73bb01253b52756d65067e584dff019db Mon Sep 17 00:00:00 2001 From: alin Date: Wed, 29 Jan 2025 09:36:57 +0100 Subject: [PATCH 6/9] Refactor requires_arguments() macro Wanted to make the "prefix" parameter optional. Sadly this is not as easy as I hoped for, when dealing with a macro. For now, this will have to do. --- cmake/rsp/helpers.cmake | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/cmake/rsp/helpers.cmake b/cmake/rsp/helpers.cmake index 6e1f5de..72e2342 100644 --- a/cmake/rsp/helpers.cmake +++ b/cmake/rsp/helpers.cmake @@ -58,14 +58,26 @@ if (NOT COMMAND "requires_arguments") # @see https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html#cmake-parse-arguments # @see https://cmake.org/cmake/help/latest/command/if.html#defined # - # @param input The parsed arguments prefix - # @param required List of required arguments + # @param required List of required arguments + # @param prefix The parsed input arguments prefix + # used in your cmake_parse_arguments() call. # # @throws If required arguments are not defined # - macro(requires_arguments input required) + macro(requires_arguments required prefix) + # Note: the "prefix" parameter cannot be made optional for this macro. + # It is unsafe to rely on any ${ARGV} in this context, ... + # @see https://cmake.org/cmake/help/latest/command/macro.html#argument-caveats + + # Append "_" to the prefix, if given. + # @see https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html#cmake-parse-arguments + set(resolved_prefix "${prefix}") + if(NOT ${prefix} EQUAL "") + set(resolved_prefix "${prefix}_") + endif () + foreach (arg ${required}) - if (NOT DEFINED "${input}_${arg}") + if (NOT DEFINED "${resolved_prefix}${arg}") message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") endif () endforeach () From c2412670850c26b82ed2af6df5ffc481093a92ba Mon Sep 17 00:00:00 2001 From: alin Date: Wed, 29 Jan 2025 09:37:19 +0100 Subject: [PATCH 7/9] Enhance tests for requires_arguments() --- .../helpers/requires_arguments_test.cmake | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/tests/unit/helpers/requires_arguments_test.cmake b/tests/unit/helpers/requires_arguments_test.cmake index d09739e..2ea2977 100644 --- a/tests/unit/helpers/requires_arguments_test.cmake +++ b/tests/unit/helpers/requires_arguments_test.cmake @@ -10,31 +10,57 @@ define_test_case( # Helpers # -------------------------------------------------------------------------------------------------------------- # -function(fn_with_required_args) +function(fn_with_named_args) set(options "") # N/A set(oneValueArgs NAME DESCRIPTION) set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - requires_arguments(INPUT "NAME;DESCRIPTION") + requires_arguments("NAME;DESCRIPTION" INPUT) +endfunction() + +function(fn_with_args name) + if (${ARGC} EQUAL 2) + set(description "${ARGV1}") + endif () + + # This may seem a bit strange to declare "description" as required + # here, however, this is fully intended (for the test...) + requires_arguments("name;description" "") endfunction() # -------------------------------------------------------------------------------------------------------------- # # Actual Tests # -------------------------------------------------------------------------------------------------------------- # +define_test("ensures required named args are defined" "ensures_required_named_args_defined") +function(ensures_required_named_args_defined) + + # If invoking method does not cause a fatal error, test passes. + fn_with_named_args(NAME "foo" DESCRIPTION "bar") + +endfunction() + define_test("ensures required args are defined" "ensures_required_args_defined") function(ensures_required_args_defined) # If invoking method does not cause a fatal error, test passes. - fn_with_required_args(NAME "foo" DESCRIPTION "bar") + fn_with_args("foo" "bar") + +endfunction() + +define_test("fails when required named args are not defined" "fails_when_required_named_arg_not_defined" EXPECT_FAILURE) +function(fails_when_required_named_arg_not_defined) + + # This should cause a failure because DESCRIPTION is required + fn_with_named_args(NAME "foo") endfunction() define_test("fails when required args are not defined" "fails_when_required_arg_not_defined" EXPECT_FAILURE) function(fails_when_required_arg_not_defined) - # This should cause a failure because DESCRIPTION is required - fn_with_required_args(NAME "foo") + # This should cause a failure because second argument is required + fn_with_args("foo") endfunction() \ No newline at end of file From 82da8a2beec0c74418accd7e144cee8b5789ccc6 Mon Sep 17 00:00:00 2001 From: alin Date: Wed, 29 Jan 2025 09:40:13 +0100 Subject: [PATCH 8/9] Change release notes --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a5aa0..f2b6cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Project's root `CMakeLists.txt`. * `dependencies.cmake` and `dev-dependencies.cmake` scripts. * `CPM.cmake` script that downloads specified version of [CPM](https://github.com/cpm-cmake/CPM.cmake). -* `dump()`, `dd()`, `fail_in_source_build()`, `extract_value()` and `safeguard_properties()` utils functions, in `helpers.cmake`. +* `fail_in_source_build()`, `extract_value()`, `requires_arguments()` and `safeguard_properties()` utils functions, in `helpers.cmake`. +* `dump()` and `dd()` in `debug.cmake`. * `semver_parse()`, `write_version_file` and `version_from_file()` utils, in `version.cmake`. * `git_find_version_tag()` util, in `git.cmake`. * `VERSION` file. From 12792af8d97a5ae1c3ed7b8ae0d7f32afa14ea45 Mon Sep 17 00:00:00 2001 From: alin Date: Wed, 29 Jan 2025 09:48:17 +0100 Subject: [PATCH 9/9] Refactor, use requires_arguments() to ensure required args are defined --- cmake/rsp/cache.cmake | 56 +++++----------------------------- cmake/rsp/git.cmake | 11 ++----- cmake/rsp/helpers.cmake | 9 +----- cmake/rsp/testing.cmake | 19 ++---------- cmake/rsp/version.cmake | 20 +++--------- cmake/rsp/version/semver.cmake | 10 ++---- 6 files changed, 22 insertions(+), 103 deletions(-) diff --git a/cmake/rsp/cache.cmake b/cmake/rsp/cache.cmake index cabdde0..1821815 100644 --- a/cmake/rsp/cache.cmake +++ b/cmake/rsp/cache.cmake @@ -7,6 +7,8 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/cache module included") +include("rsp/helpers") + if (NOT DEFINED RSP_CACHE_EXPIRES_AT_KEY_AFFIX) set(RSP_CACHE_EXPIRES_AT_KEY_AFFIX "[rsp@expires_at]") endif () @@ -37,14 +39,7 @@ if (NOT COMMAND "cache_set") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;VALUE") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("KEY;VALUE" INPUT) # Resolve optional arguments if (NOT DEFINED INPUT_TYPE) @@ -104,14 +99,7 @@ if (NOT COMMAND "cache_get") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("KEY" INPUT) # Resolve optional arguments if (NOT DEFINED INPUT_DEFAULT) @@ -160,14 +148,7 @@ if (NOT COMMAND "cache_has") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;OUTPUT") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("KEY;OUTPUT" INPUT) # Determine if entry exists in cache if (DEFINED CACHE{${INPUT_KEY}}) @@ -227,14 +208,7 @@ if (NOT COMMAND "cache_forget") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("KEY" INPUT) # Remove entry if it exists if (DEFINED CACHE{${INPUT_KEY}}) @@ -305,14 +279,7 @@ if (NOT COMMAND "cache_remember") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;CALLBACK") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("KEY;CALLBACK" INPUT) # Ensure that callback exists if (NOT COMMAND ${INPUT_CALLBACK}) @@ -389,14 +356,7 @@ if (NOT COMMAND "cache_has_expired") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "KEY;OUTPUT") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("KEY;OUTPUT" INPUT) # Make expires at key... cache_make_expires_at_key(EXPIRES_AT_KEY INPUT_KEY) diff --git a/cmake/rsp/git.cmake b/cmake/rsp/git.cmake index da32d6e..4a3827a 100644 --- a/cmake/rsp/git.cmake +++ b/cmake/rsp/git.cmake @@ -7,6 +7,8 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/git module included") +include("rsp/helpers") + # Ensure that git is available or this module will not work find_package(Git REQUIRED) @@ -40,14 +42,7 @@ if (NOT COMMAND "git_find_version_tag") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "OUTPUT;WORKING_DIRECTORY") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("OUTPUT;WORKING_DIRECTORY" INPUT) # Resolve optional arguments if (NOT DEFINED INPUT_MATCH_PATTERN) diff --git a/cmake/rsp/helpers.cmake b/cmake/rsp/helpers.cmake index 90ea49a..0d2f6e0 100644 --- a/cmake/rsp/helpers.cmake +++ b/cmake/rsp/helpers.cmake @@ -112,14 +112,7 @@ if (NOT COMMAND "safeguard_properties") set(multiValueArgs PROPERTIES) cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "CALLBACK;PROPERTIES") - foreach (arg ${requiredArgs}) - if (NOT DEFINED INPUT_${arg}) - message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("CALLBACK;PROPERTIES" INPUT) # ---------------------------------------------------------------------------------------------- # diff --git a/cmake/rsp/testing.cmake b/cmake/rsp/testing.cmake index 9a7af8a..2163e16 100644 --- a/cmake/rsp/testing.cmake +++ b/cmake/rsp/testing.cmake @@ -7,6 +7,7 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/testing module included") +include("rsp/helpers") include("rsp/cache") include("rsp/testing/asserts") @@ -90,14 +91,7 @@ if (NOT COMMAND "define_test_suite") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "DIRECTORY") - foreach (arg ${requiredArgs}) - if (NOT DEFINED INPUT_${arg}) - message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("DIRECTORY" INPUT) # ---------------------------------------------------------------------------------------------- # @@ -450,14 +444,7 @@ if (NOT COMMAND "add_ctest_using_executor") set(multiValueArgs LABELS CALLBACK_ARG) # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "NAME;CALLBACK;TEST_CASE") - foreach (arg ${requiredArgs}) - if (NOT DEFINED INPUT_${arg}) - message(FATAL_ERROR "${arg} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("NAME;CALLBACK;TEST_CASE" INPUT) # ---------------------------------------------------------------------------------------------- # # Resolve optional arguments diff --git a/cmake/rsp/version.cmake b/cmake/rsp/version.cmake index 2b07922..bc9990b 100644 --- a/cmake/rsp/version.cmake +++ b/cmake/rsp/version.cmake @@ -7,6 +7,7 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/version module included") +include("rsp/helpers") include("rsp/git") include("rsp/version/semver") @@ -20,6 +21,7 @@ if (NOT COMMAND "write_version_file") # @example # # Defaults to version obtained via git # write_version_file(FILE "version.txt") + # # # Or, use custom version # write_version_file(FILE "version.txt" VERSION "v1.4.3") # @@ -43,14 +45,7 @@ if (NOT COMMAND "write_version_file") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "FILE") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("FILE" INPUT) # Resolve version, if none specified if (NOT DEFINED INPUT_VERSION) @@ -123,14 +118,7 @@ if (NOT COMMAND "version_from_file") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - set(requiredArgs "FILE;OUTPUT") - foreach (name ${requiredArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("FILE;OUTPUT" INPUT) # Resolve optional arguments if (NOT DEFINED INPUT_DEFAULT) diff --git a/cmake/rsp/version/semver.cmake b/cmake/rsp/version/semver.cmake index 15f1516..55c671b 100644 --- a/cmake/rsp/version/semver.cmake +++ b/cmake/rsp/version/semver.cmake @@ -7,6 +7,8 @@ include_guard(GLOBAL) # Debug message(VERBOSE "rsp/version/semver module included") +include("rsp/helpers") + if (NOT COMMAND "semver_parse") #! semver_parse : Parses a semantic version string @@ -38,13 +40,7 @@ if (NOT COMMAND "semver_parse") set(multiValueArgs "") # N/A cmake_parse_arguments(INPUT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Ensure required arguments are defined - foreach (name ${oneValueArgs}) - if (NOT DEFINED INPUT_${name}) - message(FATAL_ERROR "${name} argument is missing, for ${CMAKE_CURRENT_FUNCTION}()") - endif () - endforeach () + requires_arguments("VERSION;OUTPUT" INPUT) # Remove eventual "v" prefix from given version string string(REGEX REPLACE "^[v]" "" cleanVersion "${INPUT_VERSION}")