From 3843444cb0c46dc691bbcdfeb52a4846166d82b8 Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Fri, 26 May 2023 15:44:00 +0200 Subject: [PATCH 1/5] cmake: extensions: Add zephyr_topological_sort() This function performs topological sorting of CMake targets. Its initial use case in Zephyr will be for implementing sysbuild image dependencies, i.e., specifying an image order for CMake configuration and flashing. Sourced from a comment on PR #57884 (anchor: #discussion_r1206807021) Authored-by: Torsten Rasmussen Signed-off-by: Grzegorz Swiderski --- cmake/modules/extensions.cmake | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index 90a036e94ef190..1e38ae7ba727d3 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -3005,6 +3005,76 @@ function(target_byproducts) ) endfunction() +# Usage: +# topological_sort(TARGETS [ ...] +# PROPERTY_NAME +# RESULT ) +# +# This function performs topological sorting of CMake targets using a specific +# , which dictates target dependencies. A fatal error occurs if the +# provided dependencies cannot be met, e.g., if they contain cycles. +# +# TARGETS: List of target names. +# PROPERTY_NAME: Name of the target property to be used when sorting. For every +# target listed in TARGETS, this property must contain a list +# (possibly empty) of other targets, which this target depends on +# for a particular purpose. The property must not contain any +# target which is not also found in TARGETS. +# RESULT: Output variable, where the topologically sorted list of target +# names will be returned. +# +function(topological_sort) + cmake_parse_arguments(TS "" "RESULT;PROPERTY_NAME" "TARGETS" ${ARGN}) + + set(dep_targets) + set(start_targets) + set(sorted_targets) + + foreach(target ${TS_TARGETS}) + get_target_property(${target}_dependencies ${target} ${TS_PROPERTY_NAME}) + + if(${target}_dependencies) + list(APPEND dep_targets ${target}) + else() + list(APPEND start_targets ${target}) + endif() + endforeach() + + while(TRUE) + list(POP_FRONT start_targets node) + list(APPEND sorted_targets ${node}) + set(to_remove) + foreach(target ${dep_targets}) + if("${node}" IN_LIST ${target}_dependencies) + list(REMOVE_ITEM ${target}_dependencies ${node}) + if(NOT ${target}_dependencies) + list(APPEND start_targets ${target}) + list(APPEND to_remove ${target}) + endif() + endif() + endforeach() + + foreach(target ${to_remove}) + list(REMOVE_ITEM dep_targets ${target}) + endforeach() + if(NOT start_targets) + break() + endif() + endwhile() + + if(dep_targets) + foreach(target ${dep_targets}) + get_target_property(deps ${target} ${TS_PROPERTY_NAME}) + list(JOIN deps " " deps) + list(APPEND dep_string "${target} depends on: ${deps}") + endforeach() + list(JOIN dep_string "\n" dep_string) + message(FATAL_ERROR "Unmet or cyclic dependencies:\n${dep_string}") + endif() + + set(${TS_RESULT} "${sorted_targets}" PARENT_SCOPE) +endfunction() + ######################################################## # 4. Devicetree extensions ######################################################## From dbaf74add7510f82303f80d3966402affb68dff9 Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Mon, 15 May 2023 11:19:47 +0200 Subject: [PATCH 2/5] sysbuild: Support relative ordering of images Fixes #53650 The existing solution for image ordering involves the `IMAGES` variable, which sysbuild originally provided to let users manually add a new image in a desired order. This isn't very flexible for the following reasons: * The order in which `IMAGES` is updated across multiple modules and `sysbuild.cmake` files is not well defined. * Having a single variable means that the same order is used for both configuration and flashing. Usually, there is no reason for the flashing order to be the same as the configuration order. Introduce the `sysbuild_add_dependencies()` function for more fine-tuned ordering of images. It makes one image depend on other images in either configuration or flashing order. Its usage is similar to the standard CMake function `add_dependencies()`, but with an extra parameter to distinguish between two types of dependencies: sysbuild_add_dependencies(CONFIGURE my_sample sample_a sample_b) sysbuild_add_dependencies(FLASH my_sample sample_c sample_d) CONFIGURE dependencies determine the order in which sysbuild configures (runs CMake for) the individual images. This is useful if there is some information from one application's build which needs to be available to another application. FLASH dependencies control the sequence of images used by `west flash`. This could be used if a specific flashing order is required by an SoC, a runner, or something else. Note that these dependencies are not valid for images specified as `BUILD_ONLY`. The internal `sysbuild_images_order()` function is responsible for assembling two sorted lists of images based on the added dependencies, with the help of `topological_sort()`. Signed-off-by: Grzegorz Swiderski --- share/sysbuild/CMakeLists.txt | 3 +- share/sysbuild/cmake/domains.cmake | 4 +- .../cmake/modules/sysbuild_extensions.cmake | 66 +++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/share/sysbuild/CMakeLists.txt b/share/sysbuild/CMakeLists.txt index 84cb937cc7e87e..b52f1cbabf53f2 100644 --- a/share/sysbuild/CMakeLists.txt +++ b/share/sysbuild/CMakeLists.txt @@ -85,7 +85,8 @@ while(NOT "${images_length}" EQUAL "${processed_length}") endwhile() sysbuild_module_call(PRE_CMAKE MODULES ${SYSBUILD_MODULE_NAMES} IMAGES ${IMAGES}) -foreach(image ${IMAGES}) +sysbuild_images_order(IMAGES_CONFIGURATION_ORDER CONFIGURE IMAGES ${IMAGES}) +foreach(image ${IMAGES_CONFIGURATION_ORDER}) ExternalZephyrProject_Cmake(APPLICATION ${image}) endforeach() sysbuild_module_call(POST_CMAKE MODULES ${SYSBUILD_MODULE_NAMES} IMAGES ${IMAGES}) diff --git a/share/sysbuild/cmake/domains.cmake b/share/sysbuild/cmake/domains.cmake index a4905e6e1113d9..c46d261aff21bf 100644 --- a/share/sysbuild/cmake/domains.cmake +++ b/share/sysbuild/cmake/domains.cmake @@ -2,10 +2,12 @@ # # SPDX-License-Identifier: Apache-2.0 +sysbuild_images_order(IMAGES_FLASHING_ORDER FLASH IMAGES ${IMAGES}) + set(domains_yaml "default: ${DEFAULT_IMAGE}") set(domains_yaml "${domains_yaml}\nbuild_dir: ${CMAKE_BINARY_DIR}") set(domains_yaml "${domains_yaml}\ndomains:") -foreach(image ${IMAGES}) +foreach(image ${IMAGES_FLASHING_ORDER}) get_target_property(image_is_build_only ${image} BUILD_ONLY) if(image_is_build_only) continue() diff --git a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake index e306f57f088f51..a399e643d6cda0 100644 --- a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake +++ b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake @@ -560,3 +560,69 @@ endfunction() function(set_config_string image setting value) set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=\"${value}\"\n") endfunction() + +# Usage: +# sysbuild_add_dependencies( [ ...]) +# +# This function makes an image depend on other images in the configuration or +# flashing order. Each image named "" will be ordered before +# the image named "". +# +# CONFIGURE: Add CMake configuration dependencies. This will determine the order +# in which `ExternalZephyrProject_Cmake()` will be called. +# FLASH: Add flashing dependencies. This will determine the order in which +# all images will appear in `domains.yaml`. +# +function(sysbuild_add_dependencies dependency_type image) + set(valid_dependency_types CONFIGURE FLASH) + if(NOT dependency_type IN_LIST valid_dependency_types) + list(JOIN valid_dependency_types ", " valid_dependency_types) + message(FATAL_ERROR "sysbuild_add_dependencies(...) dependency type " + "${dependency_type} must be one of the following: " + "${valid_dependency_types}" + ) + endif() + + if(NOT TARGET ${image}) + message(FATAL_ERROR + "${image} does not exist. Remember to call " + "ExternalZephyrProject_Add(APPLICATION ${image} ...) first." + ) + endif() + + get_target_property(image_is_build_only ${image} BUILD_ONLY) + if(image_is_build_only AND dependency_type STREQUAL "FLASH") + message(FATAL_ERROR + "sysbuild_add_dependencies(...) cannot add FLASH dependencies to " + "BUILD_ONLY image ${image}." + ) + endif() + + set(property_name ${dependency_type}_DEPENDS) + set_property(TARGET ${image} APPEND PROPERTY ${property_name} ${ARGN}) +endfunction() + +# Usage: +# sysbuild_images_order( IMAGES ) +# +# This function will sort the provided `` to satisfy the dependencies +# specified using `sysbuild_add_dependencies()`. The result will be returned in +# ``. +# +function(sysbuild_images_order variable dependency_type) + cmake_parse_arguments(SIS "" "" "IMAGES" ${ARGN}) + zephyr_check_arguments_required_all("sysbuild_images_order" SIS IMAGES) + + set(valid_dependency_types CONFIGURE FLASH) + if(NOT dependency_type IN_LIST valid_dependency_types) + list(JOIN valid_dependency_types ", " valid_dependency_types) + message(FATAL_ERROR "sysbuild_images_order(...) dependency type " + "${dependency_type} must be one of the following: " + "${valid_dependency_types}" + ) + endif() + + set(property_name ${dependency_type}_DEPENDS) + topological_sort(TARGETS ${SIS_IMAGES} PROPERTY_NAME ${property_name} RESULT sorted) + set(${variable} ${sorted} PARENT_SCOPE) +endfunction() From 00bb3ad35194f7aec053a8d9e10dc74e2afba9f6 Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Mon, 15 May 2023 11:19:47 +0200 Subject: [PATCH 3/5] sysbuild: Remove IMAGES variable This variable was originally provided for two indended purposes: * Let users manually add a new image in a desired order in the list. * Let users set build-only images, which are excluded from the list. Given the recent additions of the `sysbuild_add_dependencies()` function and the `BUILD_ONLY` parameter, `IMAGES` should be considered obsolete. Furthermore, the list of images added to sysbuild should be updated automatically when calling `ExternalZephyrProject_Add()`. This is now possible by using a GLOBAL property internal to sysbuild. With that, the `IMAGES` variable can be removed. Its existing usage for image ordering is replaced with `sysbuild_add_dependencies()` treewide. Signed-off-by: Grzegorz Swiderski --- samples/drivers/ipm/ipm_mcux/sysbuild.cmake | 10 +++++++--- samples/subsys/ipc/openamp/sysbuild.cmake | 10 +++++++--- share/sysbuild/CMakeLists.txt | 6 ++---- share/sysbuild/bootloader/CMakeLists.txt | 2 +- share/sysbuild/cmake/modules/sysbuild_extensions.cmake | 5 +++++ tests/boot/test_mcuboot/sysbuild.cmake | 5 +++-- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/samples/drivers/ipm/ipm_mcux/sysbuild.cmake b/samples/drivers/ipm/ipm_mcux/sysbuild.cmake index 2153852ce4d274..6ca2faf7d34810 100644 --- a/samples/drivers/ipm/ipm_mcux/sysbuild.cmake +++ b/samples/drivers/ipm/ipm_mcux/sysbuild.cmake @@ -9,9 +9,13 @@ ExternalZephyrProject_Add( BOARD ${SB_CONFIG_IPM_REMOTE_BOARD} ) -# Add a dependency so that the remote sample will be built and flashed first +# Add dependencies so that the remote sample will be built first # This is required because some primary cores need information from the # remote core's build, such as the output image's LMA add_dependencies(ipm_mcux ipm_mcux_remote) -# Place remote image first in the image list -set(IMAGES "ipm_mcux_remote" ${IMAGES}) +sysbuild_add_dependencies(CONFIGURE ipm_mcux ipm_mcux_remote) + +if(SB_CONFIG_BOOTLOADER_MCUBOOT) + # Make sure MCUboot is flashed first + sysbuild_add_dependencies(FLASH ipm_mcux_remote mcuboot) +endif() diff --git a/samples/subsys/ipc/openamp/sysbuild.cmake b/samples/subsys/ipc/openamp/sysbuild.cmake index 84df278aba3c2e..7278a1af4d46e9 100644 --- a/samples/subsys/ipc/openamp/sysbuild.cmake +++ b/samples/subsys/ipc/openamp/sysbuild.cmake @@ -9,9 +9,13 @@ ExternalZephyrProject_Add( BOARD ${SB_CONFIG_OPENAMP_REMOTE_BOARD} ) -# Add a dependency so that the remote sample will be built and flashed first +# Add dependencies so that the remote sample will be built first # This is required because some primary cores need information from the # remote core's build, such as the output image's LMA add_dependencies(openamp openamp_remote) -# Place remote image first in the image list -set(IMAGES "openamp_remote" ${IMAGES}) +sysbuild_add_dependencies(CONFIGURE openamp openamp_remote) + +if(SB_CONFIG_BOOTLOADER_MCUBOOT) + # Make sure MCUboot is flashed first + sysbuild_add_dependencies(FLASH openamp_remote mcuboot) +endif() diff --git a/share/sysbuild/CMakeLists.txt b/share/sysbuild/CMakeLists.txt index b52f1cbabf53f2..d7b9d15f4e7e87 100644 --- a/share/sysbuild/CMakeLists.txt +++ b/share/sysbuild/CMakeLists.txt @@ -24,9 +24,6 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS ${zephyr_modules project(sysbuild LANGUAGES) -# Global list of images enabled in this multi image build system. -set(IMAGES) - get_filename_component(APP_DIR ${APP_DIR} ABSOLUTE) get_filename_component(app_name ${APP_DIR} NAME) @@ -54,7 +51,6 @@ ExternalZephyrProject_Add( SOURCE_DIR ${APP_DIR} APP_TYPE MAIN ) -list(APPEND IMAGES "${app_name}") set(DEFAULT_IMAGE "${app_name}") add_subdirectory(bootloader) @@ -63,6 +59,7 @@ add_subdirectory(bootloader) 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}) @@ -73,6 +70,7 @@ while(NOT "${images_length}" EQUAL "${processed_length}") endif() endforeach() + get_property(IMAGES GLOBAL PROPERTY sysbuild_images) list(LENGTH IMAGES images_length) list(LENGTH images_sysbuild_processed processed_length_new) diff --git a/share/sysbuild/bootloader/CMakeLists.txt b/share/sysbuild/bootloader/CMakeLists.txt index 0d75ee7a407afe..c00a8e977834ce 100644 --- a/share/sysbuild/bootloader/CMakeLists.txt +++ b/share/sysbuild/bootloader/CMakeLists.txt @@ -12,7 +12,7 @@ if(SB_CONFIG_BOOTLOADER_MCUBOOT) ) # MCUBoot default configuration is to perform a full chip erase. # Placing MCUBoot first in list to ensure it is flashed before other images. - set(IMAGES ${image} ${IMAGES} PARENT_SCOPE) + sysbuild_add_dependencies(FLASH ${DEFAULT_IMAGE} ${image}) set_config_string(${image} CONFIG_BOOT_SIGNATURE_KEY_FILE "${SB_CONFIG_BOOT_SIGNATURE_KEY_FILE}") endif() diff --git a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake index a399e643d6cda0..fd776f8e4edcad 100644 --- a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake +++ b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake @@ -155,6 +155,11 @@ function(ExternalZephyrProject_Add) endif() + set_property( + GLOBAL + APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} + ) + set(sysbuild_image_conf_dir ${APP_DIR}/sysbuild) set(sysbuild_image_name_conf_dir ${APP_DIR}/sysbuild/${ZBUILD_APPLICATION}) # User defined `-D_CONF_FILE=` takes precedence over anything else. diff --git a/tests/boot/test_mcuboot/sysbuild.cmake b/tests/boot/test_mcuboot/sysbuild.cmake index 0c5ebac2690c53..dc5165114b583a 100644 --- a/tests/boot/test_mcuboot/sysbuild.cmake +++ b/tests/boot/test_mcuboot/sysbuild.cmake @@ -15,11 +15,12 @@ ExternalZephyrProject_Add( ) # Add the swapped app to the list of images to flash -# Ensure the order of images is as follows: +# Ensure the flashing order of images is as follows: # - mcuboot # - swapped app # - primary app (test_mcuboot) # This order means that if the debugger resets the MCU in between flash # iterations, the MCUBoot swap won't be triggered until the secondary app # is actually present in flash. -set(IMAGES "mcuboot" "swapped_app" "test_mcuboot") +sysbuild_add_dependencies(FLASH test_mcuboot swapped_app) +sysbuild_add_dependencies(FLASH swapped_app mcuboot) From d77152422774b9fc05c782926a2b2446d596deb9 Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Fri, 25 Aug 2023 12:45:14 +0200 Subject: [PATCH 4/5] 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()` that recursively includes `sysbuild.cmake` files for all images found in ``. With the new function, all images that are expected to be found in a given `` 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. .sysbuild-cmake 3.2. .sysbuild-cmake ... 3.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 --- share/sysbuild/CMakeLists.txt | 54 +----------------- share/sysbuild/Kconfig | 2 +- .../cmake/modules/sysbuild_extensions.cmake | 56 +++++++++++++++++++ share/sysbuild/images/CMakeLists.txt | 36 ++++++++++++ share/sysbuild/images/Kconfig | 5 ++ share/sysbuild/images/boards/CMakeLists.txt | 5 ++ .../{ => images}/bootloader/CMakeLists.txt | 0 .../sysbuild/{ => images}/bootloader/Kconfig | 0 8 files changed, 105 insertions(+), 53 deletions(-) create mode 100644 share/sysbuild/images/CMakeLists.txt create mode 100644 share/sysbuild/images/Kconfig create mode 100644 share/sysbuild/images/boards/CMakeLists.txt rename share/sysbuild/{ => images}/bootloader/CMakeLists.txt (100%) rename share/sysbuild/{ => images}/bootloader/Kconfig (100%) diff --git a/share/sysbuild/CMakeLists.txt b/share/sysbuild/CMakeLists.txt index d7b9d15f4e7e87..7bbfe1387601b4 100644 --- a/share/sysbuild/CMakeLists.txt +++ b/share/sysbuild/CMakeLists.txt @@ -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 allows for board and app specific images to be included. -include(${BOARD_DIR}/sysbuild.cmake OPTIONAL) +# This is where all Zephyr applications are added to the multi-image build. +sysbuild_add_subdirectory(images) -# 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}) diff --git a/share/sysbuild/Kconfig b/share/sysbuild/Kconfig index 74c46ba2f71a58..b08db20e5dfd1d 100644 --- a/share/sysbuild/Kconfig +++ b/share/sysbuild/Kconfig @@ -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" diff --git a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake index fd776f8e4edcad..a3838415eee204 100644 --- a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake +++ b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake @@ -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} ...) 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( []) +# +# This function extends the standard add_subdirectory() command with additional, +# recursive processing of the sysbuild images added via . +# +# After exiting , 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( [ ...]) # diff --git a/share/sysbuild/images/CMakeLists.txt b/share/sysbuild/images/CMakeLists.txt new file mode 100644 index 00000000000000..2b553181d06d86 --- /dev/null +++ b/share/sysbuild/images/CMakeLists.txt @@ -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) diff --git a/share/sysbuild/images/Kconfig b/share/sysbuild/images/Kconfig new file mode 100644 index 00000000000000..616b0a7c23d4be --- /dev/null +++ b/share/sysbuild/images/Kconfig @@ -0,0 +1,5 @@ +# Copyright (c) 2021-2023 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 + +rsource "bootloader/Kconfig" diff --git a/share/sysbuild/images/boards/CMakeLists.txt b/share/sysbuild/images/boards/CMakeLists.txt new file mode 100644 index 00000000000000..663bfc8d10b0a9 --- /dev/null +++ b/share/sysbuild/images/boards/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2021-2023 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 + +include(${BOARD_DIR}/sysbuild.cmake OPTIONAL) diff --git a/share/sysbuild/bootloader/CMakeLists.txt b/share/sysbuild/images/bootloader/CMakeLists.txt similarity index 100% rename from share/sysbuild/bootloader/CMakeLists.txt rename to share/sysbuild/images/bootloader/CMakeLists.txt diff --git a/share/sysbuild/bootloader/Kconfig b/share/sysbuild/images/bootloader/Kconfig similarity index 100% rename from share/sysbuild/bootloader/Kconfig rename to share/sysbuild/images/bootloader/Kconfig From 6b1e4f62159f96a1a70536524410d3e08ea030fa Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Mon, 15 May 2023 11:19:50 +0200 Subject: [PATCH 5/5] doc: sysbuild: Add documentation for image ordering Add a brief subsection to explain `sysbuild_add_dependencies()`. Signed-off-by: Grzegorz Swiderski --- doc/build/sysbuild/index.rst | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/build/sysbuild/index.rst b/doc/build/sysbuild/index.rst index 3435deb4bda88b..2c53cb0e88a712 100644 --- a/doc/build/sysbuild/index.rst +++ b/doc/build/sysbuild/index.rst @@ -639,6 +639,50 @@ configuration files for ``my_sample`` will be ignored. This give you full control on how images are configured when integrating those with ``application``. +.. _sysbuild_zephyr_application_dependencies: + +Adding dependencies among Zephyr applications +============================================= + +Sometimes, in a multi-image build, you may want certain Zephyr applications to +be configured or flashed in a specific order. For example, if you need some +information from one application's build system to be available to another's, +then the first thing to do is to add a configuration dependency between them. +Separately, you can also add flashing dependencies to control the sequence of +images used by ``west flash``; this could be used if a specific flashing order +is required by an SoC, a _runner_, or something else. + +By default, sysbuild will configure and flash applications in the order that +they are added, as ``ExternalZephyrProject_Add()`` calls are processed by CMake. +You can use the ``sysbuild_add_dependencies()`` function to make adjustments to +this order, according to your needs. Its usage is similar to the standard +``add_dependencies()`` function in CMake. + +Here is an example of adding configuration dependencies for ``my_sample``: + +.. code-block:: cmake + + sysbuild_add_dependencies(IMAGE CONFIGURE my_sample sample_a sample_b) + +This will ensure that sysbuild will run CMake for ``sample_a`` and ``sample_b`` +(in some order) before doing the same for ``my_sample``, when building these +domains in a single invocation. + +If you want to add flashing dependencies instead, then do it like this: + +.. code-block:: cmake + + sysbuild_add_dependencies(IMAGE FLASH my_sample sample_a sample_b) + +As a result, ``my_sample`` will be flashed after ``sample_a`` and ``sample_b`` +(in some order), when flashing these domains in a single invocation. + +.. note:: + + Adding flashing dependencies is not allowed for build-only applications. + If ``my_sample`` had been created with ``BUILD_ONLY TRUE``, then the above + call to ``sysbuild_add_dependencies()`` would have produced an error. + Adding non-Zephyr applications to sysbuild ******************************************