diff --git a/cmake/modules/zephyr_module.cmake b/cmake/modules/zephyr_module.cmake index 83d3712c76..3dcae3ea61 100644 --- a/cmake/modules/zephyr_module.cmake +++ b/cmake/modules/zephyr_module.cmake @@ -43,6 +43,9 @@ endif() file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR}) set(kconfig_modules_file ${KCONFIG_BINARY_DIR}/Kconfig.modules) +set(kconfig_sysbuild_file ${KCONFIG_BINARY_DIR}/Kconfig.sysbuild.modules) +set(cmake_modules_file ${CMAKE_BINARY_DIR}/zephyr_modules.txt) +set(cmake_sysbuild_file ${CMAKE_BINARY_DIR}/sysbuild_modules.txt) set(zephyr_settings_file ${CMAKE_BINARY_DIR}/zephyr_settings.txt) if(WEST) @@ -59,7 +62,9 @@ if(WEST OR ZEPHYR_MODULES) ${ZEPHYR_MODULES_ARG} ${ZEPHYR_EXTRA_MODULES_ARG} --kconfig-out ${kconfig_modules_file} - --cmake-out ${CMAKE_BINARY_DIR}/zephyr_modules.txt + --cmake-out ${cmake_modules_file} + --sysbuild-kconfig-out ${kconfig_sysbuild_file} + --sysbuild-cmake-out ${cmake_sysbuild_file} --settings-out ${zephyr_settings_file} WORKING_DIRECTORY ${ZEPHYR_BASE} ERROR_VARIABLE @@ -87,20 +92,32 @@ if(WEST OR ZEPHYR_MODULES) # Append ZEPHYR_BASE as a default ext root at lowest priority list(APPEND MODULE_EXT_ROOT ${ZEPHYR_BASE}) - if(EXISTS ${CMAKE_BINARY_DIR}/zephyr_modules.txt) - file(STRINGS ${CMAKE_BINARY_DIR}/zephyr_modules.txt zephyr_modules_txt - ENCODING UTF-8) - - set(ZEPHYR_MODULE_NAMES) - foreach(module ${zephyr_modules_txt}) - # Match "":"" for each line of file, each corresponding to - # one module. The use of quotes is required due to CMake not supporting - # lazy regexes (it supports greedy only). - string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module}) - list(APPEND ZEPHYR_MODULE_NAMES ${module_name}) - endforeach() + if(EXISTS ${cmake_modules_file}) + file(STRINGS ${cmake_modules_file} zephyr_modules_txt ENCODING UTF-8) endif() + set(ZEPHYR_MODULE_NAMES) + foreach(module ${zephyr_modules_txt}) + # Match "":"" for each line of file, each corresponding to + # one module. The use of quotes is required due to CMake not supporting + # lazy regexes (it supports greedy only). + string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module}) + list(APPEND ZEPHYR_MODULE_NAMES ${module_name}) + endforeach() + + if(EXISTS ${cmake_sysbuild_file}) + file(STRINGS ${cmake_sysbuild_file} sysbuild_modules_txt ENCODING UTF-8) + endif() + + set(SYSBUILD_MODULE_NAMES) + foreach(module ${sysbuild_modules_txt}) + # Match "":"" for each line of file, each corresponding to + # one module. The use of quotes is required due to CMake not supporting + # lazy regexes (it supports greedy only). + string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module}) + list(APPEND SYSBUILD_MODULE_NAMES ${module_name}) + endforeach() + # MODULE_EXT_ROOT is process order which means Zephyr module roots processed # later wins. therefore we reverse the list before processing. list(REVERSE MODULE_EXT_ROOT) @@ -112,31 +129,53 @@ if(WEST OR ZEPHYR_MODULES) include(${root}/modules/modules.cmake) endforeach() - if(DEFINED zephyr_modules_txt) - foreach(module ${zephyr_modules_txt}) - # Match "":"" for each line of file, each corresponding to - # one Zephyr module. The use of quotes is required due to CMake not - # supporting lazy regexes (it supports greedy only). - string(CONFIGURE ${module} module) - string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module}) - string(REGEX REPLACE "\".*\":\"(.*)\":\".*\"" "\\1" module_path ${module}) - string(REGEX REPLACE "\".*\":\".*\":\"(.*)\"" "\\1" cmake_path ${module}) + foreach(module ${zephyr_modules_txt}) + # Match "":"" for each line of file, each corresponding to + # one Zephyr module. The use of quotes is required due to CMake not + # supporting lazy regexes (it supports greedy only). + string(CONFIGURE ${module} module) + string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module}) + string(REGEX REPLACE "\".*\":\"(.*)\":\".*\"" "\\1" module_path ${module}) + string(REGEX REPLACE "\".*\":\".*\":\"(.*)\"" "\\1" cmake_path ${module}) - 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}) - else() - message(FATAL_ERROR "Found Zephyr module named: ${module_name}\n\ + 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}) + else() + message(FATAL_ERROR "Found Zephyr module named: ${module_name}\n\ ${MODULE_NAME_UPPER} is a restricted name for Zephyr modules as it is used for \ \${ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR} CMake variable.") - endif() - endforeach() - endif() + endif() + endforeach() + + foreach(module ${sysbuild_modules_txt}) + # Match "":"" for each line of file, each corresponding to + # one Zephyr module. The use of quotes is required due to CMake not + # supporting lazy regexes (it supports greedy only). + string(CONFIGURE ${module} module) + string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module}) + string(REGEX REPLACE "\".*\":\"(.*)\":\".*\"" "\\1" module_path ${module}) + string(REGEX REPLACE "\".*\":\".*\":\"(.*)\"" "\\1" cmake_path ${module}) + + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) + if(NOT ${MODULE_NAME_UPPER} STREQUAL CURRENT) + set(SYSBUILD_${MODULE_NAME_UPPER}_MODULE_DIR ${module_path}) + set(SYSBUILD_${MODULE_NAME_UPPER}_CMAKE_DIR ${cmake_path}) + else() + message(FATAL_ERROR "Found Zephyr module named: ${module_name}\n\ +${MODULE_NAME_UPPER} is a restricted name for Zephyr modules as it is used for \ +\${SYSBUILD_${MODULE_NAME_UPPER}_MODULE_DIR} CMake variable.") + endif() + endforeach() else() file(WRITE ${kconfig_modules_file} "# No west and no Zephyr modules\n" ) + file(WRITE ${kconfig_sysbuild_file} + "# No west and no Zephyr modules\n" + ) + endif() diff --git a/modules/Kconfig.sysbuild b/modules/Kconfig.sysbuild new file mode 100644 index 0000000000..517e0e335f --- /dev/null +++ b/modules/Kconfig.sysbuild @@ -0,0 +1,28 @@ +# Copyright (c) 2019 Intel Corporation +# Copyright (c) 2022 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 + +comment "Available modules." + +source "$(KCONFIG_BINARY_DIR)/Kconfig.sysbuild.modules" + +comment "Unavailable modules, please install those via the project manifest." + +# List of comments to display when Zephyr modules are not available, please +# use the following syntax: +# --------------------------------------------------- +# comment " module not available." +# depends on !SYSBUILD__MODULE +# +# Remember to add the following code inside the `/Kconfig file: +# --------------------------------------------------- +# config SYSBUILD__MODULE +# bool + +# This ensures that symbols are available in Kconfig for dependency checking +# and referencing, while keeping the settings themselves unavailable when the +# modules are not present in the workspace +if 0 +osource "modules/*/Kconfig.sysbuild" +endif diff --git a/scripts/zephyr_module.py b/scripts/zephyr_module.py index 65fb7c5c74..e0cf18d564 100755 --- a/scripts/zephyr_module.py +++ b/scripts/zephyr_module.py @@ -57,6 +57,20 @@ mapping: required: false type: bool default: false + sysbuild-cmake: + required: false + type: str + sysbuild-kconfig: + required: false + type: str + sysbuild-cmake-ext: + required: false + type: bool + default: false + sysbuild-kconfig-ext: + required: false + type: bool + default: false depends: required: false type: seq @@ -216,6 +230,40 @@ def process_cmake(module, meta): module_path.as_posix())) +def process_sysbuildcmake(module, meta): + section = meta.get('build', dict()) + module_path = PurePath(module) + module_yml = module_path.joinpath('zephyr/module.yml') + + cmake_extern = section.get('sysbuild-cmake-ext', False) + if cmake_extern: + return('\"{}\":\"{}\":\"{}\"\n' + .format(meta['name'], + module_path.as_posix(), + "${SYSBUILD_" + meta['name-sanitized'].upper() + "_CMAKE_DIR}")) + + cmake_setting = section.get('sysbuild-cmake', None) + if not validate_setting(cmake_setting, module, 'CMakeLists.txt'): + sys.exit('ERROR: "cmake" key in {} has folder value "{}" which ' + 'does not contain a CMakeLists.txt file.' + .format(module_yml.as_posix(), cmake_setting)) + + if cmake_setting is None: + return "" + + cmake_path = os.path.join(module, cmake_setting or 'zephyr') + cmake_file = os.path.join(cmake_path, 'CMakeLists.txt') + if os.path.isfile(cmake_file): + return('\"{}\":\"{}\":\"{}\"\n' + .format(meta['name'], + module_path.as_posix(), + Path(cmake_path).resolve().as_posix())) + else: + return('\"{}\":\"{}\":\"\"\n' + .format(meta['name'], + module_path.as_posix())) + + def process_settings(module, meta): section = meta.get('build', dict()) build_settings = section.get('settings', None) @@ -260,13 +308,14 @@ def process_blobs(module, meta): return blobs -def kconfig_snippet(meta, path, kconfig_file=None, blobs=False): +def kconfig_snippet(meta, path, kconfig_file=None, blobs=False, sysbuild=False): name = meta['name'] name_sanitized = meta['name-sanitized'] snippet = [f'menu "{name} ({path.as_posix()})"', f'osource "{kconfig_file.resolve().as_posix()}"' if kconfig_file - else f'osource "$(ZEPHYR_{name_sanitized.upper()}_KCONFIG)"', + else f'osource "$(SYSBUILD_{name_sanitized.upper()}_KCONFIG)"' if sysbuild is True + else f'osource "$(ZEPHYR_{name_sanitized.upper()}_KCONFIG)"', f'config ZEPHYR_{name_sanitized.upper()}_MODULE', ' bool', ' default y', @@ -301,6 +350,30 @@ def process_kconfig(module, meta): return "" +def process_sysbuildkconfig(module, meta): + section = meta.get('build', dict()) + module_path = PurePath(module) + module_yml = module_path.joinpath('zephyr/module.yml') + kconfig_extern = section.get('sysbuild-kconfig-ext', False) + if kconfig_extern: + return kconfig_snippet(meta, module_path, sysbuild=True) + + kconfig_setting = section.get('sysbuild-kconfig', None) + if not validate_setting(kconfig_setting, module): + sys.exit('ERROR: "kconfig" key in {} has value "{}" which does ' + 'not point to a valid Kconfig file.' + .format(module_yml, kconfig_setting)) + + if kconfig_setting is None: + return "" + + kconfig_file = os.path.join(module, kconfig_setting) + if os.path.isfile(kconfig_file): + return kconfig_snippet(meta, module_path, Path(kconfig_file)) + else: + return "" + + def process_twister(module, meta): out = "" @@ -553,6 +626,12 @@ def main(): parser.add_argument('--cmake-out', help="""File to write with resulting : values to use for including in CMake""") + parser.add_argument('--sysbuild-kconfig-out', + help="""File to write with resulting KConfig import + statements.""") + parser.add_argument('--sysbuild-cmake-out', + help="""File to write with resulting : + values to use for including in CMake""") parser.add_argument('--meta-out', help="""Write a build meta YaML file containing a list of Zephyr modules and west projects. @@ -576,6 +655,8 @@ def main(): kconfig = "" cmake = "" + sysbuild_kconfig = "" + sysbuild_cmake = "" settings = "" twister = "" @@ -586,6 +667,8 @@ def main(): for module in modules: kconfig += process_kconfig(module.project, module.meta) cmake += process_cmake(module.project, module.meta) + sysbuild_kconfig += process_sysbuildkconfig(module.project, module.meta) + sysbuild_cmake += process_sysbuildcmake(module.project, module.meta) settings += process_settings(module.project, module.meta) twister += process_twister(module.project, module.meta) @@ -597,6 +680,14 @@ def main(): with open(args.cmake_out, 'w', encoding="utf-8") as fp: fp.write(cmake) + if args.sysbuild_kconfig_out: + with open(args.sysbuild_kconfig_out, 'w', encoding="utf-8") as fp: + fp.write(sysbuild_kconfig) + + if args.sysbuild_cmake_out: + with open(args.sysbuild_cmake_out, 'w', encoding="utf-8") as fp: + fp.write(sysbuild_cmake) + if args.settings_out: with open(args.settings_out, 'w', encoding="utf-8") as fp: fp.write('''\ diff --git a/share/sysbuild/CMakeLists.txt b/share/sysbuild/CMakeLists.txt index f0ee376318..aeb0476d57 100644 --- a/share/sysbuild/CMakeLists.txt +++ b/share/sysbuild/CMakeLists.txt @@ -30,6 +30,24 @@ set(IMAGES) get_filename_component(APP_DIR ${APP_DIR} ABSOLUTE) get_filename_component(app_name ${APP_DIR} NAME) +# Include zephyr modules generated sysbuild CMake file. +foreach(module_name ${SYSBUILD_MODULE_NAMES}) + # Note the second, binary_dir parameter requires the added + # subdirectory to have its own, local cmake target(s). If not then + # 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 + zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name}) + if(NOT ${SYSBUILD_${MODULE_NAME_UPPER}_CMAKE_DIR} STREQUAL "") + set(SYSBUILD_CURRENT_MODULE_DIR ${SYSBUILD_${MODULE_NAME_UPPER}_MODULE_DIR}) + set(SYSBUILD_CURRENT_CMAKE_DIR ${SYSBUILD_${MODULE_NAME_UPPER}_CMAKE_DIR}) + add_subdirectory(${SYSBUILD_CURRENT_CMAKE_DIR} ${CMAKE_BINARY_DIR}/modules/${module_name}) + endif() +endforeach() +# Done processing modules, clear SYSBUILD_CURRENT_MODULE_DIR and SYSBUILD_CURRENT_CMAKE_DIR. +set(SYSBUILD_CURRENT_MODULE_DIR) +set(SYSBUILD_CURRENT_CMAKE_DIR) + # Propagate bootloader and signing settings from this system to the MCUboot and # application image build systems. if(SB_CONFIG_BOOTLOADER_MCUBOOT) diff --git a/share/sysbuild/Kconfig b/share/sysbuild/Kconfig index 0ef8f8b802..74c46ba2f7 100644 --- a/share/sysbuild/Kconfig +++ b/share/sysbuild/Kconfig @@ -6,6 +6,12 @@ comment "Sysbuild image configuration" osource "$(BOARD_DIR)/Kconfig.sysbuild" +menu "Modules" + +source "modules/Kconfig.sysbuild" + +endmenu + config EXPERIMENTAL bool help diff --git a/share/sysbuild/cmake/modules/sysbuild_kconfig.cmake b/share/sysbuild/cmake/modules/sysbuild_kconfig.cmake index 33e33fa59c..5dbf82728a 100644 --- a/share/sysbuild/cmake/modules/sysbuild_kconfig.cmake +++ b/share/sysbuild/cmake/modules/sysbuild_kconfig.cmake @@ -46,7 +46,6 @@ endif() # Empty files to make kconfig.py happy. file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/empty.conf) set(APPLICATION_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(KCONFIG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(AUTOCONF_H ${CMAKE_CURRENT_BINARY_DIR}/autoconf.h) set(CONF_FILE ${SB_CONF_FILE}) set(BOARD_DEFCONFIG "${CMAKE_CURRENT_BINARY_DIR}/empty.conf")