1#!/usr/bin/env python 2""" 3Utility script to assist in the migration of a board from hardware model v1 4(HWMv1) to hardware model v2 (HWMv2). 5 6.. warning:: 7 This script is not a complete migration tool. It is meant to assist in the 8 migration process, but it does not handle all cases. 9 10This script requires the following arguments: 11 12- ``-b|--board``: The name of the board to migrate. 13- ``-g|--group``: The group the board belongs to. This is used to group a set of 14 boards in the same folder. In HWMv2, the boards are no longer organized by 15 architecture. 16- ``-v|--vendor``: The vendor name. 17- ``-s|--soc``: The SoC name. 18 19In some cases, the new board name will differ from the old board name. For 20example, the old board name may have the SoC name as a suffix, while in HWMv2, 21this is no longer needed. In such cases, ``-n|--new-board`` needs to be 22provided. 23 24For boards with variants, ``--variants`` needs to be provided. 25 26For out-of-tree boards, provide ``--board-root`` pointing to the custom board 27root. 28 29Copyright (c) 2023 Nordic Semiconductor ASA 30SPDX-License-Identifier: Apache-2.0 31""" 32 33import argparse 34from pathlib import Path 35import re 36import sys 37 38import ruamel.yaml 39 40 41ZEPHYR_BASE = Path(__file__).parents[2] 42 43 44def board_v1_to_v2(board_root, board, new_board, group, vendor, soc, variants): 45 try: 46 board_path = next(board_root.glob(f"boards/*/{board}")) 47 except StopIteration: 48 sys.exit(f"Board not found: {board}") 49 50 new_board_path = board_root / "boards" / group / new_board 51 if new_board_path.exists(): 52 print("New board already exists, updating board with additional SoC") 53 if not soc: 54 sys.exit("No SoC provided") 55 56 new_board_path.mkdir(parents=True, exist_ok=True) 57 58 print("Moving files to the new board folder...") 59 for f in board_path.iterdir(): 60 f_new = new_board_path / f.name 61 if f_new.exists(): 62 print(f"Skipping existing file: {f_new}") 63 continue 64 f.rename(f_new) 65 66 print("Creating or updating board.yaml...") 67 board_settings_file = new_board_path / "board.yml" 68 if not board_settings_file.exists(): 69 board_settings = { 70 "board": { 71 "name": new_board, 72 "vendor": vendor, 73 "socs": [] 74 } 75 } 76 else: 77 with open(board_settings_file) as f: 78 yaml = ruamel.yaml.YAML(typ='safe', pure=True) 79 board_settings = yaml.load(f) # pylint: disable=assignment-from-no-return 80 81 soc = {"name": soc} 82 if variants: 83 soc["variants"] = [{"name": variant} for variant in variants] 84 85 board_settings["board"]["socs"].append(soc) 86 87 yaml = ruamel.yaml.YAML() 88 yaml.indent(sequence=4, offset=2) 89 with open(board_settings_file, "w") as f: 90 yaml.dump(board_settings, f) 91 92 print(f"Updating {board}_defconfig...") 93 board_defconfig_file = new_board_path / f"{board}_defconfig" 94 with open(board_defconfig_file) as f: 95 board_soc_settings = [] 96 board_defconfig = "" 97 98 dropped_line = False 99 for line in f.readlines(): 100 m = re.match(r"^CONFIG_BOARD_.*$", line) 101 if m: 102 dropped_line = True 103 continue 104 105 m = re.match(r"^CONFIG_(SOC_[A-Z0-9_]+).*$", line) 106 if m: 107 dropped_line = True 108 if not re.match(r"^CONFIG_SOC_SERIES_.*$", line): 109 board_soc_settings.append(m.group(1)) 110 continue 111 112 if dropped_line and re.match(r"^$", line): 113 continue 114 115 dropped_line = False 116 board_defconfig += line 117 118 with open(board_defconfig_file, "w") as f: 119 f.write(board_defconfig) 120 121 print("Updating Kconfig.defconfig...") 122 board_kconfig_defconfig_file = new_board_path / "Kconfig.defconfig" 123 with open(board_kconfig_defconfig_file) as f: 124 board_kconfig_defconfig = "" 125 has_kconfig_defconfig_entries = False 126 127 in_board = False 128 for line in f.readlines(): 129 # drop "config BOARD" entry from Kconfig.defconfig 130 m = re.match(r"^config BOARD$", line) 131 if m: 132 in_board = True 133 continue 134 135 if in_board and re.match(r"^\s+.*$", line): 136 continue 137 138 in_board = False 139 140 m = re.match(r"^config .*$", line) 141 if m: 142 has_kconfig_defconfig_entries = True 143 144 m = re.match(rf"^(.*)BOARD_{board.upper()}(.*)$", line) 145 if m: 146 board_kconfig_defconfig += ( 147 m.group(1) + "BOARD_" + new_board.upper() + m.group(2) + "\n" 148 ) 149 continue 150 151 board_kconfig_defconfig += line 152 153 if has_kconfig_defconfig_entries: 154 with open(board_kconfig_defconfig_file, "w") as f: 155 f.write(board_kconfig_defconfig) 156 else: 157 print("Removing empty Kconfig.defconfig after update...") 158 board_kconfig_defconfig_file.unlink() 159 160 print(f"Creating or updating Kconfig.{new_board}...") 161 board_kconfig_file = new_board_path / "Kconfig.board" 162 copyright = None 163 with open(board_kconfig_file) as f: 164 for line in f.readlines(): 165 if "Copyright" in line: 166 copyright = line 167 new_board_kconfig_file = new_board_path / f"Kconfig.{new_board}" 168 header = "# SPDX-License-Identifier: Apache-2.0\n" 169 if copyright is not None: 170 header = copyright + header 171 selects = "\n\t" + "\n\t".join(["select " + setting for setting in board_soc_settings]) + "\n" 172 if not new_board_kconfig_file.exists(): 173 with open(new_board_kconfig_file, "w") as f: 174 f.write( 175 header + 176 f"\nconfig BOARD_{new_board.upper()}{selects}" 177 ) 178 else: 179 with open(new_board_kconfig_file, "a") as f: 180 f.write(selects) 181 182 print("Removing old Kconfig.board...") 183 board_kconfig_file.unlink() 184 185 print("Conversion done!") 186 187 188if __name__ == "__main__": 189 parser = argparse.ArgumentParser(allow_abbrev=False) 190 191 parser.add_argument( 192 "--board-root", 193 type=Path, 194 default=ZEPHYR_BASE, 195 help="Board root", 196 ) 197 198 parser.add_argument("-b", "--board", type=str, required=True, help="Board name") 199 parser.add_argument("-n", "--new-board", type=str, help="New board name") 200 parser.add_argument("-g", "--group", type=str, required=True, help="Board group") 201 parser.add_argument("-v", "--vendor", type=str, required=True, help="Vendor name") 202 parser.add_argument("-s", "--soc", type=str, required=True, help="Board SoC") 203 parser.add_argument("--variants", nargs="+", default=[], help="Board variants") 204 205 args = parser.parse_args() 206 207 board_v1_to_v2( 208 args.board_root, 209 args.board, 210 args.new_board or args.board, 211 args.group, 212 args.vendor, 213 args.soc, 214 args.variants, 215 ) 216