diff options
-rwxr-xr-x | scripts/verify-bashisms | 56 |
1 files changed, 36 insertions, 20 deletions
diff --git a/scripts/verify-bashisms b/scripts/verify-bashisms index df071e3b90..7283980ed5 100755 --- a/scripts/verify-bashisms +++ b/scripts/verify-bashisms | |||
@@ -22,7 +22,9 @@ def is_whitelisted(s): | |||
22 | return True | 22 | return True |
23 | return False | 23 | return False |
24 | 24 | ||
25 | def process(recipe, function, script): | 25 | SCRIPT_LINENO_RE = re.compile(r' line (\d+) ') |
26 | |||
27 | def process(filename, function, lineno, script): | ||
26 | import tempfile | 28 | import tempfile |
27 | 29 | ||
28 | if not script.startswith("#!"): | 30 | if not script.startswith("#!"): |
@@ -45,13 +47,25 @@ def process(recipe, function, script): | |||
45 | print("Unexpected output from checkbashism: %s" % str(output)) | 47 | print("Unexpected output from checkbashism: %s" % str(output)) |
46 | return | 48 | return |
47 | 49 | ||
48 | # Turn the output into a list of (message, source) values | 50 | # Turn the output into a single string like this: |
51 | # /.../foobar.bb | ||
52 | # possible bashism in updatercd_postrm line 2 (type): | ||
53 | # if ${@use_updatercd(d)} && type update-rc.d >/dev/null 2>/dev/null; then | ||
54 | # ... | ||
55 | # ... | ||
49 | result = [] | 56 | result = [] |
50 | # Check the results against the whitelist | 57 | # Check the results against the whitelist |
51 | for message, source in zip(output[0::2], output[1::2]): | 58 | for message, source in zip(output[0::2], output[1::2]): |
52 | if not is_whitelisted(source): | 59 | if not is_whitelisted(source): |
53 | result.append((message, source)) | 60 | if lineno is not None: |
54 | return result | 61 | message = SCRIPT_LINENO_RE.sub(lambda m: ' line %d ' % (int(m.group(1)) + int(lineno) - 1), |
62 | message) | ||
63 | result.extend([' ' + message, ' ' + source]) | ||
64 | if result: | ||
65 | result.insert(0, filename) | ||
66 | return '\n'.join(result) | ||
67 | else: | ||
68 | return None | ||
55 | 69 | ||
56 | def get_tinfoil(): | 70 | def get_tinfoil(): |
57 | scripts_path = os.path.dirname(os.path.realpath(__file__)) | 71 | scripts_path = os.path.dirname(os.path.realpath(__file__)) |
@@ -75,12 +89,8 @@ if __name__=='__main__': | |||
75 | # initializing the pool and connecting to the | 89 | # initializing the pool and connecting to the |
76 | # bitbake server is crucial, don't change it. | 90 | # bitbake server is crucial, don't change it. |
77 | def func(item): | 91 | def func(item): |
78 | fn, scripts = item | 92 | (filename, key, lineno), script = item |
79 | result = [] | 93 | return process(filename, key, lineno, script) |
80 | for key, script in scripts: | ||
81 | r = process(fn, key, script) | ||
82 | if r: result.extend(r) | ||
83 | return fn, result | ||
84 | 94 | ||
85 | import multiprocessing | 95 | import multiprocessing |
86 | pool = multiprocessing.Pool() | 96 | pool = multiprocessing.Pool() |
@@ -97,27 +107,33 @@ if __name__=='__main__': | |||
97 | else: | 107 | else: |
98 | initial_pns = sorted(pkg_pn) | 108 | initial_pns = sorted(pkg_pn) |
99 | 109 | ||
100 | pns = {} | 110 | pns = set() |
111 | scripts = {} | ||
101 | print("Generating scripts...") | 112 | print("Generating scripts...") |
102 | for pn in initial_pns: | 113 | for pn in initial_pns: |
103 | for fn in pkg_pn[pn]: | 114 | for fn in pkg_pn[pn]: |
104 | # There's no point checking multiple BBCLASSEXTENDed variants of the same recipe | 115 | # There's no point checking multiple BBCLASSEXTENDed variants of the same recipe |
116 | # (at least in general - there is some risk that the variants contain different scripts) | ||
105 | realfn, _, _ = bb.cache.virtualfn2realfn(fn) | 117 | realfn, _, _ = bb.cache.virtualfn2realfn(fn) |
106 | if realfn not in pns: | 118 | if realfn not in pns: |
119 | pns.add(realfn) | ||
107 | data = tinfoil.parse_recipe_file(realfn) | 120 | data = tinfoil.parse_recipe_file(realfn) |
108 | scripts = [] | ||
109 | for key in data.keys(): | 121 | for key in data.keys(): |
110 | if data.getVarFlag(key, "func") and not data.getVarFlag(key, "python"): | 122 | if data.getVarFlag(key, "func") and not data.getVarFlag(key, "python"): |
111 | script = data.getVar(key, False) | 123 | script = data.getVar(key, False) |
112 | if script: | 124 | if script: |
113 | scripts.append((key, script)) | 125 | filename = data.getVarFlag(key, "filename") |
114 | pns[realfn] = scripts | 126 | lineno = data.getVarFlag(key, "lineno") |
127 | # There's no point in checking a function multiple | ||
128 | # times just because different recipes include it. | ||
129 | # We identify unique scripts by file, name, and (just in case) | ||
130 | # line number. | ||
131 | attributes = (filename or realfn, key, lineno) | ||
132 | scripts.setdefault(attributes, script) | ||
133 | |||
115 | 134 | ||
116 | print("Scanning scripts...\n") | 135 | print("Scanning scripts...\n") |
117 | for pn, results in pool.imap(func, pns.items()): | 136 | for result in pool.imap(func, scripts.items()): |
118 | if results: | 137 | if result: |
119 | print(pn) | 138 | print(result) |
120 | for message,source in results: | ||
121 | print(" %s\n %s" % (message, source)) | ||
122 | print() | ||
123 | tinfoil.shutdown() | 139 | tinfoil.shutdown() |