forked from dodola/scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
git-file-tidy
executable file
·191 lines (157 loc) · 5.79 KB
/
git-file-tidy
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/usr/bin/env python
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Runs clang-tidy on modified files.
The tool uses `git diff-index` against the newest parent commit in the upstream
branch (or against HEAD if there is no upstream branch) in order to find the
files to be formatted. In result, the tool lints files that are locally
modified, staged or touched by any commits introduced on the local branch.
"""
import argparse
import multiprocessing
import os
import platform
import re
import subprocess
import sys
import git_utils
import paths
clang_os = "linux"
if platform.platform().startswith("Darwin"):
clang_os = "mac"
CLANG_TIDY_TOOL = os.path.join(paths.BUILDTOOLS_ROOT,
"%s-x64" % clang_os, "clang", "bin",
"clang-tidy")
NINJA_TOOL = os.path.join(paths.BUILDTOOLS_ROOT, "ninja")
def find_ancestor_with(filepath, relpath):
"""Returns the lowest ancestor of |filepath| that contains |relpath|."""
cur_dir_path = os.path.abspath(os.path.dirname(filepath))
while True:
if os.path.exists(os.path.join(cur_dir_path, relpath)):
return cur_dir_path
next_dir_path = os.path.dirname(cur_dir_path)
if next_dir_path != cur_dir_path:
cur_dir_path = next_dir_path
else:
return None
def get_out_dir(args):
if os.environ.get("FUCHSIA_BUILD_DIR"):
return os.environ.get("FUCHSIA_BUILD_DIR")
if args.out_dir:
out_dir = args.out_dir
if not os.path.isabs(out_dir):
out_dir = os.path.join(paths.FUCHSIA_ROOT, out_dir)
if not os.path.isdir(out_dir):
print out_dir + " is not a directory"
sys.exit(-1)
return out_dir
print("Couldn't find the output directory, pass --out-dir " +
"(absolute or relative to Fuchsia root) or set FUCHSIA_BUILD_DIR.")
sys.exit(-1)
def generate_db(out_dir):
cmd = [NINJA_TOOL, "-C", out_dir, "-t", "compdb", "cc", "cxx"]
db = subprocess.check_output(
cmd, cwd=paths.FUCHSIA_ROOT, universal_newlines=True)
# Strip away `gomacc` from the compile commands. This seems to fix problems
# with clang-tidy not being able to load system headers.
db = re.sub("\"/[\S]+/gomacc ", "\"", db)
with open(os.path.join(out_dir, "compile_commands.json"), "w+") as db_file:
db_file.write(db)
def go(args):
out_dir = get_out_dir(args)
# generate the compilation database
generate_db(out_dir)
# Find the files to be checked.
if args.all:
files = git_utils.get_all_files()
else:
files = git_utils.get_diff_files()
filtered_files = []
for file_path in files:
# Skip deleted files.
if not os.path.isfile(file_path):
if args.verbose:
print "skipping " + file_path + " (deleted)"
continue
# Skip files with parent directories containing .nolint
if find_ancestor_with(file_path, ".nolint"):
if args.verbose:
print "skipping " + file_path + " (.nolint)"
continue
filtered_files.append(file_path)
if args.verbose:
print
print "Files to be checked:"
for file in filtered_files:
print " - " + file
if not filtered_files:
print " (no files)"
print
# change the working directory to Fuchsia root.
os.chdir(paths.FUCHSIA_ROOT)
# It's not safe to run in parallel with "--fix", as clang-tidy traverses and
# fixes header files, and we might end up with concurrent writes to the same
# header file.
if args.no_parallel or args.fix:
parallel_jobs = 1
else:
parallel_jobs = multiprocessing.cpu_count()
print("Running " + str(parallel_jobs) +
" jobs in parallel, pass --no-parallel to disable")
jobs = set()
for file_path in filtered_files:
_, extension = os.path.splitext(file_path)
if extension == ".cc":
relpath = os.path.relpath(file_path)
cmd = [CLANG_TIDY_TOOL, "-p", out_dir, relpath]
if args.checks:
cmd.append("-checks=" + args.checks)
if args.fix:
cmd.append("-fix")
if not args.verbose:
cmd.append("-quiet")
if args.verbose:
print "checking " + file_path + ": " + str(cmd)
jobs.add(subprocess.Popen(cmd))
if len(jobs) >= parallel_jobs:
os.wait()
jobs.difference_update(
[job for job in jobs if job.poll() is not None])
for job in jobs:
if job.poll() is None:
job.wait()
def main():
parser = argparse.ArgumentParser(description="Lint modified files.")
parser.add_argument(
"--all",
dest="all",
action="store_true",
default=False,
help="process all files in the repo under current working directory")
parser.add_argument(
"--fix",
dest="fix",
action="store_true",
default=False,
help="automatically generate fixes when possible")
parser.add_argument("--checks", help="overrides the list of checks to use")
parser.add_argument(
"--out-dir",
help="Output directory, needed to generate compilation db for clang.")
parser.add_argument(
"--no-parallel",
action="store_true",
default=False,
help="Process one file at a time")
parser.add_argument(
"--verbose",
dest="verbose",
action="store_true",
default=False,
help="tell me what you're doing")
args = parser.parse_args()
go(args)
return 0
if __name__ == "__main__":
sys.exit(main())