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 ENCODING UTF-8) 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# sysbuild_cache(CREATE APPLICATION <name> [CMAKE_RERUN]) 112# 113# This function works on the sysbuild cache for sysbuild managed applications. 114# 115# Arguments: 116# CREATE : Create or update existing sysbuild cache file for the application. 117# The sysbuild cache is only updated if it contain changes. 118# APPLICATION <name>: Name of the application. 119# CMAKE_RERUN : Force a CMake rerun for the application during next build 120# invocation if the sysbuild cache has changed. It is 121# advised to always use this flag. Not using this flag can 122# reduce build time, but only do so if application is 123# guaranteed to be up-to-date. 124# 125function(sysbuild_cache) 126 cmake_parse_arguments(SB_CACHE "CREATE;CMAKE_RERUN" "APPLICATION" "" ${ARGN}) 127 zephyr_check_arguments_required(sysbuild_cache SB_CACHE APPLICATION) 128 zephyr_check_flags_required(sysbuild_cache SB_CACHE CREATE) 129 130 get_target_property(${SB_CACHE_APPLICATION}_MAIN_APP ${SB_CACHE_APPLICATION} MAIN_APP) 131 get_cmake_property(sysbuild_cache CACHE_VARIABLES) 132 133 foreach(var_name ${sysbuild_cache}) 134 if(NOT "${var_name}" MATCHES "^(CMAKE_.*|BOARD)$") 135 # Perform a dummy read to prevent a false warning about unused variables 136 # being emitted due to a cmake bug: https://gitlab.kitware.com/cmake/cmake/-/issues/24555 137 set(unused_tmp_var ${${var_name}}) 138 139 # We don't want to pass internal CMake variables. 140 # Required CMake variable to be passed, like CMAKE_BUILD_TYPE must be 141 # passed using `-D` on command invocation. 142 get_property(var_type CACHE ${var_name} PROPERTY TYPE) 143 set(cache_entry "${var_name}:${var_type}=$CACHE{${var_name}}") 144 string(REPLACE ";" "\;" cache_entry "${cache_entry}") 145 list(APPEND sysbuild_cache_strings "${cache_entry}\n") 146 endif() 147 endforeach() 148 if(DEFINED BOARD_REVISION) 149 list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}@${BOARD_REVISION}${BOARD_QUALIFIERS}\n") 150 else() 151 list(APPEND sysbuild_cache_strings "BOARD:STRING=${BOARD}${BOARD_QUALIFIERS}\n") 152 endif() 153 list(APPEND sysbuild_cache_strings "SYSBUILD_NAME:STRING=${SB_CACHE_APPLICATION}\n") 154 155 if(${SB_CACHE_APPLICATION}_MAIN_APP) 156 list(APPEND sysbuild_cache_strings "SYSBUILD_MAIN_APP:BOOL=True\n") 157 endif() 158 159 if(${SB_CACHE_APPLICATION}_BOARD AND NOT DEFINED CACHE{${SB_CACHE_APPLICATION}_BOARD}) 160 # Only set image specific board if provided. 161 # The sysbuild BOARD is exported through sysbuild cache, and will be used 162 # unless <image>_BOARD is defined. 163 list(APPEND sysbuild_cache_strings 164 "${SB_CACHE_APPLICATION}_BOARD:STRING=${${SB_CACHE_APPLICATION}_BOARD}\n" 165 ) 166 endif() 167 168 get_target_property(${SB_CACHE_APPLICATION}_CACHE_FILE ${SB_CACHE_APPLICATION} CACHE_FILE) 169 file(WRITE ${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp ${sysbuild_cache_strings}) 170 if(SB_CACHE_CMAKE_RERUN) 171 execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files 172 ${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp 173 ${${SB_CACHE_APPLICATION}_CACHE_FILE} 174 RESULT_VARIABLE compare_res 175 ) 176 if(NOT compare_res EQUAL 0) 177 zephyr_file_copy(${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp 178 ${${SB_CACHE_APPLICATION}_CACHE_FILE} 179 ) 180 ExternalProject_Get_Property(${SB_CACHE_APPLICATION} BINARY_DIR) 181 file(TOUCH_NOCREATE ${BINARY_DIR}/CMakeCache.txt) 182 endif() 183 else() 184 zephyr_file_copy(${${SB_CACHE_APPLICATION}_CACHE_FILE}.tmp 185 ${${SB_CACHE_APPLICATION}_CACHE_FILE} ONLY_IF_DIFFERENT 186 ) 187 endif() 188 189endfunction() 190 191# Usage: 192# ExternalZephyrProject_Add(APPLICATION <name> 193# SOURCE_DIR <dir> 194# [BOARD <board> [BOARD_REVISION <revision>]] 195# [APP_TYPE <MAIN|BOOTLOADER>] 196# ) 197# 198# This function includes a Zephyr based build system into the multiimage 199# build system 200# 201# APPLICATION: <name>: Name of the application, name will also be used for build 202# folder of the application 203# SOURCE_DIR <dir>: Source directory of the application 204# BOARD <board>: Use <board> for application build instead user defined BOARD. 205# BOARD_REVISION <revision>: Use <revision> of <board> for application (only valid if 206# <board> is also supplied). 207# APP_TYPE <MAIN|BOOTLOADER>: Application type. 208# MAIN indicates this application is the main application 209# and where user defined settings should be passed on as-is 210# except for multi image build flags. 211# For example, -DCONF_FILES=<files> will be passed on to the 212# MAIN_APP unmodified. 213# BOOTLOADER indicates this app is a bootloader 214# BUILD_ONLY <bool>: Mark the application as build-only. If <bool> evaluates to 215# true, then this application will be excluded from flashing 216# and debugging. 217# 218function(ExternalZephyrProject_Add) 219 set(app_types MAIN BOOTLOADER FIRMWARE_LOADER) 220 cmake_parse_arguments(ZBUILD "" "APPLICATION;BOARD;BOARD_REVISION;SOURCE_DIR;APP_TYPE;BUILD_ONLY" "" ${ARGN}) 221 222 if(ZBUILD_UNPARSED_ARGUMENTS) 223 message(FATAL_ERROR 224 "ExternalZephyrProject_Add(${ARGV0} <val> ...) given unknown arguments:" 225 " ${ZBUILD_UNPARSED_ARGUMENTS}" 226 ) 227 endif() 228 229 if(TARGET ${ZBUILD_APPLICATION}) 230 message(FATAL_ERROR 231 "ExternalZephyrProject_Add(APPLICATION ${ZBUILD_APPLICATION} ...) " 232 "already exists. Application names must be unique." 233 ) 234 endif() 235 236 if(DEFINED ZBUILD_APP_TYPE) 237 if(NOT ZBUILD_APP_TYPE IN_LIST app_types) 238 message(FATAL_ERROR 239 "ExternalZephyrProject_Add(APP_TYPE <val> ...) given unknown type: ${ZBUILD_APP_TYPE}\n" 240 "Valid types are: ${app_types}" 241 ) 242 endif() 243 244 endif() 245 246 if(NOT DEFINED SYSBUILD_CURRENT_SOURCE_DIR) 247 message(FATAL_ERROR 248 "ExternalZephyrProject_Add(${ARGV0} <val> ...) must not be called outside of" 249 " sysbuild_add_subdirectory(). SYSBUILD_CURRENT_SOURCE_DIR is undefined." 250 ) 251 endif() 252 set_property( 253 DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" 254 APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} 255 ) 256 set_property( 257 GLOBAL 258 APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} 259 ) 260 261 set(sysbuild_image_conf_dir ${APP_DIR}/sysbuild) 262 set(sysbuild_image_name_conf_dir ${APP_DIR}/sysbuild/${ZBUILD_APPLICATION}) 263 # User defined `-D<image>_CONF_FILE=<file.conf>` takes precedence over anything else. 264 if(NOT ${ZBUILD_APPLICATION}_CONF_FILE) 265 if(EXISTS ${sysbuild_image_name_conf_dir}) 266 set(${ZBUILD_APPLICATION}_APPLICATION_CONFIG_DIR ${sysbuild_image_name_conf_dir} 267 CACHE INTERNAL "Application configuration dir controlled by sysbuild" 268 ) 269 endif() 270 271 # Check for sysbuild related configuration fragments. 272 # The contents of these are appended to the image existing configuration 273 # when user is not specifying custom fragments. 274 zephyr_file(CONF_FILES ${sysbuild_image_conf_dir} KCONF sysbuild_image_conf_fragment 275 NAMES ${ZBUILD_APPLICATION}.conf SUFFIX ${FILE_SUFFIX} 276 ) 277 278 if(NOT (${ZBUILD_APPLICATION}_OVERLAY_CONFIG OR ${ZBUILD_APPLICATION}_EXTRA_CONF_FILE) 279 AND EXISTS ${sysbuild_image_conf_fragment} 280 ) 281 set(${ZBUILD_APPLICATION}_EXTRA_CONF_FILE ${sysbuild_image_conf_fragment} 282 CACHE INTERNAL "Kconfig fragment defined by main application" 283 ) 284 endif() 285 286 if(NOT ${ZBUILD_APPLICATION}_DTC_OVERLAY_FILE) 287 # Check for overlay named <ZBUILD_APPLICATION>.overlay. 288 set(sysbuild_image_dts_overlay_files ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}.overlay) 289 290 # Check for overlay named <ZBUILD_APPLICATION>_<FILE_SUFFIX>.overlay. 291 if(FILE_SUFFIX) 292 list(PREPEND sysbuild_image_dts_overlay_files ${sysbuild_image_conf_dir}/${ZBUILD_APPLICATION}_${FILE_SUFFIX}.overlay) 293 endif() 294 295 foreach(overlay_file ${sysbuild_image_dts_overlay_files}) 296 if(EXISTS ${overlay_file}) 297 set(${ZBUILD_APPLICATION}_DTC_OVERLAY_FILE ${overlay_file} 298 CACHE INTERNAL "devicetree overlay file defined by main application" 299 ) 300 break() 301 endif() 302 endforeach() 303 endif() 304 endif() 305 306 # Update ROOT variables with relative paths to use absolute paths based on 307 # the source application directory. 308 foreach(type MODULE_EXT BOARD SOC ARCH SCA) 309 if(DEFINED CACHE{${ZBUILD_APPLICATION}_${type}_ROOT} AND NOT IS_ABSOLUTE $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) 310 set(rel_path $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) 311 cmake_path(ABSOLUTE_PATH rel_path BASE_DIRECTORY "${ZBUILD_SOURCE_DIR}" NORMALIZE OUTPUT_VARIABLE abs_path) 312 set(${ZBUILD_APPLICATION}_${type}_ROOT ${abs_path} CACHE PATH "Sysbuild adjusted absolute path" FORCE) 313 endif() 314 endforeach() 315 316 # CMake variables which must be known by all Zephyr CMake build systems 317 # Those are settings which controls the build and must be known to CMake at 318 # invocation time, and thus cannot be passed though the sysbuild cache file. 319 set( 320 shared_cmake_variables_list 321 CMAKE_BUILD_TYPE 322 CMAKE_VERBOSE_MAKEFILE 323 ) 324 325 set(sysbuild_cache_file ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}_sysbuild_cache.txt) 326 327 set(shared_cmake_vars_argument) 328 foreach(shared_var ${shared_cmake_variables_list}) 329 if(DEFINED CACHE{${ZBUILD_APPLICATION}_${shared_var}}) 330 get_property(var_type CACHE ${ZBUILD_APPLICATION}_${shared_var} PROPERTY TYPE) 331 list(APPEND shared_cmake_vars_argument 332 "-D${shared_var}:${var_type}=$CACHE{${ZBUILD_APPLICATION}_${shared_var}}" 333 ) 334 elseif(DEFINED CACHE{${shared_var}}) 335 get_property(var_type CACHE ${shared_var} PROPERTY TYPE) 336 list(APPEND shared_cmake_vars_argument 337 "-D${shared_var}:${var_type}=$CACHE{${shared_var}}" 338 ) 339 endif() 340 endforeach() 341 342 foreach(kconfig_target 343 menuconfig 344 hardenconfig 345 guiconfig 346 $CACHE{EXTRA_KCONFIG_TARGETS} 347 ) 348 349 if(NOT ZBUILD_APP_TYPE STREQUAL "MAIN") 350 set(image_prefix "${ZBUILD_APPLICATION}_") 351 endif() 352 353 add_custom_target(${image_prefix}${kconfig_target} 354 ${CMAKE_MAKE_PROGRAM} ${kconfig_target} 355 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION} 356 USES_TERMINAL 357 ) 358 endforeach() 359 360 set(list_separator ",") 361 set(image_extra_kconfig_targets "-DEXTRA_KCONFIG_TARGETS=$CACHE{EXTRA_KCONFIG_TARGETS}") 362 string(REPLACE ";" "${list_separator}" image_extra_kconfig_targets "${image_extra_kconfig_targets}") 363 foreach(target $CACHE{EXTRA_KCONFIG_TARGETS}) 364 list(APPEND image_extra_kconfig_targets 365 -DEXTRA_KCONFIG_TARGET_COMMAND_FOR_${target}=$CACHE{EXTRA_KCONFIG_TARGET_COMMAND_FOR_${target}} 366 ) 367 endforeach() 368 369 include(ExternalProject) 370 set(application_binary_dir ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}) 371 ExternalProject_Add( 372 ${ZBUILD_APPLICATION} 373 SOURCE_DIR ${ZBUILD_SOURCE_DIR} 374 BINARY_DIR ${application_binary_dir} 375 CONFIGURE_COMMAND "" 376 LIST_SEPARATOR "${list_separator}" 377 CMAKE_ARGS -DSYSBUILD:BOOL=True 378 -DSYSBUILD_CACHE:FILEPATH=${sysbuild_cache_file} 379 ${shared_cmake_vars_argument} 380 ${image_extra_kconfig_targets} 381 BUILD_COMMAND ${CMAKE_COMMAND} --build . 382 INSTALL_COMMAND "" 383 BUILD_ALWAYS True 384 USES_TERMINAL_BUILD True 385 ) 386 set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY APP_TYPE ${ZBUILD_APP_TYPE}) 387 set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY CONFIG 388 "# sysbuild controlled configuration settings\n" 389 ) 390 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES CACHE_FILE ${sysbuild_cache_file}) 391 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES KCONFIG_BINARY_DIR 392 ${application_binary_dir}/Kconfig 393 ) 394 if("${ZBUILD_APP_TYPE}" STREQUAL "MAIN") 395 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES MAIN_APP True) 396 endif() 397 398 set(image_default "${CMAKE_SOURCE_DIR}/image_configurations/ALL_image_default.cmake") 399 400 if(DEFINED ZBUILD_APP_TYPE) 401 list(APPEND image_default "${CMAKE_SOURCE_DIR}/image_configurations/${ZBUILD_APP_TYPE}_image_default.cmake") 402 set(image_default_dtc_overlay "${CMAKE_SOURCE_DIR}/image_configurations/${ZBUILD_APP_TYPE}_image_default.overlay") 403 404 if(EXISTS ${image_default_dtc_overlay}) 405 if(NOT ${image_default_dtc_overlay} IN_LIST ${ZBUILD_APPLICATION}_EXTRA_DTC_OVERLAY_FILE) 406 list(APPEND ${ZBUILD_APPLICATION}_EXTRA_DTC_OVERLAY_FILE ${image_default_dtc_overlay}) 407 set(${ZBUILD_APPLICATION}_EXTRA_DTC_OVERLAY_FILE 408 ${${ZBUILD_APPLICATION}_EXTRA_DTC_OVERLAY_FILE} 409 CACHE INTERNAL "Application extra DTC overlay file" FORCE 410 ) 411 endif() 412 endif() 413 endif() 414 415 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES IMAGE_CONF_SCRIPT "${image_default}") 416 417 if(DEFINED ZBUILD_BOARD) 418 # Only set image specific board if provided. 419 # The sysbuild BOARD is exported through sysbuild cache, and will be used 420 # unless <image>_BOARD is defined. 421 if(DEFINED ZBUILD_BOARD_REVISION) 422 # Use provided board revision, HWMv2 requires adding version to the board, split elements 423 # up, attach version, then reassemble into a complete string 424 string(REPLACE "/" ";" split_board_qualifiers "${ZBUILD_BOARD}") 425 list(GET split_board_qualifiers 0 target_board) 426 set(target_board ${target_board}@${ZBUILD_BOARD_REVISION}) 427 list(REMOVE_AT split_board_qualifiers 0) 428 list(PREPEND split_board_qualifiers ${target_board}) 429 string(REPLACE ";" "/" board_qualifiers "${split_board_qualifiers}") 430 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${board_qualifiers}) 431 set(split_board_qualifiers) 432 set(board_qualifiers) 433 else() 434 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${ZBUILD_BOARD}) 435 endif() 436 elseif(DEFINED ZBUILD_BOARD_REVISION) 437 message(FATAL_ERROR 438 "ExternalZephyrProject_Add(... BOARD_REVISION ${ZBUILD_BOARD_REVISION})" 439 " requires BOARD." 440 ) 441 endif() 442 443 if(DEFINED ZBUILD_BUILD_ONLY) 444 set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BUILD_ONLY ${ZBUILD_BUILD_ONLY}) 445 endif() 446endfunction() 447 448# Usage: 449# ExternalZephyrProject_Cmake(APPLICATION <name>) 450# 451# This function invokes the CMake configure step on an external Zephyr project 452# which has been added at an earlier stage using `ExternalZephyrProject_Add()` 453# 454# If the application is not due to ExternalZephyrProject_Add() being called, 455# then an error is raised. 456# 457# The image output files are added as target properties on the image target as: 458# ELF_OUT: property specifying the generated elf file. 459# BIN_OUT: property specifying the generated bin file. 460# HEX_OUT: property specifying the generated hex file. 461# S19_OUT: property specifying the generated s19 file. 462# UF2_OUT: property specifying the generated uf2 file. 463# EXE_OUT: property specifying the generated exe file. 464# 465# the property is only set if the image is configured to generate the output 466# format. Elf files are always created. 467# 468# APPLICATION: <name>: Name of the application. 469# 470function(ExternalZephyrProject_Cmake) 471 cmake_parse_arguments(ZCMAKE "" "APPLICATION" "" ${ARGN}) 472 473 if(ZBUILD_UNPARSED_ARGUMENTS) 474 message(FATAL_ERROR 475 "ExternalZephyrProject_Cmake(${ARGV0} <val> ...) given unknown arguments:" 476 " ${ZBUILD_UNPARSED_ARGUMENTS}" 477 ) 478 endif() 479 480 if(NOT DEFINED ZCMAKE_APPLICATION) 481 message(FATAL_ERROR "Missing required argument: APPLICATION") 482 endif() 483 484 if(NOT TARGET ${ZCMAKE_APPLICATION}) 485 message(FATAL_ERROR 486 "${ZCMAKE_APPLICATION} does not exists. Remember to call " 487 "ExternalZephyrProject_Add(APPLICATION ${ZCMAKE_APPLICATION} ...) first." 488 ) 489 endif() 490 491 set(image_banner "* Running CMake for ${ZCMAKE_APPLICATION} *") 492 string(LENGTH "${image_banner}" image_banner_width) 493 string(REPEAT "*" ${image_banner_width} image_banner_header) 494 message(STATUS "\n ${image_banner_header}\n" 495 " ${image_banner}\n" 496 " ${image_banner_header}\n" 497 ) 498 499 ExternalProject_Get_Property(${ZCMAKE_APPLICATION} SOURCE_DIR BINARY_DIR CMAKE_ARGS LIST_SEPARATOR) 500 get_target_property(${ZCMAKE_APPLICATION}_BOARD ${ZCMAKE_APPLICATION} BOARD) 501 502 get_property(${ZCMAKE_APPLICATION}_CONF_SCRIPT TARGET ${ZCMAKE_APPLICATION} 503 PROPERTY IMAGE_CONF_SCRIPT 504 ) 505 506 sysbuild_cache(CREATE APPLICATION ${ZCMAKE_APPLICATION}) 507 508 foreach(script ${${ZCMAKE_APPLICATION}_CONF_SCRIPT}) 509 include(${script}) 510 endforeach() 511 512 set(dotconfigsysbuild ${BINARY_DIR}/zephyr/.config.sysbuild) 513 get_target_property(config_content ${ZCMAKE_APPLICATION} CONFIG) 514 string(CONFIGURE "${config_content}" config_content) 515 file(WRITE ${dotconfigsysbuild} ${config_content}) 516 517 string(REPLACE "${LIST_SEPARATOR}" "\\;" CMAKE_ARGS "${CMAKE_ARGS}") 518 execute_process( 519 COMMAND ${CMAKE_COMMAND} 520 -G${CMAKE_GENERATOR} 521 ${CMAKE_ARGS} 522 -DFORCED_CONF_FILE:FILEPATH=${dotconfigsysbuild} 523 -B${BINARY_DIR} 524 -S${SOURCE_DIR} 525 RESULT_VARIABLE return_val 526 WORKING_DIRECTORY ${BINARY_DIR} 527 ) 528 529 if(return_val) 530 message(FATAL_ERROR 531 "CMake configure failed for Zephyr project: ${ZCMAKE_APPLICATION}\n" 532 "Location: ${SOURCE_DIR}" 533 ) 534 endif() 535 load_cache(IMAGE ${ZCMAKE_APPLICATION} BINARY_DIR ${BINARY_DIR}) 536 import_kconfig(CONFIG_ ${BINARY_DIR}/zephyr/.config TARGET ${ZCMAKE_APPLICATION}) 537 zephyr_dt_import(EDT_PICKLE_FILE ${BINARY_DIR}/zephyr/edt.pickle TARGET ${ZCMAKE_APPLICATION}) 538 539 # This custom target informs CMake how the BYPRODUCTS are generated if a target 540 # depends directly on the BYPRODUCT instead of depending on the image target. 541 get_target_property(${ZCMAKE_APPLICATION}_byproducts ${ZCMAKE_APPLICATION}_cache EXTRA_BYPRODUCTS) 542 add_custom_target(${ZCMAKE_APPLICATION}_extra_byproducts 543 COMMAND ${CMAKE_COMMAND} -E true 544 BYPRODUCTS ${${ZCMAKE_APPLICATION}_byproducts} 545 DEPENDS ${ZCMAKE_APPLICATION} 546 ) 547endfunction() 548 549# Usage: 550# sysbuild_module_call(<hook> MODULES <modules> IMAGES <images> [IMAGE <image>] [EXTRA_ARGS <arguments>]) 551# 552# This function invokes the sysbuild hook provided as <hook> for <modules>. 553# 554# `IMAGES` contains the list of images to the hook, if `IMAGE` is passed, this will be provided 555# to the hook. 556# 557# `EXTRA_ARGS` can be used to pass extra arguments to the hook. 558# 559# Valid <hook> values: 560# PRE_CMAKE : Invoke pre-CMake call for modules before CMake configure is invoked for images 561# POST_CMAKE : Invoke post-CMake call for modules after CMake configure has been invoked for 562# PRE_IMAGE_CMAKE : Invoke pre-CMake call for modules before CMake configure is invoked for each 563# image 564# POST_IMAGE_CMAKE: Invoke post-CMake call for modules after CMake configure has been invoked for 565# each image 566# PRE_DOMAINS : Invoke pre-domains call for modules before creating domains yaml 567# POST_DOMAINS : Invoke post-domains call for modules after creation of domains yaml 568# 569# For the `PRE_IMAGE_CMAKE` and `POST_IMAGE_CMAKE` hooks, `IMAGE` is provided 570# 571function(sysbuild_module_call) 572 set(options "PRE_CMAKE;POST_CMAKE;PRE_IMAGE_CMAKE;POST_IMAGE_CMAKE;PRE_DOMAINS;POST_DOMAINS") 573 set(multi_args "MODULES;IMAGES;IMAGE;EXTRA_ARGS") 574 cmake_parse_arguments(SMC "${options}" "${test_args}" "${multi_args}" ${ARGN}) 575 576 zephyr_check_flags_required("sysbuild_module_call" SMC ${options}) 577 zephyr_check_flags_exclusive("sysbuild_module_call" SMC ${options}) 578 579 if(NOT DEFINED SMC_IMAGES) 580 message(FATAL_ERROR 581 "sysbuild_module_call(...) missing required IMAGES option") 582 endif() 583 584 if(DEFINED SMC_IMAGE) 585 set(IMAGE_ARG IMAGE ${SMC_IMAGE}) 586 elseif(SMC_PRE_IMAGE_CMAKE) 587 message(FATAL_ERROR 588 "sysbuild_module_call(PRE_IMAGE_CMAKE ...) missing required IMAGE option") 589 elseif(SMC_POST_IMAGE_CMAKE) 590 message(FATAL_ERROR 591 "sysbuild_module_call(POST_IMAGE_CMAKE ...) missing required IMAGE option") 592 endif() 593 594 foreach(call ${options}) 595 if(SMC_${call}) 596 foreach(module ${SMC_MODULES}) 597 if(COMMAND ${module}_${call}) 598 cmake_language(CALL ${module}_${call} IMAGES ${SMC_IMAGES} ${IMAGE_ARG} ${SMC_EXTRA_ARGS}) 599 endif() 600 endforeach() 601 endif() 602 endforeach() 603endfunction() 604 605# Usage: 606# sysbuild_cache_set(VAR <variable> [APPEND [REMOVE_DUPLICATES]] <value>) 607# 608# This function will set the specified value of the sysbuild cache variable in 609# the CMakeCache.txt file which can then be accessed by images. 610# `VAR` specifies the variable name to set/update. 611# 612# The result will be returned in `<variable>`. 613# 614# Example use: 615# sysbuild_cache_set(VAR ATTRIBUTES APPEND REMOVE_DUPLICATES battery) 616# Will add the item `battery` to the `ATTRIBUTES` variable as a new element 617# in the list in the CMakeCache and remove any duplicates from the list. 618# 619# <variable>: Name of variable in CMake cache. 620# APPEND: If specified then will append the supplied data to the 621# existing value as a list. 622# REMOVE_DUPLICATES: If specified then remove duplicate entries contained 623# within the list before saving to the cache. 624# <value>: Value to set/update. 625function(sysbuild_cache_set) 626 cmake_parse_arguments(VARS "APPEND;REMOVE_DUPLICATES" "VAR" "" ${ARGN}) 627 628 zephyr_check_arguments_required(sysbuild_cache_set VARS VAR) 629 630 if(NOT VARS_UNPARSED_ARGUMENTS AND VARS_APPEND) 631 # Nothing to append so do nothing 632 return() 633 elseif(VARS_REMOVE_DUPLICATES AND NOT VARS_APPEND) 634 message(FATAL_ERROR 635 "sysbuild_cache_set(VAR <var> APPEND REMOVE_DUPLICATES ...) missing required APPEND option") 636 endif() 637 638 get_property(var_type CACHE ${VARS_VAR} PROPERTY TYPE) 639 get_property(var_help CACHE ${VARS_VAR} PROPERTY HELPSTRING) 640 641 # If the variable type is not set, use UNINITIALIZED which will not apply any 642 # specific formatting. 643 if(NOT var_type) 644 set(var_type "UNINITIALIZED") 645 endif() 646 647 if(VARS_APPEND) 648 set(var_new "$CACHE{${VARS_VAR}}") 649 650 # Search for these exact items in the existing value and prevent adding 651 # them if they are already present which avoids issues with double addition 652 # when cmake is reran. 653 if("${VARS_UNPARSED_ARGUMENTS}" IN_LIST var_new) 654 return() 655 endif() 656 657 list(APPEND var_new "${VARS_UNPARSED_ARGUMENTS}") 658 659 if(VARS_REMOVE_DUPLICATES) 660 list(REMOVE_DUPLICATES var_new) 661 endif() 662 else() 663 set(var_new "${VARS_UNPARSED_ARGUMENTS}") 664 endif() 665 666 set(${VARS_VAR} "${var_new}" CACHE "${var_type}" "${var_help}" FORCE) 667endfunction() 668 669function(set_config_bool image setting value) 670 if(${value}) 671 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=y\n") 672 else() 673 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=n\n") 674 endif() 675endfunction() 676 677function(set_config_string image setting value) 678 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=\"${value}\"\n") 679endfunction() 680 681function(set_config_int image setting value) 682 set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "${setting}=${value}\n") 683endfunction() 684 685# Usage: 686# sysbuild_add_subdirectory(<source_dir> [<binary_dir>]) 687# 688# This function extends the standard add_subdirectory() command with additional, 689# recursive processing of the sysbuild images added via <source_dir>. 690# 691# After exiting <source_dir>, this function will take every image added so far, 692# and include() its sysbuild.cmake file (if found). If more images get added at 693# this stage, their sysbuild.cmake files will be included as well, and so on. 694# This continues until all expected images have been added, before returning. 695# 696function(sysbuild_add_subdirectory source_dir) 697 if(ARGC GREATER 2) 698 message(FATAL_ERROR 699 "sysbuild_add_subdirectory(...) called with incorrect number of arguments" 700 " (expected at most 2, got ${ARGC})" 701 ) 702 endif() 703 set(binary_dir ${ARGN}) 704 705 # Update SYSBUILD_CURRENT_SOURCE_DIR in this scope, to support nesting 706 # of sysbuild_add_subdirectory() and even regular add_subdirectory(). 707 cmake_path(ABSOLUTE_PATH source_dir NORMALIZE OUTPUT_VARIABLE SYSBUILD_CURRENT_SOURCE_DIR) 708 add_subdirectory(${source_dir} ${binary_dir}) 709 710 while(TRUE) 711 get_property(added_images DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" PROPERTY sysbuild_images) 712 if(NOT added_images) 713 break() 714 endif() 715 set_property(DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" PROPERTY sysbuild_images "") 716 717 foreach(image ${added_images}) 718 ExternalProject_Get_property(${image} SOURCE_DIR) 719 include(${SOURCE_DIR}/sysbuild.cmake OPTIONAL) 720 endforeach() 721 endwhile() 722endfunction() 723 724# Usage: 725# sysbuild_add_dependencies(<CONFIGURE | FLASH> <image> [<image-dependency> ...]) 726# 727# This function makes an image depend on other images in the configuration or 728# flashing order. Each image named "<image-dependency>" will be ordered before 729# the image named "<image>". 730# 731# CONFIGURE: Add CMake configuration dependencies. This will determine the order 732# in which `ExternalZephyrProject_Cmake()` will be called. 733# FLASH: Add flashing dependencies. This will determine the order in which 734# all images will appear in `domains.yaml`. 735# 736function(sysbuild_add_dependencies dependency_type image) 737 set(valid_dependency_types CONFIGURE FLASH) 738 if(NOT dependency_type IN_LIST valid_dependency_types) 739 list(JOIN valid_dependency_types ", " valid_dependency_types) 740 message(FATAL_ERROR "sysbuild_add_dependencies(...) dependency type " 741 "${dependency_type} must be one of the following: " 742 "${valid_dependency_types}" 743 ) 744 endif() 745 746 if(NOT TARGET ${image}) 747 message(FATAL_ERROR 748 "${image} does not exist. Remember to call " 749 "ExternalZephyrProject_Add(APPLICATION ${image} ...) first." 750 ) 751 endif() 752 753 get_target_property(image_is_build_only ${image} BUILD_ONLY) 754 if(image_is_build_only AND dependency_type STREQUAL "FLASH") 755 message(FATAL_ERROR 756 "sysbuild_add_dependencies(...) cannot add FLASH dependencies to " 757 "BUILD_ONLY image ${image}." 758 ) 759 endif() 760 761 set(property_name ${dependency_type}_DEPENDS) 762 set_property(TARGET ${image} APPEND PROPERTY ${property_name} ${ARGN}) 763endfunction() 764 765# Usage: 766# sysbuild_images_order(<variable> <CONFIGURE | FLASH> IMAGES <images>) 767# 768# This function will sort the provided `<images>` to satisfy the dependencies 769# specified using `sysbuild_add_dependencies()`. The result will be returned in 770# `<variable>`. 771# 772function(sysbuild_images_order variable dependency_type) 773 cmake_parse_arguments(SIS "" "" "IMAGES" ${ARGN}) 774 zephyr_check_arguments_required_all("sysbuild_images_order" SIS IMAGES) 775 776 set(valid_dependency_types CONFIGURE FLASH) 777 if(NOT dependency_type IN_LIST valid_dependency_types) 778 list(JOIN valid_dependency_types ", " valid_dependency_types) 779 message(FATAL_ERROR "sysbuild_images_order(...) dependency type " 780 "${dependency_type} must be one of the following: " 781 "${valid_dependency_types}" 782 ) 783 endif() 784 785 set(property_name ${dependency_type}_DEPENDS) 786 topological_sort(TARGETS ${SIS_IMAGES} PROPERTY_NAME ${property_name} RESULT sorted) 787 set(${variable} ${sorted} PARENT_SCOPE) 788endfunction() 789