Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
lint.py
00001 """A linting utility for targets.json 00002 00003 This linting utility may be called as follows: 00004 python <path-to>/lint.py targets TARGET [TARGET ...] 00005 00006 all targets will be linted 00007 """ 00008 00009 # mbed SDK 00010 # Copyright (c) 2017 ARM Limited 00011 # 00012 # Licensed under the Apache License, Version 2.0 (the "License"); 00013 # you may not use this file except in compliance with the License. 00014 # You may obtain a copy of the License at 00015 # 00016 # http://www.apache.org/licenses/LICENSE-2.0 00017 # 00018 # Unless required by applicable law or agreed to in writing, software 00019 # distributed under the License is distributed on an "AS IS" BASIS, 00020 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00021 # See the License for the specific language governing permissions and 00022 # limitations under the License. 00023 00024 from os.path import join, abspath, dirname 00025 if __name__ == "__main__": 00026 import sys 00027 ROOT = abspath(join(dirname(__file__), "..", "..")) 00028 sys.path.insert(0, ROOT) 00029 from copy import copy 00030 from yaml import dump_all 00031 import argparse 00032 00033 from tools.targets import Target, set_targets_json_location, TARGET_MAP 00034 00035 def must_have_keys (keys, dict): 00036 """Require keys in an MCU/Board 00037 00038 is a generator for errors 00039 """ 00040 for key in keys: 00041 if key not in dict: 00042 yield "%s not found, and is required" % key 00043 00044 def may_have_keys (keys, dict): 00045 """Disable all other keys in an MCU/Board 00046 00047 is a generator for errors 00048 """ 00049 for key in dict.keys(): 00050 if key not in keys: 00051 yield "%s found, and is not allowed" % key 00052 00053 def check_extra_labels (dict): 00054 """Check that extra_labels does not contain any Target names 00055 00056 is a generator for errors 00057 """ 00058 for label in (dict.get("extra_labels", []) + 00059 dict.get("extra_labels_add", [])): 00060 if label in Target.get_json_target_data(): 00061 yield "%s is not allowed in extra_labels" % label 00062 00063 def check_release_version (dict): 00064 """Verify that release version 5 is combined with support for all toolcahins 00065 00066 is a generator for errors 00067 """ 00068 if ("release_versions" in dict and 00069 "5" in dict["release_versions"] and 00070 "supported_toolchains" in dict): 00071 for toolc in ["GCC_ARM", "ARM", "IAR"]: 00072 if toolc not in dict["supported_toolchains"]: 00073 yield ("%s not found in supported_toolchains, and is " 00074 "required by mbed OS 5" % toolc) 00075 00076 def check_inherits(dict): 00077 if ("inherits" in dict and len(dict["inherits"]) > 1): 00078 yield "multiple inheritance is forbidden" 00079 00080 DEVICE_HAS_ALLOWED = ["ANALOGIN", "ANALOGOUT", "CAN", "ETHERNET", "EMAC", 00081 "FLASH", "I2C", "I2CSLAVE", "I2C_ASYNCH", "INTERRUPTIN", 00082 "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", 00083 "PWMOUT", "RTC", "TRNG","SERIAL", "SERIAL_ASYNCH", 00084 "SERIAL_FC", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE", 00085 "STORAGE", "STCLK_OFF_DURING_SLEEP"] 00086 def check_device_has(dict): 00087 for name in dict.get("device_has", []): 00088 if name not in DEVICE_HAS_ALLOWED: 00089 yield "%s is not allowed in device_has" % name 00090 00091 MCU_REQUIRED_KEYS = ["release_versions", "supported_toolchains", 00092 "default_lib", "public", "inherits", "device_has"] 00093 MCU_ALLOWED_KEYS = ["device_has_add", "device_has_remove", "core", 00094 "extra_labels", "features", "features_add", 00095 "features_remove", "bootloader_supported", "device_name", 00096 "post_binary_hook", "default_toolchain", "config", 00097 "extra_labels_add", "extra_labels_remove", 00098 "target_overrides"] + MCU_REQUIRED_KEYS 00099 def check_mcu (mcu_json, strict=False): 00100 """Generate a list of problems with an MCU 00101 00102 :param: mcu_json the MCU's dict to check 00103 :param: strict enforce required keys 00104 """ 00105 errors = list(may_have_keys(MCU_ALLOWED_KEYS, mcu_json)) 00106 if strict: 00107 errors.extend(must_have_keys(MCU_REQUIRED_KEYS, mcu_json)) 00108 errors.extend(check_extra_labels(mcu_json)) 00109 errors.extend(check_release_version(mcu_json)) 00110 errors.extend(check_inherits(mcu_json)) 00111 errors.extend(check_device_has(mcu_json)) 00112 if 'public' in mcu_json and mcu_json['public']: 00113 errors.append("public must be false") 00114 return errors 00115 00116 BOARD_REQUIRED_KEYS = ["inherits"] 00117 BOARD_ALLOWED_KEYS = ["supported_form_factors", "is_disk_virtual", 00118 "detect_code", "extra_labels", "extra_labels_add", 00119 "extra_labels_remove", "public", "config", 00120 "forced_reset_timeout", "target_overrides"] + BOARD_REQUIRED_KEYS 00121 def check_board (board_json, strict=False): 00122 """Generate a list of problems with an board 00123 00124 :param: board_json the mcus dict to check 00125 :param: strict enforce required keys 00126 """ 00127 errors = list(may_have_keys(BOARD_ALLOWED_KEYS, board_json)) 00128 if strict: 00129 errors.extend(must_have_keys(BOARD_REQUIRED_KEYS, board_json)) 00130 errors.extend(check_extra_labels(board_json)) 00131 errors.extend(check_inherits(board_json)) 00132 return errors 00133 00134 def add_if (dict, key, val): 00135 """Add a value to a dict if it's non-empty""" 00136 if val: 00137 dict[key] = val 00138 00139 def _split_boards(resolution_order, tgt): 00140 """Split the resolution order between boards and mcus""" 00141 mcus = [] 00142 boards = [] 00143 iterable = iter(resolution_order) 00144 for name in iterable: 00145 mcu_json = tgt.json_data[name] 00146 if (len(list(check_mcu(mcu_json, True))) > 00147 len(list(check_board(mcu_json, True)))): 00148 boards.append(name) 00149 else: 00150 mcus.append(name) 00151 break 00152 mcus.extend(iterable) 00153 mcus.reverse() 00154 boards.reverse() 00155 return mcus, boards 00156 00157 00158 MCU_FORMAT_STRING = {1: "MCU (%s) ->", 00159 2: "Family (%s) -> MCU (%s) ->", 00160 3: "Family (%s) -> SubFamily (%s) -> MCU (%s) ->"} 00161 BOARD_FORMAT_STRING = {1: "Board (%s)", 00162 2: "Module (%s) -> Board (%s)"} 00163 def _generate_hierarchy_string(mcus, boards): 00164 global_errors = [] 00165 if len(mcus) < 1: 00166 global_errors.append("No MCUS found in hierarchy") 00167 mcus_string = "??? ->" 00168 elif len(mcus) > 3: 00169 global_errors.append("No name for targets %s" % ", ".join(mcus[3:])) 00170 mcus_string = MCU_FORMAT_STRING[3] % tuple(mcus[:3]) 00171 for name in mcus[3:]: 00172 mcus_string += " ??? (%s) ->" % name 00173 else: 00174 mcus_string = MCU_FORMAT_STRING[len(mcus)] % tuple(mcus) 00175 00176 if len(boards) < 1: 00177 global_errors.append("no boards found in hierarchy") 00178 boards_string = "???" 00179 elif len(boards) > 2: 00180 global_errors.append("no name for targets %s" % ", ".join(boards[2:])) 00181 boards_string = BOARD_FORMAT_STRING[2] % tuple(boards[:2]) 00182 for name in boards[2:]: 00183 boards_string += " -> ??? (%s)" % name 00184 else: 00185 boards_string = BOARD_FORMAT_STRING[len(boards)] % tuple(boards) 00186 return mcus_string + " " + boards_string, global_errors 00187 00188 00189 def check_hierarchy (tgt): 00190 """Atempts to assign labels to the hierarchy""" 00191 resolution_order = copy(tgt.resolution_order_names[:-1]) 00192 mcus, boards = _split_boards(resolution_order, tgt) 00193 00194 target_errors = {} 00195 hierachy_string, hierachy_errors = _generate_hierarchy_string(mcus, boards) 00196 to_ret = {"hierarchy": hierachy_string} 00197 add_if(to_ret, "hierarchy errors", hierachy_errors) 00198 00199 for name in mcus[:-1]: 00200 add_if(target_errors, name, list(check_mcu(tgt.json_data[name]))) 00201 if len(mcus) >= 1: 00202 add_if(target_errors, mcus[-1], 00203 list(check_mcu(tgt.json_data[mcus[-1]], True))) 00204 for name in boards: 00205 add_if(target_errors, name, list(check_board(tgt.json_data[name]))) 00206 if len(boards) >= 1: 00207 add_if(target_errors, boards[-1], 00208 list(check_board(tgt.json_data[boards[-1]], True))) 00209 add_if(to_ret, "target errors", target_errors) 00210 return to_ret 00211 00212 PARSER = argparse.ArgumentParser(prog="targets/lint.py") 00213 SUBPARSERS = PARSER.add_subparsers(title="Commands") 00214 00215 def subcommand(name, *args, **kwargs): 00216 def __subcommand(command): 00217 kwargs['description'] = command.__doc__ 00218 subparser = SUBPARSERS.add_parser(name, **kwargs) 00219 for arg in args: 00220 arg = dict(arg) 00221 opt = arg['name'] 00222 del arg['name'] 00223 00224 if isinstance(opt, basestring): 00225 subparser.add_argument(opt, **arg) 00226 else: 00227 subparser.add_argument(*opt, **arg) 00228 00229 def _thunk(parsed_args): 00230 argv = [arg['dest'] if 'dest' in arg else arg['name'] 00231 for arg in args] 00232 argv = [(arg if isinstance(arg, basestring) 00233 else arg[-1]).strip('-').replace('-', '_') 00234 for arg in argv] 00235 argv = {arg: vars(parsed_args)[arg] for arg in argv 00236 if vars(parsed_args)[arg] is not None} 00237 00238 return command(**argv) 00239 00240 subparser.set_defaults(command=_thunk) 00241 return command 00242 return __subcommand 00243 00244 @subcommand("targets", 00245 dict(name="mcus", nargs="+", metavar="MCU", 00246 choices=TARGET_MAP.keys(), type=str.upper)) 00247 def targets_cmd (mcus=[]): 00248 """Find and print errors about specific targets""" 00249 print dump_all([check_hierarchy(TARGET_MAP[m]) for m in mcus], 00250 default_flow_style=False) 00251 00252 @subcommand("all-targets") 00253 def all_targets_cmd (): 00254 """Print all errors about all parts""" 00255 print dump_all([check_hierarchy(m) for m in TARGET_MAP.values()], 00256 default_flow_style=False) 00257 00258 @subcommand("orphans") 00259 def orphans_cmd (): 00260 """Find and print all orphan targets""" 00261 orphans = Target.get_json_target_data().keys() 00262 for tgt in TARGET_MAP.values(): 00263 for name in tgt.resolution_order_names: 00264 if name in orphans: 00265 orphans.remove(name) 00266 if orphans: 00267 print dump_all([orphans], default_flow_style=False) 00268 return len(orphans) 00269 00270 def main (): 00271 """entry point""" 00272 options = PARSER.parse_args() 00273 return options.command(options) 00274 00275 if __name__ == "__main__": 00276 sys.exit(main()) 00277
Generated on Tue Jul 12 2022 13:30:21 by
