check-package 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #!/usr/bin/env python
  2. # See utils/checkpackagelib/readme.txt before editing this file.
  3. from __future__ import print_function
  4. import argparse
  5. import inspect
  6. import os
  7. import re
  8. import six
  9. import sys
  10. import checkpackagelib.lib_config
  11. import checkpackagelib.lib_hash
  12. import checkpackagelib.lib_mk
  13. import checkpackagelib.lib_patch
  14. VERBOSE_LEVEL_TO_SHOW_IGNORED_FILES = 3
  15. flags = None # Command line arguments.
  16. def parse_args():
  17. parser = argparse.ArgumentParser()
  18. # Do not use argparse.FileType("r") here because only files with known
  19. # format will be open based on the filename.
  20. parser.add_argument("files", metavar="F", type=str, nargs="*",
  21. help="list of files")
  22. parser.add_argument("--br2-external", "-b", dest='intree_only', action="store_false",
  23. help="do not apply the pathname filters used for intree files")
  24. parser.add_argument("--manual-url", action="store",
  25. default="http://nightly.buildroot.org/",
  26. help="default: %(default)s")
  27. parser.add_argument("--verbose", "-v", action="count", default=0)
  28. # Now the debug options in the order they are processed.
  29. parser.add_argument("--include-only", dest="include_list", action="append",
  30. help="run only the specified functions (debug)")
  31. parser.add_argument("--exclude", dest="exclude_list", action="append",
  32. help="do not run the specified functions (debug)")
  33. parser.add_argument("--dry-run", action="store_true", help="print the "
  34. "functions that would be called for each file (debug)")
  35. return parser.parse_args()
  36. CONFIG_IN_FILENAME = re.compile("Config\.\S*$")
  37. DO_CHECK_INTREE = re.compile("|".join([
  38. "Config.in",
  39. "arch/",
  40. "boot/",
  41. "fs/",
  42. "linux/",
  43. "package/",
  44. "system/",
  45. "toolchain/",
  46. ]))
  47. DO_NOT_CHECK_INTREE = re.compile("|".join([
  48. "boot/barebox/barebox\.mk$",
  49. "fs/common\.mk$",
  50. "package/doc-asciidoc\.mk$",
  51. "package/pkg-\S*\.mk$",
  52. "toolchain/helpers\.mk$",
  53. "toolchain/toolchain-external/pkg-toolchain-external\.mk$",
  54. ]))
  55. def get_lib_from_filename(fname):
  56. if flags.intree_only:
  57. if DO_CHECK_INTREE.match(fname) is None:
  58. return None
  59. if DO_NOT_CHECK_INTREE.match(fname):
  60. return None
  61. if CONFIG_IN_FILENAME.search(fname):
  62. return checkpackagelib.lib_config
  63. if fname.endswith(".hash"):
  64. return checkpackagelib.lib_hash
  65. if fname.endswith(".mk"):
  66. return checkpackagelib.lib_mk
  67. if fname.endswith(".patch"):
  68. return checkpackagelib.lib_patch
  69. return None
  70. def is_a_check_function(m):
  71. if not inspect.isclass(m):
  72. return False
  73. # do not call the base class
  74. if m.__name__.startswith("_"):
  75. return False
  76. if flags.include_list and m.__name__ not in flags.include_list:
  77. return False
  78. if flags.exclude_list and m.__name__ in flags.exclude_list:
  79. return False
  80. return True
  81. def print_warnings(warnings):
  82. # Avoid the need to use 'return []' at the end of every check function.
  83. if warnings is None:
  84. return 0 # No warning generated.
  85. for level, message in enumerate(warnings):
  86. if flags.verbose >= level:
  87. print(message.replace("\t", "< tab >").rstrip())
  88. return 1 # One more warning to count.
  89. def check_file_using_lib(fname):
  90. # Count number of warnings generated and lines processed.
  91. nwarnings = 0
  92. nlines = 0
  93. lib = get_lib_from_filename(fname)
  94. if not lib:
  95. if flags.verbose >= VERBOSE_LEVEL_TO_SHOW_IGNORED_FILES:
  96. print("{}: ignored".format(fname))
  97. return nwarnings, nlines
  98. classes = inspect.getmembers(lib, is_a_check_function)
  99. if flags.dry_run:
  100. functions_to_run = [c[0] for c in classes]
  101. print("{}: would run: {}".format(fname, functions_to_run))
  102. return nwarnings, nlines
  103. objects = [c[1](fname, flags.manual_url) for c in classes]
  104. for cf in objects:
  105. nwarnings += print_warnings(cf.before())
  106. if six.PY3:
  107. f = open(fname, "r", errors="surrogateescape")
  108. else:
  109. f = open(fname, "r")
  110. lastline = ""
  111. for lineno, text in enumerate(f.readlines()):
  112. nlines += 1
  113. for cf in objects:
  114. if cf.disable.search(lastline):
  115. continue
  116. nwarnings += print_warnings(cf.check_line(lineno + 1, text))
  117. lastline = text
  118. f.close()
  119. for cf in objects:
  120. nwarnings += print_warnings(cf.after())
  121. return nwarnings, nlines
  122. def __main__():
  123. global flags
  124. flags = parse_args()
  125. if flags.intree_only:
  126. # change all paths received to be relative to the base dir
  127. base_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
  128. files_to_check = [os.path.relpath(os.path.abspath(f), base_dir) for f in flags.files]
  129. # move current dir so the script find the files
  130. os.chdir(base_dir)
  131. else:
  132. files_to_check = flags.files
  133. if len(files_to_check) == 0:
  134. print("No files to check style")
  135. sys.exit(1)
  136. # Accumulate number of warnings generated and lines processed.
  137. total_warnings = 0
  138. total_lines = 0
  139. for fname in files_to_check:
  140. nwarnings, nlines = check_file_using_lib(fname)
  141. total_warnings += nwarnings
  142. total_lines += nlines
  143. # The warning messages are printed to stdout and can be post-processed
  144. # (e.g. counted by 'wc'), so for stats use stderr. Wait all warnings are
  145. # printed, for the case there are many of them, before printing stats.
  146. sys.stdout.flush()
  147. print("{} lines processed".format(total_lines), file=sys.stderr)
  148. print("{} warnings generated".format(total_warnings), file=sys.stderr)
  149. if total_warnings > 0:
  150. sys.exit(1)
  151. __main__()