Browse Source

cmake: scripts: support board extension

Fixes: #69548

Support extending an existing board with new board variants.

This commit introduces the following changes to allow a board to be
extended out-of-tree.

The board yaml schema is extended to support an extend field which
will be used to identify the board to be extended.

A board 'plank' can be extended like this:
> board:
>   extend: plank
>   variants:
>     - name: ext
>       qualifier: soc1

For the rest of the build system this means that there is no longer a
single board directory.
The existing CMake variable BOARD_DIR is kept and reference the
directory which defines the board.
A new CMake variable BOARD_DIRECTORIES provides a list of all
directories which defines board targets for the board.
This means the directory which defines the board as well as all
directories that extends the board.

Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
pull/80202/head
Torsten Rasmussen 1 year ago committed by Henrik Brix Andersen
parent
commit
536d34fa7a
  1. 4
      Kconfig.zephyr
  2. 2
      boards/Kconfig
  3. 6
      boards/Kconfig.v1
  4. 2
      boards/Kconfig.v2
  5. 34
      cmake/modules/boards.cmake
  6. 40
      cmake/modules/dts.cmake
  7. 14
      cmake/modules/hwm_v2.cmake
  8. 7
      cmake/modules/kconfig.cmake
  9. 4
      cmake/modules/kernel.cmake
  10. 12
      doc/_extensions/zephyr/kconfig/__init__.py
  11. 4
      doc/_scripts/gen_boards_catalog.py
  12. 12
      scripts/ci/check_compliance.py
  13. 4
      scripts/ci/test_plan.py
  14. 2
      scripts/kconfig/lint.py
  15. 125
      scripts/list_boards.py
  16. 2
      scripts/pylib/twister/twisterlib/testplan.py
  17. 22
      scripts/schemas/board-schema.yml
  18. 4
      scripts/west_commands/boards.py
  19. 2
      share/sysbuild/Kconfig

4
Kconfig.zephyr

@ -17,13 +17,13 @@ osource "${APPLICATION_SOURCE_DIR}/VERSION" @@ -17,13 +17,13 @@ osource "${APPLICATION_SOURCE_DIR}/VERSION"
# Shield defaults should have precedence over board defaults, which should have
# precedence over SoC defaults, so include them in that order.
#
# $ARCH and $BOARD_DIR will be glob patterns when building documentation.
# $ARCH and $KCONFIG_BOARD_DIR will be glob patterns when building documentation.
# This loads custom shields defconfigs (from BOARD_ROOT)
osource "$(KCONFIG_BINARY_DIR)/Kconfig.shield.defconfig"
# This loads Zephyr base shield defconfigs
source "boards/shields/*/Kconfig.defconfig"
osource "$(BOARD_DIR)/Kconfig.defconfig"
osource "$(KCONFIG_BOARD_DIR)/Kconfig.defconfig"
# This loads Zephyr specific SoC root defconfigs
source "$(KCONFIG_BINARY_DIR)/soc/Kconfig.defconfig"

2
boards/Kconfig

@ -129,7 +129,7 @@ config QEMU_EXTRA_FLAGS @@ -129,7 +129,7 @@ config QEMU_EXTRA_FLAGS
GDBstub over serial with `-serial tcp:127.0.0.1:5678,server`
# There might not be any board options, hence the optional source
osource "$(BOARD_DIR)/Kconfig"
osource "$(KCONFIG_BOARD_DIR)/Kconfig"
endmenu
config BOARD_HAS_TIMING_FUNCTIONS

6
boards/Kconfig.v1

@ -2,9 +2,13 @@ @@ -2,9 +2,13 @@
# SPDX-License-Identifier: Apache-2.0
# In HWMv1 the KCONFIG_BOARD_DIR points directly to the BOARD_DIR.
# Set the BOARD_DIR variable for backwards compatibility to legacy hardware model.
BOARD_DIR := $(KCONFIG_BOARD_DIR)
choice
prompt "Board Selection"
source "$(BOARD_DIR)/Kconfig.board"
source "$(KCONFIG_BOARD_DIR)/Kconfig.board"
endchoice

2
boards/Kconfig.v2

