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.
Diff: mbed-os/tools/targets/lint.py
- Revision:
- 0:8fdf9a60065b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os/tools/targets/lint.py Wed Oct 10 00:33:53 2018 +0000
@@ -0,0 +1,277 @@
+"""A linting utility for targets.json
+
+This linting utility may be called as follows:
+python <path-to>/lint.py targets TARGET [TARGET ...]
+
+all targets will be linted
+"""
+
+# mbed SDK
+# Copyright (c) 2017 ARM Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from os.path import join, abspath, dirname
+if __name__ == "__main__":
+ import sys
+ ROOT = abspath(join(dirname(__file__), "..", ".."))
+ sys.path.insert(0, ROOT)
+from copy import copy
+from yaml import dump_all
+import argparse
+
+from tools.targets import Target, set_targets_json_location, TARGET_MAP
+
+def must_have_keys(keys, dict):
+ """Require keys in an MCU/Board
+
+ is a generator for errors
+ """
+ for key in keys:
+ if key not in dict:
+ yield "%s not found, and is required" % key
+
+def may_have_keys(keys, dict):
+ """Disable all other keys in an MCU/Board
+
+ is a generator for errors
+ """
+ for key in dict.keys():
+ if key not in keys:
+ yield "%s found, and is not allowed" % key
+
+def check_extra_labels(dict):
+ """Check that extra_labels does not contain any Target names
+
+ is a generator for errors
+ """
+ for label in (dict.get("extra_labels", []) +
+ dict.get("extra_labels_add", [])):
+ if label in Target.get_json_target_data():
+ yield "%s is not allowed in extra_labels" % label
+
+def check_release_version(dict):
+ """Verify that release version 5 is combined with support for all toolcahins
+
+ is a generator for errors
+ """
+ if ("release_versions" in dict and
+ "5" in dict["release_versions"] and
+ "supported_toolchains" in dict):
+ for toolc in ["GCC_ARM", "ARM", "IAR"]:
+ if toolc not in dict["supported_toolchains"]:
+ yield ("%s not found in supported_toolchains, and is "
+ "required by mbed OS 5" % toolc)
+
+def check_inherits(dict):
+ if ("inherits" in dict and len(dict["inherits"]) > 1):
+ yield "multiple inheritance is forbidden"
+
+DEVICE_HAS_ALLOWED = ["ANALOGIN", "ANALOGOUT", "CAN", "ETHERNET", "EMAC",
+ "FLASH", "I2C", "I2CSLAVE", "I2C_ASYNCH", "INTERRUPTIN",
+ "LPTICKER", "PORTIN", "PORTINOUT", "PORTOUT",
+ "PWMOUT", "RTC", "TRNG","SERIAL", "SERIAL_ASYNCH",
+ "SERIAL_FC", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE",
+ "STORAGE", "SYSTICK_CLK_OFF_DURING_SLEEP"]
+def check_device_has(dict):
+ for name in dict.get("device_has", []):
+ if name not in DEVICE_HAS_ALLOWED:
+ yield "%s is not allowed in device_has" % name
+
+MCU_REQUIRED_KEYS = ["release_versions", "supported_toolchains",
+ "default_lib", "public", "inherits", "device_has"]
+MCU_ALLOWED_KEYS = ["device_has_add", "device_has_remove", "core",
+ "extra_labels", "features", "features_add",
+ "features_remove", "bootloader_supported", "device_name",
+ "post_binary_hook", "default_toolchain", "config",
+ "extra_labels_add", "extra_labels_remove",
+ "target_overrides"] + MCU_REQUIRED_KEYS
+def check_mcu(mcu_json, strict=False):
+ """Generate a list of problems with an MCU
+
+ :param: mcu_json the MCU's dict to check
+ :param: strict enforce required keys
+ """
+ errors = list(may_have_keys(MCU_ALLOWED_KEYS, mcu_json))
+ if strict:
+ errors.extend(must_have_keys(MCU_REQUIRED_KEYS, mcu_json))
+ errors.extend(check_extra_labels(mcu_json))
+ errors.extend(check_release_version(mcu_json))
+ errors.extend(check_inherits(mcu_json))
+ errors.extend(check_device_has(mcu_json))
+ if 'public' in mcu_json and mcu_json['public']:
+ errors.append("public must be false")
+ return errors
+
+BOARD_REQUIRED_KEYS = ["inherits"]
+BOARD_ALLOWED_KEYS = ["supported_form_factors", "is_disk_virtual",
+ "detect_code", "extra_labels", "extra_labels_add",
+ "extra_labels_remove", "public", "config",
+ "forced_reset_timeout", "target_overrides"] + BOARD_REQUIRED_KEYS
+def check_board(board_json, strict=False):
+ """Generate a list of problems with an board
+
+ :param: board_json the mcus dict to check
+ :param: strict enforce required keys
+ """
+ errors = list(may_have_keys(BOARD_ALLOWED_KEYS, board_json))
+ if strict:
+ errors.extend(must_have_keys(BOARD_REQUIRED_KEYS, board_json))
+ errors.extend(check_extra_labels(board_json))
+ errors.extend(check_inherits(board_json))
+ return errors
+
+def add_if(dict, key, val):
+ """Add a value to a dict if it's non-empty"""
+ if val:
+ dict[key] = val
+
+def _split_boards(resolution_order, tgt):
+ """Split the resolution order between boards and mcus"""
+ mcus = []
+ boards = []
+ iterable = iter(resolution_order)
+ for name in iterable:
+ mcu_json = tgt.json_data[name]
+ if (len(list(check_mcu(mcu_json, True))) >
+ len(list(check_board(mcu_json, True)))):
+ boards.append(name)
+ else:
+ mcus.append(name)
+ break
+ mcus.extend(iterable)
+ mcus.reverse()
+ boards.reverse()
+ return mcus, boards
+
+
+MCU_FORMAT_STRING = {1: "MCU (%s) ->",
+ 2: "Family (%s) -> MCU (%s) ->",
+ 3: "Family (%s) -> SubFamily (%s) -> MCU (%s) ->"}
+BOARD_FORMAT_STRING = {1: "Board (%s)",
+ 2: "Module (%s) -> Board (%s)"}
+def _generate_hierarchy_string(mcus, boards):
+ global_errors = []
+ if len(mcus) < 1:
+ global_errors.append("No MCUS found in hierarchy")
+ mcus_string = "??? ->"
+ elif len(mcus) > 3:
+ global_errors.append("No name for targets %s" % ", ".join(mcus[3:]))
+ mcus_string = MCU_FORMAT_STRING[3] % tuple(mcus[:3])
+ for name in mcus[3:]:
+ mcus_string += " ??? (%s) ->" % name
+ else:
+ mcus_string = MCU_FORMAT_STRING[len(mcus)] % tuple(mcus)
+
+ if len(boards) < 1:
+ global_errors.append("no boards found in hierarchy")
+ boards_string = "???"
+ elif len(boards) > 2:
+ global_errors.append("no name for targets %s" % ", ".join(boards[2:]))
+ boards_string = BOARD_FORMAT_STRING[2] % tuple(boards[:2])
+ for name in boards[2:]:
+ boards_string += " -> ??? (%s)" % name
+ else:
+ boards_string = BOARD_FORMAT_STRING[len(boards)] % tuple(boards)
+ return mcus_string + " " + boards_string, global_errors
+
+
+def check_hierarchy(tgt):
+ """Atempts to assign labels to the hierarchy"""
+ resolution_order = copy(tgt.resolution_order_names[:-1])
+ mcus, boards = _split_boards(resolution_order, tgt)
+
+ target_errors = {}
+ hierachy_string, hierachy_errors = _generate_hierarchy_string(mcus, boards)
+ to_ret = {"hierarchy": hierachy_string}
+ add_if(to_ret, "hierarchy errors", hierachy_errors)
+
+ for name in mcus[:-1]:
+ add_if(target_errors, name, list(check_mcu(tgt.json_data[name])))
+ if len(mcus) >= 1:
+ add_if(target_errors, mcus[-1],
+ list(check_mcu(tgt.json_data[mcus[-1]], True)))
+ for name in boards:
+ add_if(target_errors, name, list(check_board(tgt.json_data[name])))
+ if len(boards) >= 1:
+ add_if(target_errors, boards[-1],
+ list(check_board(tgt.json_data[boards[-1]], True)))
+ add_if(to_ret, "target errors", target_errors)
+ return to_ret
+
+PARSER = argparse.ArgumentParser(prog="targets/lint.py")
+SUBPARSERS = PARSER.add_subparsers(title="Commands")
+
+def subcommand(name, *args, **kwargs):
+ def __subcommand(command):
+ kwargs['description'] = command.__doc__
+ subparser = SUBPARSERS.add_parser(name, **kwargs)
+ for arg in args:
+ arg = dict(arg)
+ opt = arg['name']
+ del arg['name']
+
+ if isinstance(opt, basestring):
+ subparser.add_argument(opt, **arg)
+ else:
+ subparser.add_argument(*opt, **arg)
+
+ def _thunk(parsed_args):
+ argv = [arg['dest'] if 'dest' in arg else arg['name']
+ for arg in args]
+ argv = [(arg if isinstance(arg, basestring)
+ else arg[-1]).strip('-').replace('-', '_')
+ for arg in argv]
+ argv = {arg: vars(parsed_args)[arg] for arg in argv
+ if vars(parsed_args)[arg] is not None}
+
+ return command(**argv)
+
+ subparser.set_defaults(command=_thunk)
+ return command
+ return __subcommand
+
+@subcommand("targets",
+ dict(name="mcus", nargs="+", metavar="MCU",
+ choices=TARGET_MAP.keys(), type=str.upper))
+def targets_cmd(mcus=[]):
+ """Find and print errors about specific targets"""
+ print dump_all([check_hierarchy(TARGET_MAP[m]) for m in mcus],
+ default_flow_style=False)
+
+@subcommand("all-targets")
+def all_targets_cmd():
+ """Print all errors about all parts"""
+ print dump_all([check_hierarchy(m) for m in TARGET_MAP.values()],
+ default_flow_style=False)
+
+@subcommand("orphans")
+def orphans_cmd():
+ """Find and print all orphan targets"""
+ orphans = Target.get_json_target_data().keys()
+ for tgt in TARGET_MAP.values():
+ for name in tgt.resolution_order_names:
+ if name in orphans:
+ orphans.remove(name)
+ if orphans:
+ print dump_all([orphans], default_flow_style=False)
+ return len(orphans)
+
+def main():
+ """entry point"""
+ options = PARSER.parse_args()
+ return options.command(options)
+
+if __name__ == "__main__":
+ sys.exit(main())
+