| #!/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)) |