1# Copyright (c) 2024 The Linux Foundation 2# SPDX-License-Identifier: Apache-2.0 3 4import logging 5from collections import namedtuple 6from pathlib import Path 7 8import list_boards 9import list_hardware 10import yaml 11import zephyr_module 12from gen_devicetree_rest import VndLookup 13 14ZEPHYR_BASE = Path(__file__).parents[2] 15 16logger = logging.getLogger(__name__) 17 18 19def guess_file_from_patterns(directory, patterns, name, extensions): 20 for pattern in patterns: 21 for ext in extensions: 22 matching_file = next(directory.glob(pattern.format(name=name, ext=ext)), None) 23 if matching_file: 24 return matching_file 25 return None 26 27 28def guess_image(board_or_shield): 29 img_exts = ["jpg", "jpeg", "webp", "png"] 30 patterns = [ 31 "**/{name}.{ext}", 32 "**/*{name}*.{ext}", 33 "**/*.{ext}", 34 ] 35 img_file = guess_file_from_patterns( 36 board_or_shield.dir, patterns, board_or_shield.name, img_exts 37 ) 38 39 return (img_file.relative_to(ZEPHYR_BASE)).as_posix() if img_file else None 40 41def guess_doc_page(board_or_shield): 42 patterns = [ 43 "doc/index.{ext}", 44 "**/{name}.{ext}", 45 "**/*{name}*.{ext}", 46 "**/*.{ext}", 47 ] 48 doc_file = guess_file_from_patterns( 49 board_or_shield.dir, patterns, board_or_shield.name, ["rst"] 50 ) 51 return doc_file 52 53 54def get_catalog(): 55 vnd_lookup = VndLookup(ZEPHYR_BASE / "dts/bindings/vendor-prefixes.txt", []) 56 57 module_settings = { 58 "arch_root": [ZEPHYR_BASE], 59 "board_root": [ZEPHYR_BASE], 60 "soc_root": [ZEPHYR_BASE], 61 } 62 63 for module in zephyr_module.parse_modules(ZEPHYR_BASE): 64 for key in module_settings: 65 root = module.meta.get("build", {}).get("settings", {}).get(key) 66 if root is not None: 67 module_settings[key].append(Path(module.project) / root) 68 69 Args = namedtuple("args", ["arch_roots", "board_roots", "soc_roots", "board_dir", "board"]) 70 args_find_boards = Args( 71 arch_roots=module_settings["arch_root"], 72 board_roots=module_settings["board_root"], 73 soc_roots=module_settings["soc_root"], 74 board_dir=[], 75 board=None, 76 ) 77 78 boards = list_boards.find_v2_boards(args_find_boards) 79 systems = list_hardware.find_v2_systems(args_find_boards) 80 board_catalog = {} 81 82 for board in boards.values(): 83 # We could use board.vendor but it is often incorrect. Instead, deduce vendor from 84 # containing folder. There are a few exceptions, like the "native" and "others" folders 85 # which we know are not actual vendors so treat them as such. 86 for folder in board.dir.parents: 87 if folder.name in ["native", "others"]: 88 vendor = "others" 89 break 90 elif vnd_lookup.vnd2vendor.get(folder.name): 91 vendor = folder.name 92 break 93 94 # Grab all the twister files for this board and use them to figure out all the archs it 95 # supports. 96 archs = set() 97 pattern = f"{board.name}*.yaml" 98 for twister_file in board.dir.glob(pattern): 99 try: 100 with open(twister_file) as f: 101 board_data = yaml.safe_load(f) 102 archs.add(board_data.get("arch")) 103 except Exception as e: 104 logger.error(f"Error parsing twister file {twister_file}: {e}") 105 106 socs = {soc.name for soc in board.socs} 107 full_name = board.full_name or board.name 108 doc_page = guess_doc_page(board) 109 110 board_catalog[board.name] = { 111 "name": board.name, 112 "full_name": full_name, 113 "doc_page": doc_page.relative_to(ZEPHYR_BASE).as_posix() if doc_page else None, 114 "vendor": vendor, 115 "archs": list(archs), 116 "socs": list(socs), 117 "image": guess_image(board), 118 } 119 120 socs_hierarchy = {} 121 for soc in systems.get_socs(): 122 family = soc.family or "<no family>" 123 series = soc.series or "<no series>" 124 socs_hierarchy.setdefault(family, {}).setdefault(series, []).append(soc.name) 125 126 return { 127 "boards": board_catalog, 128 "vendors": {**vnd_lookup.vnd2vendor, "others": "Other/Unknown"}, 129 "socs": socs_hierarchy, 130 } 131