Skip to content

Commit 1a71ce8

Browse files
committed
cmake: initial sysbuild / multi image support
This is the initial commit with system build, sysbuild. Using CMake as infrastructure together with the Zephyr sysbuild allows us to support a convenient way of building a sample and allow for extra images to be built as part of a larger system. It uses Kconfig for configuration of image builds. This allows for future extension with additional build images. Signed-off-by: Torsten Rasmussen <[email protected]>
1 parent a0665bf commit 1a71ce8

File tree

5 files changed

+324
-0
lines changed

5 files changed

+324
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ scripts/build/gen_image_info.py @tejlmand
725725
/scripts/build/uf2conv.py @petejohanson
726726
/scripts/build/user_wordsize.py @cfriedt
727727
/scripts/valgrind.supp @aescolar @daor-oti
728+
/share/sysbuild/ @tejlmand
728729
/share/zephyr-package/ @tejlmand
729730
/share/zephyrunittest-package/ @tejlmand
730731
/subsys/bluetooth/ @alwa-nordic @jhedberg @Vudentz

share/sysbuild/CMakeLists.txt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright (c) 2021 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
cmake_minimum_required(VERSION 3.20)
6+
7+
if(NOT DEFINED APP_DIR)
8+
message(FATAL_ERROR "No main application specified")
9+
endif()
10+
11+
# This will update the APP_DIR cache variable to PATH type and apply a comment.
12+
# If APP_DIR is a relative path, then CMake will adjust to absolute path based
13+
# on current working dir.
14+
set(APP_DIR ${APP_DIR} CACHE PATH "Main Application Source Directory")
15+
16+
# Add sysbuild/cmake/modules to CMAKE_MODULE_PATH which allows us to integrate
17+
# sysbuild CMake modules with general Zephyr CMake modules.
18+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules)
19+
# List of Zephyr and sysbuild CMake modules we need for sysbuild.
20+
# Note: sysbuild_kconfig will internally load kconfig CMake module.
21+
set(zephyr_modules extensions sysbuild_extensions python west root zephyr_module boards shields sysbuild_kconfig)
22+
23+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS ${zephyr_modules})
24+
25+
project(sysbuild LANGUAGES)
26+
27+
# Global list of images enabled in this multi image build system.
28+
set(IMAGES)
29+
30+
get_filename_component(APP_DIR ${APP_DIR} ABSOLUTE)
31+
get_filename_component(app_name ${APP_DIR} NAME)
32+
33+
# This adds the primary application to the build.
34+
ExternalZephyrProject_Add(
35+
APPLICATION ${app_name}
36+
SOURCE_DIR ${APP_DIR}
37+
MAIN_APP
38+
)
39+
40+
# This allows for board and app specific images to be included.
41+
include(${BOARD_DIR}/sysbuild.cmake OPTIONAL)
42+
include(${APP_DIR}/sysbuild.cmake OPTIONAL)

