Currently stderr is printed only if dtc encounters warnings, but it's not printed in case of errors. However this information can be useful to quickly identify and resolve the problem. To solve this let's add "COMMAND_ERROR_IS_FATAL ANY" to execute_process() so that CMake will fail in case of errors and a proper message will be printed on the output. Signed-off-by: Valerio Setti <vsetti@baylibre.com>
424 lines
14 KiB
CMake
424 lines
14 KiB
CMake
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
include_guard(GLOBAL)
|
|
|
|
include(extensions)
|
|
include(python)
|
|
include(boards)
|
|
include(pre_dt)
|
|
find_package(HostTools)
|
|
find_package(Dtc 1.4.6)
|
|
|
|
# This module makes information from the devicetree available to
|
|
# various build stages, as well as to other arbitrary Python scripts:
|
|
#
|
|
# - To Zephyr and application source code files, as a C macro API
|
|
# defined in <zephyr/devicetree.h>
|
|
#
|
|
# - To other arbitrary Python scripts (like twister) using a
|
|
# serialized edtlib.EDT object in Python's pickle format
|
|
# (https://docs.python.org/3/library/pickle.html)
|
|
#
|
|
# - To users as a final devicetree source (DTS) file which can
|
|
# be used for debugging
|
|
#
|
|
# - To CMake files, after this module has finished running, using
|
|
# devicetree extensions defined in cmake/modules/extensions.cmake
|
|
#
|
|
# - To Kconfig files, both using some Kconfig symbols we generate
|
|
# here as well as the extension functions defined in
|
|
# scripts/kconfig/kconfigfunctions.py
|
|
#
|
|
# See the specific API documentation for each of these cases for more
|
|
# information on what is currently available to you.
|
|
#
|
|
# We rely on the C preprocessor, the devicetree python package, and
|
|
# files in scripts/dts to make all this work. We also optionally will
|
|
# run the dtc tool if it is found, in order to catch any additional
|
|
# warnings or errors it generates.
|
|
#
|
|
# Outcome:
|
|
#
|
|
# 1. The following has happened:
|
|
#
|
|
# - The pre_dt module has been included; refer to its outcome
|
|
# section for more information on the consequences
|
|
# - DTS_SOURCE: set to the path to the devicetree file which
|
|
# was used, if one was provided or found
|
|
# - ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h exists
|
|
#
|
|
# 2. The following has happened if a devicetree was found and
|
|
# no errors occurred:
|
|
#
|
|
# - CACHED_DTS_ROOT_BINDINGS is set in the cache to the
|
|
# value of DTS_ROOT_BINDINGS
|
|
# - DTS_ROOT_BINDINGS is set to a ;-list of locations where DT
|
|
# bindings were found
|
|
# - ${PROJECT_BINARY_DIR}/zephyr.dts exists
|
|
# - ${PROJECT_BINARY_DIR}/edt.pickle exists
|
|
# - ${KCONFIG_BINARY_DIR}/Kconfig.dts exists
|
|
# - DTS_INCLUDE_FILES is set to a ;-list of all devicetree files
|
|
# used in this build, including transitive includes (the build
|
|
# system will be regenerated if any of those files change)
|
|
# - the devicetree extensions in the extensions.cmake module
|
|
# will be ready for use in other CMake list files that run
|
|
# after this module
|
|
#
|
|
# Required variables:
|
|
# - BINARY_DIR_INCLUDE_GENERATED: where to put generated include files
|
|
# - DTS_ROOT: a deduplicated list of places where devicetree
|
|
# implementation files (like bindings, vendor prefixes, etc.) are
|
|
# found
|
|
# - DTS_ROOT_SYSTEM_INCLUDE_DIRS: set to "PATH1 PATH2 ...",
|
|
# with one path per potential location where C preprocessor #includes
|
|
# may be found for devicetree files
|
|
# - KCONFIG_BINARY_DIR: where to put generated Kconfig files
|
|
#
|
|
# Optional variables:
|
|
# - BOARD: board name to use when looking for DTS_SOURCE
|
|
# - BOARD_DIRECTORIES: list of board directories to use when looking for DTS_SOURCE
|
|
# - BOARD_REVISION_STRING: used when looking for a board revision's
|
|
# devicetree overlay file in one of the BOARD_DIRECTORIES
|
|
# - CMAKE_DTS_PREPROCESSOR: the path to the preprocessor to use
|
|
# for devicetree files
|
|
# - DTC_OVERLAY_FILE: list of devicetree overlay files which will be
|
|
# used to modify or extend the base devicetree.
|
|
# - EXTRA_DTC_OVERLAY_FILE: list of extra devicetree overlay files.
|
|
# This variable is similar to DTC_OVERLAY_FILE but the files in
|
|
# EXTRA_DTC_OVERLAY_FILE will be applied after DTC_OVERLAY_FILE and
|
|
# thus files specified by EXTRA_DTC_OVERLAY_FILE have higher precedence.
|
|
# - EXTRA_DTC_FLAGS: list of extra command line options to pass to
|
|
# dtc when using it to check for additional errors and warnings;
|
|
# invalid flags are automatically filtered out of the list
|
|
# - DTS_EXTRA_CPPFLAGS: extra command line options to pass to the
|
|
# C preprocessor when generating the devicetree from DTS_SOURCE
|
|
# - DTS_SOURCE: the devicetree source file to use may be pre-set
|
|
# with this variable; otherwise, it defaults to
|
|
# ${BOARD_DIRECTORIES}/<normalized_board_target>.dts
|
|
#
|
|
# Variables set by this module and not mentioned above are for internal
|
|
# use only, and may be removed, renamed, or re-purposed without prior notice.
|
|
|
|
# The directory containing devicetree related scripts.
|
|
set(DT_SCRIPTS ${ZEPHYR_BASE}/scripts/dts)
|
|
|
|
# This parses and collects the DT information
|
|
set(GEN_EDT_SCRIPT ${DT_SCRIPTS}/gen_edt.py)
|
|
# This generates DT information needed by the C macro APIs,
|
|
# along with a few other things.
|
|
set(GEN_DEFINES_SCRIPT ${DT_SCRIPTS}/gen_defines.py)
|
|
# The edtlib.EDT object in pickle format.
|
|
set(EDT_PICKLE ${PROJECT_BINARY_DIR}/edt.pickle)
|
|
# The generated file containing the final DTS, for debugging.
|
|
set(ZEPHYR_DTS ${PROJECT_BINARY_DIR}/zephyr.dts)
|
|
# The generated C header needed by <zephyr/devicetree.h>
|
|
set(DEVICETREE_GENERATED_H ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h)
|
|
# Generated build system internals.
|
|
set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/zephyr.dts.pre)
|
|
set(DTS_DEPS ${PROJECT_BINARY_DIR}/zephyr.dts.d)
|
|
|
|
# This generates DT information needed by the Kconfig APIs.
|
|
set(GEN_DRIVER_KCONFIG_SCRIPT ${DT_SCRIPTS}/gen_driver_kconfig_dts.py)
|
|
# Generated Kconfig symbols go here.
|
|
set(DTS_KCONFIG ${KCONFIG_BINARY_DIR}/Kconfig.dts)
|
|
|
|
# This generates DT information needed by the CMake APIs.
|
|
set(GEN_DTS_CMAKE_SCRIPT ${DT_SCRIPTS}/gen_dts_cmake.py)
|
|
# The generated information itself, which we include() after
|
|
# creating it.
|
|
set(DTS_CMAKE ${PROJECT_BINARY_DIR}/dts.cmake)
|
|
|
|
# The location of a file containing known vendor prefixes, relative to
|
|
# each element of DTS_ROOT. Users can define their own in their own
|
|
# modules.
|
|
set(VENDOR_PREFIXES dts/bindings/vendor-prefixes.txt)
|
|
|
|
if(NOT DEFINED DTS_SOURCE)
|
|
zephyr_build_string(board_string SHORT shortened_board_string
|
|
BOARD ${BOARD} BOARD_QUALIFIERS ${BOARD_QUALIFIERS}
|
|
)
|
|
foreach(dir ${BOARD_DIRECTORIES})
|
|
if(EXISTS ${dir}/${shortened_board_string}.dts AND NOT BOARD_${BOARD}_SINGLE_SOC)
|
|
message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name "
|
|
"(${shortened_board_string}.dts) not allowed, use '<board>_<soc>.dts' naming"
|
|
)
|
|
elseif(EXISTS ${dir}/${board_string}.dts AND EXISTS ${dir}/${shortened_board_string}.dts)
|
|
message(FATAL_ERROR "Conflicting file names discovered. Cannot use both "
|
|
"${board_string}.dts and ${shortened_board_string}.dts. "
|
|
"Please choose one naming style, ${board_string}.dts is recommended."
|
|
)
|
|
elseif(EXISTS ${dir}/${board_string}.dts)
|
|
set(DTS_SOURCE ${dir}/${board_string}.dts)
|
|
elseif(EXISTS ${dir}/${shortened_board_string}.dts)
|
|
set(DTS_SOURCE ${dir}/${shortened_board_string}.dts)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
if(EXISTS ${DTS_SOURCE})
|
|
# We found a devicetree. Append all relevant dts overlays we can find...
|
|
zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DTS DTS_SOURCE)
|
|
|
|
zephyr_file(
|
|
CONF_FILES ${BOARD_DIRECTORIES}
|
|
DTS no_rev_suffix_dts_board_overlays
|
|
BOARD ${BOARD}
|
|
BOARD_QUALIFIERS ${BOARD_QUALIFIERS}
|
|
)
|
|
|
|
# ...but remove the ones that do not include the revision suffix
|
|
list(REMOVE_ITEM DTS_SOURCE ${no_rev_suffix_dts_board_overlays})
|
|
else()
|
|
# If we don't have a devicetree, provide an empty stub
|
|
set(DTS_SOURCE ${ZEPHYR_BASE}/boards/common/stub.dts)
|
|
endif()
|
|
|
|
#
|
|
# Find all the DTS files we need to concatenate and preprocess, as
|
|
# well as all the devicetree bindings and vendor prefixes associated
|
|
# with them.
|
|
#
|
|
|
|
zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} DTS board_extension_dts_files)
|
|
|
|
set(dts_files
|
|
${DTS_SOURCE}
|
|
${board_extension_dts_files}
|
|
${shield_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 PATH ${DTC_OVERLAY_FILE_AS_LIST})
|
|
list(APPEND
|
|
dts_files
|
|
${DTC_OVERLAY_FILE_AS_LIST}
|
|
)
|
|
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 PATH ${EXTRA_DTC_OVERLAY_FILE_AS_LIST})
|
|
list(APPEND
|
|
dts_files
|
|
${EXTRA_DTC_OVERLAY_FILE_AS_LIST}
|
|
)
|
|
endif()
|
|
|
|
set(i 0)
|
|
foreach(dts_file ${dts_files})
|
|
if(i EQUAL 0)
|
|
message(STATUS "Found BOARD.dts: ${dts_file}")
|
|
else()
|
|
message(STATUS "Found devicetree overlay: ${dts_file}")
|
|
endif()
|
|
|
|
math(EXPR i "${i}+1")
|
|
endforeach()
|
|
|
|
unset(DTS_ROOT_BINDINGS)
|
|
foreach(dts_root ${DTS_ROOT})
|
|
set(bindings_path ${dts_root}/dts/bindings)
|
|
if(EXISTS ${bindings_path})
|
|
list(APPEND
|
|
DTS_ROOT_BINDINGS
|
|
${bindings_path}
|
|
)
|
|
endif()
|
|
|
|
set(vendor_prefixes ${dts_root}/${VENDOR_PREFIXES})
|
|
if(EXISTS ${vendor_prefixes})
|
|
list(APPEND EXTRA_GEN_EDT_ARGS --vendor-prefixes ${vendor_prefixes})
|
|
endif()
|
|
endforeach()
|
|
|
|
# Cache the location of the root bindings so they can be used by
|
|
# scripts which use the build directory.
|
|
set(CACHED_DTS_ROOT_BINDINGS ${DTS_ROOT_BINDINGS} CACHE INTERNAL
|
|
"DT bindings root directories")
|
|
|
|
#
|
|
# Run the C preprocessor on the devicetree source, so we can parse it
|
|
# (using the Python devicetree package) in later steps.
|
|
#
|
|
|
|
# TODO: Cut down on CMake configuration time by avoiding
|
|
# regeneration of devicetree_generated.h on every configure. How
|
|
# challenging is this? Can we cache the dts dependencies?
|
|
|
|
# Run the preprocessor on the DTS input files.
|
|
if(DEFINED CMAKE_DTS_PREPROCESSOR)
|
|
set(dts_preprocessor ${CMAKE_DTS_PREPROCESSOR})
|
|
else()
|
|
set(dts_preprocessor ${CMAKE_C_COMPILER})
|
|
endif()
|
|
zephyr_dt_preprocess(
|
|
CPP ${dts_preprocessor}
|
|
SOURCE_FILES ${dts_files}
|
|
OUT_FILE ${DTS_POST_CPP}
|
|
DEPS_FILE ${DTS_DEPS}
|
|
EXTRA_CPPFLAGS ${DTS_EXTRA_CPPFLAGS}
|
|
INCLUDE_DIRECTORIES ${DTS_ROOT_SYSTEM_INCLUDE_DIRS}
|
|
WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
|
|
)
|
|
|
|
#
|
|
# Make sure we re-run CMake if any devicetree sources or transitive
|
|
# includes change.
|
|
#
|
|
|
|
# Parse the generated dependency file to find the DT sources that
|
|
# were included, including any transitive includes.
|
|
toolchain_parse_make_rule(${DTS_DEPS}
|
|
DTS_INCLUDE_FILES # Output parameter
|
|
)
|
|
|
|
# Add the results to the list of files that, when change, force the
|
|
# build system to re-run CMake.
|
|
set_property(DIRECTORY APPEND PROPERTY
|
|
CMAKE_CONFIGURE_DEPENDS
|
|
${DTS_INCLUDE_FILES}
|
|
${GEN_EDT_SCRIPT}
|
|
${GEN_DEFINES_SCRIPT}
|
|
${GEN_DRIVER_KCONFIG_SCRIPT}
|
|
${GEN_DTS_CMAKE_SCRIPT}
|
|
)
|
|
|
|
#
|
|
# Run GEN_EDT_SCRIPT.
|
|
#
|
|
|
|
string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}")
|
|
set(CMD_GEN_EDT ${PYTHON_EXECUTABLE} ${GEN_EDT_SCRIPT}
|
|
--dts ${DTS_POST_CPP}
|
|
--dtc-flags '${EXTRA_DTC_FLAGS_RAW}'
|
|
--bindings-dirs ${DTS_ROOT_BINDINGS}
|
|
--dts-out ${ZEPHYR_DTS}.new # for debugging and dtc
|
|
--edt-pickle-out ${EDT_PICKLE}.new
|
|
${EXTRA_GEN_EDT_ARGS}
|
|
)
|
|
|
|
execute_process(
|
|
COMMAND ${CMD_GEN_EDT}
|
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
COMMAND_ERROR_IS_FATAL ANY
|
|
)
|
|
zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT)
|
|
zephyr_file_copy(${EDT_PICKLE}.new ${EDT_PICKLE} ONLY_IF_DIFFERENT)
|
|
file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new)
|
|
message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}")
|
|
message(STATUS "Generated pickled edt: ${EDT_PICKLE}")
|
|
|
|
#
|
|
# Run GEN_DEFINES_SCRIPT.
|
|
#
|
|
|
|
set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT}
|
|
--header-out ${DEVICETREE_GENERATED_H}.new
|
|
--edt-pickle ${EDT_PICKLE}
|
|
${EXTRA_GEN_DEFINES_ARGS}
|
|
)
|
|
|
|
execute_process(
|
|
COMMAND ${CMD_GEN_DEFINES}
|
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
COMMAND_ERROR_IS_FATAL ANY
|
|
)
|
|
zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT)
|
|
file(REMOVE ${DEVICETREE_GENERATED_H}.new)
|
|
message(STATUS "Generated devicetree_generated.h: ${DEVICETREE_GENERATED_H}")
|
|
|
|
#
|
|
# Run GEN_DRIVER_KCONFIG_SCRIPT.
|
|
#
|
|
|
|
execute_process(
|
|
COMMAND ${PYTHON_EXECUTABLE} ${GEN_DRIVER_KCONFIG_SCRIPT}
|
|
--kconfig-out ${DTS_KCONFIG}
|
|
--bindings-dirs ${DTS_ROOT_BINDINGS}
|
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
RESULT_VARIABLE ret
|
|
)
|
|
if(NOT "${ret}" STREQUAL "0")
|
|
message(FATAL_ERROR "gen_driver_kconfig_dts.py failed with return code: ${ret}")
|
|
endif()
|
|
|
|
#
|
|
# Run GEN_DTS_CMAKE_SCRIPT.
|
|
#
|
|
# A temporary file is copied to the original file if it differs. This prevents issue such as a
|
|
# cycle when sysbuild is used of configuring and building multiple times due to the dts.cmake file
|
|
# of images having a newer modification time than the sysbuild build.ninja file, despite the
|
|
# output having not changed
|
|
#
|
|
set(dts_cmake_tmp ${DTS_CMAKE}.new)
|
|
|
|
execute_process(
|
|
COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_CMAKE_SCRIPT}
|
|
--edt-pickle ${EDT_PICKLE}
|
|
--cmake-out ${dts_cmake_tmp}
|
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
RESULT_VARIABLE ret
|
|
)
|
|
if(NOT "${ret}" STREQUAL "0")
|
|
message(FATAL_ERROR "gen_dts_cmake.py failed with return code: ${ret}")
|
|
else()
|
|
zephyr_file_copy(${dts_cmake_tmp} ${DTS_CMAKE} ONLY_IF_DIFFERENT)
|
|
file(REMOVE ${dts_cmake_tmp})
|
|
set(dts_cmake_tmp)
|
|
message(STATUS "Including generated dts.cmake file: ${DTS_CMAKE}")
|
|
include(${DTS_CMAKE})
|
|
endif()
|
|
|
|
#
|
|
# Run dtc if it was found.
|
|
#
|
|
# This is just to generate warnings and errors; we discard the output.
|
|
#
|
|
|
|
if(DTC)
|
|
|
|
set(DTC_WARN_UNIT_ADDR_IF_ENABLED "")
|
|
check_dtc_flag("-Wunique_unit_address_if_enabled" check)
|
|
if (check)
|
|
set(DTC_WARN_UNIT_ADDR_IF_ENABLED "-Wunique_unit_address_if_enabled")
|
|
endif()
|
|
|
|
set(DTC_NO_WARN_UNIT_ADDR "")
|
|
check_dtc_flag("-Wno-unique_unit_address" check)
|
|
if (check)
|
|
set(DTC_NO_WARN_UNIT_ADDR "-Wno-unique_unit_address")
|
|
endif()
|
|
|
|
set(VALID_EXTRA_DTC_FLAGS "")
|
|
foreach(extra_opt ${EXTRA_DTC_FLAGS})
|
|
check_dtc_flag(${extra_opt} check)
|
|
if (check)
|
|
list(APPEND VALID_EXTRA_DTC_FLAGS ${extra_opt})
|
|
endif()
|
|
endforeach()
|
|
set(EXTRA_DTC_FLAGS ${VALID_EXTRA_DTC_FLAGS})
|
|
|
|
execute_process(
|
|
COMMAND ${DTC}
|
|
-O dts
|
|
-o - # Write output to stdout, which we discard below
|
|
-b 0
|
|
-E unit_address_vs_reg
|
|
${DTC_NO_WARN_UNIT_ADDR}
|
|
${DTC_WARN_UNIT_ADDR_IF_ENABLED}
|
|
${EXTRA_DTC_FLAGS} # User settable
|
|
${ZEPHYR_DTS}
|
|
OUTPUT_QUIET # Discard stdout
|
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
COMMAND_ERROR_IS_FATAL ANY
|
|
)
|
|
|
|
endif(DTC)
|
|
|
|
build_info(devicetree files PATH ${dts_files})
|
|
build_info(devicetree include-dirs PATH ${DTS_ROOT_SYSTEM_INCLUDE_DIRS})
|
|
build_info(devicetree bindings-dirs PATH ${DTS_ROOT_BINDINGS})
|