123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- #!/usr/bin/env python3
- import sys, os, subprocess, re, shutil
- whitelist = (
- # type is supported by dash
- 'if type systemctl >/dev/null 2>/dev/null; then',
- 'if type systemd-tmpfiles >/dev/null 2>/dev/null; then',
- 'if type update-rc.d >/dev/null 2>/dev/null; then',
- 'command -v',
- # HOSTNAME is set locally
- 'buildhistory_single_commit "$CMDLINE" "$HOSTNAME"',
- # False-positive, match is a grep not shell expression
- 'grep "^$groupname:[^:]*:[^:]*:\\([^,]*,\\)*$username\\(,[^,]*\\)*"',
- # TODO verify dash's '. script args' behaviour
- '. $target_sdk_dir/${oe_init_build_env_path} $target_sdk_dir >> $LOGFILE'
- )
- def is_whitelisted(s):
- for w in whitelist:
- if w in s:
- return True
- return False
- def process(recipe, function, script):
- import tempfile
- if not script.startswith("#!"):
- script = "#! /bin/sh\n" + script
- fn = tempfile.NamedTemporaryFile(mode="w+t")
- fn.write(script)
- fn.flush()
- try:
- subprocess.check_output(("checkbashisms.pl", fn.name), universal_newlines=True, stderr=subprocess.STDOUT)
- # No bashisms, so just return
- return
- except subprocess.CalledProcessError as e:
- # TODO check exit code is 1
- # Replace the temporary filename with the function and split it
- output = e.output.replace(fn.name, function).splitlines()
- if len(results) % 2 != 0:
- print("Unexpected output from checkbashism: %s" % str(output))
- return
- # Turn the output into a list of (message, source) values
- result = []
- # Check the results against the whitelist
- for message, source in zip(output[0::2], output[1::2]):
- if not is_whitelisted(source):
- result.append((message, source))
- return result
- def get_tinfoil():
- scripts_path = os.path.dirname(os.path.realpath(__file__))
- lib_path = scripts_path + '/lib'
- sys.path = sys.path + [lib_path]
- import scriptpath
- scriptpath.add_bitbake_lib_path()
- import bb.tinfoil
- tinfoil = bb.tinfoil.Tinfoil()
- tinfoil.prepare()
- # tinfoil.logger.setLevel(logging.WARNING)
- return tinfoil
- if __name__=='__main__':
- import shutil
- if shutil.which("checkbashisms.pl") is None:
- print("Cannot find checkbashisms.pl on $PATH")
- sys.exit(1)
- tinfoil = get_tinfoil()
- # This is only the default configuration and should iterate over
- # recipecaches to handle multiconfig environments
- pkg_pn = tinfoil.cooker.recipecaches[""].pkg_pn
- # TODO: use argparse and have --help
- if len(sys.argv) > 1:
- initial_pns = sys.argv[1:]
- else:
- initial_pns = sorted(pkg_pn)
- pns = []
- print("Generating file list...")
- for pn in initial_pns:
- for fn in pkg_pn[pn]:
- # There's no point checking multiple BBCLASSEXTENDed variants of the same recipe
- realfn, _, _ = bb.cache.virtualfn2realfn(fn)
- if realfn not in pns:
- pns.append(realfn)
- def func(fn):
- result = []
- data = tinfoil.parse_recipe_file(fn)
- for key in data.keys():
- if data.getVarFlag(key, "func", True) and not data.getVarFlag(key, "python", True):
- script = data.getVar(key, False)
- if not script: continue
- #print ("%s:%s" % (fn, key))
- r = process(fn, key, script)
- if r: result.extend(r)
- return fn, result
- print("Scanning scripts...\n")
- import multiprocessing
- pool = multiprocessing.Pool()
- for pn,results in pool.imap(func, pns):
- if results:
- print(pn)
- for message,source in results:
- print(" %s\n %s" % (message, source))
- print()
|