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