127 lines
4.0 KiB
Python
Executable File
127 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# Copyright (c) 2021 Intel Corporation
|
|
|
|
import os
|
|
import sh
|
|
import argparse
|
|
import re
|
|
from unidiff import PatchSet
|
|
|
|
if "ZEPHYR_BASE" not in os.environ:
|
|
exit("$ZEPHYR_BASE environment variable undefined.")
|
|
|
|
RESERVED_NAMES_SCRIPT = "/scripts/coccinelle/reserved_names.cocci"
|
|
|
|
coccinelle_scripts = [RESERVED_NAMES_SCRIPT,
|
|
"/scripts/coccinelle/same_identifier.cocci",
|
|
#"/scripts/coccinelle/identifier_length.cocci",
|
|
]
|
|
|
|
coccinelle_reserved_names_exclude_regex = [
|
|
r"lib/libc/.*",
|
|
r"lib/posix/.*",
|
|
r"include/zephyr/posix/.*",
|
|
]
|
|
|
|
def parse_coccinelle(contents: str, violations: dict):
|
|
reg = re.compile("([a-zA-Z0-9_/]*\\.[ch]:[0-9]*)(:[0-9\\-]*: )(.*)")
|
|
for line in contents.split("\n"):
|
|
r = reg.match(line)
|
|
if r:
|
|
f = r.group(1)
|
|
if f in violations:
|
|
violations[f].append(r.group(3))
|
|
else:
|
|
violations[r.group(1)] = [r.group(3)]
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(
|
|
description="Check commits against Cocccinelle rules", allow_abbrev=False)
|
|
parser.add_argument('-r', "--repository", required=False,
|
|
help="Path to repository")
|
|
parser.add_argument('-c', '--commits', default=None,
|
|
help="Commit range in the form: a..b")
|
|
parser.add_argument("-o", "--output", required=False,
|
|
help="Print violation into a file")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
if not args.commits:
|
|
exit("missing commit range")
|
|
|
|
if args.repository is None:
|
|
repository_path = os.environ['ZEPHYR_BASE']
|
|
else:
|
|
repository_path = args.repository
|
|
|
|
sh_special_args = {
|
|
'_tty_out': False,
|
|
'_cwd': repository_path
|
|
}
|
|
|
|
# pylint does not like the 'sh' library
|
|
# pylint: disable=too-many-function-args,unexpected-keyword-arg
|
|
commit = sh.git("diff", args.commits, **sh_special_args)
|
|
patch_set = PatchSet(commit)
|
|
zephyr_base = os.getenv("ZEPHYR_BASE")
|
|
violations = {}
|
|
numViolations = 0
|
|
|
|
for f in patch_set:
|
|
if not f.path.endswith(".c") and not f.path.endswith(".h") or not os.path.exists(zephyr_base + "/" + f.path):
|
|
continue
|
|
|
|
for script in coccinelle_scripts:
|
|
|
|
skip_reserved_names = False
|
|
if script == RESERVED_NAMES_SCRIPT:
|
|
for path in coccinelle_reserved_names_exclude_regex:
|
|
if re.match(path, f.path):
|
|
skip_reserved_names = True
|
|
break
|
|
|
|
if skip_reserved_names:
|
|
continue
|
|
|
|
script_path =zephyr_base + "/" + script
|
|
print(f"Running {script} on {f.path}")
|
|
try:
|
|
cocci = sh.coccicheck(
|
|
"--mode=report",
|
|
"--cocci=" +
|
|
script_path,
|
|
f.path,
|
|
_timeout=10,
|
|
**sh_special_args)
|
|
parse_coccinelle(cocci, violations)
|
|
except sh.TimeoutException:
|
|
print("we timed out waiting, skipping...")
|
|
|
|
for hunk in f:
|
|
for line in hunk:
|
|
if line.is_added:
|
|
violation = "{}:{}".format(f.path, line.target_line_no)
|
|
if violation in violations:
|
|
numViolations += 1
|
|
if args.output:
|
|
with open(args.output, "a+") as fp:
|
|
fp.write("{}:{}\n".format(
|
|
violation, "\t\n".join(
|
|
violations[violation])))
|
|
else:
|
|
print(
|
|
"{}:{}".format(
|
|
violation, "\t\n".join(
|
|
violations[violation])))
|
|
|
|
return numViolations
|
|
|
|
|
|
if __name__ == "__main__":
|
|
ret = main()
|
|
exit(ret)
|