如何将漂亮的打印内容集成到bazel中作为构建的一部分

not*_*eus 3 c++ clang-format bazel

现在,我有一个非常愚蠢的漂亮打印脚本,该脚本做了一点git-fu查找(无条件地)格式化的文件,然后通过clang-format -i运行它们。这种方法有几个缺点:

  1. 有些文件非常庞大,需要花费很长时间才能打印出来。
  2. 无论基础文件是否实际更改,始终都会进行漂亮的打印。

过去,我能够使用CMake进行处理,这些处理具有一些好特性,我想在bazel中重现这些特性:

  1. 经过棉绒/漂亮印刷/等才能构建代码。
  2. 只有皮棉/精美印刷/等已更改的东西
  3. 无论是否在VC下,都可以打印漂亮的东西

在CMake-land中,我受SCons代理目标欺骗的启发使用了这种策略:

  1. 介绍一个虚拟目标(例如source-> source.formatted)。与该目标关联的操作有两件事:a)运行clang-format -i source,b)输出/触摸名为source.formatted的文件(如果source.formatted比source更新,则对于合理的文件系统,这保证了合理的文件系统)不需要重新格式化)

  2. 添加一个虚拟目标(target_name.aggregated_formatted),该目标汇总与特定库/可执行目标的源相对应的所有.formatted文件

  3. 使库/可执行目标依赖于target_name.aggregated_formatted作为预构建步骤

任何帮助将不胜感激。

Jin*_*Jin 5

@abergmeier是正确的。通过实现宏及其组件,使我们更进一步。

我们将在中使用C ++ Stage 1教程bazelbuild/examples

让我们先搞砸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;
}
Run Code Online (Sandbox Code Playgroud)

这是BUILD文件:

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)
Run Code Online (Sandbox Code Playgroud)

由于cc_binary通常不了解clang-format或不了解任何东西,因此我们创建一个名为的宏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"],
)
Run Code Online (Sandbox Code Playgroud)

接下来,创建一个名为的文件clang_format.bzl,该宏名为clang_formatted_cc_binary,只是一个包装器native.cc_binary

# In clang_format.bzl
def clang_formatted_cc_binary(**kwargs):
    native.cc_binary(**kwargs)
Run Code Online (Sandbox Code Playgroud)

此时,您可以构建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
    )
Run Code Online (Sandbox Code Playgroud)

请注意,我们已native.cc_binary使用格式化的文件替换了的源,但保留了名称以允许在BUILD文件中就地替换cc_binary-> clang_formatted_cc_binary

最后,我们将clang_format_srcs在同一clang_format.bzl文件中编写规则的实现:

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,
)
Run Code Online (Sandbox Code Playgroud)

该规则遍历目标srcs属性中的每个文件,声明带有formatted_前缀的“虚拟”输出文件,并clang-format在未格式化的文件上运行以生成虚拟输出。

现在,如果您运行bazel build :hello-world,Bazel将在格式化文件上clang_format_srcs运行cc_binary编译操作之前运行这些操作。我们可以通过运行证明这bazel build--subcommands标志:

$ 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']
..
Run Code Online (Sandbox Code Playgroud)

查看的内容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;
}
Run Code Online (Sandbox Code Playgroud)

如果只需要格式化的源代码而无需编译它们,则可以直接使用_formatted_srcs后缀运行构建目标clang_format_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
Run Code Online (Sandbox Code Playgroud)


abe*_*ier 4

您也许可以使用方面来实现这一点。由于不确定,Bazel 开发人员可能会指出这是否确实可能。

如果您熟悉规则和操作等,快速而肮脏的方法(类似于 CMake hackery)是编写一个宏。例如cc_library你会这样做:

def clean_cc_library(name, srcs, **kwargs):
  lint_sources(
      name = "%s_linted" % name,
      srcs = srcs,
  )

  pretty_print_sources(
      name = "%s_pretty" % name,
      srcs = ["%s_linted"],
  )

  return native.cc_library(
    name = name,
    srcs = ["%s_pretty"],
    **kwargs
  ) 
Run Code Online (Sandbox Code Playgroud)

那么你当然需要将 every 替换cc_libraryclean_cc_library. 和lint_sourcespretty_print_sources您必须自己实施并需要生成已清理文件列表的规则。