sysbuild: Make the image processing order well-defined

Adjust the order in which image-specific `sysbuild.cmake` files are
iteratively included by sysbuild.

This is motivated by the introduction of `sysbuild_add_dependencies()`.
In the following example:

  sysbuild_add_dependencies(CONFIGURE my_sample sample_a sample_b)

the `my_sample` image is expected to be added before this function is
called. Success depends not only on the placement of the call, but on
the order in which new images are added, which itself is influenced by
the order in which `sysbuild.cmake` files are included. This last order
can be tweaked to make the "dependencies" feature more user-friendly.

This is done by rewriting the internal `sysbuild.cmake` processing loop
into a new, general purpose function - `sysbuild_add_subdirectory()` -
which is a wrapper for `add_subdirectory(<source_dir>)` that recursively
includes `sysbuild.cmake` files for all images found in `<source_dir>`.

With the new function, all images that are expected to be found in a
given `<source_dir>` are guaranteed to be added around the same time.
This wasn't the case with the old processing loop, because the image-
specific `sysbuild.cmake` files (where "sub-images" could be defined)
were left to be processed at the very end.

Below is the initial order in which sysbuild will add all images.
Note: the order of Zephyr modules (from 1 to n) is well-defined.

  1. Main application (aka DEFAULT_IMAGE)
  2. MCUboot (optional)
  3. All images added via these directories:
     3.1. <module-1>.sysbuild-cmake
     3.2. <module-2>.sysbuild-cmake
     ...
     3.n. <module-n>.sysbuild-cmake

  4. All images added via these files:
     4.1. ${BOARD_DIR}/sysbuild.cmake
     4.2. ${APP_DIR}/sysbuild.cmake (aka sub-images of DEFAULT_IMAGE)

These images are intended to be sorted for the users' convenience, from
most to least important in the build system, or least to most dependent
on other images for configuration (potentially).

Finally, the use of `sysbuild_add_subdirectory()` requires updating the
directory structure in sysbuild:

  ./images
    - All images should belong here. The `DEFAULT_IMAGE` should be the
      first and only image at the top level, so that it gets added first
      and its sub-images get added last.

  ./images/bootloader
    - Moved from ./bootloader.

  ./images/boards
    - Adds images from the board-specific `sysbuild.cmake` file.

Signed-off-by: Grzegorz Swiderski <grzegorz.swiderski@nordicsemi.no>
This commit is contained in:
Grzegorz Swiderski 2023-08-25 12:45:14 +02:00 committed by Carles Cufí
parent 6640c04df6
commit 4cfea45c44
8 changed files with 105 additions and 53 deletions

View file

