You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.2 KiB
136 lines
4.2 KiB
#!/usr/bin/env python3 |
|
|
|
# Copyright (c) 2024 Vestas Wind Systems A/S |
|
# Copyright (c) 2020 Nordic Semiconductor ASA |
|
# SPDX-License-Identifier: Apache-2.0 |
|
|
|
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 |
|
# sure to test both ways it can be used. |
|
# |
|
# (It's done this way to keep west optional, making it possible to run |
|
# 'ninja shields' in a build directory without west installed.) |
|
# |
|
|
|
@dataclass(frozen=True) |
|
class Shield: |
|
name: str |
|
dir: Path |
|
full_name: str | None = None |
|
vendor: str | None = None |
|
supported_features: list[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'), |
|
supported_features=shield_data.get('supported_features', []), |
|
) |
|
|
|
def find_shields(args): |
|
ret = [] |
|
|
|
for root in args.board_roots: |
|
for shields in find_shields_in(root): |
|
ret.append(shields) |
|
|
|
return sorted(ret, key=shield_key) |
|
|
|
def find_shields_in(root): |
|
shields = root / 'boards' / 'shields' |
|
ret = [] |
|
|
|
if not shields.exists(): |
|
return ret |
|
|
|
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(): |
|
file_name = maybe_overlay.name |
|
if file_name.endswith('.overlay'): |
|
shield_name = file_name[:-len('.overlay')] |
|
ret.append(Shield(shield_name, maybe_shield)) |
|
|
|
return sorted(ret, key=shield_key) |
|
|
|
def parse_args(): |
|
parser = argparse.ArgumentParser(allow_abbrev=False) |
|
add_args(parser) |
|
add_args_formatting(parser) |
|
return parser.parse_args() |
|
|
|
def add_args(parser): |
|
# Remember to update west-completion.bash if you add or remove |
|
# flags |
|
parser.add_argument("--board-root", dest='board_roots', default=[], |
|
type=Path, action='append', |
|
help='add a board root, may be given more than once') |
|
|
|
def add_args_formatting(parser): |
|
parser.add_argument("--json", action='store_true', |
|
help='''output list of shields in JSON format''') |
|
|
|
def dump_shields(shields): |
|
if args.json: |
|
print( |
|
json.dumps([{'dir': shield.dir.as_posix(), 'name': shield.name} for shield in shields]) |
|
) |
|
else: |
|
for shield in shields: |
|
print(f' {shield.name}') |
|
|
|
if __name__ == '__main__': |
|
args = parse_args() |
|
dump_shields(find_shields(args))
|
|
|