sysbuild: support Zephyr modules

This commit extends the Zephyr module yaml scheme with additional
entries for sysbuild in the build section.

This allows for Zephyr modules to extend the sysbuild infrastructure
by providing additional CMake and Kconfig files to be included in
sysbuild.

The new settings are:
build:
  sysbuild-cmake: <path>
  sysbuild-kconfig: <path>/<file>
  sysbuild-ext: <true>|<false>
  sysbuild-kconfig-ext:  <true>|<false>

those settings follow the same pattern as the equivalent Zephyr build
settings but are processed by sysbuild.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
Jamie McCrae 2023-02-20 10:00:38 +00:00 committed by Carles Cufí
parent 631fa63610
commit df9027a64a
6 changed files with 215 additions and 34 deletions

View file

@ -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,9 +92,9 @@ 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)
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})
@ -99,8 +104,20 @@ if(WEST OR ZEPHYR_MODULES)
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 "<name>":"<path>" 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,7 +129,6 @@ if(WEST OR ZEPHYR_MODULES)
include(${root}/modules/modules.cmake)
endforeach()
if(DEFINED zephyr_modules_txt)
foreach(module ${zephyr_modules_txt})
# Match "<name>":"<path>" for each line of file, each corresponding to
# one Zephyr module. The use of quotes is required due to CMake not
@ -132,11 +148,34 @@ ${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()
foreach(module ${sysbuild_modules_txt})
# Match "<name>":"<path>" 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()

28
modules/Kconfig.sysbuild Normal file
View file

@ -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_name> module not available."
# depends on !SYSBUILD_<MODULE_NAME_UPPER>_MODULE
#
# Remember to add the following code inside the `<module>/Kconfig file:
# ---------------------------------------------------
# config SYSBUILD_<MODULE_NAME_UPPER>_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

View file

@ -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,12 +308,13 @@ 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 "$(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',
@ -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 <name>:<path>
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 <name>:<path>
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('''\

View file

@ -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)

View file

@ -6,6 +6,12 @@ comment "Sysbuild image configuration"
osource "$(BOARD_DIR)/Kconfig.sysbuild"
menu "Modules"
source "modules/Kconfig.sysbuild"
endmenu
config EXPERIMENTAL
bool
help

View file

@ -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")