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