Skip to content
Merged
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
56 changes: 8 additions & 48 deletions cmake/rsp/cache.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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}})
Expand Down Expand Up @@ -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}})
Expand Down Expand Up @@ -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})
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 3 additions & 8 deletions cmake/rsp/git.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
45 changes: 37 additions & 8 deletions cmake/rsp/helpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,42 @@ if (NOT COMMAND "extract_value")
endfunction()
endif ()

if (NOT COMMAND "requires_arguments")

#! 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.
#
# @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 <list> required List of required arguments
# @param <variable|string> prefix The parsed input arguments prefix
# used in your cmake_parse_arguments() call.
#
# @throws If required arguments are not defined
#
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 "${resolved_prefix}${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
Expand Down Expand Up @@ -76,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)

# ---------------------------------------------------------------------------------------------- #

Expand Down
19 changes: 3 additions & 16 deletions cmake/rsp/testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ include_guard(GLOBAL)
# Debug
message(VERBOSE "rsp/testing module included")

include("rsp/helpers")
include("rsp/cache")
include("rsp/testing/asserts")

Expand Down Expand Up @@ -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)

# ---------------------------------------------------------------------------------------------- #

Expand Down Expand Up @@ -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
Expand Down
20 changes: 4 additions & 16 deletions cmake/rsp/version.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ include_guard(GLOBAL)
# Debug
message(VERBOSE "rsp/version module included")

include("rsp/helpers")
include("rsp/git")
include("rsp/version/semver")

Expand All @@ -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")
#
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 3 additions & 7 deletions cmake/rsp/version/semver.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}")
Expand Down
66 changes: 66 additions & 0 deletions tests/unit/helpers/requires_arguments_test.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
include("rsp/testing")
include("rsp/helpers")

define_test_case(
"Requires Arguments Test"
LABELS "requires;args;helpers"
)

# -------------------------------------------------------------------------------------------------------------- #
# Helpers
# -------------------------------------------------------------------------------------------------------------- #

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("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_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 second argument is required
fn_with_args("foo")

endfunction()