Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lint.py Source File

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 heirarchy")
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 heirarchy")
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 heirarchy"""
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