blob: 00e0b3c6dbbacab540203f6d69db2be99d117583 [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
Chris Friedt1c28ded2024-08-14 16:20:28 -040014RESERVED_NAMES_SCRIPT = "/scripts/coccinelle/reserved_names.cocci"
15
16coccinelle_scripts = [RESERVED_NAMES_SCRIPT,
Flavio Ceolin85525422021-03-23 15:06:01 -070017 "/scripts/coccinelle/same_identifier.cocci",
Anas Nashif01c1bcf2021-05-06 14:17:29 -040018 #"/scripts/coccinelle/identifier_length.cocci",
Flavio Ceolin85525422021-03-23 15:06:01 -070019 ]
20
Chris Friedt1c28ded2024-08-14 16:20:28 -040021coccinelle_reserved_names_exclude_regex = [
22 r"lib/libc/.*",
23 r"lib/posix/.*",
24 r"include/zephyr/posix/.*",
25]
Flavio Ceolin85525422021-03-23 15:06:01 -070026
27def parse_coccinelle(contents: str, violations: dict):
Anas Nashifb669f0c2021-04-30 23:01:03 -040028 reg = re.compile("([a-zA-Z0-9_/]*\\.[ch]:[0-9]*)(:[0-9\\-]*: )(.*)")
Flavio Ceolin85525422021-03-23 15:06:01 -070029 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(
Jordan Yatesac78b972023-12-10 16:04:57 +100041 description="Check commits against Cocccinelle rules", allow_abbrev=False)
42 parser.add_argument('-r', "--repository", required=False,
43 help="Path to repository")
Flavio Ceolin85525422021-03-23 15:06:01 -070044 parser.add_argument('-c', '--commits', default=None,
45 help="Commit range in the form: a..b")
Flavio Ceolin58fdc012021-03-24 15:54:16 -070046 parser.add_argument("-o", "--output", required=False,
47 help="Print violation into a file")
Flavio Ceolin85525422021-03-23 15:06:01 -070048 return parser.parse_args()
49
50
51def main():
52 args = parse_args()
53 if not args.commits:
54 exit("missing commit range")
55
Jordan Yatesac78b972023-12-10 16:04:57 +100056 if args.repository is None:
57 repository_path = os.environ['ZEPHYR_BASE']
58 else:
59 repository_path = args.repository
60
61 sh_special_args = {
62 '_tty_out': False,
63 '_cwd': repository_path
64 }
65
Flavio Ceolin0bbe5e42021-03-24 16:18:43 -070066 # pylint does not like the 'sh' library
67 # pylint: disable=too-many-function-args,unexpected-keyword-arg
Flavio Ceolin85525422021-03-23 15:06:01 -070068 commit = sh.git("diff", args.commits, **sh_special_args)
69 patch_set = PatchSet(commit)
70 zephyr_base = os.getenv("ZEPHYR_BASE")
71 violations = {}
72 numViolations = 0
73
74 for f in patch_set:
75 if not f.path.endswith(".c") and not f.path.endswith(".h") or not os.path.exists(zephyr_base + "/" + f.path):
76 continue
77
78 for script in coccinelle_scripts:
Chris Friedt1c28ded2024-08-14 16:20:28 -040079
80 skip_reserved_names = False
81 if script == RESERVED_NAMES_SCRIPT:
82 for path in coccinelle_reserved_names_exclude_regex:
83 if re.match(path, f.path):
84 skip_reserved_names = True
85 break
86
87 if skip_reserved_names:
88 continue
89
90 script_path =zephyr_base + "/" + script
Anas Nashif8ebc67e2021-05-03 10:40:42 -040091 print(f"Running {script} on {f.path}")
92 try:
93 cocci = sh.coccicheck(
94 "--mode=report",
95 "--cocci=" +
96 script_path,
97 f.path,
98 _timeout=10,
99 **sh_special_args)
100 parse_coccinelle(cocci, violations)
101 except sh.TimeoutException:
102 print("we timed out waiting, skipping...")
Flavio Ceolin85525422021-03-23 15:06:01 -0700103
104 for hunk in f:
105 for line in hunk:
106 if line.is_added:
107 violation = "{}:{}".format(f.path, line.target_line_no)
108 if violation in violations:
109 numViolations += 1
Flavio Ceolin58fdc012021-03-24 15:54:16 -0700110 if args.output:
111 with open(args.output, "a+") as fp:
112 fp.write("{}:{}\n".format(
113 violation, "\t\n".join(
114 violations[violation])))
115 else:
116 print(
117 "{}:{}".format(
118 violation, "\t\n".join(
119 violations[violation])))
Flavio Ceolin85525422021-03-23 15:06:01 -0700120
121 return numViolations
122
123
124if __name__ == "__main__":
125 ret = main()
126 exit(ret)