From 3d88083bf1222ea8fa54ee37360db2bbef1cd417 Mon Sep 17 00:00:00 2001 From: Torsten Rasmussen Date: Tue, 19 Jan 2021 12:01:38 +0100 Subject: [PATCH] cmake: zephyr modules: sanitize all module name when used as variable The introduction of Zephyr module glue code in the Zephyr repository introduces a Kconfig variable in the form of: `config ZEPHYR__MODULE`. All Kconfig variables go into `autoconf.h`, therefore it is necessary to sanitize the Kconfig variable, so that it does not contain special characters. To ensure consistent variable name, then the module name will be sanitized in all variable use in both Kconfig and CMake. The sanitization is done be replacing all special characters with an underscore, `_`. Signed-off-by: Torsten Rasmussen --- CMakeLists.txt | 2 +- cmake/extensions.cmake | 41 ++++++++++++++++++++++++++++++++++ cmake/kconfig.cmake | 2 +- cmake/zephyr_module.cmake | 2 +- doc/guides/modules.rst | 7 ++++++ modules/modules.cmake | 6 +++-- scripts/ci/check_compliance.py | 2 +- scripts/zephyr_module.py | 18 ++++++++++----- 8 files changed, 68 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ed7c18733..bc95d53ee0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -454,7 +454,7 @@ foreach(module_name ${ZEPHYR_MODULE_NAMES}) # this binary_dir is created but stays empty. Object files land in # the main binary dir instead. # https://cmake.org/pipermail/cmake/2019-June/069547.html - string(TOUPPER ${module_name} MODULE_NAME_UPPER) + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) if(NOT ${ZEPHYR_${MODULE_NAME_UPPER}_CMAKE_DIR} STREQUAL "") set(ZEPHYR_CURRENT_MODULE_DIR ${ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR}) set(ZEPHYR_CURRENT_CMAKE_DIR ${ZEPHYR_${MODULE_NAME_UPPER}_CMAKE_DIR}) diff --git a/cmake/extensions.cmake b/cmake/extensions.cmake index c70eac0dc6..47e100f50a 100644 --- a/cmake/extensions.cmake +++ b/cmake/extensions.cmake @@ -1960,6 +1960,47 @@ Relative paths are only allowed with `-D${ARGV1}=`") endif() endfunction() +# Usage: +# zephyr_string( ...) +# +# Zephyr string function extension. +# This function extends the CMake string function by providing additional +# manipulation arguments to CMake string. +# +# SANITIZE: Ensure that the output string does not contain any special +# characters. Special characters, such as -, +, =, $, etc. are +# converted to underscores '_'. +# +# SANITIZE TOUPPER: Ensure that the output string does not contain any special +# characters. Special characters, such as -, +, =, $, etc. are +# converted to underscores '_'. +# The sanitized string will be returned in UPPER case. +# +# returns the updated string +function(zephyr_string) + set(options SANITIZE TOUPPER) + cmake_parse_arguments(ZEPHYR_STRING "${options}" "" "" ${ARGN}) + + if (NOT ZEPHYR_STRING_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Function zephyr_string() called without a return variable") + endif() + + list(GET ZEPHYR_STRING_UNPARSED_ARGUMENTS 0 return_arg) + list(REMOVE_AT ZEPHYR_STRING_UNPARSED_ARGUMENTS 0) + + list(JOIN ZEPHYR_STRING_UNPARSED_ARGUMENTS "" work_string) + + if(ZEPHYR_STRING_SANITIZE) + string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" work_string ${work_string}) + endif() + + if(ZEPHYR_STRING_TOUPPER) + string(TOUPPER ${work_string} work_string) + endif() + + set(${return_arg} ${work_string} PARENT_SCOPE) +endfunction() + # Usage: # zephyr_check_cache( [REQUIRED]) # diff --git a/cmake/kconfig.cmake b/cmake/kconfig.cmake index 1442e356d0..5cffa64c41 100644 --- a/cmake/kconfig.cmake +++ b/cmake/kconfig.cmake @@ -69,7 +69,7 @@ string(REPLACE ";" "?" DTS_ROOT_BINDINGS "${DTS_ROOT_BINDINGS}") # This allows Kconfig files to refer relative from a modules root as: # source "$(ZEPHYR_FOO_MODULE_DIR)/Kconfig" foreach(module_name ${ZEPHYR_MODULE_NAMES}) - string(TOUPPER ${module_name} MODULE_NAME_UPPER) + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) list(APPEND ZEPHYR_KCONFIG_MODULES_DIR "ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR=${ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR}" diff --git a/cmake/zephyr_module.cmake b/cmake/zephyr_module.cmake index 43230a1229..8c6735586e 100644 --- a/cmake/zephyr_module.cmake +++ b/cmake/zephyr_module.cmake @@ -104,7 +104,7 @@ if(WEST OR ZEPHYR_MODULES) list(APPEND ZEPHYR_MODULE_NAMES ${module_name}) - string(TOUPPER ${module_name} MODULE_NAME_UPPER) + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) if(NOT ${MODULE_NAME_UPPER} STREQUAL CURRENT) set(ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR ${module_path}) set(ZEPHYR_${MODULE_NAME_UPPER}_CMAKE_DIR ${cmake_path}) diff --git a/doc/guides/modules.rst b/doc/guides/modules.rst index 7a8873756c..3ace14825a 100644 --- a/doc/guides/modules.rst +++ b/doc/guides/modules.rst @@ -421,6 +421,13 @@ CMake variable ``ZEPHYR__MODULE_DIR`` and the variable ``ZEPHYR__CMAKE_DIR`` holds the location of the directory containing the module's :file:`CMakeLists.txt` file. +.. note:: + When used for CMake and Kconfig variables, all letters in module names are + converted to uppercase and all non-alphanumeric characters are converted + to underscores (_). + As example, the module ``foo-bar`` must be referred to as + ``ZEPHYR_FOO_BAR_MODULE_DIR`` in CMake and Kconfig. + Here is an example for the Zephyr module ``foo``: .. code-block:: yaml diff --git a/modules/modules.cmake b/modules/modules.cmake index 8f849c2cc7..0242b9d5b1 100644 --- a/modules/modules.cmake +++ b/modules/modules.cmake @@ -5,7 +5,8 @@ file(GLOB cmake_modules "${CMAKE_CURRENT_LIST_DIR}/*/CMakeLists.txt") foreach(module ${cmake_modules}) get_filename_component(module_dir ${module} DIRECTORY) get_filename_component(module_name ${module_dir} NAME) - string(TOUPPER ${module_name} MODULE_NAME_UPPER) + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) + set(ZEPHYR_${MODULE_NAME_UPPER}_CMAKE_DIR ${module_dir}) endforeach() @@ -14,6 +15,7 @@ file(GLOB kconfig_modules "${CMAKE_CURRENT_LIST_DIR}/*/Kconfig") foreach(module ${kconfig_modules}) get_filename_component(module_dir ${module} DIRECTORY) get_filename_component(module_name ${module_dir} NAME) - string(TOUPPER ${module_name} MODULE_NAME_UPPER) + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) + set(ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG ${module_dir}/Kconfig) endforeach() diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 76906bcb02..85c438e6a5 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -262,7 +262,7 @@ class KconfigCheck(ComplianceTest): with open(modules_file, 'w') as fp_module_file: for module in modules: fp_module_file.write("ZEPHYR_{}_KCONFIG = {}\n".format( - module.upper(), + re.sub('[^a-zA-Z0-9]', '_', module).upper(), modules_dir + '/' + module + '/Kconfig' )) fp_module_file.write(content) diff --git a/scripts/zephyr_module.py b/scripts/zephyr_module.py index 8725095ae3..18dedaf852 100755 --- a/scripts/zephyr_module.py +++ b/scripts/zephyr_module.py @@ -19,6 +19,7 @@ maintained in modules in addition to what is available in the main Zephyr tree. import argparse import os +import re import sys import yaml import pykwalify.core @@ -128,11 +129,13 @@ def process_module(module): .format(module_yml.as_posix(), e)) meta['name'] = meta.get('name', module_path.name) + meta['name-sanitized'] = re.sub('[^a-zA-Z0-9]', '_', meta['name']) return meta if Path(module_path.joinpath('zephyr/CMakeLists.txt')).is_file() and \ Path(module_path.joinpath('zephyr/Kconfig')).is_file(): return {'name': module_path.name, + 'name-sanitized': re.sub('[^a-zA-Z0-9]', '_', module_path.name), 'build': {'cmake': 'zephyr', 'kconfig': 'zephyr/Kconfig'}} return None @@ -148,7 +151,7 @@ def process_cmake(module, meta): return('\"{}\":\"{}\":\"{}\"\n' .format(meta['name'], module_path.as_posix(), - "${ZEPHYR_" + meta['name'].upper() + "_CMAKE_DIR}")) + "${ZEPHYR_" + meta['name-sanitized'].upper() + "_CMAKE_DIR}")) cmake_setting = section.get('cmake', None) if not validate_setting(cmake_setting, module, 'CMakeLists.txt'): @@ -185,11 +188,14 @@ def process_settings(module, meta): return out_text -def kconfig_snippet(name, path, kconfig_file=None): +def kconfig_snippet(meta, path, kconfig_file=None): + name = meta['name'] + name_sanitized = meta['name-sanitized'] + snippet = (f'menu "{name} ({path})"', f'osource "{kconfig_file.resolve().as_posix()}"' if kconfig_file - else f'osource "$(ZEPHYR_{name.upper()}_KCONFIG)"', - f'config ZEPHYR_{name.upper()}_MODULE', + else f'osource "$(ZEPHYR_{name_sanitized.upper()}_KCONFIG)"', + f'config ZEPHYR_{name_sanitized.upper()}_MODULE', ' bool', ' default y', 'endmenu\n') @@ -202,7 +208,7 @@ def process_kconfig(module, meta): module_yml = module_path.joinpath('zephyr/module.yml') kconfig_extern = section.get('kconfig-ext', False) if kconfig_extern: - return kconfig_snippet(meta['name'], module_path) + return kconfig_snippet(meta, module_path) kconfig_setting = section.get('kconfig', None) if not validate_setting(kconfig_setting, module): @@ -212,7 +218,7 @@ def process_kconfig(module, meta): kconfig_file = os.path.join(module, kconfig_setting or 'zephyr/Kconfig') if os.path.isfile(kconfig_file): - return kconfig_snippet(meta['name'], module_path, Path(kconfig_file)) + return kconfig_snippet(meta, module_path, Path(kconfig_file)) else: return ""