@ -25,4 +25,4 @@ config BOARD_QUALIFIERS @@ -25,4 +25,4 @@ config BOARD_QUALIFIERS
For example, if building for ``nrf5340dk/nrf5340/cpuapp`` then this will contain the
value ``nrf5340/cpuapp``.
osource "$(BOARD_DIR)/Kconfig.$(BOARD)"
osource "$(KCONFIG_BOARD_DIR)/Kconfig.$(BOARD)"

34
cmake/modules/boards.cmake

@ -185,9 +185,7 @@ set(format_str "{NAME}\;{DIR}\;{HWM}\;") @@ -185,9 +185,7 @@ set(format_str "{NAME}\;{DIR}\;{HWM}\;")
set(format_str "${format_str}{REVISION_FORMAT}\;{REVISION_DEFAULT}\;{REVISION_EXACT}\;")
set(format_str "${format_str}{REVISIONS}\;{SOCS}\;{QUALIFIERS}")
if(BOARD_DIR)
set(board_dir_arg "--board-dir=${BOARD_DIR}")
endif()
list(TRANSFORM BOARD_DIRECTORIES PREPEND "--board-dir=" OUTPUT_VARIABLE board_dir_arg)
execute_process(${list_boards_commands} --board=${BOARD} ${board_dir_arg}
--cmakeformat=${format_str}
OUTPUT_VARIABLE ret_board
@ -200,29 +198,15 @@ endif() @@ -200,29 +198,15 @@ endif()
if(NOT "${ret_board}" STREQUAL "")
string(STRIP "${ret_board}" ret_board)
string(FIND "${ret_board}" "\n" idx REVERSE)
if(idx GREATER -1)
while(TRUE)
math(EXPR start "${idx} + 1")
string(SUBSTRING "${ret_board}" ${start} -1 line)
string(SUBSTRING "${ret_board}" 0 ${idx} ret_board)
cmake_parse_arguments(LIST_BOARD "" "DIR" "" ${line})
set(board_dirs "${board_dirs}\n${LIST_BOARD_DIR}")
if(idx EQUAL -1)
break()
endif()
string(FIND "${ret_board}" "\n" idx REVERSE)
endwhile()
message(FATAL_ERROR "Multiple boards named '${BOARD}' found in:${board_dirs}")
endif()
set(single_val "NAME;DIR;HWM;REVISION_FORMAT;REVISION_DEFAULT;REVISION_EXACT")
set(multi_val "REVISIONS;SOCS;QUALIFIERS")
set(single_val "NAME;HWM;REVISION_FORMAT;REVISION_DEFAULT;REVISION_EXACT")
set(multi_val "DIR;REVISIONS;SOCS;QUALIFIERS")
cmake_parse_arguments(LIST_BOARD "" "${single_val}" "${multi_val}" ${ret_board})
set(BOARD_DIR ${LIST_BOARD_DIR} CACHE PATH "Board directory for board (${BOARD})" FORCE)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${BOARD_DIR}/board.yml)
list(GET LIST_BOARD_DIR 0 BOARD_DIR)
set(BOARD_DIR ${BOARD_DIR} CACHE PATH "Main board directory for board (${BOARD})" FORCE)
set(BOARD_DIRECTORIES ${LIST_BOARD_DIR} CACHE INTERNAL "List of board directories for board (${BOARD})" FORCE)
foreach(dir ${BOARD_DIRECTORIES})
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/board.yml)
endforeach()
# Create two CMake variables identifying the hw model.
# CMake variable: HWM=[v1,v2]

40
cmake/modules/dts.cmake

