cmake: shields: introduce shield.yml

While legacy shields are still supported, this introduces a shield.yml
file similar to board.yml that allows to more explicitly declare a
shield and to set some useful metadata such as vendor and full name.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
This commit is contained in:
Benjamin Cabé 2025-05-15 15:11:23 +02:00 committed by Benjamin Cabé
commit 3a62b17b85
3 changed files with 99 additions and 0 deletions

View file

@ -17,6 +17,7 @@ under :zephyr_file:`boards/shields`:
.. code-block:: none
boards/shields/<shield>
├── shield.yml
├── <shield>.overlay
├── Kconfig.shield
├── Kconfig.defconfig
@ -24,6 +25,21 @@ under :zephyr_file:`boards/shields`:
These files provides shield configuration as follows:
* **shield.yml**: This file provides metadata about the shield in YAML format.
It must contain the following fields:
* ``name``: Name of the shield used in Kconfig and build system (required)
* ``full_name``: Full commercial name of the shield (required)
* ``vendor``: Manufacturer/vendor of the shield (required)
Example:
.. code-block:: yaml
name: foo_shield
full_name: Foo Shield for Arduino
vendor: acme
* **<shield>.overlay**: This file provides a shield description in devicetree
format that is merged with the board's :ref:`devicetree <dt-guide>`
before compilation.

View file

@ -6,9 +6,24 @@
import argparse
import json
import sys
from dataclasses import dataclass
from pathlib import Path
import pykwalify.core
import yaml
try:
from yaml import CSafeLoader as SafeLoader
except ImportError:
from yaml import SafeLoader
SHIELD_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'shield-schema.yml')
with open(SHIELD_SCHEMA_PATH) as f:
shield_schema = yaml.load(f.read(), Loader=SafeLoader)
SHIELD_YML = 'shield.yml'
#
# This is shared code between the build system's 'shields' target
# and the 'west shields' extension command. If you change it, make
@ -22,10 +37,21 @@ from pathlib import Path
class Shield:
name: str
dir: Path
full_name: str | None = None
vendor: str | None = None
def shield_key(shield):
return shield.name
def process_shield_data(shield_data, shield_dir):
# Create shield from yaml data
return Shield(
name=shield_data['name'],
dir=shield_dir,
full_name=shield_data.get('full_name'),
vendor=shield_data.get('vendor')
)
def find_shields(args):
ret = []
@ -45,6 +71,28 @@ def find_shields_in(root):
for maybe_shield in (shields).iterdir():
if not maybe_shield.is_dir():
continue
# Check for shield.yml first
shield_yml = maybe_shield / SHIELD_YML
if shield_yml.is_file():
with shield_yml.open('r', encoding='utf-8') as f:
shield_data = yaml.load(f.read(), Loader=SafeLoader)
try:
pykwalify.core.Core(source_data=shield_data, schema_data=shield_schema).validate()
except pykwalify.errors.SchemaError as e:
sys.exit(f'ERROR: Malformed shield.yml in file: {shield_yml.as_posix()}\n{e}')
if 'shields' in shield_data:
# Multiple shields format
for shield_info in shield_data['shields']:
ret.append(process_shield_data(shield_info, maybe_shield))
elif 'shield' in shield_data:
# Single shield format
ret.append(process_shield_data(shield_data['shield'], maybe_shield))
continue
# Fallback to legacy method if no shield.yml
for maybe_kconfig in maybe_shield.iterdir():
if maybe_kconfig.name == 'Kconfig.shield':
for maybe_overlay in maybe_shield.iterdir():

View file

@ -0,0 +1,35 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright The Zephyr Project Contributors
# A pykwalify schema for basic validation of the structure of a shield metadata YAML file.
#
# The shield.yml file can contain either a single shield definition or a list of shields.
schema;shield-schema:
type: map
mapping:
name:
required: true
type: str
desc: Name of the shield (used in Kconfig and build system)
full_name:
required: true
type: str
desc: Full name of the shield (typically the commercial name)
vendor:
required: true
type: str
desc: Manufacturer/vendor of the shield
type: map
range:
min: 1
max: 1
mapping:
shield:
include: shield-schema
shields:
type: seq
sequence:
- include: shield-schema