blob: 57c878f651f27744ecbb04a2b2fb0f6838cfc406 [file] [log] [blame]
Flavio Ceolin85525422021-03-23 15:06:01 -07001#!/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
Flavio Ceolin85525422021-03-23 15:06:01 -070014coccinelle_scripts = ["/scripts/coccinelle/reserved_names.cocci",
15 "/scripts/coccinelle/same_identifier.cocci",
Anas Nashif01c1bcf2021-05-06 14:17:29 -040016 #"/scripts/coccinelle/identifier_length.cocci",
Flavio Ceolin85525422021-03-23 15:06:01 -070017 ]
18
19
20def parse_coccinelle(contents: str, violations: dict):
Anas Nashifb669f0c2021-04-30 23:01:03 -040021 reg = re.compile("([a-zA-Z0-9_/]*\\.[ch]:[0-9]*)(:[0-9\\-]*: )(.*)")
Flavio Ceolin85525422021-03-23 15:06:01 -070022 for line in contents.split("\n"):
23 r = reg.match(line)
24 if r:
25 f = r.group(1)
26 if f in violations:
27 violations[f].append(r.group(3))
28 else:
29 violations[r.group(1)] = [r.group(3)]
30
31
32def parse_args():
33 parser = argparse.ArgumentParser(
Jordan Yatesac78b972023-12-10 16:04:57 +100034 description="Check commits against Cocccinelle rules", allow_abbrev=False)
35 parser.add_argument('-r', "--repository", required=False,
36 help="Path to repository")
Flavio Ceolin85525422021-03-23 15:06:01 -070037 parser.add_argument('-c', '--commits', default=None,
38 help="Commit range in the form: a..b")
Flavio Ceolin58fdc012021-03-24 15:54:16 -070039 parser.add_argument("-o", "--output", required=False,
40 help="Print violation into a file")
Flavio Ceolin85525422021-03-23 15:06:01 -070041 return parser.parse_args()
42
43
44def main():
45 args = parse_args()
46 if not args.commits:
47 exit("missing commit range")
48
Jordan Yatesac78b972023-12-10 16:04:57 +100049 if args.repository is None:
50 repository_path = os.environ['ZEPHYR_BASE']
51 else:
52 repository_path = args.repository
53
54 sh_special_args = {
55 '_tty_out': False,
56 '_cwd': repository_path
57 }
58
Flavio Ceolin0bbe5e42021-03-24 16:18:43 -070059 # pylint does not like the 'sh' library
60 # pylint: disable=too-many-function-args,unexpected-keyword-arg
Flavio Ceolin85525422021-03-23 15:06:01 -070061 commit = sh.git("diff", args.commits, **sh_special_args)
62 patch_set = PatchSet(commit)
63 zephyr_base = os.getenv("ZEPHYR_BASE")
64 violations = {}
65 numViolations = 0
66
67 for f in patch_set:
68 if not f.path.endswith(".c") and not f.path.endswith(".h") or not os.path.exists(zephyr_base + "/" + f.path):
69 continue
70
71 for script in coccinelle_scripts:
72 script_path = os.getenv("ZEPHYR_BASE") + "/" + script
Anas Nashif8ebc67e2021-05-03 10:40:42 -040073 print(f"Running {script} on {f.path}")
74 try:
75 cocci = sh.coccicheck(
76 "--mode=report",
77 "--cocci=" +
78 script_path,
79 f.path,
80 _timeout=10,
81 **sh_special_args)
82 parse_coccinelle(cocci, violations)
83 except sh.TimeoutException:
84 print("we timed out waiting, skipping...")
Flavio Ceolin85525422021-03-23 15:06:01 -070085
86 for hunk in f:
87 for line in hunk:
88 if line.is_added:
89 violation = "{}:{}".format(f.path, line.target_line_no)
90 if violation in violations:
91 numViolations += 1
Flavio Ceolin58fdc012021-03-24 15:54:16 -070092 if args.output:
93 with open(args.output, "a+") as fp:
94 fp.write("{}:{}\n".format(
95 violation, "\t\n".join(
96 violations[violation])))
97 else:
98 print(
99 "{}:{}".format(
100 violation, "\t\n".join(
101 violations[violation])))
Flavio Ceolin85525422021-03-23 15:06:01 -0700102
103 return numViolations
104
105
106if __name__ == "__main__":
107 ret = main()
108 exit(ret)