Skip to content

Commit a8aa1b4

Browse files
committed
Add python script
1 parent f9aed35 commit a8aa1b4

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

scripts/run-clang-format.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Copyright (c) 2019-2020, NVIDIA CORPORATION.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
16+
from __future__ import print_function
17+
import sys
18+
import re
19+
import os
20+
import subprocess
21+
import argparse
22+
import tempfile
23+
24+
25+
EXPECTED_VERSION = "8.0.1"
26+
VERSION_REGEX = re.compile(r"clang-format version ([0-9.]+)")
27+
# NOTE: populate this list with more top-level dirs as we add more of them to the rmm repo
28+
DEFAULT_DIRS = ["src",
29+
"include"
30+
"tests",
31+
"benchmarks"]
32+
33+
34+
def parse_args():
35+
argparser = argparse.ArgumentParser("Runs clang-format on a project")
36+
argparser.add_argument("-dstdir", type=str, default=None,
37+
help="Directory to store the temporary outputs of"
38+
" clang-format. If nothing is passed for this, then"
39+
" a temporary dir will be created using `mkdtemp`")
40+
argparser.add_argument("-exe", type=str, default="clang-format",
41+
help="Path to clang-format exe")
42+
argparser.add_argument("-inplace", default=False, action="store_true",
43+
help="Replace the source files itself.")
44+
argparser.add_argument("-regex", type=str,
45+
default=r"[.](cu|cuh|h|hpp|cpp)$",
46+
help="Regex string to filter in sources")
47+
argparser.add_argument("-ignore", type=str, default=r"cannylab/bh[.]cu$",
48+
help="Regex used to ignore files from matched list")
49+
argparser.add_argument("-v", dest="verbose", action="store_true",
50+
help="Print verbose messages")
51+
argparser.add_argument("dirs", type=str, nargs="*",
52+
help="List of dirs where to find sources")
53+
args = argparser.parse_args()
54+
args.regex_compiled = re.compile(args.regex)
55+
args.ignore_compiled = re.compile(args.ignore)
56+
if args.dstdir is None:
57+
args.dstdir = tempfile.mkdtemp()
58+
ret = subprocess.check_output("%s --version" % args.exe, shell=True)
59+
ret = ret.decode("utf-8")
60+
version = VERSION_REGEX.match(ret)
61+
if version is None:
62+
raise Exception("Failed to figure out clang-format version!")
63+
version = version.group(1)
64+
if version != EXPECTED_VERSION:
65+
raise Exception("clang-format exe must be v%s found '%s'" % \
66+
(EXPECTED_VERSION, version))
67+
if len(args.dirs) == 0:
68+
args.dirs = DEFAULT_DIRS
69+
return args
70+
71+
72+
def list_all_src_files(file_regex, ignore_regex, srcdirs, dstdir, inplace):
73+
allFiles = []
74+
for srcdir in srcdirs:
75+
for root, dirs, files in os.walk(srcdir):
76+
for f in files:
77+
if re.search(file_regex, f):
78+
src = os.path.join(root, f)
79+
if re.search(ignore_regex, src):
80+
continue
81+
if inplace:
82+
_dir = root
83+
else:
84+
_dir = os.path.join(dstdir, root)
85+
dst = os.path.join(_dir, f)
86+
allFiles.append((src, dst))
87+
return allFiles
88+
89+
90+
def run_clang_format(src, dst, exe, verbose):
91+
dstdir = os.path.dirname(dst)
92+
if not os.path.exists(dstdir):
93+
os.makedirs(dstdir)
94+
# run the clang format command itself
95+
if src == dst:
96+
cmd = "%s -i %s" % (exe, src)
97+
else:
98+
cmd = "%s %s > %s" % (exe, src, dst)
99+
try:
100+
subprocess.check_call(cmd, shell=True)
101+
except subprocess.CalledProcessError:
102+
print("Failed to run clang-format! Maybe your env is not proper?")
103+
raise
104+
# run the diff to check if there are any formatting issues
105+
cmd = "diff -q %s %s >/dev/null" % (src, dst)
106+
try:
107+
subprocess.check_call(cmd, shell=True)
108+
if verbose:
109+
print("%s passed" % os.path.basename(src))
110+
except subprocess.CalledProcessError:
111+
print("%s failed! 'diff %s %s' will show formatting violations!" % \
112+
(os.path.basename(src), src, dst))
113+
return False
114+
return True
115+
116+
117+
def main():
118+
args = parse_args()
119+
# Attempt to making sure that we run this script from root of repo always
120+
if not os.path.exists(".git"):
121+
print("Error!! This needs to always be run from the root of repo")
122+
sys.exit(-1)
123+
all_files = list_all_src_files(args.regex_compiled, args.ignore_compiled,
124+
args.dirs, args.dstdir, args.inplace)
125+
# actual format checker
126+
status = True
127+
for src, dst in all_files:
128+
if not run_clang_format(src, dst, args.exe, args.verbose):
129+
status = False
130+
if not status:
131+
print("clang-format failed! You have 2 options:")
132+
print(" 1. Look at formatting differences above and fix them manually")
133+
print(" 2. Or run the below command to bulk-fix all these at once")
134+
print("Bulk-fix command: ")
135+
print(" python scripts/run-clang-format.py %s -inplace" % \
136+
" ".join(sys.argv[1:]))
137+
sys.exit(-1)
138+
return
139+
140+
141+
if __name__ == "__main__":
142+
main()

0 commit comments

Comments
 (0)