@ -76,9 +76,9 @@ find_package(Dtc 1.4.6) @@ -76,9 +76,9 @@ find_package(Dtc 1.4.6)
#
# Optional variables:
# - BOARD: board name to use when looking for DTS_SOURCE
# - BOARD_DIR: board directory 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 BOARD_DIR
# 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
@ -94,7 +94,7 @@ find_package(Dtc 1.4.6) @@ -94,7 +94,7 @@ find_package(Dtc 1.4.6)
# 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_DIR}/${BOARD}.dts
# ${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.
@ -137,28 +137,30 @@ if(NOT DEFINED DTS_SOURCE) @@ -137,28 +137,30 @@ if(NOT DEFINED DTS_SOURCE)
zephyr_build_string(board_string SHORT shortened_board_string
BOARD ${BOARD} BOARD_QUALIFIERS ${BOARD_QUALIFIERS}
)
if(EXISTS ${BOARD_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 ${BOARD_DIR}/${board_string}.dts AND EXISTS ${BOARD_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 ${BOARD_DIR}/${board_string}.dts)
set(DTS_SOURCE ${BOARD_DIR}/${board_string}.dts)
elseif(EXISTS ${BOARD_DIR}/${shortened_board_string}.dts)
set(DTS_SOURCE ${BOARD_DIR}/${shortened_board_string}.dts)
endif()
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_DIR} DTS DTS_SOURCE)
zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DTS DTS_SOURCE)
zephyr_file(
CONF_FILES ${BOARD_DIR}
CONF_FILES ${BOARD_DIRECTORIES}
DTS no_rev_suffix_dts_board_overlays
BOARD ${BOARD}
BOARD_QUALIFIERS ${BOARD_QUALIFIERS}

14
cmake/modules/hwm_v2.cmake

@ -95,11 +95,15 @@ endwhile() @@ -95,11 +95,15 @@ endwhile()
list(REMOVE_DUPLICATES kconfig_soc_source_dir)
# Support multiple ARCH_ROOT, SOC_ROOT and BOARD_ROOT
kconfig_gen("arch" "Kconfig" "${kconfig_arch_source_dir}" "Zephyr Arch Kconfig")
kconfig_gen("soc" "Kconfig.defconfig" "${kconfig_soc_source_dir}" "Zephyr SoC defconfig")
kconfig_gen("soc" "Kconfig" "${kconfig_soc_source_dir}" "Zephyr SoC Kconfig")
kconfig_gen("soc" "Kconfig.soc" "${kconfig_soc_source_dir}" "SoC Kconfig")
kconfig_gen("soc" "Kconfig.sysbuild" "${kconfig_soc_source_dir}" "Sysbuild SoC Kconfig")
kconfig_gen("arch" "Kconfig" "${kconfig_arch_source_dir}" "Zephyr Arch Kconfig")
kconfig_gen("soc" "Kconfig.defconfig" "${kconfig_soc_source_dir}" "Zephyr SoC defconfig")
kconfig_gen("soc" "Kconfig" "${kconfig_soc_source_dir}" "Zephyr SoC Kconfig")
kconfig_gen("soc" "Kconfig.soc" "${kconfig_soc_source_dir}" "SoC Kconfig")
kconfig_gen("soc" "Kconfig.sysbuild" "${kconfig_soc_source_dir}" "Sysbuild SoC Kconfig")
kconfig_gen("boards" "Kconfig.defconfig" "${BOARD_DIRECTORIES}" "Zephyr board defconfig")
kconfig_gen("boards" "Kconfig.${BOARD}" "${BOARD_DIRECTORIES}" "board Kconfig")
kconfig_gen("boards" "Kconfig" "${BOARD_DIRECTORIES}" "Zephyr board Kconfig")
kconfig_gen("boards" "Kconfig.sysbuild" "${BOARD_DIRECTORIES}" "Sysbuild board Kconfig")
# Clear variables created by cmake_parse_arguments
unset(SOC_V2_NAME)

7
cmake/modules/kconfig.cmake

@ -21,9 +21,12 @@ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/config) @@ -21,9 +21,12 @@ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/config)
set_ifndef(KCONFIG_NAMESPACE "CONFIG")
set_ifndef(KCONFIG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Kconfig)
set(KCONFIG_BOARD_DIR ${KCONFIG_BINARY_DIR}/boards)
file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR})
if(HWMv1)
# HWMv1 only supoorts a single board dir which points directly to the board dir.
set(KCONFIG_BOARD_DIR ${BOARD_DIR})
# Support multiple SOC_ROOT
file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR}/soc)
set(kconfig_soc_root ${SOC_ROOT})
@ -73,7 +76,7 @@ else() @@ -73,7 +76,7 @@ else()
endif()
if(NOT DEFINED BOARD_DEFCONFIG)
zephyr_file(CONF_FILES ${BOARD_DIR} DEFCONFIG BOARD_DEFCONFIG)
zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DEFCONFIG BOARD_DEFCONFIG)
endif()
if(DEFINED BOARD_REVISION)
@ -157,7 +160,7 @@ set(COMMON_KCONFIG_ENV_SETTINGS @@ -157,7 +160,7 @@ set(COMMON_KCONFIG_ENV_SETTINGS
APP_VERSION_TWEAK_STRING=${APP_VERSION_TWEAK_STRING}
CONFIG_=${KCONFIG_NAMESPACE}_
KCONFIG_CONFIG=${DOTCONFIG}
BOARD_DIR=${BOARD_DIR}
KCONFIG_BOARD_DIR=${KCONFIG_BOARD_DIR}
BOARD=${BOARD}
BOARD_REVISION=${BOARD_REVISION}
BOARD_QUALIFIERS=${BOARD_QUALIFIERS}

4
cmake/modules/kernel.cmake

@ -173,7 +173,9 @@ if(CONFIG_LLEXT AND CONFIG_LLEXT_TYPE_ELF_SHAREDLIB) @@ -173,7 +173,9 @@ if(CONFIG_LLEXT AND CONFIG_LLEXT_TYPE_ELF_SHAREDLIB)
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
endif()
include(${BOARD_DIR}/board.cmake OPTIONAL)
foreach(dir ${BOARD_DIRECTORIES})
include(${dir}/board.cmake OPTIONAL)
endforeach()
# If we are using a suitable ethernet driver inside qemu, then these options
# must be set, otherwise a zephyr instance cannot receive any network packets.

12
doc/_extensions/zephyr/kconfig/__init__.py

@ -91,7 +91,7 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]: @@ -91,7 +91,7 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]:
root_args = argparse.Namespace(**{'soc_roots': [Path(ZEPHYR_BASE)]})
v2_systems = list_hardware.find_v2_systems(root_args)
soc_folders = {soc.folder for soc in v2_systems.get_socs()}
soc_folders = {soc.folder[0] for soc in v2_systems.get_socs()}
with open(Path(td) / "soc" / "Kconfig.defconfig", "w") as f:
f.write('')
@ -114,8 +114,9 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]: @@ -114,8 +114,9 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]:
(Path(td) / 'boards').mkdir(exist_ok=True)
root_args = argparse.Namespace(**{'board_roots': [Path(ZEPHYR_BASE)],
'soc_roots': [Path(ZEPHYR_BASE)], 'board': None})
v2_boards = list_boards.find_v2_boards(root_args)
'soc_roots': [Path(ZEPHYR_BASE)], 'board': None,
'board_dir': []})
v2_boards = list_boards.find_v2_boards(root_args).values()
with open(Path(td) / "boards" / "Kconfig.boards", "w") as f:
for board in v2_boards:
@ -126,7 +127,8 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]: @@ -126,7 +127,8 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]:
board_str = 'BOARD_' + re.sub(r"[^a-zA-Z0-9_]", "_", qualifier).upper()
f.write('config ' + board_str + '\n')
f.write('\t bool\n')
f.write('source "' + (board.dir / ('Kconfig.' + board.name)).as_posix() + '"\n\n')
f.write('source "' +
(board.directories[0] / ('Kconfig.' + board.name)).as_posix() + '"\n\n')
# base environment
os.environ["ZEPHYR_BASE"] = str(ZEPHYR_BASE)
@ -140,7 +142,7 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]: @@ -140,7 +142,7 @@ def kconfig_load(app: Sphinx) -> Tuple[kconfiglib.Kconfig, Dict[str, str]]:
os.environ["HWM_SCHEME"] = "v2"
os.environ["BOARD"] = "boards"
os.environ["BOARD_DIR"] = str(Path(td) / "boards")
os.environ["KCONFIG_BOARD_DIR"] = str(Path(td) / "boards")
# insert external Kconfigs to the environment
module_paths = dict()