share/sysbuild/Kconfig

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright (c) 2021 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
comment "Sysbuild image configuration"
6+
7+
osource "$(BOARD_DIR)/Kconfig.sysbuild"
8+
9+
config EXPERIMENTAL
10+
bool
11+
help
12+
Symbol that must be selected by a feature if it is considered to be
13+
at an experimental implementation stage.
14+
15+
config WARN_EXPERIMENTAL
16+
bool
17+
prompt "Warn on experimental usage"
18+
help
19+
Print a warning when the Kconfig tree is parsed if any experimental
20+
features are enabled.
21+
22+
config DEPRECATED
23+
bool
24+
help
25+
Symbol that must be selected by a feature or module if it is
26+
considered to be deprecated.
27+
28+
config WARN_DEPRECATED
29+
bool
30+
default y
31+
prompt "Warn on deprecated usage"
32+
help
33+
Print a warning when the Kconfig tree is parsed if any deprecated
34+
features are enabled.
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# Copyright (c) 2021 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Usage:
6+
# ExternalZephyrProject_Add(APPLICATION <name>
7+
# SOURCE_DIR <dir>
8+
# [BOARD <board>]
9+
# [MAIN_APP]
10+
# )
11+
#
12+
# This function includes a Zephyr based build system into the multiimage
13+
# build system
14+
#
15+
# APPLICATION: <name>: Name of the application, name will also be used for build
16+
# folder of the application
17+
# SOURCE_DIR <dir>: Source directory of the application
18+
# BOARD <board>: Use <board> for application build instead user defined BOARD.
19+
# MAIN_APP: Flag indicating this application is the main application
20+
# and where user defined settings should be passed on as-is
21+
# except for multi image build flags.
22+
# For example, -DCONF_FILES=<files> will be passed on to the
23+
# MAIN_APP unmodified.
24+
#
25+
function(ExternalZephyrProject_Add)
26+
cmake_parse_arguments(ZBUILD "MAIN_APP" "APPLICATION;BOARD;SOURCE_DIR" "" ${ARGN})
27+
28+
if(ZBUILD_UNPARSED_ARGUMENTS)
29+
message(FATAL_ERROR
30+
"ExternalZephyrProject_Add(${ARGV0} <val> ...) given unknown arguments:"
31+
" ${ZBUILD_UNPARSED_ARGUMENTS}"
32+
)
33+
endif()
34+
35+
set(sysbuild_vars
36+
"APP_DIR"
37+
"SB_CONF_FILE"
38+
)
39+
40+
# General variables that should be propagated to all Zephyr builds, for example:
41+
# - ZEPHYR_MODULES / ZEPHYR_EXTRA_MODULES
42+
# - ZEPHYR_TOOLCHAIN_VARIANT
43+
# - *_TOOLCHAIN_PATH
44+
# - *_ROOT
45+
# etc.
46+
# Note: setting vars on a single image can be done by using
47+
# `<image>_CONF_FILE`, like `mcuboot_CONF_FILE`
48+
set(
49+
shared_image_variables_list
50+
CMAKE_BUILD_TYPE
51+
CMAKE_VERBOSE_MAKEFILE
52+
BOARD
53+
ZEPHYR_MODULES
54+
ZEPHYR_EXTRA_MODULES
55+
ZEPHYR_TOOLCHAIN_VARIANT
56+
EXTRA_KCONFIG_TARGETS
57+
)
58+
59+
set(shared_image_variables_regex
60+
"^[^_]*_TOOLCHAIN_PATH|^[^_]*_ROOT"
61+
)
62+
63+
set(app_cache_file ${CMAKE_BINARY_DIR}/CMake${ZBUILD_APPLICATION}PreloadCache.txt)
64+
65+
if(EXISTS ${app_cache_file})
66+
file(STRINGS ${app_cache_file} app_cache_strings)
67+
set(app_cache_strings_current ${app_cache_strings})
68+
endif()
69+
70+
get_cmake_property(variables_cached CACHE_VARIABLES)
71+
foreach(var_name ${variables_cached})
72+
# Any var of the form `<app>_<var>` should be propagated.
73+
# For example mcuboot_<VAR>=<val> ==> -D<VAR>=<val> for mcuboot build.
74+
if("${var_name}" MATCHES "^${ZBUILD_APPLICATION}_.*")
75+
list(APPEND application_vars ${var_name})
76+
continue()
77+
endif()
78+
79+
# This means there is a match to another image than current one, ignore.
80+
if("${var_name}" MATCHES "^.*_CONFIG_.*")
81+
continue()
82+
endif()
83+
84+
# sysbuild reserved namespace.
85+
if(var_name IN_LIST sysbuild_vars OR "${var_name}" MATCHES "^SB_CONFIG_.*")
86+
continue()
87+
endif()
88+
89+
if("${var_name}" MATCHES "^CONFIG_.*")
90+
if(ZBUILD_MAIN_APP)
91+
list(APPEND application_vars ${var_name})
92+
endif()
93+
continue()
94+
endif()
95+
96+
if(var_name IN_LIST shared_image_variables_list)
97+
list(APPEND application_vars ${var_name})
98+
continue()
99+
endif()
100+
101+
if("${var_name}" MATCHES "${shared_image_variables_regex}")
102+
list(APPEND application_vars ${var_name})
103+
endif()
104+
endforeach()
105+
106+
foreach(app_var_name ${application_vars})
107+
string(REGEX REPLACE "^${ZBUILD_APPLICATION}_" "" var_name "${app_var_name}")
108+
get_property(var_type CACHE ${app_var_name} PROPERTY TYPE)
109+
set(new_cache_entry "${var_name}:${var_type}=${${app_var_name}}")
110+
if(NOT new_cache_entry IN_LIST app_cache_strings)
111+
# This entry does not exists, let's see if it has been updated.
112+
foreach(entry ${app_cache_strings})
113+
if("${entry}" MATCHES "^${var_name}:.*")
114+
list(REMOVE_ITEM app_cache_strings "${entry}")
115+
break()
116+
endif()
117+
endforeach()
118+
list(APPEND app_cache_strings "${var_name}:${var_type}=${${app_var_name}}")
119+
list(APPEND app_cache_entries "-D${var_name}:${var_type}=${${app_var_name}}")
120+
endif()
121+
endforeach()
122+
123+
if(NOT "${app_cache_strings_current}" STREQUAL "${app_cache_strings}")
124+
string(REPLACE ";" "\n" app_cache_strings "${app_cache_strings}")
125+
file(WRITE ${app_cache_file} ${app_cache_strings})
126+
endif()
127+
128+
if(DEFINED ZBUILD_BOARD)
129+
list(APPEND app_cache_entries "-DBOARD=${ZBUILD_BOARD}")
130+
elseif(NOT ZBUILD_MAIN_APP)
131+
list(APPEND app_cache_entries "-DBOARD=${BOARD}")
132+
endif()
133+
134+
set(image_banner "* Running CMake for ${ZBUILD_APPLICATION} *")
135+
string(LENGTH "${image_banner}" image_banner_width)
136+
string(REPEAT "*" ${image_banner_width} image_banner_header)
137+
message(STATUS "\n ${image_banner_header}\n"
138+
" ${image_banner}\n"
139+
" ${image_banner_header}\n"
140+
)
141+
142+
execute_process(
143+
COMMAND ${CMAKE_COMMAND}
144+
-G${CMAKE_GENERATOR}
145+
${app_cache_entries}
146+
-B${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}
147+
-S${ZBUILD_SOURCE_DIR}
148+
RESULT_VARIABLE return_val
149+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
150+
)
151+
152+
if(return_val)
153+
message(FATAL_ERROR
154+
"CMake configure failed for Zephyr project: ${ZBUILD_APPLICATION}\n"
155+
"Location: ${ZBUILD_SOURCE_DIR}"
156+
)
157+
endif()
158+
159+
foreach(kconfig_target
160+
menuconfig
161+
hardenconfig
162+
guiconfig
163+
${EXTRA_KCONFIG_TARGETS}
164+
)
165+
166+
if(NOT ZBUILD_MAIN_APP)
167+
set(image_prefix "${ZBUILD_APPLICATION}_")
168+
endif()
169+
170+
add_custom_target(${image_prefix}${kconfig_target}
171+
${CMAKE_MAKE_PROGRAM} ${kconfig_target}
172+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}
173+
USES_TERMINAL
174+
)
175+
endforeach()
176+
include(ExternalProject)
177+
ExternalProject_Add(
178+
${ZBUILD_APPLICATION}
179+
SOURCE_DIR ${ZBUILD_SOURCE_DIR}
180+
BINARY_DIR ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}
181+
CONFIGURE_COMMAND ""
182+
BUILD_COMMAND ${CMAKE_COMMAND} --build .
183+
INSTALL_COMMAND ""
184+
BUILD_ALWAYS True
185+
USES_TERMINAL_BUILD True
186+
)
187+
endfunction()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright (c) 2021 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_sysbuild_menuconfig
6+
${ZEPHYR_BASE}/scripts/kconfig/menuconfig.py
7+
)
8+
9+
set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_sysbuild_guiconfig
10+
${ZEPHYR_BASE}/scripts/kconfig/guiconfig.py
11+
)
12+
13+
set(KCONFIG_TARGETS sysbuild_menuconfig sysbuild_guiconfig)
14+
list(TRANSFORM EXTRA_KCONFIG_TARGETS PREPEND "sysbuild_")
15+
16+
if(DEFINED SB_CONF_FILE)
17+
# SB_CONF_FILE already set so nothing to do.
18+
elseif(DEFINED ENV{SB_CONF_FILE})
19+
set(SB_CONF_FILE $ENV{SB_CONF_FILE})
20+
elseif(EXISTS ${APP_DIR}/sysbuild.conf)
21+
set(SB_CONF_FILE ${APP_DIR}/sysbuild.conf)
22+
else()
23+
# Because SYSBuild is opt-in feature, then it is permitted to not have a
24+
# SYSBuild dedicated configuration file.
25+
endif()
26+
27+
if(DEFINED SB_CONF_FILE AND NOT IS_ABSOLUTE SB_CONF_FILE)
28+
cmake_path(ABSOLUTE_PATH SB_CONF_FILE BASE_DIRECTORY ${APP_DIR} OUTPUT_VARIABLE SB_CONF_FILE)
29+
endif()
30+
31+
if(DEFINED SB_CONF_FILE AND NOT DEFINED CACHE{SB_CONF_FILE})
32+
# We only want to set this in cache it has been defined and is not already there.
33+
set(SB_CONF_FILE ${SB_CONF_FILE} CACHE STRING "If desired, you can build the application with \
34+
SYSbuild configuration settings specified in an alternate .conf file using this parameter. \
35+
These settings will override the settings in the application’s SYSBuild config file or its \
36+
default .conf file. Multiple files may be listed, e.g. SB_CONF_FILE=\"sys1.conf sys2.conf\"")
37+
endif()
38+
39+
if(NOT DEFINED SB_CONF_FILE)
40+
# If there is no SB_CONF_FILE, then use empty.conf to make kconfiglib happy.
41+
# Not adding it to CMake cache ensures that a later created sysbuild.conf
42+
# will be automatically detected.
43+
set(SB_CONF_FILE ${CMAKE_CURRENT_BINARY_DIR}/empty.conf)
44+
endif()
45+
46+
# Empty files to make kconfig.py happy.
47+
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/empty.conf)
48+
set(APPLICATION_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
49+
set(KCONFIG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
50+
set(AUTOCONF_H ${CMAKE_CURRENT_BINARY_DIR}/autoconf.h)
51+
set(CONF_FILE ${SB_CONF_FILE})
52+
set(BOARD_DEFCONFIG "${CMAKE_CURRENT_BINARY_DIR}/empty.conf")
53+
list(APPEND ZEPHYR_KCONFIG_MODULES_DIR BOARD=${BOARD})
54+
set(KCONFIG_NAMESPACE SB_CONFIG)
55+
56+
if(EXISTS ${APP_DIR}/Kconfig.sysbuild)
57+
set(KCONFIG_ROOT ${APP_DIR}/Kconfig.sysbuild)
58+
endif()
59+
include(${ZEPHYR_BASE}/cmake/modules/kconfig.cmake)
60+
set(CONF_FILE)

0 commit comments

Comments
 (0)