Why is a publicly visible Bazel ProtoBuf target &#

2019-08-02 17:55发布

问题:

I'm attempting to use Bazel's Protocol Buffer Rules to compile (generate) Python language bindings and any dependencies. The layout of my project is simple, with a single directory, proto, containing the .proto file and BUILD file.

WORKSPACE
BUILD.six
|-- proto
|    |-- example.proto 
|    |-- BUILD

My WORKSPACE file:

workspace(name = "com_example")

http_archive(
    name = "com_google_protobuf",
    strip_prefix = "protobuf-3.4.1",
    urls = ["https://github.com/google/protobuf/archive/v3.4.1.zip"],
)

new_http_archive(
    name = "six_archive",
    build_file = "six.BUILD",
    url = "https://pypi.python.org/packages/source/s/six/six-1.10.0.tar.gz",
)

bind(
    name = "six",
    actual = "@six_archive//:six",
)

In my WORKSPACE file, the expected SHA-256 hash of the file downloaded has been omitted for readability. The http_archive WORKSPACE rule is used to since the ProtoBuf GitHub repo contains Bazel WORKSPACE and BUILD files.

The new_http_archive must be used for the six library since it's not a Bazel workspace. Also worth noting that Bazel transitive dependencies must be provided in my WORKSPACE file (from Bazel documentation):

Bazel only reads dependencies listed in your WORKSPACE file. If your project (A) depends on another project (B) which list a dependency on a third project (C) in its WORKSPACE file, you'll have to add both B and C to your project's WORKSPACE file.

six.BUILD is taken directly from the repo and saved locally:

  • https://github.com/google/protobuf/blob/master/six.BUILD

My BUILD file

load("@com_google_protobuf//:protobuf.bzl", "py_proto_library")

py_proto_library(
    name = "py",
    use_grpc_plugin = True,
    deps = [
        "@com_google_protobuf//:protobuf_python",
        ":example_proto",
    ],
    visibility = ["//visibility:public"],
    # protoc = "@com_google_protobuf//:protoc",
)

proto_library(
    name = "example_proto",
    srcs = ["example.proto"],
)

The problem arrises when building:

bazel build //proto:py

Output (formatted for readability):

proto/BUILD:3:1:
no such target '//:protobuf_python':
target 'protobuf_python' not declared in package '' defined by BUILD and referenced by '//proto:py'.
ERROR: Analysis of target '//proto:py' failed; build aborted.

However, building the external dependency from my command line works:

bazel build @com_google_protobuf//:protobuf_python

Output (truncated for readability):

INFO: Found 1 target...
...
INFO: Elapsed time: 51.577s, Critical Path: 8.63s

The protobuf_python target is clearly defined and public:

  • https://github.com/google/protobuf/blob/77f64bb7779ec2195f9bc4dc82497d12c18fc6b7/BUILD#L756

回答1:

The problem is that your target (//proto:py) depends on //:protobuf_python, not @com_gooogle_protobuf//:protobuf_python. You can confirm this with bazel query.

$ bazel query --output build //proto:py
# proto/BUILD:3:1
py_library(
  name = "py",
  visibility = ["//visibility:public"],
  generator_name = "py",
  generator_function = "py_proto_library",
  generator_location = "proto/BUILD:3",
  deps = ["//:protobuf_python", "@com_google_protobuf//:protobuf_python", "//proto:example_proto"],
  imports = [],
  srcs = [],
)

You can see it in the deps list. So now the question is, why does it depend on that? You certainly didn't set that anywhere. The answer is that, since py_proto_library is a macro, it can do whatever it wants.

In particular, these lines of the macro are causing you trouble:

https://github.com/google/protobuf/blob/6032746882ea48ff6d983df8cb77e2ebf399bf0c/protobuf.bzl#L320 https://github.com/google/protobuf/blob/6032746882ea48ff6d983df8cb77e2ebf399bf0c/protobuf.bzl#L373-L374

py_proto_library has an attribute called default_runtime that it appends to the deps list. The default value is ":protobuf_python". But that only works if you use the macro in the same repository that declares protobuf_python.

So, you can fix this by setting default_runtime = "@com_google_protobuf//:protobuf_python" in your py_proto_librarys attributes.