@ -26,62 +26,12 @@ project(sysbuild LANGUAGES)
get_filename_component(APP_DIR ${APP_DIR} ABSOLUTE)
get_filename_component(app_name ${APP_DIR} NAME)
# Include zephyr modules generated sysbuild CMake file.
foreach(SYSBUILD_CURRENT_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 ${SYSBUILD_CURRENT_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/${SYSBUILD_CURRENT_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)
# This adds the primary application to the build.
ExternalZephyrProject_Add(
APPLICATION ${app_name}
SOURCE_DIR ${APP_DIR}
APP_TYPE MAIN
)
set(DEFAULT_IMAGE "${app_name}")
add_subdirectory(bootloader)
# This is where all Zephyr applications are added to the multi-image build.
sysbuild_add_subdirectory(images)
# This allows for board and app specific images to be included.
include(${BOARD_DIR}/sysbuild.cmake OPTIONAL)
# This allows image specific sysbuild.cmake to be processed.
get_property(IMAGES GLOBAL PROPERTY sysbuild_images)
list(LENGTH IMAGES images_length)
while(NOT "${images_length}" EQUAL "${processed_length}")
foreach(image ${IMAGES})
if(NOT image IN_LIST images_sysbuild_processed)
ExternalProject_Get_property(${image} SOURCE_DIR)
include(${SOURCE_DIR}/sysbuild.cmake OPTIONAL)
list(APPEND images_sysbuild_processed ${image})
endif()
endforeach()
get_property(IMAGES GLOBAL PROPERTY sysbuild_images)
list(LENGTH IMAGES images_length)
list(LENGTH images_sysbuild_processed processed_length_new)
# Check for any duplicate entries in image names to prevent an infinite loop
if("${processed_length_new}" EQUAL "${processed_length}")
# Image length was different than processed length, but no new images are processed.
message(FATAL_ERROR "A duplicate image name was provided, image names must be unique.")
endif()
set(processed_length ${processed_length_new})
endwhile()
sysbuild_module_call(PRE_CMAKE MODULES ${SYSBUILD_MODULE_NAMES} IMAGES ${IMAGES})
sysbuild_images_order(IMAGES_CONFIGURATION_ORDER CONFIGURE IMAGES ${IMAGES})
foreach(image ${IMAGES_CONFIGURATION_ORDER})

View file

@ -39,4 +39,4 @@ config WARN_DEPRECATED
Print a warning when the Kconfig tree is parsed if any deprecated
features are enabled.
rsource "bootloader/Kconfig"
rsource "images/Kconfig"

View file

@ -145,6 +145,13 @@ function(ExternalZephyrProject_Add)
)
endif()
if(TARGET ${ZBUILD_APPLICATION})
message(FATAL_ERROR
"ExternalZephyrProject_Add(APPLICATION ${ZBUILD_APPLICATION} ...) "
"already exists. Application names must be unique."
)
endif()
if(DEFINED ZBUILD_APP_TYPE)
if(NOT ZBUILD_APP_TYPE IN_LIST app_types)
message(FATAL_ERROR
@ -155,6 +162,16 @@ function(ExternalZephyrProject_Add)
endif()
if(NOT DEFINED SYSBUILD_CURRENT_SOURCE_DIR)
message(FATAL_ERROR
"ExternalZephyrProject_Add(${ARGV0} <val> ...) must not be called outside of"
" sysbuild_add_subdirectory(). SYSBUILD_CURRENT_SOURCE_DIR is undefined."
)
endif()
set_property(
DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}"
APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION}
)
set_property(
GLOBAL
APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION}
@ -566,6 +583,45 @@ function(set_config_string image setting value)
set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=\"${value}\"\n")
endfunction()
# Usage:
# sysbuild_add_subdirectory(<source_dir> [<binary_dir>])
#
# This function extends the standard add_subdirectory() command with additional,
# recursive processing of the sysbuild images added via <source_dir>.
#
# After exiting <source_dir>, this function will take every image added so far,
# and include() its sysbuild.cmake file (if found). If more images get added at
# this stage, their sysbuild.cmake files will be included as well, and so on.
# This continues until all expected images have been added, before returning.
#
function(sysbuild_add_subdirectory source_dir)
if(ARGC GREATER 2)
message(FATAL_ERROR
"sysbuild_add_subdirectory(...) called with incorrect number of arguments"
" (expected at most 2, got ${ARGC})"
)
endif()
set(binary_dir ${ARGV1})
# Update SYSBUILD_CURRENT_SOURCE_DIR in this scope, to support nesting
# of sysbuild_add_subdirectory() and even regular add_subdirectory().
cmake_path(ABSOLUTE_PATH source_dir NORMALIZE OUTPUT_VARIABLE SYSBUILD_CURRENT_SOURCE_DIR)
add_subdirectory(${source_dir} ${binary_dir})
while(TRUE)
get_property(added_images DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" PROPERTY sysbuild_images)
if(NOT added_images)
break()
endif()
set_property(DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" PROPERTY sysbuild_images "")
foreach(image ${added_images})
ExternalProject_Get_property(${image} SOURCE_DIR)
include(${SOURCE_DIR}/sysbuild.cmake OPTIONAL)
endforeach()
endwhile()
endfunction()
# Usage:
# sysbuild_add_dependencies(<CONFIGURE | FLASH> <image> [<image-dependency> ...])
#

View file

@ -0,0 +1,36 @@
# Copyright (c) 2021-2023 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0
# The primary application is the first image to be added to the build, so that
# it is available while processing the remaining images.
ExternalZephyrProject_Add(
APPLICATION ${DEFAULT_IMAGE}
SOURCE_DIR ${APP_DIR}
APP_TYPE MAIN
)
# This allows for MCUboot to be included.
sysbuild_add_subdirectory(bootloader)
# Include zephyr modules generated sysbuild CMake file.
foreach(SYSBUILD_CURRENT_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 ${SYSBUILD_CURRENT_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})
sysbuild_add_subdirectory(${SYSBUILD_CURRENT_CMAKE_DIR}
${CMAKE_BINARY_DIR}/modules/${SYSBUILD_CURRENT_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)
# This allows for board specific images to be included.
sysbuild_add_subdirectory(boards)

View file

@ -0,0 +1,5 @@
# Copyright (c) 2021-2023 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0
rsource "bootloader/Kconfig"

View file

@ -0,0 +1,5 @@
# Copyright (c) 2021-2023 Nordic Semiconductor
#
# SPDX-License-Identifier: Apache-2.0
include(${BOARD_DIR}/sysbuild.cmake OPTIONAL)