diff --git a/SoCMakeConfig.cmake b/SoCMakeConfig.cmake index 81e3ffd..2dc5fe4 100644 --- a/SoCMakeConfig.cmake +++ b/SoCMakeConfig.cmake @@ -12,7 +12,7 @@ include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/add_subdirs.cmake") include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/get_all_targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/print_help.cmake") include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/graphviz.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/multi_option.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/option.cmake") include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/find_python.cmake") include("${CMAKE_CURRENT_LIST_DIR}/cmake/utils/print_list.cmake") diff --git a/cmake/utils/multi_option.cmake b/cmake/utils/multi_option.cmake deleted file mode 100644 index c9773c8..0000000 --- a/cmake/utils/multi_option.cmake +++ /dev/null @@ -1,30 +0,0 @@ -function(multi_option variable docstring value default) - set(possible_values "${value}") - if(ARGC GREATER "3") - list(LENGTH ARGN length) - list(PREPEND ARGN "${value}") - list(GET ARGN "${length}" value) - set(possible_values "${ARGN}") - endif() - - set(${variable} ${default} CACHE STRING "${docstring}") - set_property(CACHE ${variable} PROPERTY STRINGS "${possible_values}") - if(NOT ${variable}) - set(${variable} ${default}) - set(${variable} ${default} PARENT_SCOPE) - endif() - if(NOT "${${variable}}" IN_LIST possible_values) - message(FATAL_ERROR "The variable \"${variable}\" has an unknown value: ${${variable}}\nPossible values are: ${possible_values}") - endif() -endfunction() - -function(option_str variable docstring default) - set(${variable} ${default} CACHE STRING "${docstring}") -endfunction() - -function(option_int variable docstring default) - set(${variable} ${default} CACHE STRING "${docstring}") - if(NOT ${${variable}} MATCHES "^[0-9]+$") - message(FATAL_ERROR "The value of option \"${variable}\" must be a non-negative integer.") - endif() -endfunction() \ No newline at end of file diff --git a/cmake/utils/option.cmake b/cmake/utils/option.cmake new file mode 100644 index 0000000..1c66126 --- /dev/null +++ b/cmake/utils/option.cmake @@ -0,0 +1,87 @@ +function(__define_socmake_option NAME TYPE DESCRIPTION DEFAULT) + cmake_parse_arguments(ARG "" "" "POSSIBLE_VALUES" ${ARGN}) + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") + endif() + + set_property(GLOBAL PROPERTY SOCMAKE_${NAME}_DESCRIPTION "${DESCRIPTION}") + set_property(GLOBAL PROPERTY SOCMAKE_${NAME}_TYPE ${TYPE}) + set_property(GLOBAL PROPERTY SOCMAKE_${NAME}_DEFAULT ${DEFAULT}) + if(ARG_POSSIBLE_VALUES) + set_property(GLOBAL PROPERTY SOCMAKE_${NAME}_VALUES ${ARG_POSSIBLE_VALUES}) + endif() + set_property(GLOBAL APPEND PROPERTY SOCMAKE_OPTIONS ${NAME}) +endfunction() + +#[[[ +# Create a CMake integer option that can be modified through CLI. +# Option defined this way will be visible in `cmake-gui` interface as well as SoCMake `help_options()` help menu. +# To override the variable use `cmake -D=` +# +# :param VARIABLE: name of the variable. +# :type VARIABLE: string +# :param DESCRIPTION: short description string for the variable +# :type DESCRIPTION: string +# :param ENUM_VALUES: possible values variable can have +# :type ENUM_VALUES: list[string] +# :param DEFAULT: default value of the variable +# :type DEFAULT: integer +#]] +function(option_enum VARIABLE DESCRIPTION ENUM_VALUES DEFAULT) + __define_socmake_option(${VARIABLE} "Enum" ${DESCRIPTION} ${DEFAULT} POSSIBLE_VALUES "${ENUM_VALUES}") + + set(${VARIABLE} ${DEFAULT} CACHE STRING "${DESCRIPTION}") + set_property(CACHE ${VARIABLE} PROPERTY STRINGS "${ENUM_VALUES}") + if(NOT ${VARIABLE}) + set(${VARIABLE} ${DEFAULT}) + set(${VARIABLE} ${DEFAULT} PARENT_SCOPE) + endif() + if(NOT "${${VARIABLE}}" IN_LIST ENUM_VALUES) + message(FATAL_ERROR "The VARIABLE \"${VARIABLE}\" has an unknown value: ${${VARIABLE}}\nPossible values are: ${ENUM_VALUES}") + endif() +endfunction() + +function(option_string VARIABLE DESCRIPTION DEFAULT) + __define_socmake_option(${VARIABLE} "String" ${DESCRIPTION} ${DEFAULT}) + + set(${VARIABLE} ${DEFAULT} CACHE STRING "${DESCRIPTION}") +endfunction() + +#[[[ +# Create a CMake integer option that can be modified through CLI. +# Option defined this way will be visible in `cmake-gui` interface as well as SoCMake `help_options()` help menu. +# To override the variable use `cmake -D=` +# +# :param VARIABLE: name of the variable. +# :type VARIABLE: string +# :param DESCRIPTION: short description string for the variable +# :type DESCRIPTION: string +# :param DEFAULT: default value of the variable +# :type DEFAULT: integer +#]] +function(option_integer VARIABLE DESCRIPTION DEFAULT) + __define_socmake_option(${VARIABLE} "Integer" ${DESCRIPTION} ${DEFAULT}) + + set(${VARIABLE} ${DEFAULT} CACHE STRING "${DESCRIPTION}") + if(NOT ${${VARIABLE}} MATCHES "^[0-9]+$") + message(FATAL_ERROR "The value of option \"${VARIABLE}\" must be a non-negative integer.") + endif() +endfunction() + +#[[[ +# Create a CMake boolean option that can be modified through CLI. +# Option defined this way will be visible in `cmake-gui` interface as well as SoCMake `help_options()` help menu. +# To override the variable use `cmake -D=` +# +# :param VARIABLE: name of the variable. +# :type VARIABLE: string +# :param DESCRIPTION: short description string for the variable +# :type DESCRIPTION: string +# :param DEFAULT: default value of the variable +# :type DEFAULT: boolean +#]] +function(option_boolean VARIABLE DESCRIPTION DEFAULT) + __define_socmake_option(${VARIABLE} "Boolean" ${DESCRIPTION} ${DEFAULT} POSSIBLE_VALUES "ON;OFF") + + set(${VARIABLE} ${DEFAULT} CACHE STRING "${DESCRIPTION}") +endfunction() diff --git a/cmake/utils/print_help.cmake b/cmake/utils/print_help.cmake index 1da9d36..c411eb5 100644 --- a/cmake/utils/print_help.cmake +++ b/cmake/utils/print_help.cmake @@ -17,6 +17,17 @@ function(__find_longest_target_name TYPE OUTVAR) set(${OUTVAR} ${__max_length} PARENT_SCOPE) endfunction() +function(__find_longest_string OUTVAR) + set(__max_length 0) + foreach(var ${ARGN}) + string(LENGTH ${var} str_length) + if(${str_length} GREATER ${__max_length}) + set(__max_length ${str_length}) + endif() + endforeach() + set(${OUTVAR} ${__max_length} PARENT_SCOPE) +endfunction() + function(__get_target_help OUTVAR TARGET DESCRIPTION COL_WIDTH) # Get the length of the target string @@ -140,9 +151,9 @@ function(help_ips) get_all_targets(ALL_TARGETS) __find_longest_target_name(INTERFACE_LIBRARY MAX_LEN ${ALL_TARGETS}) - math(EXPR padding_length "${MAX_LEN} + 14") + math(EXPR padding_length "${MAX_LEN} + 18") string(REPEAT " " ${padding_length} padding) - string(APPEND OUT_STRING "${Yellow}Target${padding}Description${ColourReset}\n") + string(APPEND OUT_STRING "${Yellow}IP${padding}Description${ColourReset}\n") math(EXPR line_length "${MAX_LEN} + 50") string(REPEAT "-" ${line_length} line) string(APPEND OUT_STRING "${line}\n") @@ -172,6 +183,146 @@ function(help_ips) set_property(TARGET help_ips PROPERTY DESCRIPTION ${DESCRIPTION}) endfunction() +function(__get_help_option_string OUTSTR VALUE MAX_STR_LEN) + cmake_parse_arguments(ARG "" "COLOUR" "" ${ARGN}) + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") + endif() + + string(LENGTH ${VALUE} option_len) + math(EXPR padding_len "${MAX_STR_LEN} - ${option_len}") + string(REPEAT " " ${padding_len} padding) + + set(__out_str ${${ARG_COLOUR}}${VALUE}${ColourReset}${padding}) + + set(${OUTSTR} ${__out_str} PARENT_SCOPE) +endfunction() + +# [[[ +# This function creates a help target for printing CMake options information. +# The options need to be added with the SoCMake options_boolean/options_integer/options_string/options_enum functions. +# +# It should be called only once in the build flow. +# +# Preferably at the end of the CMakeLists.txt +# +# In order to run it only once at the top level, following trick can be used. +#``` +# if(PROJECT_IS_TOP_LEVEL) +# help_options() +# endif() +#``` +# +# **Keyword Arguments** +# +# :keyword PRINT_ON_CONF: Print the help message during configure phase +# :type PRINT_ON_CONF: boolean +# ]]] +function(help_options) + cmake_parse_arguments(ARG "PRINT_ON_CONF" "" "" ${ARGN}) + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") + endif() + include(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/colours.cmake) + + unset(OUT_STRING) + string(APPEND OUT_STRING "${Yellow}Available Options:${ColourReset}\n") + string(APPEND OUT_STRING "------------------\n") + string(APPEND OUT_STRING "\n") + + get_property(ALL_OPTIONS GLOBAL PROPERTY SOCMAKE_OPTIONS) + # get_all_targets(ALL_TARGETS) + __find_longest_string(MAX_OPTIONS_LEN "Option;${ALL_OPTIONS}") + + unset(list_defaults) + unset(list_possible_values) + unset(list_curr_values) + foreach(option ${ALL_OPTIONS}) + get_property(default GLOBAL PROPERTY SOCMAKE_${option}_DEFAULT) + list(APPEND list_defaults ${default}) + + get_property(values GLOBAL PROPERTY SOCMAKE_${option}_VALUES) + string(REPLACE ";" "," values "${values}") + list(APPEND list_possible_values ${values}) + + list(APPEND list_curr_values ${${option}}) + endforeach() + __find_longest_string(MAX_DEFAULT_LEN "Default;${list_defaults}") + __find_longest_string(MAX_POSSIBLE_VALUES_LEN "Values;${list_possible_values}") + __find_longest_string(MAX_CURRENT_VALUES_LEN "Current value;${list_curr_values}") + + math(EXPR option_str_space "${MAX_OPTIONS_LEN} + 5") # 5 is spacing + math(EXPR option_padding_len "${option_str_space} - 6") # 6 is len of "Option" + string(REPEAT " " ${option_padding_len} padding_option) + + __find_longest_string(MAX_TYPE_LEN "Type;Boolean;String;Integer;Enum") + math(EXPR type_str_space "${MAX_TYPE_LEN} + 5") + math(EXPR type_padding_len "${type_str_space} - 4") # 4 is len of "Type" + string(REPEAT " " ${type_padding_len} padding_type) + + math(EXPR current_value_str_space "${MAX_CURRENT_VALUES_LEN} + 5") + math(EXPR current_value_padding_len "${current_value_str_space} - 13") # 7 is len of "Current Value" + string(REPEAT " " ${current_value_padding_len} padding_current_value) + + math(EXPR default_str_space "${MAX_DEFAULT_LEN} + 5") + math(EXPR default_padding_len "${default_str_space} - 7") # 7 is len of "Default" + string(REPEAT " " ${default_padding_len} padding_default) + + math(EXPR possible_values_str_space "${MAX_POSSIBLE_VALUES_LEN} + 7") + math(EXPR possible_values_padding_len "${possible_values_str_space} - 6") # 6 is len of "Values" + string(REPEAT " " ${possible_values_padding_len} padding_possible_values) + + string(APPEND OUT_STRING "${Yellow}Option${padding_option}Type${padding_type}Current value${padding_current_value}Default${padding_default}Values${padding_possible_values}Description${ColourReset}\n") + math(EXPR line_length "${MAX_OPTIONS_LEN} + 100") + string(REPEAT "-" ${line_length} line) + string(APPEND OUT_STRING "${line}\n") + + foreach(option ${ALL_OPTIONS}) + get_property(type GLOBAL PROPERTY SOCMAKE_${option}_TYPE) + get_property(description GLOBAL PROPERTY SOCMAKE_${option}_DESCRIPTION) + get_property(default GLOBAL PROPERTY SOCMAKE_${option}_DEFAULT) + get_property(values GLOBAL PROPERTY SOCMAKE_${option}_VALUES) + + __get_help_option_string(__out_str ${option} ${option_str_space} COLOUR Cyan) + string(APPEND OUT_STRING ${__out_str}) + + __get_help_option_string(__out_str ${type} ${type_str_space}) + string(APPEND OUT_STRING ${__out_str}) + + __get_help_option_string(__out_str ${${option}} ${current_value_str_space}) + string(APPEND OUT_STRING ${__out_str}) + + __get_help_option_string(__out_str ${default} ${default_str_space}) + string(APPEND OUT_STRING ${__out_str}) + + if(values) + set(values "[${values}]") + string(REPLACE ";" "," values "${values}") + else() + set(values " ") + endif() + + __get_help_option_string(__out_str ${values} ${possible_values_str_space}) + string(APPEND OUT_STRING ${__out_str}) + + string(APPEND OUT_STRING "${description}\n") + + endforeach() + string(APPEND OUT_STRING "${line}\n") + + if(ARG_PRINT_ON_CONF) + message("${OUT_STRING}") + endif() + + set(DESCRIPTION "Print Options help") + file(WRITE ${PROJECT_BINARY_DIR}/help_options.txt ${OUT_STRING}) + add_custom_target(help_options + COMMAND cat ${PROJECT_BINARY_DIR}/help_options.txt + COMMENT ${DESCRIPTION} + ) + set_property(TARGET help_options PROPERTY DESCRIPTION ${DESCRIPTION}) +endfunction() + # [[[ # This function creates a help target for printing target and IPs information. # @@ -202,6 +353,8 @@ function(help) set(ARG_PRINT_ON_CONF PRINT_ON_CONF) endif() + get_property(ALL_OPTIONS GLOBAL PROPERTY SOCMAKE_OPTIONS) + help_ips(${ARG_PRINT_ON_CONF}) help_targets(${ARG_PRINT_ON_CONF}) @@ -209,4 +362,9 @@ function(help) DEPENDS help_targets help_ips ) + if(ALL_OPTIONS) + help_options(${ARG_PRINT_ON_CONF}) + add_dependencies(help_all help_options) + endif() + endfunction() diff --git a/tests/add_ip/add_ip_fail_3_tokens.cmake b/tests/add_ip/add_ip_fail_3_tokens.cmake index dcb7301..f1faa5b 100644 --- a/tests/add_ip/add_ip_fail_3_tokens.cmake +++ b/tests/add_ip/add_ip_fail_3_tokens.cmake @@ -48,4 +48,3 @@ ct_add_test(NAME ${TEST_NAME} EXPECTFAIL) function(${${TEST_NAME}}) add_ip(vendor::::) endfunction() - diff --git a/tests/add_ip/add_ip_fail_4_tokens.cmake b/tests/add_ip/add_ip_fail_4_tokens.cmake index f65f9dc..6336055 100644 --- a/tests/add_ip/add_ip_fail_4_tokens.cmake +++ b/tests/add_ip/add_ip_fail_4_tokens.cmake @@ -30,5 +30,3 @@ ct_add_test(NAME ${TEST_NAME} EXPECTFAIL) function(${${TEST_NAME}}) add_ip(vendor::lib::ip::) endfunction() - -