@abergmeier 是对的。让我们更进一步,实现宏及其组件。
我们将使用bazelbuild/examples 中的 C++ 第 1 阶段教程。
我们先搞砸hello-world.cc:
#include <ctime>
#include <string>
#include <iostream>
std::string get_greet(const std::string& who) {
return "Hello " + who;
}
void print_localtime() {
std::time_t result =
std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
}
int main(int argc, char** argv) {
std::string who = "world";
if (argc > 1) {who = argv[1];}
std::cout << get_greet(who) << std::endl;
print_localtime();
return 0;
}
这是构建文件:
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
由于cc_binary 对clang-format 或一般的linting 一无所知,让我们创建一个名为clang_formatted_cc_binary 的宏并用它替换cc_binary。 BUILD 文件现在如下所示:
load(":clang_format.bzl", "clang_formatted_cc_binary")
clang_formatted_cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
接下来,创建一个名为 clang_format.bzl 的文件,其中包含一个名为 clang_formatted_cc_binary 的宏,它只是 native.cc_binary 的包装:
# In clang_format.bzl
def clang_formatted_cc_binary(**kwargs):
native.cc_binary(**kwargs)
此时,您可以构建cc_binary 目标,但它还没有运行clang-format。我们需要在clang_formatted_cc_binary 中添加一个中间规则来做到这一点,我们称之为clang_format_srcs:
def clang_formatted_cc_binary(name, srcs, **kwargs):
# Using a filegroup for code cleaniness
native.filegroup(
name = name + "_unformatted_srcs",
srcs = srcs,
)
clang_format_srcs(
name = name + "_formatted_srcs",
srcs = [name + "_unformatted_srcs"],
)
native.cc_binary(
name = name,
srcs = [name + "_formatted_srcs"],
**kwargs
)
请注意,我们已将 native.cc_binary 的源代码替换为格式化文件,但保留名称以允许在 BUILD 文件中就地替换 cc_binary -> clang_formatted_cc_binary。
最后,我们将在同一个clang_format.bzl 文件中编写clang_format_srcs 规则的实现:
def _clang_format_srcs_impl(ctx):
formatted_files = []
for unformatted_file in ctx.files.srcs:
formatted_file = ctx.actions.declare_file("formatted_" + unformatted_file.basename)
formatted_files += [formatted_file]
ctx.actions.run_shell(
inputs = [unformatted_file],
outputs = [formatted_file],
progress_message = "Running clang-format on %s" % unformatted_file.short_path,
command = "clang-format %s > %s" % (unformatted_file.path, formatted_file.path),
)
return struct(files = depset(formatted_files))
clang_format_srcs = rule(
attrs = {
"srcs": attr.label_list(allow_files = True),
},
implementation = _clang_format_srcs_impl,
)
此规则遍历目标的 srcs 属性中的每个文件,声明带有 formatted_ 前缀的“虚拟”输出文件,并在未格式化的文件上运行 clang-format 以生成虚拟输出。
现在,如果您运行 bazel build :hello-world,Bazel 将运行 clang_format_srcs 中的操作,然后再对格式化文件运行 cc_binary 编译操作。我们可以通过运行带有--subcommands 标志的bazel build 来证明这一点:
$ bazel build //main:hello-world --subcommands
..
SUBCOMMAND: # //main:hello-world_formatted_srcs [action 'Running clang-format on main/hello-world.cc']
..
SUBCOMMAND: # //main:hello-world [action 'Compiling main/formatted_hello-world.cc']
..
SUBCOMMAND: # //main:hello-world [action 'Linking main/hello-world']
..
查看formatted_hello-world.cc 的内容,看起来clang-format 完成了它的工作:
#include <ctime>
#include <string>
#include <iostream>
std::string get_greet(const std::string& who) { return "Hello " + who; }
void print_localtime() {
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
}
int main(int argc, char** argv) {
std::string who = "world";
if (argc > 1) {
who = argv[1];
}
std::cout << get_greet(who) << std::endl;
print_localtime();
return 0;
}
如果你想要的只是格式化的源而不编译它们,你可以直接从clang_format_srcs 运行构建带有_formatted_srcs 后缀的目标:
$ bazel build //main:hello-world_formatted_srcs
INFO: Analysed target //main:hello-world_formatted_srcs (0 packages loaded).
INFO: Found 1 target...
Target //main:hello-world_formatted_srcs up-to-date:
bazel-bin/main/formatted_hello-world.cc
INFO: Elapsed time: 0.247s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action