4
doc/_scripts/gen_boards_catalog.py

@ -70,7 +70,7 @@ def get_catalog(): @@ -70,7 +70,7 @@ def get_catalog():
arch_roots=module_settings["arch_root"],
board_roots=module_settings["board_root"],
soc_roots=module_settings["soc_root"],
board_dir=ZEPHYR_BASE / "boards",
board_dir=[],
board=None,
)
@ -78,7 +78,7 @@ def get_catalog(): @@ -78,7 +78,7 @@ def get_catalog():
systems = list_hardware.find_v2_systems(args_find_boards)
board_catalog = {}
for board in boards:
for board in boards.values():
# We could use board.vendor but it is often incorrect. Instead, deduce vendor from
# containing folder. There are a few exceptions, like the "native" and "others" folders
# which we know are not actual vendors so treat them as such.

12
scripts/ci/check_compliance.py

@ -511,8 +511,9 @@ class KconfigCheck(ComplianceTest): @@ -511,8 +511,9 @@ class KconfigCheck(ComplianceTest):
soc_roots = self.get_module_setting_root('soc', settings_file)
soc_roots.insert(0, Path(ZEPHYR_BASE))
root_args = argparse.Namespace(**{'board_roots': board_roots,
'soc_roots': soc_roots, 'board': None})
v2_boards = list_boards.find_v2_boards(root_args)
'soc_roots': soc_roots, 'board': None,
'board_dir': []})
v2_boards = list_boards.find_v2_boards(root_args).values()
with open(kconfig_defconfig_file, 'w') as fp:
for board in v2_boards:
@ -546,7 +547,7 @@ class KconfigCheck(ComplianceTest): @@ -546,7 +547,7 @@ class KconfigCheck(ComplianceTest):
root_args = argparse.Namespace(**{'soc_roots': soc_roots})
v2_systems = list_hardware.find_v2_systems(root_args)
soc_folders = {soc.folder for soc in v2_systems.get_socs()}
soc_folders = {soc.folder[0] for soc in v2_systems.get_socs()}
with open(kconfig_defconfig_file, 'w') as fp:
for folder in soc_folders:
fp.write('osource "' + (Path(folder) / 'Kconfig.defconfig').as_posix() + '"\n')
@ -616,7 +617,7 @@ class KconfigCheck(ComplianceTest): @@ -616,7 +617,7 @@ class KconfigCheck(ComplianceTest):
os.makedirs(os.path.join(kconfiglib_dir, 'soc'), exist_ok=True)
os.makedirs(os.path.join(kconfiglib_dir, 'arch'), exist_ok=True)
os.environ["BOARD_DIR"] = kconfiglib_boards_dir
os.environ["KCONFIG_BOARD_DIR"] = kconfiglib_boards_dir
self.get_v2_model(kconfiglib_dir, os.path.join(kconfiglib_dir, "settings_file.txt"))
# Tells Kconfiglib to generate warnings for all references to undefined
@ -920,6 +921,9 @@ flagged. @@ -920,6 +921,9 @@ flagged.
# Zephyr toolchain variant and therefore not
# visible to compliance.
"BOARD_", # Used as regex in scripts/utils/board_v1_to_v2.py
"BOARD_MPS2_AN521_CPUTEST", # Used for board and SoC extension feature tests
"BOARD_NATIVE_SIM_NATIVE_64_TWO", # Used for board and SoC extension feature tests
"BOARD_NATIVE_SIM_NATIVE_ONE", # Used for board and SoC extension feature tests
"BOOT_DIRECT_XIP", # Used in sysbuild for MCUboot configuration
"BOOT_DIRECT_XIP_REVERT", # Used in sysbuild for MCUboot configuration
"BOOT_FIRMWARE_LOADER", # Used in sysbuild for MCUboot configuration

