summaryrefslogtreecommitdiffstats
path: root/scripts/verify-bashisms
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2017-01-31 13:50:31 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-01-31 15:28:41 +0000
commit0b95e47180588306ad72f6ee0a6a65937a2c74b0 (patch)
treea3189e84f5c3e6ee56611640ff5ad5653fe6a69a /scripts/verify-bashisms
parent3108bff175850914c71e02c47bac467f59661296 (diff)
downloadpoky-0b95e47180588306ad72f6ee0a6a65937a2c74b0.tar.gz
verify-bashisms: check scripts only once, include original file and line
Several scripts that are defined in .bbclass files end up in multiple different recipes. It's better (faster, less repetitive error reports) to check them only once. In addition, the real information for the developer is where he can find the script, not which recipe file uses it. verify-bashisms now prints the original file instead of the recipe whenever possible (i.e. 'filename' is set) and also bumps the line number so that it is relative to the file and not the script. Example with one real error and one added just for testing: $ verify-bashisms core-image-minimal core-image-sato Loading cache: 100% |#################################################################################| Time: 0:00:00 Loaded 2935 entries from dependency cache. Parsing recipes: 100% |###############################################################################| Time: 0:00:01 Parsing of 2137 .bb files complete (2101 cached, 36 parsed). 2935 targets, 412 skipped, 0 masked, 0 errors. Generating scripts... Scanning scripts... /.../openembedded-core/meta/classes/populate_sdk_ext.bbclass possible bashism in install_tools line 515 (should be 'b = a'): if [ "${SDK_INCLUDE_TOOLCHAIN}" == "1" -a ! -e $unfsd_path ] ; then possible bashism in install_tools line 521 (type): type fixme (From OE-Core rev: ca4932b60f464430266cc43e34122b2973e8a200) Signed-off-by: Patrick Ohly <patrick.ohly@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/verify-bashisms')
-rwxr-xr-xscripts/verify-bashisms56
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
25def process(recipe, function, script): 25SCRIPT_LINENO_RE = re.compile(r' line (\d+) ')
26
27def 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
56def get_tinfoil(): 70def 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()