Bazel规则:用于Protobuf C++编译器的编译。

9

我正在使用Bazel和Google的协议缓冲区。我想添加一个Bazel规则,以便可以从.proto文件生成C++ API。在GNU make中,我会这样做(简化示例):

%.h: %.cc
%.cc: %.proto
    protoc --cpp_out=. $<

我该如何使用Bazel实现相同的功能(即在mymessage.proto更改时生成API)?

1
Bazel最近已经原生支持cc_proto_library:https://bazel.build/blog/2017/02/27/protocol-buffers.html。如果这对您有帮助,我会将此评论转换为答案。 - user1071136
@user1071136 那会很好! - morxa
4个回答

8

我尝试了上述方法,但似乎并没有起作用。在尝试创建两个目录时,protoc 出现了错误提示,然后指出 my-proto.h 目录不存在。相反,我做了如下操作:

genrule(
    name = "my-proto-gen",
    outs = ["my-proto.pb.h my-proto.pb.cc"],
    cmd = "$(location //third_party/protobuf:protoc) --cpp_out=$(GENDIR) $<",
    srcs = ["my-proto.proto"],
    tools = ["//third_party/protobuf:protoc"],
)

cc_library(
    name = "my-proto",
    srcs = ["my-proto.pb.cc"],
    hdrs = ["my-proto.pb.h"]
)

这实际上只是检查头文件是否已创建并在 bazel-genfiles 目录中创建它。

然后,您可以将 proto 构建包含在您的 cc_library 中,例如::my-proto

更新: 要使整个过程正常工作,请执行以下操作:

  1. Add the following to your WORKSPACE file. This downloads the protobuf library:

    http_archive(
        name = "protobuf",
        url = "https://github.com/google/protobuf/releases/download/v3.0.0/protobuf-cpp-3.0.0.zip",
        strip_prefix = "protobuf-3.0.0",
    )
    
  2. Create a .bzl file (let's say protobuf.bzl) and put in the following:

    def cpp_proto(name, src):
      native.genrule(
          name = "%s-gen" % name,
          outs = ["%s.pb.cc" % name, "%s.pb.h" % name],
          cmd = "$(location @protobuf//:protoc) --cpp_out=$(GENDIR) $<",
          srcs = [src],
          tools = ["@protobuf//:protoc"],
      )
    
      native.cc_library(
          name = name,
          srcs = ["%s.pb.cc" % name],
          hdrs = ["%s.pb.h" % name],
      )
    
  3. In your build files, add in load(':protobuf.bzl', 'cpp_proto')

  4. You can now use the macro as such:

    cpp_proto(
        name = "my-proto",
        src = "my-proto.proto"
    )
    
    cc_library(
        name = "my-program",
        srcs = ["my-program.cc"],
        deps = [
            ":my-proto",
        ],
    )
    

只有当 proto 文件在工作区中时才适用。如果它在子文件夹中,则生成的文件不在 bazel 预期的文件夹中,从而导致失败。我需要在 protoc 调用中添加 --proto_path - morxa

5
最近,Bazel已经原生支持cc_proto_library。详情请见:http://bazel.build/blog/2017/02/27/protocol-buffers.html。简而言之,在设置一次WORKSPACE文件后,
cc_proto_library(
    name = "person_cc_proto",
    deps = [":person_proto"],
)

proto_library(
    name = "person_proto",
    srcs = ["person.proto"],
    deps = [":address_proto"],
)

...

然后,
$ bazel build :person_cc_proto

https://github.com/cgrushko/proto_library上有一个例子。

这个例子的主要思想是,你需要定义一个proto_library来将你的.proto文件导入到Bazel中,然后使用cc_proto_library将其编译为C++代码。协议缓冲区的编译器和运行时默认从@com_google_protobuf//:protoc@com_google_protobuf_cc//:cc_toolchain获取。

这种分离的原因是为了支持需要编译为多种语言的大型proto图。


3
有几种方法。 作为一次性操作,您可以创建一个genrule来在某些输入上执行命令:
genrule(
    name = "my-proto-gen",
    outs = ["my-proto.cc", "my-proto.h"],
    cmd = "$(location //path/to:protoc) --cpp_out=$@ $<",
    srcs = ["my-proto.proto"],
    tools = ["//path/to:protoc"],
)

cc_library(
    name = "my-proto",
    srcs = ["my-proto.cc"],
    hdrs = ["my-proto.h"],
)

根据你的构建规则,我猜测你想要多次执行此操作。在这种情况下,你可以在.bzl文件中定义一个宏(macro)。宏本质上是调用构建规则的函数:

# In, say, foo/bar.bzl.
def cpp_proto(name, src):
  native.genrule(
      name = "%s-gen" % name,
      outs = ["%s.cc" % name, "%s.h" % name],
      cmd = "$(location //path/to:protoc) --cpp_out=$@ $<",
      srcs = [src],
      tools = ["//path/to:protoc"],
  )

  native.cc_library(
      name = name,
      srcs = ["%s.cc" % name],
      hdrs = ["%s.h" % name],
  )

然后,在 foo/BUILD 中,您可以导入并使用您的宏来简洁地调用规则:
load('//foo:bar.bzl', 'cpp_proto')
cpp_proto('my-proto', 'my_proto.proto')

那么你可以在 cc_library, cc_binarycc_test 中使用 //foo:my-proto.

最后,您可能希望遵循 https://github.com/bazelbuild/bazel/issues/52 (并只是使用 mzhaom 的宏)。


感谢mzhaom的宏指针,我不知怎么错过了它。然而,我并没有真正理解如何使用它。在该线程的末尾还提出了另外两种替代方案,但我都无法运行。本地protobuf支持似乎缺少cpp部分,而pubref规则总是抛出异常。 - morxa

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接