Skip to content

Commit c40ae7a

Browse files
committed
[GLUTEN-11027] Use clang-tidy to check cpp files
1 parent 1030678 commit c40ae7a

2 files changed

Lines changed: 219 additions & 0 deletions

File tree

cpp/run-clang-tidy.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
#!/usr/bin/env python3
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from __future__ import print_function
18+
import argparse
19+
import os
20+
import regex
21+
import subprocess
22+
import sys
23+
24+
25+
class string(str):
26+
def extract(self, rexp):
27+
return regex.match(rexp, self).group(1)
28+
29+
def json(self):
30+
return json.loads(self, object_hook=attrdict)
31+
32+
33+
CODE_CHECKS = """*
34+
-abseil-*
35+
-android-*
36+
-cert-err58-cpp
37+
-clang-analyzer-osx-*
38+
-cppcoreguidelines-avoid-c-arrays
39+
-cppcoreguidelines-avoid-magic-numbers
40+
-cppcoreguidelines-pro-bounds-array-to-pointer-decay
41+
-cppcoreguidelines-pro-bounds-pointer-arithmetic
42+
-cppcoreguidelines-pro-type-reinterpret-cast
43+
-cppcoreguidelines-pro-type-vararg
44+
-fuchsia-*
45+
-google-*
46+
-hicpp-avoid-c-arrays
47+
-hicpp-deprecated-headers
48+
-hicpp-no-array-decay
49+
-hicpp-use-equals-default
50+
-hicpp-vararg
51+
-llvmlibc-*
52+
-llvm-header-guard
53+
-llvm-include-order
54+
-mpi-*
55+
-misc-non-private-member-variables-in-classes
56+
-misc-no-recursion
57+
-misc-unused-parameters
58+
-modernize-avoid-c-arrays
59+
-modernize-deprecated-headers
60+
-modernize-use-nodiscard
61+
-modernize-use-trailing-return-type
62+
-objc-*
63+
-openmp-*
64+
-readability-avoid-const-params-in-decls
65+
-readability-convert-member-functions-to-static
66+
-readability-magic-numbers
67+
-zircon-*
68+
"""
69+
70+
71+
def run(command, compressed=False, **kwargs):
72+
if "input" in kwargs:
73+
input = kwargs["input"]
74+
75+
if type(input) == list:
76+
input = "\n".join(input) + "\n"
77+
78+
kwargs["input"] = input.encode("utf-8")
79+
reply = subprocess.run(
80+
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs
81+
)
82+
83+
if compressed:
84+
stdout = gzip.decompress(reply.stdout)
85+
else:
86+
stdout = reply.stdout
87+
88+
stdout = (
89+
string(stdout.decode("utf-8", errors="ignore").strip())
90+
if stdout is not None
91+
else ""
92+
)
93+
stderr = (
94+
string(reply.stderr.decode("utf-8").strip()) if reply.stderr is not None else ""
95+
)
96+
97+
if stderr != "":
98+
print(stderr, file=sys.stderr)
99+
100+
return reply.returncode, stdout, stderr
101+
102+
103+
def check_list(check_string):
104+
return ",".join([c.strip() for c in check_string.strip().splitlines() if c.strip()])
105+
106+
107+
CODE_CHECKS = check_list(CODE_CHECKS)
108+
109+
110+
def check_output_has_warnings(output):
111+
if not output:
112+
return False
113+
return bool(regex.search(r"(?i)\b(warning|error):", output))
114+
115+
116+
def get_modified_cpp_files(commit="HEAD"):
117+
cmd = f"git diff-tree --no-commit-id --name-status -r {commit}"
118+
status, stdout, stderr = run(cmd)
119+
if status != 0:
120+
print("Error running git diff-tree:", stderr, file=sys.stderr)
121+
return []
122+
123+
files = []
124+
for line in stdout.splitlines():
125+
parts = line.strip().split("\t")
126+
if len(parts) < 2:
127+
continue
128+
change_type, path = parts[0], parts[1]
129+
if change_type in ("A", "M") and path.endswith((".cpp", ".cc", ".h")):
130+
files.append(path)
131+
132+
return files
133+
134+
135+
def ensure_compile_db(build_dir):
136+
path = os.path.join(build_dir, "compile_commands.json")
137+
return os.path.exists(path)
138+
139+
140+
def tidy(args):
141+
clang_tidy_bin = args.clang_tidy or "clang-tidy"
142+
build_dir = args.build_dir or "cpp/build"
143+
project_root = args.project_root or "/app/dps-ssc-core-gluten/incubator-gluten"
144+
145+
files = get_modified_cpp_files()
146+
if not files:
147+
print("No modified .cc/.cpp/.h files in the latest commit. Nothing to do.")
148+
return 0
149+
150+
if not ensure_compile_db(build_dir):
151+
print("No compile_commands.json found in '{}'.".format(build_dir))
152+
return 1
153+
154+
fix = "--fix" if args.fix == "fix" else ""
155+
header_filter = "^(?!.*build).*".format(regex.escape(project_root))
156+
157+
cmd = (
158+
"xargs {clang} -p={build}/releases --format-style=file --header-filter='{hf}' "
159+
"--checks='{checks}' {fix} --quiet".format(
160+
clang=clang_tidy_bin,
161+
build=build_dir,
162+
hf=header_filter,
163+
checks=CODE_CHECKS,
164+
fix=fix,
165+
)
166+
)
167+
168+
print("Running clang-tidy on {} file(s)...".format(len(files)))
169+
status, stdout, stderr = run(cmd, input=files)
170+
171+
combined_output = ""
172+
if stdout:
173+
combined_output += stdout + "\n"
174+
if stderr:
175+
combined_output += stderr
176+
177+
if check_output_has_warnings(combined_output):
178+
print("clang-tidy found warnings/errors.")
179+
# Print a reasonably sized portion to avoid flooding logs; print all if small
180+
if len(combined_output) > 20000:
181+
print(combined_output[:20000])
182+
print("... (output truncated)")
183+
else:
184+
print(combined_output)
185+
return 1
186+
187+
# Also check the return code (status) - clang-tidy may return non-zero for internal errors
188+
if status != 0:
189+
print("clang-tidy returned non-zero exit code:", status)
190+
if combined_output:
191+
print(combined_output)
192+
return 1
193+
194+
print("clang-tidy completed successfully (no warnings).")
195+
return 0
196+
197+
198+
def parse_args():
199+
parser = argparse.ArgumentParser(
200+
description="Run clang-tidy with project settings (compatible with older Python versions)"
201+
)
202+
parser.add_argument("--fix", help="Apply automatic fixes (use 'fix' to enable)")
203+
parser.add_argument("--clang-tidy", help="Path to clang-tidy binary")
204+
parser.add_argument(
205+
"--build-dir", help="Path to build directory with compile_commands.json"
206+
)
207+
parser.add_argument(
208+
"--project-root", help="Root path of the source project (for header-filter)"
209+
)
210+
return parser.parse_args()
211+
212+
213+
def main():
214+
return tidy(parse_args())
215+
216+
217+
if __name__ == "__main__":
218+
sys.exit(main())

dev/builddeps-veloxbe.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ function build_gluten_cpp {
243243
-DENABLE_HDFS=$ENABLE_HDFS \
244244
-DENABLE_ABFS=$ENABLE_ABFS \
245245
-DENABLE_GPU=$ENABLE_GPU \
246+
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
246247
-DENABLE_ENHANCED_FEATURES=$ENABLE_ENHANCED_FEATURES"
247248

248249
if [ $OS == 'Darwin' ]; then

0 commit comments

Comments
 (0)