123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- #!/usr/bin/env python
- # See utils/checkpackagelib/readme.txt before editing this file.
- from __future__ import print_function
- import argparse
- import inspect
- import os
- import re
- import six
- import sys
- import checkpackagelib.lib_config
- import checkpackagelib.lib_hash
- import checkpackagelib.lib_mk
- import checkpackagelib.lib_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("--br2-external", "-b", dest='intree_only', action="store_false",
- help="do not apply the pathname filters used for intree 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*$")
- DO_CHECK_INTREE = re.compile("|".join([
- "Config.in",
- "arch/",
- "boot/",
- "fs/",
- "linux/",
- "package/",
- "system/",
- "toolchain/",
- ]))
- DO_NOT_CHECK_INTREE = re.compile("|".join([
- "boot/barebox/barebox\.mk$",
- "fs/common\.mk$",
- "package/doc-asciidoc\.mk$",
- "package/pkg-\S*\.mk$",
- "toolchain/helpers\.mk$",
- "toolchain/toolchain-external/pkg-toolchain-external\.mk$",
- ]))
- def get_lib_from_filename(fname):
- if flags.intree_only:
- if DO_CHECK_INTREE.match(fname) is None:
- return None
- if DO_NOT_CHECK_INTREE.match(fname):
- return None
- if CONFIG_IN_FILENAME.search(fname):
- return checkpackagelib.lib_config
- if fname.endswith(".hash"):
- return checkpackagelib.lib_hash
- if fname.endswith(".mk"):
- return checkpackagelib.lib_mk
- if fname.endswith(".patch"):
- return checkpackagelib.lib_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())
- if six.PY3:
- f = open(fname, "r", errors="surrogateescape")
- else:
- f = open(fname, "r")
- lastline = ""
- for lineno, text in enumerate(f.readlines()):
- nlines += 1
- for cf in objects:
- if cf.disable.search(lastline):
- continue
- nwarnings += print_warnings(cf.check_line(lineno + 1, text))
- lastline = text
- f.close()
- for cf in objects:
- nwarnings += print_warnings(cf.after())
- return nwarnings, nlines
- def __main__():
- global flags
- flags = parse_args()
- if flags.intree_only:
- # change all paths received to be relative to the base dir
- base_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
- files_to_check = [os.path.relpath(os.path.abspath(f), base_dir) for f in flags.files]
- # move current dir so the script find the files
- os.chdir(base_dir)
- else:
- files_to_check = flags.files
- if len(files_to_check) == 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 files_to_check:
- 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__()
|