1
- """A linting utility for targets.json"""
1
+ """A linting utility for targets.json
2
+
3
+ This linting utility may be called as follows:
4
+ python <path-to>/lint.py TARGET [TARGET ...]
5
+
6
+ all targets will be linted
7
+ """
2
8
3
9
from os .path import join , abspath , dirname
4
10
if __name__ == "__main__" :
5
11
import sys
6
12
ROOT = abspath (join (dirname (__file__ ), ".." , ".." ))
7
13
sys .path .insert (0 , ROOT )
8
14
from copy import copy
9
- from yaml import dump
15
+ from yaml import dump_all
16
+ import argparse
10
17
from tools .targets import Target , set_targets_json_location , TARGET_MAP
11
18
12
19
def must_have_keys (keys , dict ):
@@ -27,49 +34,74 @@ def may_have_keys(keys, dict):
27
34
if key not in keys :
28
35
yield "%s found, and is not allowed" % key
29
36
37
+ def check_extra_labels (dict ):
38
+ """Check that extra_labels does not contain any Target names
39
+
40
+ is a generator for errors
41
+ """
42
+ for label in (dict .get ("extra_labels" , []) +
43
+ dict .get ("extra_labels_add" , [])):
44
+ if label in Target .get_json_target_data ():
45
+ yield "%s is not allowed in extra_labels" % label
46
+
47
+ def check_release_version (dict ):
48
+ """Verify that release version 5 is combined with support for all toolcahins
49
+
50
+ is a generator for errors
51
+ """
52
+ if ("release_versions" in dict and
53
+ "5" in dict ["release_versions" ] and
54
+ "supported_toolchains" in dict ):
55
+ for toolc in ["GCC_ARM" , "ARM" , "IAR" ]:
56
+ if toolc not in dict ["supported_toolchains" ]:
57
+ yield ("%s not found in supported_toolchains, and is "
58
+ "required by mbed OS 5" % toolc )
59
+
60
+ def check_inherits (dict ):
61
+ if ("inherits" in dict and len (dict ["inherits" ]) > 1 ):
62
+ yield "multiple inheritance is forbidden"
30
63
31
64
MCU_REQUIRED_KEYS = ["release_versions" , "supported_toolchains" ,
32
- "default_lib" , "public" , "inherits" ]
33
- MCU_ALLOWED_KEYS = ["device_has" , "core" , "extra_labels" , "features" ,
34
- "bootloader_supported" , "device_name" , "post_binary_hook" ,
35
- "default_toolchain" ] + MCU_REQUIRED_KEYS
65
+ "default_lib" , "public" , "inherits" , "device_has" ]
66
+ MCU_ALLOWED_KEYS = ["device_has" , "device_has_add" , "device_has_remove" , "core" ,
67
+ "extra_labels" , "features" , "features_add" ,
68
+ "features_remove" , "bootloader_supported" , "device_name" ,
69
+ "post_binary_hook" , "default_toolchain" , "config" ,
70
+ "extra_labels_add" , "extra_labels_remove" ,
71
+ "target_overrides" ] + MCU_REQUIRED_KEYS
36
72
def check_mcu (mcu_json , strict = False ):
37
73
"""Generate a list of problems with an MCU
38
74
39
75
:param: mcu_json the MCU's dict to check
40
76
:param: strict enforce required keys
41
77
"""
78
+ errors = list (may_have_keys (MCU_ALLOWED_KEYS , mcu_json ))
42
79
if strict :
43
- for err in must_have_keys (MCU_REQUIRED_KEYS , mcu_json ):
44
- yield err
45
- for err in may_have_keys ( MCU_ALLOWED_KEYS , mcu_json ):
46
- yield err
80
+ errors . extend ( must_have_keys (MCU_REQUIRED_KEYS , mcu_json ))
81
+ errors . extend ( check_extra_labels ( mcu_json ))
82
+ errors . extend ( check_release_version ( mcu_json ))
83
+ errors . extend ( check_inherits ( mcu_json ))
47
84
if 'public' in mcu_json and mcu_json ['public' ]:
48
- yield "public must be false"
49
- if ("release_versions" in mcu_json and
50
- "5" in mcu_json ["release_versions" ] and
51
- "supported_toolchains" in mcu_json ):
52
- for toolc in ["GCC_ARM" , "ARM" , "IAR" ]:
53
- if toolc not in mcu_json ["supported_toolchains" ]:
54
- yield ("%s not found in supported_toolchains, and is "
55
- "required by mbed OS 5" % toolc )
85
+ errors .append ("public must be false" )
86
+ return errors
56
87
57
88
BOARD_REQUIRED_KEYS = ["inherits" ]
58
89
BOARD_ALLOWED_KEYS = ["supported_form_factors" , "is_disk_virtual" ,
59
- "detect_code" , "device_name" , "extra_labels" ,
60
- "public" ] + BOARD_REQUIRED_KEYS
90
+ "detect_code" , "extra_labels" , "extra_labels_add" ,
91
+ "extra_labels_remove" , "public" , "config" ,
92
+ "forced_reset_timeout" , "target_overrides" ] + BOARD_REQUIRED_KEYS
61
93
def check_board (board_json , strict = False ):
62
94
"""Generate a list of problems with an board
63
95
64
96
:param: board_json the mcus dict to check
65
97
:param: strict enforce required keys
66
98
"""
99
+ errors = list (may_have_keys (BOARD_ALLOWED_KEYS , board_json ))
67
100
if strict :
68
- for err in must_have_keys (BOARD_REQUIRED_KEYS , board_json ):
69
- yield err
70
- for err in may_have_keys (BOARD_ALLOWED_KEYS , board_json ):
71
- yield err
72
-
101
+ errors .extend (must_have_keys (BOARD_REQUIRED_KEYS , board_json ))
102
+ errors .extend (check_extra_labels (board_json ))
103
+ errors .extend (check_inherits (board_json ))
104
+ return errors
73
105
74
106
def add_if (dict , key , val ):
75
107
"""Add a value to a dict if it's non-empty"""
@@ -106,7 +138,7 @@ def _generate_hierarchy_string(mcus, boards):
106
138
global_errors .append ("No MCUS found in heirarchy" )
107
139
mcus_string = "??? ->"
108
140
elif len (mcus ) > 3 :
109
- global_errors .append ("No name for targets: %s" % mcus [3 :])
141
+ global_errors .append ("No name for targets %s" % ", " . join ( mcus [3 :]) )
110
142
mcus_string = MCU_FORMAT_STRING [3 ] % tuple (mcus [:3 ])
111
143
for name in mcus [3 :]:
112
144
mcus_string += " ??? (%s) ->" % name
@@ -117,10 +149,10 @@ def _generate_hierarchy_string(mcus, boards):
117
149
global_errors .append ("no boards found in heirarchy" )
118
150
boards_string = "???"
119
151
elif len (boards ) > 2 :
120
- global_errors .append ("no name for targets: %s" % boards [2 :])
121
- boards_string = BOARD_FORMAT_STRING [3 ] % tuple (boards [:2 ])
152
+ global_errors .append ("no name for targets %s" % ", " . join ( boards [2 :]) )
153
+ boards_string = BOARD_FORMAT_STRING [2 ] % tuple (boards [:2 ])
122
154
for name in boards [2 :]:
123
- boards_string += " ??? (%s)" % name
155
+ boards_string += " -> ??? (%s)" % name
124
156
else :
125
157
boards_string = BOARD_FORMAT_STRING [len (boards )] % tuple (boards )
126
158
return mcus_string + " " + boards_string , global_errors
@@ -134,7 +166,7 @@ def check_hierarchy(tgt):
134
166
target_errors = {}
135
167
hierachy_string , hierachy_errors = _generate_hierarchy_string (mcus , boards )
136
168
to_ret = {"hierarchy" : hierachy_string }
137
- add_if (to_ret , "hierachy errors" , hierachy_errors )
169
+ add_if (to_ret , "hierarchy errors" , hierachy_errors )
138
170
139
171
for name in mcus [:- 1 ]:
140
172
add_if (target_errors , name , list (check_mcu (tgt .json_data [name ])))
@@ -149,16 +181,68 @@ def check_hierarchy(tgt):
149
181
add_if (to_ret , "target errors" , target_errors )
150
182
return to_ret
151
183
184
+ PARSER = argparse .ArgumentParser (prog = "targets/lint.py" )
185
+ SUBPARSERS = PARSER .add_subparsers (title = "Commands" )
186
+
187
+ def subcommand (name , * args , ** kwargs ):
188
+ def __subcommand (command ):
189
+ kwargs ['description' ] = command .__doc__
190
+ subparser = SUBPARSERS .add_parser (name , ** kwargs )
191
+ for arg in args :
192
+ arg = dict (arg )
193
+ opt = arg ['name' ]
194
+ del arg ['name' ]
195
+
196
+ if isinstance (opt , basestring ):
197
+ subparser .add_argument (opt , ** arg )
198
+ else :
199
+ subparser .add_argument (* opt , ** arg )
200
+
201
+ def _thunk (parsed_args ):
202
+ argv = [arg ['dest' ] if 'dest' in arg else arg ['name' ]
203
+ for arg in args ]
204
+ argv = [(arg if isinstance (arg , basestring )
205
+ else arg [- 1 ]).strip ('-' ).replace ('-' , '_' )
206
+ for arg in argv ]
207
+ argv = {arg : vars (parsed_args )[arg ] for arg in argv
208
+ if vars (parsed_args )[arg ] is not None }
209
+
210
+ return command (** argv )
211
+
212
+ subparser .set_defaults (command = _thunk )
213
+ return command
214
+ return __subcommand
215
+
216
+ @subcommand ("targets" ,
217
+ dict (name = "mcus" , nargs = "+" , metavar = "MCU" ,
218
+ choices = TARGET_MAP .keys (), type = str .upper ))
219
+ def targets_cmd (mcus = []):
220
+ """Find and print errors about specific targets"""
221
+ print dump_all ([check_hierarchy (TARGET_MAP [m ]) for m in mcus ],
222
+ default_flow_style = False )
223
+
224
+ @subcommand ("all-targets" )
225
+ def all_targets_cmd ():
226
+ """Print all errors about all parts"""
227
+ print dump_all ([check_hierarchy (m ) for m in TARGET_MAP .values ()],
228
+ default_flow_style = False )
229
+
230
+ @subcommand ("orphans" )
231
+ def orphans_cmd ():
232
+ """Find and print all orphan targets"""
233
+ orphans = Target .get_json_target_data ().keys ()
234
+ for tgt in TARGET_MAP .values ():
235
+ for name in tgt .resolution_order_names :
236
+ if name in orphans :
237
+ orphans .remove (name )
238
+ if orphans :
239
+ print dump_all ([orphans ], default_flow_style = False )
240
+ return len (orphans )
152
241
153
242
def main ():
154
243
"""entry point"""
155
- import argparse
156
- parser = argparse .ArgumentParser ()
157
- parser .add_argument ("mcu" , choices = TARGET_MAP .keys (), metavar = "MCU" , )
158
- options = parser .parse_args ()
159
- print dump (check_hierarchy (TARGET_MAP [options .mcu ]),
160
- default_flow_style = False )
161
- return 0
244
+ options = PARSER .parse_args ()
245
+ return options .command (options )
162
246
163
247
if __name__ == "__main__" :
164
248
sys .exit (main ())
0 commit comments