#!/usr/bin/env python # See support/scripts/check-package.txt before editing this file. from __future__ import print_function import argparse import inspect import re import sys import checkpackagelib_config import checkpackagelib_hash import checkpackagelib_mk import checkpackagelib_patch VERBOSE_LEVEL_TO_SHOW_IGNORED_FILES = 3 flags = None # Command line arguments. def parse_args(): parser = argparse.ArgumentParser() # Do not use argparse.FileType("r") here because only files with known # format will be open based on the filename. parser.add_argument("files", metavar="F", type=str, nargs="*", help="list of files") parser.add_argument("--manual-url", action="store", default="http://nightly.buildroot.org/", help="default: %(default)s") parser.add_argument("--verbose", "-v", action="count", default=0) # Now the debug options in the order they are processed. parser.add_argument("--include-only", dest="include_list", action="append", help="run only the specified functions (debug)") parser.add_argument("--exclude", dest="exclude_list", action="append", help="do not run the specified functions (debug)") parser.add_argument("--dry-run", action="store_true", help="print the " "functions that would be called for each file (debug)") return parser.parse_args() CONFIG_IN_FILENAME = re.compile("/Config\.\S*$") FILE_IS_FROM_A_PACKAGE = re.compile("package/[^/]*/") def get_lib_from_filename(fname): if FILE_IS_FROM_A_PACKAGE.search(fname) is None: return None if CONFIG_IN_FILENAME.search(fname): return checkpackagelib_config if fname.endswith(".hash"): return checkpackagelib_hash if fname.endswith(".mk"): return checkpackagelib_mk if fname.endswith(".patch"): return checkpackagelib_patch return None def is_a_check_function(m): if not inspect.isclass(m): return False # do not call the base class if m.__name__.startswith("_"): return False if flags.include_list and m.__name__ not in flags.include_list: return False if flags.exclude_list and m.__name__ in flags.exclude_list: return False return True def print_warnings(warnings): # Avoid the need to use 'return []' at the end of every check function. if warnings is None: return 0 # No warning generated. for level, message in enumerate(warnings): if flags.verbose >= level: print(message.replace("\t", "< tab >").rstrip()) return 1 # One more warning to count. def check_file_using_lib(fname): # Count number of warnings generated and lines processed. nwarnings = 0 nlines = 0 lib = get_lib_from_filename(fname) if not lib: if flags.verbose >= VERBOSE_LEVEL_TO_SHOW_IGNORED_FILES: print("{}: ignored".format(fname)) return nwarnings, nlines classes = inspect.getmembers(lib, is_a_check_function) if flags.dry_run: functions_to_run = [c[0] for c in classes] print("{}: would run: {}".format(fname, functions_to_run)) return nwarnings, nlines objects = [c[1](fname, flags.manual_url) for c in classes] for cf in objects: nwarnings += print_warnings(cf.before()) for lineno, text in enumerate(open(fname, "r").readlines()): nlines += 1 for cf in objects: nwarnings += print_warnings(cf.check_line(lineno + 1, text)) for cf in objects: nwarnings += print_warnings(cf.after()) return nwarnings, nlines def __main__(): global flags flags = parse_args() if len(flags.files) == 0: print("No files to check style") sys.exit(1) # Accumulate number of warnings generated and lines processed. total_warnings = 0 total_lines = 0 for fname in flags.files: nwarnings, nlines = check_file_using_lib(fname) total_warnings += nwarnings total_lines += nlines # The warning messages are printed to stdout and can be post-processed # (e.g. counted by 'wc'), so for stats use stderr. Wait all warnings are # printed, for the case there are many of them, before printing stats. sys.stdout.flush() print("{} lines processed".format(total_lines), file=sys.stderr) print("{} warnings generated".format(total_warnings), file=sys.stderr) if total_warnings > 0: sys.exit(1) __main__()