1#!/usr/bin/env python3 2# SPDX-License-Identifier: Apache-2.0 3# Copyright (c) 2021 Intel Corporation 4 5import os 6import sh 7import argparse 8import re 9from unidiff import PatchSet 10 11if "ZEPHYR_BASE" not in os.environ: 12 exit("$ZEPHYR_BASE environment variable undefined.") 13 14repository_path = os.environ['ZEPHYR_BASE'] 15 16sh_special_args = { 17 '_tty_out': False, 18 '_cwd': repository_path 19} 20 21coccinelle_scripts = ["/scripts/coccinelle/reserved_names.cocci", 22 "/scripts/coccinelle/same_identifier.cocci", 23 #"/scripts/coccinelle/identifier_length.cocci", 24 ] 25 26 27def parse_coccinelle(contents: str, violations: dict): 28 reg = re.compile("([a-zA-Z0-9_/]*\\.[ch]:[0-9]*)(:[0-9\\-]*: )(.*)") 29 for line in contents.split("\n"): 30 r = reg.match(line) 31 if r: 32 f = r.group(1) 33 if f in violations: 34 violations[f].append(r.group(3)) 35 else: 36 violations[r.group(1)] = [r.group(3)] 37 38 39def parse_args(): 40 parser = argparse.ArgumentParser( 41 description="Check if change requires full twister") 42 parser.add_argument('-c', '--commits', default=None, 43 help="Commit range in the form: a..b") 44 parser.add_argument("-o", "--output", required=False, 45 help="Print violation into a file") 46 return parser.parse_args() 47 48 49def main(): 50 args = parse_args() 51 if not args.commits: 52 exit("missing commit range") 53 54 # pylint does not like the 'sh' library 55 # pylint: disable=too-many-function-args,unexpected-keyword-arg 56 commit = sh.git("diff", args.commits, **sh_special_args) 57 patch_set = PatchSet(commit) 58 zephyr_base = os.getenv("ZEPHYR_BASE") 59 violations = {} 60 numViolations = 0 61 62 for f in patch_set: 63 if not f.path.endswith(".c") and not f.path.endswith(".h") or not os.path.exists(zephyr_base + "/" + f.path): 64 continue 65 66 for script in coccinelle_scripts: 67 script_path = os.getenv("ZEPHYR_BASE") + "/" + script 68 print(f"Running {script} on {f.path}") 69 try: 70 cocci = sh.coccicheck( 71 "--mode=report", 72 "--cocci=" + 73 script_path, 74 f.path, 75 _timeout=10, 76 **sh_special_args) 77 parse_coccinelle(cocci, violations) 78 except sh.TimeoutException: 79 print("we timed out waiting, skipping...") 80 81 for hunk in f: 82 for line in hunk: 83 if line.is_added: 84 violation = "{}:{}".format(f.path, line.target_line_no) 85 if violation in violations: 86 numViolations += 1 87 if args.output: 88 with open(args.output, "a+") as fp: 89 fp.write("{}:{}\n".format( 90 violation, "\t\n".join( 91 violations[violation]))) 92 else: 93 print( 94 "{}:{}".format( 95 violation, "\t\n".join( 96 violations[violation]))) 97 98 return numViolations 99 100 101if __name__ == "__main__": 102 ret = main() 103 exit(ret) 104