forked from chipsalliance/verible-linter-action
-
Notifications
You must be signed in to change notification settings - Fork 0
/
action.py
executable file
·116 lines (92 loc) · 3.44 KB
/
action.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env python3
import os
import subprocess
import click
import warnings
def recursive_find(file_extensions, pwd):
files = set()
for obj in os.listdir(pwd):
obj = os.path.join(pwd, obj)
if os.path.isdir(obj):
files.update(recursive_find(file_extensions, obj))
if os.path.isfile(obj):
_, ext = os.path.splitext(obj)
if ext in file_extensions:
files.add(obj)
return files
def log_raw(issues, filename):
with open(filename, 'w') as f:
f.write(issues)
def print_annotations(issues):
'''
Print linter output in a way
that is parsed by GitHub and used to annotate code automatically
'''
for issue in issues.splitlines():
file_name, line, column, issue = issue.split(":", 3)
print(f'::error file={file_name},line={line},col={column}::{issue}')
def unwrap_from_gha_string(args):
# unwrap paths stored in GHA argument as a multiline string
# each element in {args} can be either a file name
# or multiple file names concatenated with '\n'
# for example: path=('./ibex/rtl\n./ibex/rtl/ibex_alu.sv\n', 'foobar')
# returns a set of separate file names
elements = set()
for arg in args:
new_elems = arg.split()
elements.update(new_elems)
return elements
def find_sources_in_paths(paths):
'''Scan {paths} for Verilog and SystemVerilog files
'''
files = set()
for filename in paths:
# check if filename is a directory or a source file
if os.path.isdir(filename):
new_files = recursive_find({".v", ".sv"}, filename)
files.update(new_files)
elif os.path.isfile(filename):
files.add(filename)
else:
warnings.warn(f"{filename} is not a valid file name!")
return files
@click.command()
@click.option('--conf-file', '-c', type=str, required=False,
help='lint rules configuration file')
@click.option('--extra-opts', '-e', required=False,
help='extra options for the linter')
@click.option('--exclude-paths', '-x', required=False,
help='exclude these paths from the files to lint')
@click.option('--log-file', '-l', type=str, required=False,
help='log file name')
@click.argument('path', nargs=-1, required=True)
def main(conf_file, extra_opts, exclude_paths, log_file, path):
'''
Lint .v and .sv files specified in PATHs
'''
if conf_file: # GitHub Actions passes "" here if it's not used
conf_file = ['--rules_config', conf_file]
else:
conf_file = []
if extra_opts:
extra_opts = extra_opts.split()
else:
extra_opts = []
paths = unwrap_from_gha_string(path)
# set of target files to lint
files = find_sources_in_paths(paths)
if exclude_paths:
for p in exclude_paths.split():
# get rid of every file which starts with this path
files = {f for f in files if not f.startswith(p)}
if not files:
warnings.warn("File set is empty, the action has nothing to do.")
exit()
command = ["verible-verilog-lint"] + conf_file + extra_opts + list(files)
print("Running " + " ".join(command) + "\n\n")
verible_linted = subprocess.run(command, capture_output=True)
issues = verible_linted.stdout.decode("utf-8")
log_raw(issues, log_file if log_file else 'verible-verilog-lint.log')
exit(issues != "")
if __name__ == '__main__':
main()