diff --git a/CMakeLists.txt b/CMakeLists.txt index c14f5f7f17a0e5..3ce4b5ff1e9e91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2235,3 +2235,10 @@ add_subdirectory_ifdef( ) toolchain_linker_finalize() + +yaml_context(EXISTS NAME build_info result) +if(result) + build_info(zephyr version VALUE ${PROJECT_VERSION_STR}) + build_info(zephyr zephyr-base VALUE ${ZEPHYR_BASE}) + yaml_save(NAME build_info) +endif() diff --git a/cmake/modules/FindHostTools.cmake b/cmake/modules/FindHostTools.cmake index 8d3c9eccaaf373..876a86934b140d 100644 --- a/cmake/modules/FindHostTools.cmake +++ b/cmake/modules/FindHostTools.cmake @@ -114,3 +114,6 @@ set_ifndef(TOOLCHAIN_KCONFIG_DIR ${TOOLCHAIN_ROOT}/cmake/toolchain/${ZEPHYR_TOOL set(HostTools_FOUND TRUE) set(HOSTTOOLS_FOUND TRUE) +build_info(toolchain name VALUE ${ZEPHYR_TOOLCHAIN_VARIANT}) +string(TOUPPER ${ZEPHYR_TOOLCHAIN_VARIANT} zephyr_toolchain_variant_upper) +build_info(toolchain path VALUE "${${zephyr_toolchain_variant_upper}_TOOLCHAIN_PATH}") diff --git a/cmake/modules/FindZephyr-sdk.cmake b/cmake/modules/FindZephyr-sdk.cmake index b1c1c8cf02b404..1b56379ec669f1 100644 --- a/cmake/modules/FindZephyr-sdk.cmake +++ b/cmake/modules/FindZephyr-sdk.cmake @@ -175,3 +175,4 @@ if(LOAD IN_LIST Zephyr-sdk_FIND_COMPONENTS) endif() endif() endif() +set(ZEPHYR_TOOLCHAIN_PATH ${ZEPHYR_SDK_INSTALL_DIR}) diff --git a/cmake/modules/boards.cmake b/cmake/modules/boards.cmake index d2c0666f2c576c..a1b05b07b5754c 100644 --- a/cmake/modules/boards.cmake +++ b/cmake/modules/boards.cmake @@ -369,3 +369,7 @@ if(BOARD_EXTENSIONS) list(APPEND BOARD_EXTENSION_DIRS ${board_extension_dir}) endforeach() endif() +build_info(board name VALUE ${BOARD}) +string(REGEX REPLACE "^/" "" qualifiers "${BOARD_QUALIFIERS}") +build_info(board qualifiers VALUE ${qualifiers}) +build_info(board revision VALUE ${BOARD_REVISION}) diff --git a/cmake/modules/configuration_files.cmake b/cmake/modules/configuration_files.cmake index 84af8c5b6353e8..ff7a172445c38f 100644 --- a/cmake/modules/configuration_files.cmake +++ b/cmake/modules/configuration_files.cmake @@ -99,3 +99,5 @@ zephyr_boilerplate_watch(DTC_OVERLAY_FILE) zephyr_get(EXTRA_CONF_FILE SYSBUILD LOCAL VAR EXTRA_CONF_FILE OVERLAY_CONFIG MERGE REVERSE) zephyr_get(EXTRA_DTC_OVERLAY_FILE SYSBUILD LOCAL MERGE REVERSE) zephyr_get(DTS_EXTRA_CPPFLAGS SYSBUILD LOCAL MERGE REVERSE) +build_info(application source-dir VALUE ${APPLICATION_SOURCE_DIR}) +build_info(application configuration-dir VALUE ${APPLICATION_CONFIG_DIR}) diff --git a/cmake/modules/dts.cmake b/cmake/modules/dts.cmake index c22761b9c71784..02fed1b4a8f581 100644 --- a/cmake/modules/dts.cmake +++ b/cmake/modules/dts.cmake @@ -188,6 +188,7 @@ set(dts_files if(DTC_OVERLAY_FILE) zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS OUTPUT_VARIABLE DTC_OVERLAY_FILE_AS_LIST) + build_info(devicetree user-files VALUE ${DTC_OVERLAY_FILE_AS_LIST}) list(APPEND dts_files ${DTC_OVERLAY_FILE_AS_LIST} @@ -197,6 +198,7 @@ endif() if(EXTRA_DTC_OVERLAY_FILE) zephyr_list(TRANSFORM EXTRA_DTC_OVERLAY_FILE NORMALIZE_PATHS OUTPUT_VARIABLE EXTRA_DTC_OVERLAY_FILE_AS_LIST) + build_info(devicetree extra-user-files VALUE ${EXTRA_DTC_OVERLAY_FILE_AS_LIST}) list(APPEND dts_files ${EXTRA_DTC_OVERLAY_FILE_AS_LIST} @@ -413,3 +415,7 @@ elseif(stderr) message(WARNING "dtc raised one or more warnings:\n${stderr}") endif() endif(DTC) + +build_info(devicetree files VALUE ${dts_files}) +build_info(devicetree include-dirs VALUE ${DTS_ROOT_SYSTEM_INCLUDE_DIRS}) +build_info(devicetree bindings-dirs VALUE ${DTS_ROOT_BINDINGS}) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index 2ac8c20058bf78..9fc4726a6654f7 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -38,6 +38,7 @@ include(CheckCXXCompilerFlag) # 7.1 llext_* configuration functions # 7.2 add_llext_* build control functions # 7.3 llext helper functions +# 8. Script mode handling ######################################################## # 1. Zephyr-aware extensions @@ -3656,6 +3657,67 @@ function(topological_sort) set(${TS_RESULT} "${sorted_targets}" PARENT_SCOPE) endfunction() +# Usage: +# build_info(... VALUE ...) +# +# This function populates updates the build_info.yml info file with exchangable build information +# related to the current build. +# +# Example: +# build_info(devicetree files VALUE file1.dts file2.dts file3.dts) +# Will update the 'devicetree files' key in the build info yaml with the list +# of files, file1.dts file2.dts file3.dts. +# +# build_info(vendor-specific foo VALUE bar) +# Will place the vendor specific key 'foo' with value 'bar' in the vendor specific section +# of the build info file. +# +# ...: One of the pre-defined valid CMake keys supported by build info or vendor-specific. +# See 'scripts/schemas/build-schema.yml' CMake section for valid tags. +# VALUE ... : value(s) to place in the build_info.yml file. +function(build_info) + set(arg_list ${ARGV}) + list(FIND arg_list VALUE index) + if(index EQUAL -1) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}(...) missing a required argument: VALUE") + endif() + + yaml_context(EXISTS NAME build_info result) + if(NOT result) + yaml_load(FILE ${ZEPHYR_BASE}/scripts/schemas/build-schema.yml NAME build_info_schema) + if(EXISTS ${CMAKE_BINARY_DIR}/build_info.yml) + yaml_load(FILE ${CMAKE_BINARY_DIR}/build_info.yml NAME build_info) + else() + yaml_create(FILE ${CMAKE_BINARY_DIR}/build_info.yml NAME build_info) + endif() + yaml_set(NAME build_info KEY version VALUE "0.1.0") + endif() + + list(SUBLIST arg_list 0 ${index} keys) + list(SUBLIST arg_list ${index} -1 values) + list(POP_FRONT values) + + if(ARGV0 STREQUAL "vendor-specific") + set(type VALUE) + else() + set(schema_check ${keys}) + list(TRANSFORM schema_check PREPEND "mapping;") + yaml_get(check NAME build_info_schema KEY mapping cmake ${schema_check}) + if(check MATCHES ".*-NOTFOUND") + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}(...) called with invalid tag: ${keys}") + endif() + + yaml_get(type NAME build_info_schema KEY mapping cmake ${schema_check} type) + if(type MATCHES "seq|sequence") + set(type LIST) + else() + set(type VALUE) + endif() + endif() + + yaml_set(NAME build_info KEY cmake ${keys} ${type} "${values}") +endfunction() + ######################################################## # 4. Devicetree extensions ######################################################## @@ -5759,3 +5821,47 @@ function(llext_filter_zephyr_flags filter flags outvar) set(${outvar} ${zephyr_filtered_flags} PARENT_SCOPE) endfunction() + +######################################################## +# 8. Script mode handling +######################################################## +# +# Certain features are not available when CMake is used in script mode. +# For example custom targets, and thus features related to custom targets, such +# as target properties are not available in script mode. +# +# This section defines behavior for functions whose default implementation does +# not work correctly in script mode. +# +# The script mode function can be a simple stub or a more complex solution +# depending on the exact use of the function in script mode. +# +# Current Zephyr CMake scripts which includes `extensions.cmake` in script mode +# are: package_helper.cmake, verify-toolchain.cmake +# + +if(CMAKE_SCRIPT_MODE_FILE) + # add_custom_target and set_target_properties are not supported in script mode. + # However, Zephyr CMake functions like `zephyr_get()`, `zephyr_create_scope()`, + # llext functions creates or relies on custom CMake targets. + function(add_custom_target) + # This silence the error: 'add_custom_target command is not scriptable' + endfunction() + + function(set_target_properties) + # This silence the error: 'set_target_properties command is not scriptable' + endfunction() + + function(zephyr_set variable) + # This silence the error: zephyr_set(... SCOPE ) doesn't exists. + endfunction() + + # Build info creates a custom target for handling of build info. + # build_info is not needed in script mode but still called by Zephyr CMake + # modules. Therefore disable build_info(...) in when including + # extensions.cmake in script mode. + function(build_info) + # This silence the error: 'YAML context 'build_info' does not exist.' + # 'Remember to create a YAML context' + endfunction() +endif() diff --git a/cmake/modules/kconfig.cmake b/cmake/modules/kconfig.cmake index 3f374db5aea1d5..3f7310aabb5db4 100644 --- a/cmake/modules/kconfig.cmake +++ b/cmake/modules/kconfig.cmake @@ -95,11 +95,13 @@ set(PARSED_KCONFIG_SOURCES_TXT ${PROJECT_BINARY_DIR}/kconfig/sources.txt) if(CONF_FILE) string(CONFIGURE "${CONF_FILE}" CONF_FILE_EXPANDED) string(REPLACE " " ";" CONF_FILE_AS_LIST "${CONF_FILE_EXPANDED}") + build_info(kconfig user-files VALUE ${CONF_FILE_AS_LIST}) endif() if(EXTRA_CONF_FILE) string(CONFIGURE "${EXTRA_CONF_FILE}" EXTRA_CONF_FILE_EXPANDED) string(REPLACE " " ";" EXTRA_CONF_FILE_AS_LIST "${EXTRA_CONF_FILE_EXPANDED}") + build_info(kconfig extra-user-files VALUE ${EXTRA_CONF_FILE_AS_LIST}) endif() zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} KCONF board_extension_conf_files SUFFIX ${FILE_SUFFIX}) @@ -354,6 +356,7 @@ endif() if(CREATE_NEW_DOTCONFIG) set(input_configs_flags --handwritten-input-configs) set(input_configs ${merge_config_files} ${FORCED_CONF_FILE}) + build_info(kconfig files VALUE ${input_configs}) else() set(input_configs ${DOTCONFIG} ${FORCED_CONF_FILE}) endif() diff --git a/cmake/modules/unittest.cmake b/cmake/modules/unittest.cmake index 9598bf8b84ae16..6565e89aec53a7 100644 --- a/cmake/modules/unittest.cmake +++ b/cmake/modules/unittest.cmake @@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.20.0) include(extensions) include(west) +include(yaml) include(root) include(zephyr_module) include(boards) diff --git a/cmake/package_helper.cmake b/cmake/package_helper.cmake index 55412b941108b8..886dbfff71ddf6 100644 --- a/cmake/package_helper.cmake +++ b/cmake/package_helper.cmake @@ -44,20 +44,6 @@ cmake_minimum_required(VERSION 3.20.5) -# add_custom_target and set_target_properties are not supported in script mode. -# However, several Zephyr CMake modules create custom target for user convenience -# like menuconfig, boards, shields, etc. -# As we are not generating a build system with this tool, only running part of -# the modules, then we simply override those functions to allow running those -# modules. -function(add_custom_target) - # This silence the error: 'add_custom_target command is not scriptable' -endfunction() - -function(set_target_properties) - # This silence the error: 'set_target_properties command is not scriptable' -endfunction() - # Find last `-B` and `-S` instances. foreach(i RANGE ${CMAKE_ARGC}) if(CMAKE_ARGV${i} MATCHES "^-B(.*)") @@ -111,16 +97,5 @@ if(NOT DEFINED MODULES) ) endif() -# Loading Zephyr CMake extension commands, which allows us to overload Zephyr -# scoping rules. -find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS extensions) - -# Zephyr scoping creates custom targets for handling of properties. -# However, custom targets cannot be used in CMake script mode. -# Therefore disable zephyr_set(... SCOPE ...) in package helper as it is not needed. -function(zephyr_set variable) - # This silence the error: zephyr_set(... SCOPE ) doesn't exists. -endfunction() - string(REPLACE ";" "," MODULES "${MODULES}") find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS zephyr_default:${MODULES}) diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index 2e8e0cc1eb5215..91b8e937521955 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -229,3 +229,4 @@ mdk_svd_ifdef(CONFIG_SOC_NRF9160 nrf9160.svd) mdk_svd_ifdef(CONFIG_SOC_NRF9230_ENGB_CPUAPP nrf9230_engb_application.svd) mdk_svd_ifdef(CONFIG_SOC_NRF9230_ENGB_CPUPPR nrf9230_engb_ppr.svd) mdk_svd_ifdef(CONFIG_SOC_NRF9230_ENGB_CPURAD nrf9230_engb_radiocore.svd) +build_info(vendor-specific nordic svdfile VALUE ${SOC_SVD_FILE}) diff --git a/scripts/schemas/build-schema.yml b/scripts/schemas/build-schema.yml new file mode 100644 index 00000000000000..5086afbb25b178 --- /dev/null +++ b/scripts/schemas/build-schema.yml @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024, Nordic Semiconductor ASA + +# A pykwalify schema for basic validation of the Zephyr build info YAML file. + +type: map +mapping: + version: + required: true + type: str + cmake: + type: map + mapping: + application: + type: map + mapping: + source-dir: + type: str + configuration-dir: + type: str + board: + type: map + mapping: + name: + required: true + type: str + qualifiers: + type: str + revision: + type: str + path: + type: seq + sequence: + - type: str + devicetree: + type: map + mapping: + files: + type: seq + sequence: + - type: str + user-files: + type: seq + sequence: + - type: str + extra-user-files: + type: seq + sequence: + - type: str + include-dirs: + type: seq + sequence: + - type: str + bindings-dirs: + type: seq + sequence: + - type: str + kconfig: + type: map + mapping: + files: + type: seq + sequence: + - type: str + user-files: + type: seq + sequence: + - type: str + extra-user-files: + type: seq + sequence: + - type: str + sysbuild: + type: bool + toolchain: + type: map + mapping: + name: + type: str + version: + type: str + path: + type: str + zephyr: + type: map + mapping: + zephyr-base: + type: str + version: + type: str + vendor-specific: + type: map + mapping: + regex;(.*): + type: map + mapping: + regex;(.*): + type: str + west: + type: map + mapping: + command: + type: str + topdir: + type: str + version: + type: str diff --git a/scripts/west_commands/build.py b/scripts/west_commands/build.py index 891a842bb88668..63cfa9cd5ec8f4 100644 --- a/scripts/west_commands/build.py +++ b/scripts/west_commands/build.py @@ -11,6 +11,8 @@ from west import log from west.configuration import config +from west.util import west_topdir +from west.version import __version__ from zcmake import DEFAULT_CMAKE_GENERATOR, run_cmake, run_build, CMakeCache from build_helpers import is_zephyr_build, find_build_dir, load_domains, \ FIND_BUILD_DIR_DESCRIPTION @@ -22,6 +24,8 @@ SYSBUILD_PROJ_DIR = pathlib.Path(__file__).resolve().parent.parent.parent \ / pathlib.Path('share/sysbuild') +BUILD_INFO_LOG = 'build_info.yml' + BUILD_USAGE = '''\ west build [-h] [-b BOARD[@REV]]] [-d BUILD_DIR] [-S SNIPPET] [--shield SHIELD] @@ -244,9 +248,25 @@ def do_run(self, args, remainder): self.run_cmake = True else: self.run_cmake = True + self.source_dir = self._find_source_dir() self._sanity_check() + build_info_path = self.build_dir + build_info_file = os.path.join(build_info_path, BUILD_INFO_LOG) + west_workspace = west_topdir(self.source_dir) + if not os.path.exists(build_info_path): + os.makedirs(build_info_path) + if not os.path.exists(build_info_file): + build_command = {'west': {'command': ' '.join(sys.argv[:]), + 'topdir': str(west_workspace), + 'version': str(__version__)}} + try: + with open(build_info_file, "w") as f: + yaml.dump(build_command, f, default_flow_style=False) + except Exception as e: + log.wrn(f'Failed to create info file: {build_info_file},', e) + board, origin = self._find_board() self._run_cmake(board, origin, self.args.cmake_opts) if args.cmake_only: diff --git a/share/sysbuild/CMakeLists.txt b/share/sysbuild/CMakeLists.txt index 8370490fb94766..f2fbb4b271841b 100644 --- a/share/sysbuild/CMakeLists.txt +++ b/share/sysbuild/CMakeLists.txt @@ -25,3 +25,10 @@ if(EXISTS ${APP_DIR}/sysbuild/CMakeLists.txt) else() add_subdirectory(template _sysbuild) endif() + +build_info(sysbuild VALUE true) +build_info(application source-dir VALUE ${CMAKE_CURRENT_SOURCE_DIR}) +yaml_context(EXISTS NAME build_info result) +if(result) + yaml_save(NAME build_info) +endif()