#!/usr/bin/env python """ Utility script to assist in the migration of a board from hardware model v1 (HWMv1) to hardware model v2 (HWMv2). .. warning:: This script is not a complete migration tool. It is meant to assist in the migration process, but it does not handle all cases. This script requires the following arguments: - ``-b|--board``: The name of the board to migrate. - ``-g|--group``: The group the board belongs to. This is used to group a set of boards in the same folder. In HWMv2, the boards are no longer organized by architecture. - ``-v|--vendor``: The vendor name. - ``-s|--soc``: The SoC name. In some cases, the new board name will differ from the old board name. For example, the old board name may have the SoC name as a suffix, while in HWMv2, this is no longer needed. In such cases, ``-n|--new-board`` needs to be provided. For boards with variants, ``--variants`` needs to be provided. For out-of-tree boards, provide ``--board-root`` pointing to the custom board root. Copyright (c) 2023 Nordic Semiconductor ASA SPDX-License-Identifier: Apache-2.0 """ import argparse from pathlib import Path import re import sys import ruamel.yaml ZEPHYR_BASE = Path(__file__).parents[2] def board_v1_to_v2(board_root, board, new_board, group, vendor, soc, variants): try: board_path = next(board_root.glob(f"boards/*/{board}")) except StopIteration: sys.exit(f"Board not found: {board}") new_board_path = board_root / "boards" / group / new_board if new_board_path.exists(): print("New board already exists, updating board with additional SoC") if not soc: sys.exit("No SoC provided") new_board_path.mkdir(parents=True, exist_ok=True) print("Moving files to the new board folder...") for f in board_path.iterdir(): f_new = new_board_path / f.name if f_new.exists(): print(f"Skipping existing file: {f_new}") continue f.rename(f_new) print("Creating or updating board.yaml...") board_settings_file = new_board_path / "board.yml" if not board_settings_file.exists(): board_settings = { "board": { "name": new_board, "vendor": vendor, "socs": [] } } else: with open(board_settings_file) as f: yaml = ruamel.yaml.YAML(typ='safe', pure=True) board_settings = yaml.load(f) # pylint: disable=assignment-from-no-return soc = {"name": soc} if variants: soc["variants"] = [{"name": variant} for variant in variants] board_settings["board"]["socs"].append(soc) yaml = ruamel.yaml.YAML() yaml.indent(sequence=4, offset=2) with open(board_settings_file, "w") as f: yaml.dump(board_settings, f) print(f"Updating {board}_defconfig...") board_defconfig_file = new_board_path / f"{board}_defconfig" with open(board_defconfig_file) as f: board_soc_settings = [] board_defconfig = "" dropped_line = False for line in f.readlines(): m = re.match(r"^CONFIG_BOARD_.*$", line) if m: dropped_line = True continue m = re.match(r"^CONFIG_(SOC_[A-Z0-9_]+).*$", line) if m: dropped_line = True if not re.match(r"^CONFIG_SOC_SERIES_.*$", line): board_soc_settings.append(m.group(1)) continue if dropped_line and re.match(r"^$", line): continue dropped_line = False board_defconfig += line with open(board_defconfig_file, "w") as f: f.write(board_defconfig) print("Updating Kconfig.defconfig...") board_kconfig_defconfig_file = new_board_path / "Kconfig.defconfig" with open(board_kconfig_defconfig_file) as f: board_kconfig_defconfig = "" has_kconfig_defconfig_entries = False in_board = False for line in f.readlines(): # drop "config BOARD" entry from Kconfig.defconfig m = re.match(r"^config BOARD$", line) if m: in_board = True continue if in_board and re.match(r"^\s+.*$", line): continue in_board = False m = re.match(r"^config .*$", line) if m: has_kconfig_defconfig_entries = True m = re.match(rf"^(.*)BOARD_{board.upper()}(.*)$", line) if m: board_kconfig_defconfig += ( m.group(1) + "BOARD_" + new_board.upper() + m.group(2) + "\n" ) continue board_kconfig_defconfig += line if has_kconfig_defconfig_entries: with open(board_kconfig_defconfig_file, "w") as f: f.write(board_kconfig_defconfig) else: print("Removing empty Kconfig.defconfig after update...") board_kconfig_defconfig_file.unlink() print(f"Creating or updating Kconfig.{new_board}...") board_kconfig_file = new_board_path / "Kconfig.board" copyright = None with open(board_kconfig_file) as f: for line in f.readlines(): if "Copyright" in line: copyright = line new_board_kconfig_file = new_board_path / f"Kconfig.{new_board}" header = "# SPDX-License-Identifier: Apache-2.0\n" if copyright is not None: header = copyright + header selects = "\n\t" + "\n\t".join(["select " + setting for setting in board_soc_settings]) + "\n" if not new_board_kconfig_file.exists(): with open(new_board_kconfig_file, "w") as f: f.write( header + f"\nconfig BOARD_{new_board.upper()}{selects}" ) else: with open(new_board_kconfig_file, "a") as f: f.write(selects) print("Removing old Kconfig.board...") board_kconfig_file.unlink() print("Conversion done!") if __name__ == "__main__": parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument( "--board-root", type=Path, default=ZEPHYR_BASE, help="Board root", ) parser.add_argument("-b", "--board", type=str, required=True, help="Board name") parser.add_argument("-n", "--new-board", type=str, help="New board name") parser.add_argument("-g", "--group", type=str, required=True, help="Board group") parser.add_argument("-v", "--vendor", type=str, required=True, help="Vendor name") parser.add_argument("-s", "--soc", type=str, required=True, help="Board SoC") parser.add_argument("--variants", nargs="+", default=[], help="Board variants") args = parser.parse_args() board_v1_to_v2( args.board_root, args.board, args.new_board or args.board, args.group, args.vendor, args.soc, args.variants, )