22# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
33
44from pathlib import Path
5- import re
5+ import json
66import shutil
77
88from jinja2 import Environment , FileSystemLoader
99from west .commands import WestCommand
1010from west import log
1111from yaml import load
12+ import jsonschema
1213
1314try :
1415 from yaml import CLoader as Loader
1920SCRIPT_DIR = Path (__file__ ).absolute ().parent
2021TEMPLATE_DIR = SCRIPT_DIR / "templates"
2122CONFIG = SCRIPT_DIR / "config.yml"
22-
23- VENDOR_RE = re .compile (r"^[a-zA-Z0-9_-]+$" )
24- BOARD_RE = re .compile (r"^[a-zA-Z0-9_-]+$" )
23+ SCHEMA = SCRIPT_DIR / "schema.json"
2524
2625
2726class NcsGenboard (WestCommand ):
@@ -36,31 +35,38 @@ def do_add_parser(self, parser_adder):
3635 self .name , help = self .help , description = self .description
3736 )
3837
39- parser .add_argument (
40- "-o" , "--output" , required = True , type = Path , help = "Output directory"
38+ group = parser .add_mutually_exclusive_group (required = True )
39+ group .add_argument (
40+ "-s" , "--json-schema" , action = "store_true" , help = "Provide JSON schema"
4141 )
42- parser .add_argument ("-e" , "--vendor" , required = True , help = "Vendor name" )
43- parser .add_argument ("-b" , "--board" , required = True , help = "Board name" )
44- parser .add_argument (
45- "-d" , "--board-desc" , required = True , help = "Board description"
42+ group .add_argument (
43+ "-r" , "--json-schema-response" , type = str , help = "JSON schema response"
4644 )
47- parser .add_argument ("-s" , "--soc" , required = True , help = "SoC" )
48- parser .add_argument ("-v" , "--variant" , required = True , help = "Variant" )
4945
5046 return parser
5147
5248 def do_run (self , args , unknown_args ):
49+ with open (SCHEMA , "r" ) as f :
50+ schema = json .loads (f .read ())
51+
52+ if args .json_schema :
53+ print (json .dumps (schema ))
54+ return
55+
5356 with open (CONFIG , "r" ) as f :
5457 config = load (f , Loader = Loader )
5558
5659 # validate input
57- if not VENDOR_RE .match (args .vendor ):
58- log .err (f"Invalid vendor name: { args .vendor } " )
59- return
60+ input = json .loads (args .json_schema_response )
6061
61- if not BOARD_RE .match (args .board ):
62- log .err (f"Invalid board name: { args .board } " )
63- return
62+ try :
63+ jsonschema .validate (input , schema )
64+ except jsonschema .ValidationError as e :
65+ raise Exception ("Board configuration is not valid" ) from e
66+
67+ soc_parts = input ["soc" ].split ("-" )
68+ req_soc = soc_parts [0 ].lower ()
69+ req_variant = soc_parts [1 ].lower ()
6470
6571 series = None
6672 soc = None
@@ -69,18 +75,18 @@ def do_run(self, args, unknown_args):
6975 break
7076
7177 for soc_ in product ["socs" ]:
72- if args . soc == soc_ ["name" ]:
78+ if req_soc == soc_ ["name" ]:
7379 series = product ["series" ]
7480 soc = soc_
7581 break
7682
7783 if not series :
78- log .err (f"Invalid/unsupported SoC: { args . soc } " )
84+ log .err (f"Invalid/unsupported SoC: { req_soc } " )
7985 return
8086
8187 targets = []
8288 for variant in soc ["variants" ]:
83- if args . variant == variant ["name" ]:
89+ if req_variant == variant ["name" ]:
8490 if "cores" in variant :
8591 for core in variant ["cores" ]:
8692 target = {
@@ -119,7 +125,7 @@ def do_run(self, args, unknown_args):
119125 break
120126
121127 if not targets :
122- log .err (f"Invalid/unsupported variant: { args . variant } " )
128+ log .err (f"Invalid/unsupported variant: { req_variant } " )
123129 return
124130
125131 # prepare Jinja environment
@@ -129,16 +135,16 @@ def do_run(self, args, unknown_args):
129135 loader = FileSystemLoader (TEMPLATE_DIR / series ),
130136 )
131137
132- env .globals ["vendor" ] = args . vendor
133- env .globals ["board" ] = args . board
134- env .globals ["board_desc" ] = args . board_desc
138+ env .globals ["vendor" ] = input [ " vendor" ]
139+ env .globals ["board" ] = input [ " board" ]
140+ env .globals ["board_desc" ] = input [ "description" ]
135141 env .globals ["series" ] = series
136- env .globals ["soc" ] = args . soc
137- env .globals ["variant" ] = args . variant
142+ env .globals ["soc" ] = req_soc
143+ env .globals ["variant" ] = req_variant
138144 env .globals ["targets" ] = targets
139145
140146 # render templates/copy files
141- out_dir = args . output / args . vendor / args . board
147+ out_dir = Path ( input [ "root" ]) / "boards" / input [ " vendor" ] / input [ " board" ]
142148 if not out_dir .exists ():
143149 out_dir .mkdir (parents = True )
144150
@@ -147,14 +153,14 @@ def do_run(self, args, unknown_args):
147153 shutil .copy (tmpl , out_dir )
148154
149155 tmpl = TEMPLATE_DIR / series / "board-pinctrl.dtsi"
150- shutil .copy (tmpl , out_dir / f"{ args . board } -pinctrl.dtsi" )
156+ shutil .copy (tmpl , out_dir / f"{ input [ ' board' ] } -pinctrl.dtsi" )
151157
152158 tmpl = env .get_template ("board.cmake.jinja2" )
153159 with open (out_dir / "board.cmake" , "w" ) as f :
154160 f .write (tmpl .render ())
155161
156162 tmpl = env .get_template ("Kconfig.board.jinja2" )
157- with open (out_dir / f"Kconfig.{ args . board } " , "w" ) as f :
163+ with open (out_dir / f"Kconfig.{ input [ ' board' ] } " , "w" ) as f :
158164 f .write (tmpl .render ())
159165
160166 tmpl = env .get_template ("board.yml.jinja2" )
@@ -168,23 +174,23 @@ def do_run(self, args, unknown_args):
168174 # nrf53 specific files
169175 if series == "nrf53" :
170176 tmpl = env .get_template ("board-cpuapp_partitioning.dtsi.jinja2" )
171- with open (out_dir / f"{ args . board } -cpuapp_partitioning.dtsi" , "w" ) as f :
177+ with open (out_dir / f"{ input [ ' board' ] } -cpuapp_partitioning.dtsi" , "w" ) as f :
172178 f .write (tmpl .render (config ))
173179
174180 tmpl = TEMPLATE_DIR / series / "board-shared_sram.dtsi"
175- shutil .copy (tmpl , out_dir / f"{ args . board } -shared_sram.dtsi" )
181+ shutil .copy (tmpl , out_dir / f"{ input [ ' board' ] } -shared_sram.dtsi" )
176182
177183 # nrf91 specific files
178184 if series == "nrf91" :
179185 tmpl = env .get_template ("board-partitioning.dtsi.jinja2" )
180- with open (out_dir / f"{ args . board } -partitioning.dtsi" , "w" ) as f :
186+ with open (out_dir / f"{ input [ ' board' ] } -partitioning.dtsi" , "w" ) as f :
181187 f .write (tmpl .render (config ))
182188
183189 # per-target files
184190 for target in targets :
185- name = args . board
191+ name = input [ " board" ]
186192 if target .get ("core" ):
187- name += f"_{ args . soc } _{ target ['core' ]} "
193+ name += f"_{ req_soc } _{ target ['core' ]} "
188194 if target ["ns" ]:
189195 name += "_ns"
190196 if target ["xip" ]:
@@ -201,3 +207,5 @@ def do_run(self, args, unknown_args):
201207 tmpl = env .get_template ("board_twister.yml.jinja2" )
202208 with open (out_dir / f"{ name } .yml" , "w" ) as f :
203209 f .write (tmpl .render (target = target ))
210+
211+ print (f"Board { input ['board' ]} generated successfully" )
0 commit comments