1# Copyright (c) 2021-2023 Nordic Semiconductor 2# 3# SPDX-License-Identifier: Apache-2.0 4 5# Usage: 6# load_cache(IMAGE <image> BINARY_DIR <dir>) 7# 8# This function will load the CMakeCache.txt file from the binary directory 9# given by the BINARY_DIR argument. 10# 11# All CMake cache variables are stored in a custom target which is identified by 12# the name given as value to the IMAGE argument. 13# 14# IMAGE: image name identifying the cache for later sysbuild_get() lookup calls. 15# BINARY_DIR: binary directory (build dir) containing the CMakeCache.txt file to load. 16function(load_cache) 17 set(single_args IMAGE BINARY_DIR) 18 cmake_parse_arguments(LOAD_CACHE "" "${single_args}" "" ${ARGN}) 19 20 if(NOT TARGET ${LOAD_CACHE_IMAGE}_cache) 21 add_custom_target(${LOAD_CACHE_IMAGE}_cache) 22 endif() 23 file(STRINGS "${LOAD_CACHE_BINARY_DIR}/CMakeCache.txt" cache_strings) 24 foreach(str ${cache_strings}) 25 # Using a regex for matching whole 'VAR_NAME:TYPE=VALUE' will strip semi-colons 26 # thus resulting in lists to become strings. 27 # Therefore we first fetch VAR_NAME and TYPE, and afterwards extract 28 # remaining of string into a value that populates the property. 29 # This method ensures that both quoted values and ;-separated list stays intact. 30 string(REGEX MATCH "([^:]*):([^=]*)=" variable_identifier ${str}) 31 if(NOT "${variable_identifier}" STREQUAL "") 32 string(LENGTH ${variable_identifier} variable_identifier_length) 33 string(SUBSTRING "${str}" ${variable_identifier_length} -1 variable_value) 34 set_property(TARGET ${LOAD_CACHE_IMAGE}_cache APPEND PROPERTY "CACHE:VARIABLES" "${CMAKE_MATCH_1}") 35 set_property(TARGET ${LOAD_CACHE_IMAGE}_cache PROPERTY "${CMAKE_MATCH_1}:TYPE" "${CMAKE_MATCH_2}") 36 set_property(TARGET ${LOAD_CACHE_IMAGE}_cache PROPERTY "${CMAKE_MATCH_1}" "${variable_value}") 37 if("${CMAKE_MATCH_1}" MATCHES "^BYPRODUCT_.*") 38 set_property(TARGET ${LOAD_CACHE_IMAGE}_cache APPEND 39 PROPERTY "EXTRA_BYPRODUCTS" "${variable_value}" 40 ) 41 endif() 42 endif() 43 endforeach() 44endfunction() 45 46# Usage: 47# sysbuild_get(<variable> IMAGE <image> [VAR <image-variable>] <KCONFIG|CACHE>) 48# 49# This function will return the variable found in the CMakeCache.txt file 50# identified by image. 51# If `VAR` is provided, the name given as parameter will be looked up, but if 52# `VAR` is not given, the `<variable>` name provided will be used both for 53# lookup and value return. 54# 55# The result will be returned in `<variable>`. 56# 57# Example use: 58# sysbuild_get(PROJECT_NAME IMAGE my_sample CACHE) 59# will lookup PROJECT_NAME from the CMakeCache identified by `my_sample` and 60# and return the value in the local variable `PROJECT_NAME`. 61# 62# sysbuild_get(my_sample_PROJECT_NAME IMAGE my_sample VAR PROJECT_NAME CACHE) 63# will lookup PROJECT_NAME from the CMakeCache identified by `my_sample` and 64# and return the value in the local variable `my_sample_PROJECT_NAME`. 65# 66# sysbuild_get(my_sample_CONFIG_FOO IMAGE my_sample VAR CONFIG_FOO KCONFIG) 67# will lookup CONFIG_FOO from the KConfig identified by `my_sample` and 68# and return the value in the local variable `my_sample_CONFIG_FOO`. 69# 70# <variable>: variable used for returning CMake cache value. Also used as lookup 71# variable if `VAR` is not provided. 72# IMAGE: image name identifying the cache to use for variable lookup. 73# VAR: name of the CMake cache variable name to lookup. 74# KCONFIG: Flag indicating that a Kconfig setting should be fetched. 75# CACHE: Flag indicating that a CMake cache variable should be fetched. 76function(sysbuild_get variable) 77 cmake_parse_arguments(GET_VAR "CACHE;KCONFIG" "IMAGE;VAR" "" ${ARGN}) 78 79 if(NOT DEFINED GET_VAR_IMAGE) 80 message(FATAL_ERROR "sysbuild_get(...) requires IMAGE.") 81 endif() 82 83 if(DEFINED ${variable} AND NOT DEFINED GET_VAR_VAR) 84 message(WARNING "Return variable ${variable} already defined with a value. " 85 "sysbuild_get(${variable} ...) may overwrite existing value. " 86 "Please use sysbuild_get(<variable> ... VAR <image-variable>) " 87 "where <variable> is undefined." 88 ) 89 endif() 90 91 if(NOT DEFINED GET_VAR_VAR) 92 set(GET_VAR_VAR ${variable}) 93 endif() 94 95 if(GET_VAR_KCONFIG) 96 set(variable_target ${GET_VAR_IMAGE}) 97 elseif(GET_VAR_CACHE) 98 set(variable_target ${GET_VAR_IMAGE}_cache) 99 else() 100 message(WARNING "<CACHE> or <KCONFIG> not specified, defaulting to CACHE") 101 set(variable_target ${GET_VAR_IMAGE}_cache) 102 endif() 103 104 get_property(${GET_VAR_IMAGE}_${GET_VAR_VAR} TARGET ${variable_target} PROPERTY ${GET_VAR_VAR}) 105 if(DEFINED ${GET_VAR_IMAGE}_${GET_VAR_VAR}) 106 set(${variable} ${${GET_VAR_IMAGE}_${GET_VAR_VAR}} PARENT_SCOPE) 107 endif() 108endfunction() 109 110# Usage: 111# ExternalZephyrProject_Add(APPLICATION <name> 112# SOURCE_DIR <dir> 113# [BOARD <board> [BOARD_REVISION <revision>]] 114# [APP_TYPE <MAIN|BOOTLOADER>] 115# ) 116# 117# This function includes a Zephyr based build system into the multiimage 118# build system 119# 120# APPLICATION: <name>: Name of the application, name will also be used for build 121# folder of the application 122# SOURCE_DIR <dir>: Source directory of the application 123# BOARD <board>: Use <board> for application build instead user defined BOARD. 124# BOARD_REVISION <revision>: Use <revision> of <board> for application (only valid if 125# <board> is also supplied). 126# APP_TYPE <MAIN|BOOTLOADER>: Application type. 127# MAIN indicates this application is the main application 128# and where user defined settings should be passed on as-is 129# except for multi image build flags. 130# For example, -DCONF_FILES=<files> will be passed on to the 131# MAIN_APP unmodified. 132# BOOTLOADER indicates this app is a bootloader 133# BUILD_ONLY <bool>: Mark the application as build-only. If <bool> evaluates to 134# true, then this application will be excluded from flashing 135# and debugging. 136# 137function(ExternalZephyrProject_Add) 138 set(app_types MAIN BOOTLOADER) 139 cmake_parse_arguments(ZBUILD "" "APPLICATION;BOARD;BOARD_REVISION;SOURCE_DIR;APP_TYPE;BUILD_ONLY" "" ${ARGN}) 140 141 if(ZBUILD_UNPARSED_ARGUMENTS) 142 message(FATAL_ERROR 143 "ExternalZephyrProject_Add(${ARGV0} <val> ...) given unknown arguments:" 144 " ${ZBUILD_UNPARSED_ARGUMENTS}" 145 ) 146 endif() 147 148 if(TARGET ${ZBUILD_APPLICATION}) 149 message(FATAL_ERROR 150 "ExternalZephyrProject_Add(APPLICATION ${ZBUILD_APPLICATION} ...) " 151 "already exists. Application names must be unique." 152 ) 153 endif() 154 155 if(DEFINED ZBUILD_APP_TYPE) 156 if(NOT ZBUILD_APP_TYPE IN_LIST app_types) 157 message(FATAL_ERROR 158 "ExternalZephyrProject_Add(APP_TYPE <val> ...) given unknown type: ${ZBUILD_APP_TYPE}\n" 159 "Valid types are: ${app_types}" 160 ) 161 endif() 162 163 endif() 164 165 if(NOT DEFINED SYSBUILD_CURRENT_SOURCE_DIR) 166 message(FATAL_ERROR 167 "ExternalZephyrProject_Add(${ARGV0} <val> ...) must not be called outside of" 168 " sysbuild_add_subdirectory(). SYSBUILD_CURRENT_SOURCE_DIR is undefined." 169 ) 170 endif() 171 set_property( 172 DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" 173 APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} 174 ) 175 set_property( 176 GLOBAL 177 APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} 178 ) 179 180 set(sysbuild_image_conf_dir ${APP_DIR}/sysbuild) 181 set(sysbuild_image_name_conf_dir ${APP_DIR}/sysbuild/${ZBUILD_APPLICATION}) 182 # User defined `-D<image>_CONF_FILE=<file.conf>` takes precedence over anything else. 183 if (NOT ${ZBUILD_APPLICATION}_CONF_FILE) 184 if(EXISTS ${sysbuild_image_name_conf_dir}) 185 set(${ZBUILD_APPLICATION}_APPLICATION_CONFIG_DIR ${sysbuild_image_name_conf_dir} 186 CACHE INTERNAL "Application configuration dir controlled by sysbuild" 187 ) 188 endif() 189 190 # Check for sysbuild related configuration fragments. 191 # The contents of these are appended to the image existing configuration 192 # when user is not specifying custom fragments. 193 if(NOT "${CONF_FILE_BUILD_TYPE}" STREQUAL "") 194 set(sysbuild_image_conf_fragment ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}_${CONF_FILE_BUILD_TYPE}.conf) 195 else() 196 set(sysbuild_image_conf_fragment ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}.conf) 197 endif() 198 199 if (NOT (${ZBUILD_APPLICATION}_OVERLAY_CONFIG OR ${ZBUILD_APPLICATION}_EXTRA_CONF_FILE) 200 AND EXISTS ${sysbuild_image_conf_fragment} 201 ) 202 set(${ZBUILD_APPLICATION}_EXTRA_CONF_FILE ${sysbuild_image_conf_fragment} 203 CACHE INTERNAL "Kconfig fragment defined by main application" 204 ) 205 endif() 206 207 # Check for overlay named <ZBUILD_APPLICATION>.overlay. 208 set(sysbuild_image_dts_overlay ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}.overlay) 209 if (NOT ${ZBUILD_APPLICATION}_DTC_OVERLAY_FILE AND EXISTS ${sysbuild_image_dts_overlay}) 210 set(${ZBUILD_APPLICATION}_DTC_OVERLAY_FILE ${sysbuild_image_dts_overlay} 211 CACHE INTERNAL "devicetree overlay file defined by main application" 212 ) 213 endif() 214 endif() 215 216 # Update ROOT variables with relative paths to use absolute paths based on 217 # the source application directory. 218 foreach(type MODULE_EXT BOARD SOC ARCH SCA) 219 if(DEFINED CACHE{${ZBUILD_APPLICATION}_${type}_ROOT} AND NOT IS_ABSOLUTE $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) 220 set(rel_path $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) 221 cmake_path(ABSOLUTE_PATH rel_path BASE_DIRECTORY "${ZBUILD_SOURCE_DIR}" NORMALIZE OUTPUT_VARIABLE abs_path) 222 set(${ZBUILD_APPLICATION}_${type}_ROOT ${abs_path} CACHE PATH "Sysbuild adjusted absolute path" FORCE) 223 endif() 224 endforeach() 225 226 # CMake variables which must be known by all Zephyr CMake build systems 227 # Those are settings which controls the build and must be known to CMake at 228 # invocation time, and thus cannot be passed though the sysbuild cache file. 229 set( 230 shared_cmake_variables_list 231 CMAKE_BUILD_TYPE 232 CMAKE_VERBOSE_MAKEFILE 233 ) 234 235 set(sysbuild_cache_file ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}_sysbuild_cache.txt) 236 237 set(shared_cmake_vars_argument) 238 foreach(shared_var ${shared_cmake_variables_list}) 239 if(DEFINED CACHE{${ZBUILD_APPLICATION}_${shared_var}}) 240 get_property(var_type CACHE ${ZBUILD_APPLICATION}_${shared_var} PROPERTY TYPE) 241 list(APPEND shared_cmake_vars_argument 242 "-D${shared_var}:${var_type}=$CACHE{${ZBUILD_APPLICATION}_${shared_var}}" 243 ) 244 elseif(DEFINED CACHE{${shared_var}}) 245 get_property(var_type CACHE ${shared_var} PROPERTY TYPE) 246 list(APPEND shared_cmake_vars_argument 247 "-D${shared_var}:${var_type}=$CACHE{${shared_var}}" 248 ) 249 endif() 250 endforeach() 251 252 foreach(kconfig_target 253 menuconfig 254 hardenconfig 255 guiconfig 256 ${EXTRA_KCONFIG_TARGETS} 257 ) 258 259 if(NOT ZBUILD_APP_TYPE STREQUAL "MAIN") 260 set(image_prefix "${ZBUILD_APPLICATION}_") 261 endif() 262 263 add_custom_target(${image_prefix}${kconfig_target} 264 ${CMAKE_MAKE_PROGRAM} ${kconfig_target} 265 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION} 266 USES_TERMINAL 267 ) 268 endforeach() 269 include(ExternalProject) 270 set(application_binary_dir ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}) 271 ExternalProject_Add( 272 ${ZBUILD_APPLICATION} 273 SOURCE_DIR ${ZBUILD_SOURCE_DIR} 274 BINARY_DIR ${application_binary_dir} 275 CONFIGURE_COMMAND "" 276 CMAKE_ARGS -DSYSBUILD:BOOL=True 277 -DSYSBUILD_CACHE:FILEPATH=${sysbuild_cache_file} 278 ${shared_cmake_vars_argument} 279 BUILD_COMMAND ${CMAKE_COMMAND} --build . 280 INSTALL_COMMAND "" 281 BUILD_ALWAYS True 282 USES_TERMINAL_BUILD True 283 ) 284 set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY APP_TYPE ${ZBUILD_APP_TYPE}) 285 set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY CONFIG 286 "# sysbuild controlled configuration settings\n" 287 ) 288 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES CACHE_FILE ${sysbuild_cache_file}) 289 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES KCONFIG_BINARY_DIR 290 ${application_binary_dir}/Kconfig 291 ) 292 if("${ZBUILD_APP_TYPE}" STREQUAL "MAIN") 293 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES MAIN_APP True) 294 endif() 295 296 if(DEFINED ZBUILD_APP_TYPE) 297 set(image_default "${CMAKE_SOURCE_DIR}/image_configurations/${ZBUILD_APP_TYPE}_image_default.cmake") 298 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES IMAGE_CONF_SCRIPT ${image_default}) 299 endif() 300 301 if(DEFINED ZBUILD_BOARD) 302 # Only set image specific board if provided. 303 # The sysbuild BOARD is exported through sysbuild cache, and will be used 304 # unless <image>_BOARD is defined. 305 if(DEFINED ZBUILD_BOARD_REVISION) 306 # Use provided board revision 307 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${ZBUILD_BOARD}@${ZBUILD_BOARD_REVISION}) 308 else() 309 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${ZBUILD_BOARD}) 310 endif() 311 elseif(DEFINED ZBUILD_BOARD_REVISION) 312 message(FATAL_ERROR 313 "ExternalZephyrProject_Add(... BOARD_REVISION ${ZBUILD_BOARD_REVISION})" 314 " requires BOARD." 315 ) 316 endif() 317 318 if(DEFINED ZBUILD_BUILD_ONLY) 319 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BUILD_ONLY ${ZBUILD_BUILD_ONLY}) 320 endif() 321endfunction() 322 323# Usage: 324# ExternalZephyrProject_Cmake(APPLICATION <name>) 325# 326# This function invokes the CMake configure step on an external Zephyr project 327# which has been added at an earlier stage using `ExternalZephyrProject_Add()` 328# 329# If the application is not due to ExternalZephyrProject_Add() being called, 330# then an error is raised. 331# 332# The image output files are added as target properties on the image target as: 333# ELF_OUT: property specifying the generated elf file. 334# BIN_OUT: property specifying the generated bin file. 335# HEX_OUT: property specifying the generated hex file. 336# S19_OUT: property specifying the generated s19 file. 337# UF2_OUT: property specifying the generated uf2 file. 338# EXE_OUT: property specifying the generated exe file. 339# 340# the property is only set if the image is configured to generate the output 341# format. Elf files are always created. 342# 343# APPLICATION: <name>: Name of the application. 344# 345function(ExternalZephyrProject_Cmake) 346 cmake_parse_arguments(ZCMAKE "" "APPLICATION" "" ${ARGN}) 347 348 if(ZBUILD_UNPARSED_ARGUMENTS) 349 message(FATAL_ERROR 350 "ExternalZephyrProject_Cmake(${ARGV0} <val> ...) given unknown arguments:" 351 " ${ZBUILD_UNPARSED_ARGUMENTS}" 352 ) 353 endif() 354 355 if(NOT DEFINED ZCMAKE_APPLICATION) 356 message(FATAL_ERROR "Missing required argument: APPLICATION") 357 endif() 358 359 if(NOT TARGET ${ZCMAKE_APPLICATION}) 360 message(FATAL_ERROR 361 "${ZCMAKE_APPLICATION} does not exists. Remember to call " 362 "ExternalZephyrProject_Add(APPLICATION ${ZCMAKE_APPLICATION} ...) first." 363 ) 364 endif() 365 366 set(image_banner "* Running CMake for ${ZCMAKE_APPLICATION} *") 367 string(LENGTH "${image_banner}" image_banner_width) 368 string(REPEAT "*" ${image_banner_width} image_banner_header) 369 message(STATUS "\n ${image_banner_header}\n" 370 " ${image_banner}\n" 371 " ${image_banner_header}\n" 372 ) 373 374 ExternalProject_Get_Property(${ZCMAKE_APPLICATION} SOURCE_DIR BINARY_DIR CMAKE_ARGS) 375 get_target_property(${ZCMAKE_APPLICATION}_CACHE_FILE ${ZCMAKE_APPLICATION} CACHE_FILE) 376 get_target_property(${ZCMAKE_APPLICATION}_BOARD ${ZCMAKE_APPLICATION} BOARD) 377 get_target_property(${ZCMAKE_APPLICATION}_MAIN_APP ${ZCMAKE_APPLICATION} MAIN_APP) 378 379 get_property(${ZCMAKE_APPLICATION}_CONF_SCRIPT TARGET ${ZCMAKE_APPLICATION} 380 PROPERTY IMAGE_CONF_SCRIPT 381 ) 382 383 # Update ROOT variables with relative paths to use absolute paths based on 384 # the source application directory. 385 foreach(type MODULE_EXT BOARD SOC ARCH SCA) 386 if(DEFINED CACHE{${type}_ROOT} AND NOT IS_ABSOLUTE $CACHE{${type}_ROOT}) 387 set(rel_path $CACHE{${type}_ROOT}) 388 cmake_path(ABSOLUTE_PATH rel_path BASE_DIRECTORY "${APP_DIR}" NORMALIZE OUTPUT_VARIABLE abs_path) 389 set(${type}_ROOT ${abs_path} CACHE PATH "Sysbuild adjusted absolute path" FORCE) 390 endif() 391 endforeach() 392 393 get_cmake_property(sysbuild_cache CACHE_VARIABLES) 394 foreach(var_name ${sysbuild_cache}) 395 if(NOT "${var_name}" MATCHES "^(CMAKE_.*|BOARD)$") 396 # Perform a dummy read to prevent a false warning about unused variables 397 # being emitted due to a cmake bug: https://gitlab.kitware.com/cmake/cmake/-/issues/24555 398 set(unused_tmp_var ${${var_name}}) 399 400 # We don't want to pass internal CMake variables. 401 # Required CMake variable to be passed, like CMAKE_BUILD_TYPE must be 402 # passed using `-D` on command invocation. 403 get_property(var_type CACHE ${var_name} PROPERTY TYPE) 404 set(cache_entry "${var_name}:${var_type}=$CACHE{${var_name}}") 405 string(REPLACE ";" "\;" cache_entry "${cache_entry}") 406 list(APPEND sysbuild_cache_strings "${cache_entry}\n") 407 endif() 408 endforeach() 409 if(DEFINED BOARD_REVISION) 410 list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}@${BOARD_REVISION}\n") 411 else() 412 list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}\n") 413 endif() 414 list(APPEND sysbuild_cache_strings "SYSBUILD_NAME:STRING=${ZCMAKE_APPLICATION}\n") 415 416 if(${ZCMAKE_APPLICATION}_MAIN_APP) 417 list(APPEND sysbuild_cache_strings "SYSBUILD_MAIN_APP:BOOL=True\n") 418 endif() 419 420 if(${ZCMAKE_APPLICATION}_BOARD AND NOT DEFINED CACHE{${ZCMAKE_APPLICATION}_BOARD}) 421 # Only set image specific board if provided. 422 # The sysbuild BOARD is exported through sysbuild cache, and will be used 423 # unless <image>_BOARD is defined. 424 list(APPEND sysbuild_cache_strings 425 "${ZCMAKE_APPLICATION}_BOARD:STRING=${${ZCMAKE_APPLICATION}_BOARD}\n" 426 ) 427 endif() 428 429 file(WRITE ${${ZCMAKE_APPLICATION}_CACHE_FILE}.tmp ${sysbuild_cache_strings}) 430 zephyr_file_copy(${${ZCMAKE_APPLICATION}_CACHE_FILE}.tmp 431 ${${ZCMAKE_APPLICATION}_CACHE_FILE} ONLY_IF_DIFFERENT 432 ) 433 434 foreach(script ${${ZCMAKE_APPLICATION}_CONF_SCRIPT}) 435 include(${script}) 436 endforeach() 437 438 set(dotconfigsysbuild ${BINARY_DIR}/zephyr/.config.sysbuild) 439 get_target_property(config_content ${ZCMAKE_APPLICATION} CONFIG) 440 string(CONFIGURE "${config_content}" config_content) 441 file(WRITE ${dotconfigsysbuild} ${config_content}) 442 443 execute_process( 444 COMMAND ${CMAKE_COMMAND} 445 -G${CMAKE_GENERATOR} 446 ${CMAKE_ARGS} 447 -DFORCED_CONF_FILE:FILEPATH=${dotconfigsysbuild} 448 -B${BINARY_DIR} 449 -S${SOURCE_DIR} 450 RESULT_VARIABLE return_val 451 WORKING_DIRECTORY ${BINARY_DIR} 452 ) 453 454 if(return_val) 455 message(FATAL_ERROR 456 "CMake configure failed for Zephyr project: ${ZCMAKE_APPLICATION}\n" 457 "Location: ${SOURCE_DIR}" 458 ) 459 endif() 460 load_cache(IMAGE ${ZCMAKE_APPLICATION} BINARY_DIR ${BINARY_DIR}) 461 import_kconfig(CONFIG_ ${BINARY_DIR}/zephyr/.config TARGET ${ZCMAKE_APPLICATION}) 462 463 # This custom target informs CMake how the BYPRODUCTS are generated if a target 464 # depends directly on the BYPRODUCT instead of depending on the image target. 465 get_target_property(${ZCMAKE_APPLICATION}_byproducts ${ZCMAKE_APPLICATION}_cache EXTRA_BYPRODUCTS) 466 add_custom_target(${ZCMAKE_APPLICATION}_extra_byproducts 467 COMMAND ${CMAKE_COMMAND} -E true 468 BYPRODUCTS ${${ZCMAKE_APPLICATION}_byproducts} 469 DEPENDS ${ZCMAKE_APPLICATION} 470 ) 471endfunction() 472 473# Usage: 474# sysbuild_module_call(<hook> MODULES <modules> [IMAGES <images>] [EXTRA_ARGS <arguments>]) 475# 476# This function invokes the sysbuild hook provided as <hook> for <modules>. 477# 478# If `IMAGES` is passed, then the provided list of of images will be passed to 479# the hook. 480# 481# `EXTRA_ARGS` can be used to pass extra arguments to the hook. 482# 483# Valid <hook> values: 484# PRE_CMAKE : Invoke pre-CMake call for modules before CMake configure is invoked for images 485# POST_CMAKE : Invoke post-CMake call for modules after CMake configure has been invoked for images 486# PRE_DOMAINS : Invoke pre-domains call for modules before creating domains yaml. 487# POST_DOMAINS: Invoke post-domains call for modules after creation of domains yaml. 488# 489function(sysbuild_module_call) 490 set(options "PRE_CMAKE;POST_CMAKE;PRE_DOMAINS;POST_DOMAINS") 491 set(multi_args "MODULES;IMAGES;EXTRA_ARGS") 492 cmake_parse_arguments(SMC "${options}" "${test_args}" "${multi_args}" ${ARGN}) 493 494 zephyr_check_flags_required("sysbuild_module_call" SMC ${options}) 495 zephyr_check_flags_exclusive("sysbuild_module_call" SMC ${options}) 496 497 foreach(call ${options}) 498 if(SMC_${call}) 499 foreach(module ${SMC_MODULES}) 500 if(COMMAND ${module}_${call}) 501 cmake_language(CALL ${module}_${call} IMAGES ${SMC_IMAGES} ${SMC_EXTRA_ARGS}) 502 endif() 503 endforeach() 504 endif() 505 endforeach() 506endfunction() 507 508# Usage: 509# sysbuild_cache_set(VAR <variable> [APPEND [REMOVE_DUPLICATES]] <value>) 510# 511# This function will set the specified value of the sysbuild cache variable in 512# the CMakeCache.txt file which can then be accessed by images. 513# `VAR` specifies the variable name to set/update. 514# 515# The result will be returned in `<variable>`. 516# 517# Example use: 518# sysbuild_cache_set(VAR ATTRIBUTES APPEND REMOVE_DUPLICATES battery) 519# Will add the item `battery` to the `ATTRIBUTES` variable as a new element 520# in the list in the CMakeCache and remove any duplicates from the list. 521# 522# <variable>: Name of variable in CMake cache. 523# APPEND: If specified then will append the supplied data to the 524# existing value as a list. 525# REMOVE_DUPLICATES: If specified then remove duplicate entries contained 526# within the list before saving to the cache. 527# <value>: Value to set/update. 528function(sysbuild_cache_set) 529 cmake_parse_arguments(VARS "APPEND;REMOVE_DUPLICATES" "VAR" "" ${ARGN}) 530 531 zephyr_check_arguments_required(sysbuild_cache_set VARS VAR) 532 533 if(NOT VARS_UNPARSED_ARGUMENTS AND VARS_APPEND) 534 # Nothing to append so do nothing 535 return() 536 elseif(VARS_REMOVE_DUPLICATES AND NOT VARS_APPEND) 537 message(FATAL_ERROR 538 "sysbuild_set(VAR <var> APPEND REMOVE_DUPLICATES ...) missing required APPEND option") 539 endif() 540 541 get_property(var_type CACHE ${VARS_VAR} PROPERTY TYPE) 542 get_property(var_help CACHE ${VARS_VAR} PROPERTY HELPSTRING) 543 544 # If the variable type is not set, use UNINITIALIZED which will not apply any 545 # specific formatting. 546 if(NOT var_type) 547 set(var_type "UNINITIALIZED") 548 endif() 549 550 if(VARS_APPEND) 551 set(var_new "$CACHE{${VARS_VAR}}") 552 553 # Search for these exact items in the existing value and prevent adding 554 # them if they are already present which avoids issues with double addition 555 # when cmake is reran. 556 string(FIND "$CACHE{${VARS_VAR}}" "${VARS_UNPARSED_ARGUMENTS}" index) 557 558 if(NOT ${index} EQUAL -1) 559 return() 560 endif() 561 562 list(APPEND var_new "${VARS_UNPARSED_ARGUMENTS}") 563 564 if(VARS_REMOVE_DUPLICATES) 565 list(REMOVE_DUPLICATES var_new) 566 endif() 567 else() 568 set(var_new "${VARS_UNPARSED_ARGUMENTS}") 569 endif() 570 571 set(${VARS_VAR} "${var_new}" CACHE "${var_type}" "${var_help}" FORCE) 572endfunction() 573 574function(set_config_bool image setting value) 575 if(${value}) 576 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=y\n") 577 else() 578 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=n\n") 579 endif() 580endfunction() 581 582function(set_config_string image setting value) 583 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=\"${value}\"\n") 584endfunction() 585 586# Usage: 587# sysbuild_add_subdirectory(<source_dir> [<binary_dir>]) 588# 589# This function extends the standard add_subdirectory() command with additional, 590# recursive processing of the sysbuild images added via <source_dir>. 591# 592# After exiting <source_dir>, this function will take every image added so far, 593# and include() its sysbuild.cmake file (if found). If more images get added at 594# this stage, their sysbuild.cmake files will be included as well, and so on. 595# This continues until all expected images have been added, before returning. 596# 597function(sysbuild_add_subdirectory source_dir) 598 if(ARGC GREATER 2) 599 message(FATAL_ERROR 600 "sysbuild_add_subdirectory(...) called with incorrect number of arguments" 601 " (expected at most 2, got ${ARGC})" 602 ) 603 endif() 604 set(binary_dir ${ARGV1}) 605 606 # Update SYSBUILD_CURRENT_SOURCE_DIR in this scope, to support nesting 607 # of sysbuild_add_subdirectory() and even regular add_subdirectory(). 608 cmake_path(ABSOLUTE_PATH source_dir NORMALIZE OUTPUT_VARIABLE SYSBUILD_CURRENT_SOURCE_DIR) 609 add_subdirectory(${source_dir} ${binary_dir}) 610 611 while(TRUE) 612 get_property(added_images DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" PROPERTY sysbuild_images) 613 if(NOT added_images) 614 break() 615 endif() 616 set_property(DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" PROPERTY sysbuild_images "") 617 618 foreach(image ${added_images}) 619 ExternalProject_Get_property(${image} SOURCE_DIR) 620 include(${SOURCE_DIR}/sysbuild.cmake OPTIONAL) 621 endforeach() 622 endwhile() 623endfunction() 624 625# Usage: 626# sysbuild_add_dependencies(<CONFIGURE | FLASH> <image> [<image-dependency> ...]) 627# 628# This function makes an image depend on other images in the configuration or 629# flashing order. Each image named "<image-dependency>" will be ordered before 630# the image named "<image>". 631# 632# CONFIGURE: Add CMake configuration dependencies. This will determine the order 633# in which `ExternalZephyrProject_Cmake()` will be called. 634# FLASH: Add flashing dependencies. This will determine the order in which 635# all images will appear in `domains.yaml`. 636# 637function(sysbuild_add_dependencies dependency_type image) 638 set(valid_dependency_types CONFIGURE FLASH) 639 if(NOT dependency_type IN_LIST valid_dependency_types) 640 list(JOIN valid_dependency_types ", " valid_dependency_types) 641 message(FATAL_ERROR "sysbuild_add_dependencies(...) dependency type " 642 "${dependency_type} must be one of the following: " 643 "${valid_dependency_types}" 644 ) 645 endif() 646 647 if(NOT TARGET ${image}) 648 message(FATAL_ERROR 649 "${image} does not exist. Remember to call " 650 "ExternalZephyrProject_Add(APPLICATION ${image} ...) first." 651 ) 652 endif() 653 654 get_target_property(image_is_build_only ${image} BUILD_ONLY) 655 if(image_is_build_only AND dependency_type STREQUAL "FLASH") 656 message(FATAL_ERROR 657 "sysbuild_add_dependencies(...) cannot add FLASH dependencies to " 658 "BUILD_ONLY image ${image}." 659 ) 660 endif() 661 662 set(property_name ${dependency_type}_DEPENDS) 663 set_property(TARGET ${image} APPEND PROPERTY ${property_name} ${ARGN}) 664endfunction() 665 666# Usage: 667# sysbuild_images_order(<variable> <CONFIGURE | FLASH> IMAGES <images>) 668# 669# This function will sort the provided `<images>` to satisfy the dependencies 670# specified using `sysbuild_add_dependencies()`. The result will be returned in 671# `<variable>`. 672# 673function(sysbuild_images_order variable dependency_type) 674 cmake_parse_arguments(SIS "" "" "IMAGES" ${ARGN}) 675 zephyr_check_arguments_required_all("sysbuild_images_order" SIS IMAGES) 676 677 set(valid_dependency_types CONFIGURE FLASH) 678 if(NOT dependency_type IN_LIST valid_dependency_types) 679 list(JOIN valid_dependency_types ", " valid_dependency_types) 680 message(FATAL_ERROR "sysbuild_images_order(...) dependency type " 681 "${dependency_type} must be one of the following: " 682 "${valid_dependency_types}" 683 ) 684 endif() 685 686 set(property_name ${dependency_type}_DEPENDS) 687 topological_sort(TARGETS ${SIS_IMAGES} PROPERTY_NAME ${property_name} RESULT sorted) 688 set(${variable} ${sorted} PARENT_SCOPE) 689endfunction() 690