.. _sysbuild_images:

Sysbuild images
###############

Sysbuild can be used to add additional images to builds, these can be added by projects or boards
though at present must be Zephyr applications.

Methods of adding images
************************

Images can be added to project or projects in various ways, multiple ways can be used
simultaneously, they can be added in the following ways:

Applications
============

Applications can add sysbuild images using the ``sysbuild.cmake`` file in the application
directory, the inclusion of images can be controlled with a ``Kconfig.sysbuild`` file in the
application directory.

Boards
======

Boards can add sysbuild images by using the ``sysbuild.cmake`` file in the board directory, the
inclusion of images can be controlled with a ``Kconfig.sysbuild`` file in the board directory.

SoCs
====

SoCs can add sysbuild images by using the ``sysbuild.cmake`` file in the soc directory.

Modules
=======

:ref:`modules` can add sysbuild images with the ``sysbuild-cmake`` and ``sysbuild-kconfig``
options in a ``module.yml`` file, see :ref:`sysbuild_module_integration` for details.

Adding images
*************

Images can be added in one of two ways:

Single unchangeable image
=========================

With this setup, the image to be added is fixed to a specific application and cannot be changed
(although it cannot be changed to another image, the version of the image itself can be changed by
use of a west manifest bringing in a different version of the application repo or from an
alternate source, assuming that the image is in it's own repository).

.. note::

   This method should only be used if the image is locked to a specific interface and has no
   extensibility.

An example of how to create such an image, this assumes that a
:ref:`Zephyr application has been created <application>`:

.. tabs::

   .. group-tab:: ``Kconfig.sysbuild``

      .. code-block:: kconfig

         config MY_IMAGE
                 bool "Include my amazing image"
                 help
                   If enabled, will include my amazing image in the build which does...

      .. note::

         Remember to have ``source "share/sysbuild/Kconfig"`` in the file if this
         is being applied in an application ``Kconfig.sysbuild`` file.


   .. group-tab:: ``sysbuild.cmake``

      .. code-block:: cmake

         if(SB_CONFIG_MY_IMAGE)
           ExternalZephyrProject_Add(
             APPLICATION my_image
             SOURCE_DIR ${ZEPHYR_MY_IMAGE_MODULE_DIR}/path/to/my_image
           )
         endif()

Additional dependency ordering can be set here if needed, see
:ref:`sysbuild_zephyr_application_dependencies` for details, image configuration can also be set
here, see :ref:`sysbuild_images_config` for details.

This image can be enabled when building with west like so:

.. zephyr-app-commands::
   :tool: west
   :zephyr-app: <app>
   :board: nrf52840dk/nrf52840
   :goals: build
   :west-args: --sysbuild
   :gen-args: -DSB_CONFIG_MY_IMAGE=y
   :compact:

Extensible changeable image
===========================

With this setup, the image to be added can be one of any number of possible applications which the
user has selection over, this list of selections can also be extended downstream to add additional
options for out-of-tree specific applications that are not available in upstream Zephyr. This is
more complex to create but is the preferred method for adding images to upstream Zephyr.

.. tabs::

   .. group-tab:: ``Kconfig.sysbuild``

      .. code-block:: kconfig

         config SUPPORT_OTHER_APP
                 bool
                 # Conditions can be placed here if this application type is only usable on certain platforms
                 default y

         config SUPPORT_OTHER_APP_MY_IMAGE
                 bool
                 # Conditions can be placed here if this image is only usable on certain platforms
                 default y

         choice OTHER_APP
                 prompt "Other app image"
                 # Defaults can be specified here if a default image should be loaded if e.g. it is supported
                 default OTHER_APP_NONE
                 depends on SUPPORT_OTHER_APP

         config OTHER_APP_IMAGE_NONE
                 bool "None"
                 help
                   Do not Include an other app image in the build.

         config OTHER_APP_IMAGE_MY_IMAGE
                 bool "my_image"
                 depends on SUPPORT_OTHER_APP_MY_IMAGE
                 help
                   Include my amazing image as the other app image to use, which does...

         endchoice

         config OTHER_APP_IMAGE_NAME
                 string
                 default "my_image" if OTHER_APP_IMAGE_MY_IMAGE
                 help
                   Name of other app image.

         config OTHER_APP_IMAGE_PATH
                 string
                 default "${ZEPHYR_MY_IMAGE_MODULE_DIR}/path/to/my_image" if OTHER_APP_IMAGE_MY_IMAGE
                 help
                   Source directory of other app image.

      .. note::

         Remember to have ``source "${ZEPHYR_BASE}/share/sysbuild/Kconfig"`` in the file if this
         is being applied in an application ``Kconfig.sysbuild`` file.

   .. group-tab:: ``sysbuild.cmake``

      .. code-block:: cmake

         if(SB_CONFIG_OTHER_APP_IMAGE_PATH)
           ExternalZephyrProject_Add(
             APPLICATION ${SB_CONFIG_OTHER_APP_IMAGE_NAME}
             SOURCE_DIR ${SB_CONFIG_OTHER_APP_IMAGE_PATH}
           )
         endif()

Additional dependency ordering can be set here if needed, see
:ref:`sysbuild_zephyr_application_dependencies` for details, image configuration can also be set
here, see :ref:`sysbuild_images_config` for details.

This secondary image can be enabled when building with west like so:

.. zephyr-app-commands::
   :tool: west
   :zephyr-app: <app>
   :board: nrf52840dk/nrf52840
   :goals: build
   :west-args: --sysbuild
   :gen-args: -DSB_CONFIG_MY_IMAGE=y
   :compact:

This can then be extended by :ref:`modules` like so:

.. tabs::

   .. group-tab:: ``Kconfig.sysbuild``

      .. code-block:: kconfig

         config SUPPORT_OTHER_APP_MY_SECOND_IMAGE
                 bool
                 default y

         choice OTHER_APP

         config OTHER_APP_IMAGE_MY_SECOND_IMAGE
                 bool "my_second_image"
                 depends on SUPPORT_OTHER_APP_MY_SECOND_IMAGE
                 help
                   Include my other amazing image as the other app image to use, which does...

         endchoice

         config OTHER_APP_IMAGE_NAME
                 default "my_second_image" if OTHER_APP_IMAGE_MY_SECOND_IMAGE

         config OTHER_APP_IMAGE_PATH
                 default "${ZEPHYR_MY_SECOND_IMAGE_MODULE_DIR}/path/to/my_second_image" if OTHER_APP_IMAGE_MY_SECOND_IMAGE

As can be seen, no additional CMake changes are needed to add an alternative image as the base
CMake code will add the replacement image instead of the original image, if selected.

This alternative secondary image can be enabled when building with west like so:

.. zephyr-app-commands::
   :tool: west
   :zephyr-app: <app>
   :board: nrf52840dk/nrf52840
   :goals: build
   :west-args: --sysbuild
   :gen-args: -DSB_CONFIG_MY_SECOND_IMAGE=y
   :compact:

.. _sysbuild_images_config:

Image configuration
*******************

Sysbuild supports being able to set image configuration (Kconfig options) and also supports
reading the output of image configuration (Kconfig) which can be used to allow an
option to be added to sysbuild itself and then to configure it globally or selectively.

Setting image configuration
===========================

Kconfig
-------

Sysbuild can be used to set image configuration **before the CMake configuration of images takes
place**. An important note about setting Kconfig options in images is that these are persistent
and cannot be changed by the images. The following functions can be used to set configuration on
an image:

.. code-block:: cmake

   set_config_bool(<image> CONFIG_<setting> <value>)
   set_config_string(<image> CONFIG_<setting> <value>)
   set_config_int(<image> CONFIG_<setting> <value>)

For example, to change the default image to output a hex file:

.. code-block:: cmake

   set_config_bool(${DEFAULT_IMAGE} CONFIG_BUILD_OUTPUT_HEX y)

These can safely be used in an application, board or SoC ``sysbuild.cmake`` file, as that file is
included before the image CMake processes are invoked. When extending
:ref:`sysbuild using modules <sysbuild_module_integration>` the pre-CMake hook should be used
instead, for example:

.. code-block:: cmake

   function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
     cmake_parse_arguments(PRE_CMAKE "" "" "IMAGES" ${ARGN})

     foreach(image ${PRE_CMAKE_IMAGES})
       set_config_bool(${image} CONFIG_BUILD_OUTPUT_HEX y)
     endforeach()
   endfunction()

Image configuration script
==========================

An image configuration script is a CMake file that can be used to configure an image with common
configuration values, multiple can be used per image, the configuration should be transferrable to
different images to correctly configure them based upon options set in sysbuild. MCUboot
configuration options are configured in both the application the MCUboot image using this method
which allows sysbuild to be the central location for things like the signing key which are then
kept in-sync in the main application bootloader images.

Inside image configuration scripts, the ``ZCMAKE_APPLICATION`` variable is set to the name of the
application being configured, the ``set_config_*`` sysbuild CMake functions can be used to set
configuration and the sysbuild Kconfig can be read, for example:

.. code-block:: cmake

   if(SB_CONFIG_BOOTLOADER_MCUBOOT AND "${SB_CONFIG_SIGNATURE_TYPE}" STREQUAL "NONE")
     set_config_bool(${ZCMAKE_APPLICATION} CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE y)
   endif()

Image configuration script (module/application)
-----------------------------------------------

Module/application image configuration scripts can be set from module or application code, this
must be done in a ``sysbuild.cmake`` file for an application. This can be used to add an image
configuration script as follows:

.. tabs::

   .. group-tab:: ``sysbuild.cmake``

      .. code-block:: cmake

         # This applies the image configuration script to the default image only
         get_property(tmp_conf_scripts TARGET ${DEFAULT_IMAGE} PROPERTY IMAGE_CONF_SCRIPT)
         list(APPEND tmp_conf_scripts "${CMAKE_SOURCE_DIR}/image_configurations/MY_CUSTOM_TYPE_image_default.cmake")
         set_target_properties(${DEFAULT_IMAGE} PROPERTIES IMAGE_CONF_SCRIPT "${tmp_conf_scripts}")


   .. group-tab:: Module CMake

      .. code-block:: cmake

         function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
           cmake_parse_arguments(PRE_CMAKE "" "" "IMAGES" ${ARGN})

           # This applies the image configuration script to all images
           foreach(image ${PRE_CMAKE_IMAGES})
             get_property(tmp_conf_scripts TARGET ${image} PROPERTY IMAGE_CONF_SCRIPT)
             list(APPEND tmp_conf_scripts "${CMAKE_SOURCE_DIR}/image_configurations/MY_CUSTOM_TYPE_image_default.cmake")
             set_target_properties(${image} PROPERTIES IMAGE_CONF_SCRIPT "${tmp_conf_scripts}")
           endforeach()
         endfunction(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)

Image configuration script (Zephyr-wide)
----------------------------------------

Global Zephyr provided image configuration scripts, which allow specifying the type when using
``ExternalZephyrProject_Add()`` require changes to sysbuild code in Zephyr. This should only be
added to when adding a new type that any project should be able to select, generally this should
only be needed for upstream Zephyr though forked versions of Zephyr might use this to add
additional types without restriction.

Image configuration has an allow-list of names which must be set in the Zephyr file
:zephyr_file:`share/sysbuild/cmake/modules/sysbuild_extensions.cmake` in the
``ExternalZephyrProject_Add`` function. After adding a new type, it can be used when adding a
sysbuild image, for example:

.. tabs::

   .. group-tab:: ``sysbuild_extensions.cmake``

      Full file path: ``share/sysbuild/cmake/modules/sysbuild_extensions.cmake``

      .. code-block:: cmake

         # ...
         # Usage:
         #   ExternalZephyrProject_Add(APPLICATION <name>
         #                             SOURCE_DIR <dir>
         #                             [BOARD <board> [BOARD_REVISION <revision>]]
         #                             [APP_TYPE <MAIN|BOOTLOADER|MY_CUSTOM_TYPE>]
         #   )
         # ...
         # APP_TYPE <MAIN|BOOTLOADER|MY_CUSTOM_TYPE>: Application type.
         #                                            MAIN indicates this application is the main application
         #                                            and where user defined settings should be passed on as-is
         #                                            except for multi image build flags.
         #                                            For example, -DCONF_FILES=<files> will be passed on to the
         #                                            MAIN_APP unmodified.
         #                                            BOOTLOADER indicates this app is a bootloader
         #                                            MY_CUSTOM_TYPE indicates this app is...
         # ...
         function(ExternalZephyrProject_Add)
           set(app_types MAIN BOOTLOADER MY_CUSTOM_TYPE)
         # ...

   .. group-tab:: ``sysbuild.cmake``

      .. code-block:: cmake

         if(SB_CONFIG_OTHER_APP_IMAGE_PATH)
           ExternalZephyrProject_Add(
             APPLICATION ${SB_CONFIG_OTHER_APP_IMAGE_NAME}
             SOURCE_DIR ${SB_CONFIG_OTHER_APP_IMAGE_PATH}
             APP_TYPE MY_CUSTOM_TYPE
           )
         endif()


   .. group-tab:: ``MY_CUSTOM_TYPE_image_default.cmake``

      Full file path: ``share/sysbuild/image_configurations/MY_CUSTOM_TYPE_image_default.cmake``

      .. code-block:: cmake

         # Here, the ZCMAKE_APPLICATION variable will be replaced with the image being configured
         set_config_bool(${ZCMAKE_APPLICATION} CONFIG_BUILD_OUTPUT_HEX y)

Reading image configuration
===========================

Kconfig
-------

Kconfig values from images can be read by sysbuild **after the CMake configuration of images has
taken place**. This can be used to check configuration or to adjust additional sysbuild tasks
depending upon configuration. The following function can be used for this purpose:

.. code-block:: cmake

   sysbuild_get(<variable> IMAGE <image> [VAR <image-variable>] KCONFIG)

This function can only be used when
:ref:`sysbuild is extended by a module <sysbuild_module_integration>` or inside of a ``sysbuild/CMakeLists.txt`` file after ``find_package(Sysbuild)`` has been used. An example of outputting
values from all images is shown:

.. tabs::

   .. group-tab:: Module CMake

      .. code-block:: cmake

         function(${SYSBUILD_CURRENT_MODULE_NAME}_post_cmake)
           cmake_parse_arguments(POST_CMAKE "" "" "IMAGES" ${ARGN})

           foreach(image ${POST_CMAKE_IMAGES})
             # Note that the variable to read in to must not be set before using the sysbuild_get() function
             set(tmp_val)
             sysbuild_get(tmp_val IMAGE ${image} VAR CONFIG_BUILD_OUTPUT_HEX KCONFIG)
             message(STATUS "Image ${image} build hex: ${tmp_val}")
           endforeach()
         endfunction()

   .. group-tab:: ``sysbuild/CMakeLists.txt``

      .. code-block:: cmake

         find_package(Sysbuild REQUIRED HINTS $ENV{ZEPHYR_BASE})

         project(sysbuild LANGUAGES)

         sysbuild_get(tmp_val IMAGE ${DEFAULT_IMAGE} VAR CONFIG_BUILD_OUTPUT_HEX KCONFIG)
         message(STATUS "Image ${DEFAULT_IMAGE} build hex: ${tmp_val}")
