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 9ed7c187331..bc95d53ee04 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 c70eac0dc62..47e100f50a1 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 1442e356d06..5cffa64c410 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 43230a12297..8c6735586e7 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 7a8873756c1..3ace14825af 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 8f849c2cc77..0242b9d5b15 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 76906bcb029..85c438e6a59 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 8725095ae30..18dedaf8526 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 ""