4
scripts/ci/test_plan.py

@ -239,12 +239,12 @@ class Filters: @@ -239,12 +239,12 @@ class Filters:
# Look for boards in monitored repositories
lb_args = argparse.Namespace(**{'arch_roots': roots, 'board_roots': roots, 'board': None, 'soc_roots':roots,
'board_dir': None})
known_boards = list_boards.find_v2_boards(lb_args)
known_boards = list_boards.find_v2_boards(lb_args).values()
for changed in changed_boards:
for board in known_boards:
c = (zephyr_base / changed).resolve()
if c.is_relative_to(board.dir.resolve()):
if c.is_relative_to(board.directories[0].resolve()):
for file in glob.glob(os.path.join(board.dir, f"{board.name}*.yaml")):
with open(file, 'r', encoding='utf-8') as f:
b = yaml.load(f.read(), Loader=SafeLoader)

2
scripts/kconfig/lint.py

@ -209,7 +209,7 @@ def init_kconfig(): @@ -209,7 +209,7 @@ def init_kconfig():
ZEPHYR_BASE=TOP_DIR,
SOC_DIR="soc",
ARCH_DIR="arch",
BOARD_DIR="boards/*/*",
KCONFIG_BOARD_DIR="boards/*/*",
ARCH="*")
kconf = kconfiglib.Kconfig(suppress_traceback=True)

