-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathclang_tidy.bzl
More file actions
212 lines (179 loc) · 7.17 KB
/
clang_tidy.bzl
File metadata and controls
212 lines (179 loc) · 7.17 KB
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
def _run_tidy(
ctx,
wrapper,
exe,
additional_deps,
config,
flags,
compilation_context,
infile,
discriminator):
inputs = depset(
direct = (
[infile, config] +
additional_deps.files.to_list() +
([exe.files_to_run.executable] if exe.files_to_run.executable else [])
),
transitive = [compilation_context.headers],
)
args = ctx.actions.args()
# specify the output file - twice
outfile = ctx.actions.declare_file(
"bazel_clang_tidy_" + infile.path + "." + discriminator + ".clang-tidy.yaml",
)
# this is consumed by the wrapper script
if len(exe.files.to_list()) == 0:
args.add("clang-tidy")
else:
args.add(exe.files_to_run.executable)
args.add(outfile.path) # this is consumed by the wrapper script
args.add(config.path)
args.add("--export-fixes", outfile.path)
# add source to check
args.add(infile.path)
# start args passed to the compiler
args.add("--")
# add args specified by the toolchain, on the command line and rule copts
args.add_all(flags)
# add defines
for define in compilation_context.defines.to_list():
args.add("-D" + define)
for define in compilation_context.local_defines.to_list():
args.add("-D" + define)
# add includes
for i in compilation_context.framework_includes.to_list():
args.add("-F" + i)
isystems = compilation_context.system_includes.to_list()
if ctx.attr._clang_tidy_virtual_includes_are_system_includes:
# Include anything that uses include_prefix or strip_include_prefix as a system include
include_dirs = []
for include in compilation_context.includes.to_list():
if "/_virtual_includes/" in include:
isystems.append(include)
else:
include_dirs.append(include)
else:
include_dirs = compilation_context.includes.to_list()
if ctx.attr._clang_tidy_bazel_out_includes_are_system_includes:
# Treat generated code in bazel-out/ as system include
iquotes = []
for quote_include in compilation_context.quote_includes.to_list():
if quote_include.startswith("bazel-out/"):
isystems.append(quote_include)
else:
iquotes.append(quote_include)
else:
iquotes = compilation_context.quote_includes.to_list()
args.add_all(include_dirs, before_each = "-I")
args.add_all(iquotes, before_each = "-iquote")
args.add_all(isystems, before_each = "-isystem")
ctx.actions.run(
inputs = inputs,
outputs = [outfile],
executable = wrapper,
arguments = [args],
mnemonic = "ClangTidy",
use_default_shell_env = True,
progress_message = "Run clang-tidy on {}".format(infile.short_path),
)
return outfile
def _rule_sources(ctx):
def check_valid_file_type(src):
"""
Returns True if the file type matches one of the permitted srcs file types for C and C++ header/source files.
"""
permitted_file_types = [
".c", ".cc", ".cpp", ".cxx", ".c++", ".C", ".h", ".hh", ".hpp", ".hxx", ".inc", ".inl", ".H",
]
for file_type in permitted_file_types:
if src.basename.endswith(file_type):
return True
return False
srcs = []
if hasattr(ctx.rule.attr, "srcs"):
for src in ctx.rule.attr.srcs:
srcs += [src for src in src.files.to_list() if src.is_source and check_valid_file_type(src)]
return srcs
def _toolchain_flags(ctx, action_name = ACTION_NAMES.cpp_compile):
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
)
compile_variables = cc_common.create_compile_variables(
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
user_compile_flags = ctx.fragments.cpp.cxxopts + ctx.fragments.cpp.copts,
)
flags = cc_common.get_memory_inefficient_command_line(
feature_configuration = feature_configuration,
action_name = action_name,
variables = compile_variables,
)
return flags
def _safe_flags(flags):
# Some flags might be used by GCC, but not understood by Clang.
# Remove them here, to allow users to run clang-tidy, without having
# a clang toolchain configured (that would produce a good command line with --compiler clang)
unsupported_flags = [
"-fno-canonical-system-headers",
"-fstack-usage",
]
return [flag for flag in flags if flag not in unsupported_flags]
def _clang_tidy_aspect_impl(target, ctx):
# if not a C/C++ target, we are not interested
if not CcInfo in target:
return []
# Ignore external targets
if target.label.workspace_root.startswith("external"):
return []
# Targets with specific tags will not be formatted
ignore_tags = [
"noclangtidy",
"no-clang-tidy",
]
for tag in ignore_tags:
if tag in ctx.rule.attr.tags:
return []
wrapper = ctx.attr._clang_tidy_wrapper.files_to_run
exe = ctx.attr._clang_tidy_executable
additional_deps = ctx.attr._clang_tidy_additional_deps
config = ctx.attr._clang_tidy_config.files.to_list()[0]
compilation_context = target[CcInfo].compilation_context
rule_flags = ctx.rule.attr.copts if hasattr(ctx.rule.attr, "copts") else []
c_flags = _safe_flags(_toolchain_flags(ctx, ACTION_NAMES.c_compile) + rule_flags) + ["-xc"]
cxx_flags = _safe_flags(_toolchain_flags(ctx, ACTION_NAMES.cpp_compile) + rule_flags) + ["-xc++"]
srcs = _rule_sources(ctx)
outputs = [
_run_tidy(
ctx,
wrapper,
exe,
additional_deps,
config,
c_flags if src.extension == "c" else cxx_flags,
compilation_context,
src,
target.label.name,
)
for src in srcs
]
return [
OutputGroupInfo(report = depset(direct = outputs)),
]
clang_tidy_aspect = aspect(
implementation = _clang_tidy_aspect_impl,
fragments = ["cpp"],
attrs = {
"_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
"_clang_tidy_wrapper": attr.label(default = Label("//clang_tidy:clang_tidy")),
"_clang_tidy_executable": attr.label(default = Label("//:clang_tidy_executable")),
"_clang_tidy_additional_deps": attr.label(default = Label("//:clang_tidy_additional_deps")),
"_clang_tidy_config": attr.label(default = Label("//:clang_tidy_config")),
"_clang_tidy_bazel_out_includes_are_system_includes": attr.label(default = Label("//:clang_tidy_bazel_out_includes_are_system_includes")),
"_clang_tidy_virtual_includes_are_system_includes": attr.label(default = Label("//:clang_tidy_virtual_includes_are_system_includes")),
},
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
)