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() |
