Fixes: #52353 If a CMake variable is available in both local scope and as CMake cache variable, then the use of `${<var>}` fetches the local scoped variable. If only the cache variable is set, then things works as expected. Ensure that the cached variable is always the variable being shared to images by using `$CACHE{<var>}` instead. Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
279 lines
11 KiB
CMake
279 lines
11 KiB
CMake
# Copyright (c) 2021 Nordic Semiconductor
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Usage:
|
|
# load_cache(IMAGE <image> BINARY_DIR <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}")
|
|
endif()
|
|
endforeach()
|
|
endfunction()
|
|
|
|
# Usage:
|
|
# sysbuild_get(<variable> IMAGE <image> [VAR <image-variable>] <KCONFIG|CACHE>)
|
|
#
|
|
# 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 `<variable>` name provided will be used both for
|
|
# lookup and value return.
|
|
#
|
|
# The result will be returned in `<variable>`.
|
|
#
|
|
# 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>: 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})
|
|
message(WARNING "Return variable ${variable} already defined with a value. "
|
|
"sysbuild_get(${variable} ...) may overwrite existing value. "
|
|
"Please use sysbuild_get(<variable> ... VAR <image-variable>) "
|
|
"where <variable> 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 "<CACHE> or <KCONFIG> 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 <name>
|
|
# SOURCE_DIR <dir>
|
|
# [BOARD <board>]
|
|
# [MAIN_APP]
|
|
# )
|
|
#
|
|
# This function includes a Zephyr based build system into the multiimage
|
|
# build system
|
|
#
|
|
# APPLICATION: <name>: Name of the application, name will also be used for build
|
|
# folder of the application
|
|
# SOURCE_DIR <dir>: Source directory of the application
|
|
# BOARD <board>: Use <board> for application build instead user defined BOARD.
|
|
# MAIN_APP: Flag indicating 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.
|
|
#
|
|
function(ExternalZephyrProject_Add)
|
|
cmake_parse_arguments(ZBUILD "MAIN_APP" "APPLICATION;BOARD;SOURCE_DIR" "" ${ARGN})
|
|
|
|
if(ZBUILD_UNPARSED_ARGUMENTS)
|
|
message(FATAL_ERROR
|
|
"ExternalZephyrProject_Add(${ARGV0} <val> ...) given unknown arguments:"
|
|
" ${ZBUILD_UNPARSED_ARGUMENTS}"
|
|
)
|
|
endif()
|
|
|
|
set(sysbuild_image_conf_dir ${APP_DIR}/sysbuild)
|
|
set(sysbuild_image_name_conf_dir ${APP_DIR}/sysbuild/${ZBUILD_APPLICATION})
|
|
# User defined `-D<image>_CONF_FILE=<file.conf>` 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(sysbuil_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 AND EXISTS ${sysbuild_image_conf_fragment})
|
|
set(${ZBUILD_APPLICATION}_OVERLAY_CONFIG ${sysbuild_image_conf_fragment}
|
|
CACHE INTERNAL "Kconfig fragment defined by main application"
|
|
)
|
|
endif()
|
|
|
|
# Check for overlay named <ZBUILD_APPLICATION>.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()
|
|
# 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)
|
|
|
|
get_cmake_property(sysbuild_cache CACHE_VARIABLES)
|
|
foreach(var_name ${sysbuild_cache})
|
|
if(NOT "${var_name}" MATCHES "^CMAKE_.*")
|
|
# 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()
|
|
list(APPEND sysbuild_cache_strings "SYSBUILD_NAME:STRING=${ZBUILD_APPLICATION}\n")
|
|
|
|
if(ZBUILD_MAIN_APP)
|
|
list(APPEND sysbuild_cache_strings "SYSBUILD_MAIN_APP:BOOL=True\n")
|
|
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 <image>_BOARD is defined.
|
|
list(APPEND sysbuild_cache_strings "${ZBUILD_APPLICATION}_BOARD:STRING=${ZBUILD_BOARD}\n")
|
|
endif()
|
|
|
|
file(WRITE ${sysbuild_cache_file}.tmp ${sysbuild_cache_strings})
|
|
zephyr_file_copy(${sysbuild_cache_file}.tmp ${sysbuild_cache_file} ONLY_IF_DIFFERENT)
|
|
|
|
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()
|
|
|
|
set(image_banner "* Running CMake for ${ZBUILD_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"
|
|
)
|
|
|
|
execute_process(
|
|
COMMAND ${CMAKE_COMMAND}
|
|
-G${CMAKE_GENERATOR}
|
|
-DSYSBUILD:BOOL=True
|
|
-DSYSBUILD_CACHE:FILEPATH=${sysbuild_cache_file}
|
|
${shared_cmake_vars_argument}
|
|
-B${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}
|
|
-S${ZBUILD_SOURCE_DIR}
|
|
RESULT_VARIABLE return_val
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
)
|
|
|
|
if(return_val)
|
|
message(FATAL_ERROR
|
|
"CMake configure failed for Zephyr project: ${ZBUILD_APPLICATION}\n"
|
|
"Location: ${ZBUILD_SOURCE_DIR}"
|
|
)
|
|
endif()
|
|
load_cache(IMAGE ${ZBUILD_APPLICATION} BINARY_DIR ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION})
|
|
|
|
foreach(kconfig_target
|
|
menuconfig
|
|
hardenconfig
|
|
guiconfig
|
|
${EXTRA_KCONFIG_TARGETS}
|
|
)
|
|
|
|
if(NOT ZBUILD_MAIN_APP)
|
|
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)
|
|
ExternalProject_Add(
|
|
${ZBUILD_APPLICATION}
|
|
SOURCE_DIR ${ZBUILD_SOURCE_DIR}
|
|
BINARY_DIR ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}
|
|
CONFIGURE_COMMAND ""
|
|
BUILD_COMMAND ${CMAKE_COMMAND} --build .
|
|
INSTALL_COMMAND ""
|
|
BUILD_ALWAYS True
|
|
USES_TERMINAL_BUILD True
|
|
)
|
|
import_kconfig(CONFIG_ ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}/zephyr/.config TARGET ${ZBUILD_APPLICATION})
|
|
endfunction()
|