# Copyright (c) 2021-2023 Nordic Semiconductor # # SPDX-License-Identifier: Apache-2.0 # Usage: # load_cache(IMAGE BINARY_DIR ) # # This function will load the CMakeCache.txt file from the binary directory # given by the BINARY_DIR argument. # # All CMake cache variables are stored in a custom target which is identified by # the name given as value to the IMAGE argument. # # IMAGE: image name identifying the cache for later sysbuild_get() lookup calls. # BINARY_DIR: binary directory (build dir) containing the CMakeCache.txt file to load. function(load_cache) set(single_args IMAGE BINARY_DIR) cmake_parse_arguments(LOAD_CACHE "" "${single_args}" "" ${ARGN}) if(NOT TARGET ${LOAD_CACHE_IMAGE}_cache) add_custom_target(${LOAD_CACHE_IMAGE}_cache) endif() file(STRINGS "${LOAD_CACHE_BINARY_DIR}/CMakeCache.txt" cache_strings) foreach(str ${cache_strings}) # Using a regex for matching whole 'VAR_NAME:TYPE=VALUE' will strip semi-colons # thus resulting in lists to become strings. # Therefore we first fetch VAR_NAME and TYPE, and afterwards extract # remaining of string into a value that populates the property. # This method ensures that both quoted values and ;-separated list stays intact. string(REGEX MATCH "([^:]*):([^=]*)=" variable_identifier ${str}) if(NOT "${variable_identifier}" STREQUAL "") string(LENGTH ${variable_identifier} variable_identifier_length) string(SUBSTRING "${str}" ${variable_identifier_length} -1 variable_value) set_property(TARGET ${LOAD_CACHE_IMAGE}_cache APPEND PROPERTY "CACHE:VARIABLES" "${CMAKE_MATCH_1}") set_property(TARGET ${LOAD_CACHE_IMAGE}_cache PROPERTY "${CMAKE_MATCH_1}:TYPE" "${CMAKE_MATCH_2}") set_property(TARGET ${LOAD_CACHE_IMAGE}_cache PROPERTY "${CMAKE_MATCH_1}" "${variable_value}") if("${CMAKE_MATCH_1}" MATCHES "^BYPRODUCT_.*") set_property(TARGET ${LOAD_CACHE_IMAGE}_cache APPEND PROPERTY "EXTRA_BYPRODUCTS" "${variable_value}" ) endif() endif() endforeach() endfunction() # Usage: # sysbuild_get( IMAGE [VAR ] ) # # This function will return the variable found in the CMakeCache.txt file # identified by image. # If `VAR` is provided, the name given as parameter will be looked up, but if # `VAR` is not given, the `` name provided will be used both for # lookup and value return. # # The result will be returned in ``. # # Example use: # sysbuild_get(PROJECT_NAME IMAGE my_sample CACHE) # will lookup PROJECT_NAME from the CMakeCache identified by `my_sample` and # and return the value in the local variable `PROJECT_NAME`. # # sysbuild_get(my_sample_PROJECT_NAME IMAGE my_sample VAR PROJECT_NAME CACHE) # will lookup PROJECT_NAME from the CMakeCache identified by `my_sample` and # and return the value in the local variable `my_sample_PROJECT_NAME`. # # sysbuild_get(my_sample_CONFIG_FOO IMAGE my_sample VAR CONFIG_FOO KCONFIG) # will lookup CONFIG_FOO from the KConfig identified by `my_sample` and # and return the value in the local variable `my_sample_CONFIG_FOO`. # # : variable used for returning CMake cache value. Also used as lookup # variable if `VAR` is not provided. # IMAGE: image name identifying the cache to use for variable lookup. # VAR: name of the CMake cache variable name to lookup. # KCONFIG: Flag indicating that a Kconfig setting should be fetched. # CACHE: Flag indicating that a CMake cache variable should be fetched. function(sysbuild_get variable) cmake_parse_arguments(GET_VAR "CACHE;KCONFIG" "IMAGE;VAR" "" ${ARGN}) if(NOT DEFINED GET_VAR_IMAGE) message(FATAL_ERROR "sysbuild_get(...) requires IMAGE.") endif() if(DEFINED ${variable} AND NOT DEFINED GET_VAR_VAR) message(WARNING "Return variable ${variable} already defined with a value. " "sysbuild_get(${variable} ...) may overwrite existing value. " "Please use sysbuild_get( ... VAR ) " "where is undefined." ) endif() if(NOT DEFINED GET_VAR_VAR) set(GET_VAR_VAR ${variable}) endif() if(GET_VAR_KCONFIG) set(variable_target ${GET_VAR_IMAGE}) elseif(GET_VAR_CACHE) set(variable_target ${GET_VAR_IMAGE}_cache) else() message(WARNING " or not specified, defaulting to CACHE") set(variable_target ${GET_VAR_IMAGE}_cache) endif() get_property(${GET_VAR_IMAGE}_${GET_VAR_VAR} TARGET ${variable_target} PROPERTY ${GET_VAR_VAR}) if(DEFINED ${GET_VAR_IMAGE}_${GET_VAR_VAR}) set(${variable} ${${GET_VAR_IMAGE}_${GET_VAR_VAR}} PARENT_SCOPE) endif() endfunction() # Usage: # ExternalZephyrProject_Add(APPLICATION # SOURCE_DIR # [BOARD [BOARD_REVISION ]] # [APP_TYPE ] # ) # # This function includes a Zephyr based build system into the multiimage # build system # # APPLICATION: : Name of the application, name will also be used for build # folder of the application # SOURCE_DIR : Source directory of the application # BOARD : Use for application build instead user defined BOARD. # BOARD_REVISION : Use of for application (only valid if # is also supplied). # APP_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= will be passed on to the # MAIN_APP unmodified. # BOOTLOADER indicates this app is a bootloader # BUILD_ONLY : Mark the application as build-only. If evaluates to # true, then this application will be excluded from flashing # and debugging. # function(ExternalZephyrProject_Add) set(app_types MAIN BOOTLOADER) cmake_parse_arguments(ZBUILD "" "APPLICATION;BOARD;BOARD_REVISION;SOURCE_DIR;APP_TYPE;BUILD_ONLY" "" ${ARGN}) if(ZBUILD_UNPARSED_ARGUMENTS) message(FATAL_ERROR "ExternalZephyrProject_Add(${ARGV0} ...) given unknown arguments:" " ${ZBUILD_UNPARSED_ARGUMENTS}" ) 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 "ExternalZephyrProject_Add(APP_TYPE ...) given unknown type: ${ZBUILD_APP_TYPE}\n" "Valid types are: ${app_types}" ) endif() 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} ) 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. if (NOT ${ZBUILD_APPLICATION}_CONF_FILE) if(EXISTS ${sysbuild_image_name_conf_dir}) set(${ZBUILD_APPLICATION}_APPLICATION_CONFIG_DIR ${sysbuild_image_name_conf_dir} CACHE INTERNAL "Application configuration dir controlled by sysbuild" ) endif() # Check for sysbuild related configuration fragments. # The contents of these are appended to the image existing configuration # when user is not specifying custom fragments. if(NOT "${CONF_FILE_BUILD_TYPE}" STREQUAL "") set(sysbuild_image_conf_fragment ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}_${CONF_FILE_BUILD_TYPE}.conf) else() set(sysbuild_image_conf_fragment ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}.conf) endif() if (NOT (${ZBUILD_APPLICATION}_OVERLAY_CONFIG OR ${ZBUILD_APPLICATION}_EXTRA_CONF_FILE) AND EXISTS ${sysbuild_image_conf_fragment} ) set(${ZBUILD_APPLICATION}_EXTRA_CONF_FILE ${sysbuild_image_conf_fragment} CACHE INTERNAL "Kconfig fragment defined by main application" ) endif() # Check for overlay named .overlay. set(sysbuild_image_dts_overlay ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}.overlay) if (NOT ${ZBUILD_APPLICATION}_DTC_OVERLAY_FILE AND EXISTS ${sysbuild_image_dts_overlay}) set(${ZBUILD_APPLICATION}_DTC_OVERLAY_FILE ${sysbuild_image_dts_overlay} CACHE INTERNAL "devicetree overlay file defined by main application" ) endif() endif() # Update ROOT variables with relative paths to use absolute paths based on # the source application directory. foreach(type MODULE_EXT BOARD SOC ARCH SCA) if(DEFINED CACHE{${ZBUILD_APPLICATION}_${type}_ROOT} AND NOT IS_ABSOLUTE $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) set(rel_path $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) cmake_path(ABSOLUTE_PATH rel_path BASE_DIRECTORY "${ZBUILD_SOURCE_DIR}" NORMALIZE OUTPUT_VARIABLE abs_path) set(${ZBUILD_APPLICATION}_${type}_ROOT ${abs_path} CACHE PATH "Sysbuild adjusted absolute path" FORCE) endif() endforeach() # CMake variables which must be known by all Zephyr CMake build systems # Those are settings which controls the build and must be known to CMake at # invocation time, and thus cannot be passed though the sysbuild cache file. set( shared_cmake_variables_list CMAKE_BUILD_TYPE CMAKE_VERBOSE_MAKEFILE ) set(sysbuild_cache_file ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}_sysbuild_cache.txt) set(shared_cmake_vars_argument) foreach(shared_var ${shared_cmake_variables_list}) if(DEFINED CACHE{${ZBUILD_APPLICATION}_${shared_var}}) get_property(var_type CACHE ${ZBUILD_APPLICATION}_${shared_var} PROPERTY TYPE) list(APPEND shared_cmake_vars_argument "-D${shared_var}:${var_type}=$CACHE{${ZBUILD_APPLICATION}_${shared_var}}" ) elseif(DEFINED CACHE{${shared_var}}) get_property(var_type CACHE ${shared_var} PROPERTY TYPE) list(APPEND shared_cmake_vars_argument "-D${shared_var}:${var_type}=$CACHE{${shared_var}}" ) endif() endforeach() foreach(kconfig_target menuconfig hardenconfig guiconfig ${EXTRA_KCONFIG_TARGETS} ) if(NOT ZBUILD_APP_TYPE STREQUAL "MAIN") set(image_prefix "${ZBUILD_APPLICATION}_") endif() add_custom_target(${image_prefix}${kconfig_target} ${CMAKE_MAKE_PROGRAM} ${kconfig_target} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION} USES_TERMINAL ) endforeach() include(ExternalProject) set(application_binary_dir ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}) ExternalProject_Add( ${ZBUILD_APPLICATION} SOURCE_DIR ${ZBUILD_SOURCE_DIR} BINARY_DIR ${application_binary_dir} CONFIGURE_COMMAND "" CMAKE_ARGS -DSYSBUILD:BOOL=True -DSYSBUILD_CACHE:FILEPATH=${sysbuild_cache_file} ${shared_cmake_vars_argument} BUILD_COMMAND ${CMAKE_COMMAND} --build . INSTALL_COMMAND "" BUILD_ALWAYS True USES_TERMINAL_BUILD True ) set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY APP_TYPE ${ZBUILD_APP_TYPE}) set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY CONFIG "# sysbuild controlled configuration settings\n" ) set_target_properties(${ZBUILD_APPLICATION} PROPERTIES CACHE_FILE ${sysbuild_cache_file}) set_target_properties(${ZBUILD_APPLICATION} PROPERTIES KCONFIG_BINARY_DIR ${application_binary_dir}/Kconfig ) if("${ZBUILD_APP_TYPE}" STREQUAL "MAIN") set_target_properties(${ZBUILD_APPLICATION} PROPERTIES MAIN_APP True) endif() if(DEFINED ZBUILD_APP_TYPE) set(image_default "${CMAKE_SOURCE_DIR}/image_configurations/${ZBUILD_APP_TYPE}_image_default.cmake") set_target_properties(${ZBUILD_APPLICATION} PROPERTIES IMAGE_CONF_SCRIPT ${image_default}) endif() if(DEFINED ZBUILD_BOARD) # Only set image specific board if provided. # The sysbuild BOARD is exported through sysbuild cache, and will be used # unless _BOARD is defined. if(DEFINED ZBUILD_BOARD_REVISION) # Use provided board revision set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${ZBUILD_BOARD}@${ZBUILD_BOARD_REVISION}) else() set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${ZBUILD_BOARD}) endif() elseif(DEFINED ZBUILD_BOARD_REVISION) message(FATAL_ERROR "ExternalZephyrProject_Add(... BOARD_REVISION ${ZBUILD_BOARD_REVISION})" " requires BOARD." ) endif() if(DEFINED ZBUILD_BUILD_ONLY) set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BUILD_ONLY ${ZBUILD_BUILD_ONLY}) endif() endfunction() # Usage: # ExternalZephyrProject_Cmake(APPLICATION ) # # This function invokes the CMake configure step on an external Zephyr project # which has been added at an earlier stage using `ExternalZephyrProject_Add()` # # If the application is not due to ExternalZephyrProject_Add() being called, # then an error is raised. # # The image output files are added as target properties on the image target as: # ELF_OUT: property specifying the generated elf file. # BIN_OUT: property specifying the generated bin file. # HEX_OUT: property specifying the generated hex file. # S19_OUT: property specifying the generated s19 file. # UF2_OUT: property specifying the generated uf2 file. # EXE_OUT: property specifying the generated exe file. # # the property is only set if the image is configured to generate the output # format. Elf files are always created. # # APPLICATION: : Name of the application. # function(ExternalZephyrProject_Cmake) cmake_parse_arguments(ZCMAKE "" "APPLICATION" "" ${ARGN}) if(ZBUILD_UNPARSED_ARGUMENTS) message(FATAL_ERROR "ExternalZephyrProject_Cmake(${ARGV0} ...) given unknown arguments:" " ${ZBUILD_UNPARSED_ARGUMENTS}" ) endif() if(NOT DEFINED ZCMAKE_APPLICATION) message(FATAL_ERROR "Missing required argument: APPLICATION") endif() if(NOT TARGET ${ZCMAKE_APPLICATION}) message(FATAL_ERROR "${ZCMAKE_APPLICATION} does not exists. Remember to call " "ExternalZephyrProject_Add(APPLICATION ${ZCMAKE_APPLICATION} ...) first." ) endif() set(image_banner "* Running CMake for ${ZCMAKE_APPLICATION} *") string(LENGTH "${image_banner}" image_banner_width) string(REPEAT "*" ${image_banner_width} image_banner_header) message(STATUS "\n ${image_banner_header}\n" " ${image_banner}\n" " ${image_banner_header}\n" ) ExternalProject_Get_Property(${ZCMAKE_APPLICATION} SOURCE_DIR BINARY_DIR CMAKE_ARGS) get_target_property(${ZCMAKE_APPLICATION}_CACHE_FILE ${ZCMAKE_APPLICATION} CACHE_FILE) get_target_property(${ZCMAKE_APPLICATION}_BOARD ${ZCMAKE_APPLICATION} BOARD) get_target_property(${ZCMAKE_APPLICATION}_MAIN_APP ${ZCMAKE_APPLICATION} MAIN_APP) get_property(${ZCMAKE_APPLICATION}_CONF_SCRIPT TARGET ${ZCMAKE_APPLICATION} PROPERTY IMAGE_CONF_SCRIPT ) # Update ROOT variables with relative paths to use absolute paths based on # the source application directory. foreach(type MODULE_EXT BOARD SOC ARCH SCA) if(DEFINED CACHE{${type}_ROOT} AND NOT IS_ABSOLUTE $CACHE{${type}_ROOT}) set(rel_path $CACHE{${type}_ROOT}) cmake_path(ABSOLUTE_PATH rel_path BASE_DIRECTORY "${APP_DIR}" NORMALIZE OUTPUT_VARIABLE abs_path) set(${type}_ROOT ${abs_path} CACHE PATH "Sysbuild adjusted absolute path" FORCE) endif() endforeach() get_cmake_property(sysbuild_cache CACHE_VARIABLES) foreach(var_name ${sysbuild_cache}) if(NOT "${var_name}" MATCHES "^(CMAKE_.*|BOARD)$") # Perform a dummy read to prevent a false warning about unused variables # being emitted due to a cmake bug: https://gitlab.kitware.com/cmake/cmake/-/issues/24555 set(unused_tmp_var ${${var_name}}) # We don't want to pass internal CMake variables. # Required CMake variable to be passed, like CMAKE_BUILD_TYPE must be # passed using `-D` on command invocation. get_property(var_type CACHE ${var_name} PROPERTY TYPE) set(cache_entry "${var_name}:${var_type}=$CACHE{${var_name}}") string(REPLACE ";" "\;" cache_entry "${cache_entry}") list(APPEND sysbuild_cache_strings "${cache_entry}\n") endif() endforeach() if(DEFINED BOARD_REVISION) list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}@${BOARD_REVISION}\n") else() list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}\n") endif() list(APPEND sysbuild_cache_strings "SYSBUILD_NAME:STRING=${ZCMAKE_APPLICATION}\n") if(${ZCMAKE_APPLICATION}_MAIN_APP) list(APPEND sysbuild_cache_strings "SYSBUILD_MAIN_APP:BOOL=True\n") endif() if(${ZCMAKE_APPLICATION}_BOARD AND NOT DEFINED CACHE{${ZCMAKE_APPLICATION}_BOARD}) # Only set image specific board if provided. # The sysbuild BOARD is exported through sysbuild cache, and will be used # unless _BOARD is defined. list(APPEND sysbuild_cache_strings "${ZCMAKE_APPLICATION}_BOARD:STRING=${${ZCMAKE_APPLICATION}_BOARD}\n" ) endif() file(WRITE ${${ZCMAKE_APPLICATION}_CACHE_FILE}.tmp ${sysbuild_cache_strings}) zephyr_file_copy(${${ZCMAKE_APPLICATION}_CACHE_FILE}.tmp ${${ZCMAKE_APPLICATION}_CACHE_FILE} ONLY_IF_DIFFERENT ) foreach(script ${${ZCMAKE_APPLICATION}_CONF_SCRIPT}) include(${script}) endforeach() set(dotconfigsysbuild ${BINARY_DIR}/zephyr/.config.sysbuild) get_target_property(config_content ${ZCMAKE_APPLICATION} CONFIG) string(CONFIGURE "${config_content}" config_content) file(WRITE ${dotconfigsysbuild} ${config_content}) execute_process( COMMAND ${CMAKE_COMMAND} -G${CMAKE_GENERATOR} ${CMAKE_ARGS} -DFORCED_CONF_FILE:FILEPATH=${dotconfigsysbuild} -B${BINARY_DIR} -S${SOURCE_DIR} RESULT_VARIABLE return_val WORKING_DIRECTORY ${BINARY_DIR} ) if(return_val) message(FATAL_ERROR "CMake configure failed for Zephyr project: ${ZCMAKE_APPLICATION}\n" "Location: ${SOURCE_DIR}" ) endif() load_cache(IMAGE ${ZCMAKE_APPLICATION} BINARY_DIR ${BINARY_DIR}) import_kconfig(CONFIG_ ${BINARY_DIR}/zephyr/.config TARGET ${ZCMAKE_APPLICATION}) # This custom target informs CMake how the BYPRODUCTS are generated if a target # depends directly on the BYPRODUCT instead of depending on the image target. get_target_property(${ZCMAKE_APPLICATION}_byproducts ${ZCMAKE_APPLICATION}_cache EXTRA_BYPRODUCTS) add_custom_target(${ZCMAKE_APPLICATION}_extra_byproducts COMMAND ${CMAKE_COMMAND} -E true BYPRODUCTS ${${ZCMAKE_APPLICATION}_byproducts} DEPENDS ${ZCMAKE_APPLICATION} ) endfunction() # Usage: # sysbuild_module_call( MODULES [IMAGES ] [EXTRA_ARGS ]) # # This function invokes the sysbuild hook provided as for . # # If `IMAGES` is passed, then the provided list of of images will be passed to # the hook. # # `EXTRA_ARGS` can be used to pass extra arguments to the hook. # # Valid values: # PRE_CMAKE : Invoke pre-CMake call for modules before CMake configure is invoked for images # POST_CMAKE : Invoke post-CMake call for modules after CMake configure has been invoked for images # PRE_DOMAINS : Invoke pre-domains call for modules before creating domains yaml. # POST_DOMAINS: Invoke post-domains call for modules after creation of domains yaml. # function(sysbuild_module_call) set(options "PRE_CMAKE;POST_CMAKE;PRE_DOMAINS;POST_DOMAINS") set(multi_args "MODULES;IMAGES;EXTRA_ARGS") cmake_parse_arguments(SMC "${options}" "${test_args}" "${multi_args}" ${ARGN}) zephyr_check_flags_required("sysbuild_module_call" SMC ${options}) zephyr_check_flags_exclusive("sysbuild_module_call" SMC ${options}) foreach(call ${options}) if(SMC_${call}) foreach(module ${SMC_MODULES}) if(COMMAND ${module}_${call}) cmake_language(CALL ${module}_${call} IMAGES ${SMC_IMAGES} ${SMC_EXTRA_ARGS}) endif() endforeach() endif() endforeach() endfunction() # Usage: # sysbuild_cache_set(VAR [APPEND [REMOVE_DUPLICATES]] ) # # This function will set the specified value of the sysbuild cache variable in # the CMakeCache.txt file which can then be accessed by images. # `VAR` specifies the variable name to set/update. # # The result will be returned in ``. # # Example use: # sysbuild_cache_set(VAR ATTRIBUTES APPEND REMOVE_DUPLICATES battery) # Will add the item `battery` to the `ATTRIBUTES` variable as a new element # in the list in the CMakeCache and remove any duplicates from the list. # # : Name of variable in CMake cache. # APPEND: If specified then will append the supplied data to the # existing value as a list. # REMOVE_DUPLICATES: If specified then remove duplicate entries contained # within the list before saving to the cache. # : Value to set/update. function(sysbuild_cache_set) cmake_parse_arguments(VARS "APPEND;REMOVE_DUPLICATES" "VAR" "" ${ARGN}) zephyr_check_arguments_required(sysbuild_cache_set VARS VAR) if(NOT VARS_UNPARSED_ARGUMENTS AND VARS_APPEND) # Nothing to append so do nothing return() elseif(VARS_REMOVE_DUPLICATES AND NOT VARS_APPEND) message(FATAL_ERROR "sysbuild_set(VAR APPEND REMOVE_DUPLICATES ...) missing required APPEND option") endif() get_property(var_type CACHE ${VARS_VAR} PROPERTY TYPE) get_property(var_help CACHE ${VARS_VAR} PROPERTY HELPSTRING) # If the variable type is not set, use UNINITIALIZED which will not apply any # specific formatting. if(NOT var_type) set(var_type "UNINITIALIZED") endif() if(VARS_APPEND) set(var_new "$CACHE{${VARS_VAR}}") # Search for these exact items in the existing value and prevent adding # them if they are already present which avoids issues with double addition # when cmake is reran. string(FIND "$CACHE{${VARS_VAR}}" "${VARS_UNPARSED_ARGUMENTS}" index) if(NOT ${index} EQUAL -1) return() endif() list(APPEND var_new "${VARS_UNPARSED_ARGUMENTS}") if(VARS_REMOVE_DUPLICATES) list(REMOVE_DUPLICATES var_new) endif() else() set(var_new "${VARS_UNPARSED_ARGUMENTS}") endif() set(${VARS_VAR} "${var_new}" CACHE "${var_type}" "${var_help}" FORCE) endfunction() function(set_config_bool image setting value) if(${value}) set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=y\n") else() set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=n\n") endif() endfunction() 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( [ ...]) # # 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()