125
scripts/list_boards.py

@ -4,13 +4,13 @@ @@ -4,13 +4,13 @@
# SPDX-License-Identifier: Apache-2.0
import argparse
from collections import defaultdict
from collections import defaultdict, Counter
from dataclasses import dataclass, field
import itertools
from pathlib import Path
import pykwalify.core
import sys
from typing import List
from typing import List, Union
import yaml
import list_hardware
from list_hardware import unique_paths
@ -91,7 +91,8 @@ class Soc: @@ -91,7 +91,8 @@ class Soc:
@dataclass(frozen=True)
class Board:
name: str
dir: Path
# HWMv1 only supports a single Path, and requires Board dataclass to be hashable.
directories: Union[Path, List[Path]]
hwm: str
full_name: str = None
arch: str = None
@ -103,6 +104,41 @@ class Board: @@ -103,6 +104,41 @@ class Board:
socs: List[Soc] = field(default_factory=list, compare=False)
variants: List[str] = field(default_factory=list, compare=False)
def from_qualifier(self, qualifiers):
qualifiers_list = qualifiers.split('/')
node = Soc(None)
n = len(qualifiers_list)
if n > 0:
soc_qualifier = qualifiers_list.pop(0)
for s in self.socs:
if s.name == soc_qualifier:
node = s
break
if n > 1:
if node.cpuclusters:
cpu_qualifier = qualifiers_list.pop(0)
for c in node.cpuclusters:
if c.name == cpu_qualifier:
node = c
break
else:
node = Variant(None)
for q in qualifiers_list:
for v in node.variants:
if v.name == q:
node = v
break
else:
node = Variant(None)
if node in (Soc(None), Variant(None)):
sys.exit(f'ERROR: qualifiers {qualifiers} not found when extending board {self.name}')
return node
def board_key(board):
return board.name
@ -165,11 +201,10 @@ def find_arch2board_set_in(root, arches, board_dir): @@ -165,11 +201,10 @@ def find_arch2board_set_in(root, arches, board_dir):
for arch in arches:
if not (boards / arch).is_dir():
continue
for maybe_board in (boards / arch).iterdir():
if not maybe_board.is_dir():
continue
if board_dir is not None and board_dir != maybe_board:
if board_dir and maybe_board not in board_dir:
continue
for maybe_defconfig in maybe_board.iterdir():
file_name = maybe_defconfig.name
@ -181,7 +216,8 @@ def find_arch2board_set_in(root, arches, board_dir): @@ -181,7 +216,8 @@ def find_arch2board_set_in(root, arches, board_dir):
def load_v2_boards(board_name, board_yml, systems):
boards = []
boards = {}
board_extensions = []
if board_yml.is_file():
with board_yml.open('r', encoding='utf-8') as f:
b = yaml.load(f.read(), Loader=SafeLoader)
@ -199,6 +235,18 @@ def load_v2_boards(board_name, board_yml, systems): @@ -199,6 +235,18 @@ def load_v2_boards(board_name, board_yml, systems):
board_array = b.get('boards', [b.get('board', None)])
for board in board_array:
mutual_exclusive = {'name', 'extend'}
if len(mutual_exclusive - board.keys()) < 1:
sys.exit(f'ERROR: Malformed "board" section in file: {board_yml.as_posix()}\n'
f'{mutual_exclusive} are mutual exclusive at this level.')
# This is a extending an existing board, place in array to allow later processing.
if 'extend' in board:
board.update({'dir': board_yml.parent})
board_extensions.append(board)
continue
# Create board
if board_name is not None:
if board['name'] != board_name:
# Not the board we're looking for, ignore.
@ -220,9 +268,9 @@ def load_v2_boards(board_name, board_yml, systems): @@ -220,9 +268,9 @@ def load_v2_boards(board_name, board_yml, systems):
socs = [Soc.from_soc(systems.get_soc(s['name']), s.get('variants', []))
for s in board.get('socs', {})]
board = Board(
boards[board['name']] = Board(
name=board['name'],
dir=board_yml.parent,
directories=[board_yml.parent],
vendor=board.get('vendor'),
full_name=board.get('full_name'),
revision_format=board.get('revision', {}).get('format'),
@ -234,8 +282,28 @@ def load_v2_boards(board_name, board_yml, systems): @@ -234,8 +282,28 @@ def load_v2_boards(board_name, board_yml, systems):
variants=[Variant.from_dict(v) for v in board.get('variants', [])],
hwm='v2',
)
boards.append(board)
return boards
board_qualifiers = board_v2_qualifiers(boards[board['name']])
duplicates = [q for q, n in Counter(board_qualifiers).items() if n > 1]
if duplicates:
sys.exit(f'ERROR: Duplicated board qualifiers detected {duplicates} for board: '
f'{board["name"]}.\nPlease check content of: {board_yml.as_posix()}\n')
return boards, board_extensions
def extend_v2_boards(boards, board_extensions):
for e in board_extensions:
board = boards.get(e['extend'])
if board is None:
continue
board.directories.append(e['dir'])
for v in e.get('variants', []):
node = board.from_qualifier(v['qualifier'])
if str(v['qualifier'] + '/' + v['name']) in board_v2_qualifiers(board):
board_yml = e['dir'] / BOARD_YML
sys.exit(f'ERROR: Variant: {v["name"]}, defined multiple times for board: '
f'{board.name}.\nLast defined in {board_yml}')
node.variants.append(Variant.from_dict(v))
# Note that this does not share the args.board functionality of find_v2_boards
@ -253,14 +321,25 @@ def find_v2_boards(args): @@ -253,14 +321,25 @@ def find_v2_boards(args):
root_args = argparse.Namespace(**{'soc_roots': args.soc_roots})
systems = list_hardware.find_v2_systems(root_args)
boards = []
boards = {}
board_extensions = []
board_files = []
for root in unique_paths(args.board_roots):
board_files.extend((root / 'boards').rglob(BOARD_YML))
if args.board_dir:
board_files = [d / BOARD_YML for d in args.board_dir]
else:
for root in unique_paths(args.board_roots):
board_files.extend((root / 'boards').rglob(BOARD_YML))
for board_yml in board_files:
b = load_v2_boards(args.board, board_yml, systems)
boards.extend(b)
b, e = load_v2_boards(args.board, board_yml, systems)
conflict_boards = set(boards.keys()).intersection(b.keys())
if conflict_boards:
sys.exit(f'ERROR: Board(s): {conflict_boards}, defined multiple times.\n'
f'Last defined in {board_yml}')
boards.update(b)
board_extensions.extend(e)
extend_v2_boards(boards, board_extensions)
return boards
@ -285,7 +364,7 @@ def add_args(parser): @@ -285,7 +364,7 @@ def add_args(parser):
help='add a soc root, may be given more than once')
parser.add_argument("--board", dest='board', default=None,
help='lookup the specific board, fail if not found')
parser.add_argument("--board-dir", default=None, type=Path,
parser.add_argument("--board-dir", default=[], type=Path, action='append',
help='Only look for boards at the specific location')
@ -327,20 +406,16 @@ def board_v2_qualifiers_csv(board): @@ -327,20 +406,16 @@ def board_v2_qualifiers_csv(board):
def dump_v2_boards(args):
if args.board_dir:
root_args = argparse.Namespace(**{'soc_roots': args.soc_roots})
systems = list_hardware.find_v2_systems(root_args)
boards = load_v2_boards(args.board, args.board_dir / BOARD_YML, systems)
else:
boards = find_v2_boards(args)
boards = find_v2_boards(args)
for b in boards:
for b in boards.values():
qualifiers_list = board_v2_qualifiers(b)
if args.cmakeformat is not None:
notfound = lambda x: x or 'NOTFOUND'
info = args.cmakeformat.format(
NAME='NAME;' + b.name,
DIR='DIR;' + str(b.dir.as_posix()),
DIR='DIR;' + ';'.join(
[str(x.as_posix()) for x in b.directories]),
VENDOR='VENDOR;' + notfound(b.vendor),
HWM='HWM;' + b.hwm,
REVISION_DEFAULT='REVISION_DEFAULT;' + notfound(b.revision_default),
@ -365,7 +440,7 @@ def dump_boards(args): @@ -365,7 +440,7 @@ def dump_boards(args):
if args.cmakeformat is not None:
info = args.cmakeformat.format(
NAME='NAME;' + board.name,
DIR='DIR;' + str(board.dir.as_posix()),
DIR='DIR;' + str(board.directories.as_posix()),
HWM='HWM;' + board.hwm,
VENDOR='VENDOR;NOTFOUND',
REVISION_DEFAULT='REVISION_DEFAULT;NOTFOUND',

2
scripts/pylib/twister/twisterlib/testplan.py

@ -442,7 +442,7 @@ class TestPlan: @@ -442,7 +442,7 @@ class TestPlan:
logger.debug(f"Adding platform {platform.name} with aliases {platform.aliases}")
self.platforms.append(platform)
for board in known_boards:
for board in known_boards.values():
new_config_found = False
# don't load the same board data twice
if not bdirs.get(board.dir):

22
scripts/schemas/board-schema.yml

@ -23,17 +23,33 @@ schema;variant-schema: @@ -23,17 +23,33 @@ schema;variant-schema:
required: false
include: variant-schema
schema;extend-variant-schema:
required: false
type: seq
sequence:
- type: map
mapping:
name:
required: true
type: str
qualifier:
required: true
type: str
schema;board-schema:
type: map
mapping:
name:
required: true
required: false # Note: either name or extend is required, but that is handled in python
type: str
desc: Name of the board
full_name:
required: false
type: str
desc: Full name of the board. Typically set to the commercial name of the board.
extend:
required: false # Note: either name or extend is required, but that is handled in python
type: str
vendor:
required: false
type: str
@ -63,7 +79,7 @@ schema;board-schema: @@ -63,7 +79,7 @@ schema;board-schema:
required: true
type: str
socs:
required: true
required: false # Required for name:, but not for extend.
type: seq
sequence:
- type: map
@ -73,6 +89,8 @@ schema;board-schema: @@ -73,6 +89,8 @@ schema;board-schema:
type: str
variants:
include: variant-schema
variants:
include: extend-variant-schema
type: map
mapping:

4
scripts/west_commands/boards.py

@ -97,14 +97,14 @@ class Boards(WestCommand): @@ -97,14 +97,14 @@ class Boards(WestCommand):
log.inf(args.format.format(name=board.name, arch=board.arch,
dir=board.dir, hwm=board.hwm, qualifiers=''))
for board in list_boards.find_v2_boards(args):
for board in list_boards.find_v2_boards(args).values():
if name_re is not None and not name_re.search(board.name):
continue
log.inf(
args.format.format(
name=board.name,
full_name=board.full_name,
dir=board.dir,
dir=board.directories[0],
hwm=board.hwm,
vendor=board.vendor,
qualifiers=list_boards.board_v2_qualifiers_csv(board),

2
share/sysbuild/Kconfig

@ -6,7 +6,7 @@ rsource "Kconfig.$(HWM_SCHEME)" @@ -6,7 +6,7 @@ rsource "Kconfig.$(HWM_SCHEME)"
comment "Sysbuild image configuration"
osource "$(BOARD_DIR)/Kconfig.sysbuild"
osource "$(KCONFIG_BOARD_DIR)/Kconfig.sysbuild"
osource "$(KCONFIG_BINARY_DIR)/soc/Kconfig.sysbuild"
menu "Modules"

Loading…
Cancel
Save