Skip to content

Commit a542a7c

Browse files
committed
- Move attribute implementation to the language class files.
- Cleanup of some dnagling unused attributes and wonky semantics. - Attempt to explain the import mechanism/support.
1 parent 921f43e commit a542a7c

File tree

11 files changed

+220
-114
lines changed

11 files changed

+220
-114
lines changed

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,78 @@ protobuf_repositories(
144144
)
145145
```
146146

147+
148+
# Imports
149+
150+
In all cases, these rules will include a `--proto_path=.` argument.
151+
This is functionally equivalent to `--proto_path=$(bazel info
152+
execution_root)`. Therefore, when the protoc tool is invoked, it will
153+
'see' whatever directory struture exists at the bazel execution root
154+
for your workspace. To better learn what this looks like, `cd $(bazel
155+
info execution_root)` and look around. In general, it contains all
156+
your sourcefiles as they appear in your workspace, with an additional
157+
`external/WORKSPACE_NAME` directory for all dependencies used.
158+
159+
This has implications for import statements in your protobuf
160+
sourcefiles, if you use them. The two cases to consider are imports
161+
*within* your workspace (referred to here as 'internal' imports), and
162+
imports of other protobuf files in an external workspace.
163+
164+
### Internal Imports
165+
166+
Internal imports should require no additional parameters if your
167+
import statements follow the directory structure of your workspace.
168+
For example, the `examples/helloworld/proto/helloworld.proto` file
169+
imports the `examples/proto/common.proto` file. Since this follows
170+
the same directory structure as the workspace, `protoc` can find it,
171+
and no additional arguments to a `cc_proto_library` are required for
172+
protoc tool.
173+
174+
*However*, the `cc_proto_library` rule in
175+
`examples/helloworld/proto/BUILD:cpp` names the
176+
`//examples/proto:cpp`'s `cc_proto_library` rule as a dependency in
177+
order to (1) trigger generation of the `common.pb.{h,cc}` files AND
178+
(2) include those generated files in the `cc_library` rule for
179+
compiling the object files.
180+
181+
Additional `--proto_path` (`-I`) arguments can be supplied via the
182+
`imports = []` attribute common to all rules.
183+
184+
### External Imports
185+
186+
The same logic applied to external imports. The two questions to answer are
187+
188+
1. *Can protoc "see" the imported file?* In order to satisfy this
189+
requirement, pass in the full path of the required file relative to
190+
your execution root. For example, the the well-known descriptor
191+
proto could be made visible to protoc via something like...
192+
193+
```python
194+
java_proto_library(
195+
name = 'fooprotos',
196+
srcs = 'foo.proto`,
197+
imports = [
198+
"external/com_github_google_protobuf/src/",
199+
],
200+
)
201+
```
202+
203+
...given that the file
204+
`@com_github_google_protobuf/src/google/protobuf/descriptor.proto` is
205+
in the package `google.protobuf`.
206+
207+
2. *Can the `{LANG}_proto_library` rule "see" the generated protobuf
208+
files (in this case `descripttor.pb.{h,cc}`. Just because the file
209+
was imported does not imply that protoc will generate outputs for
210+
it, so somewhere in the `cc_library` rule dependency chain these
211+
files must be present. This could be via another
212+
`cc_proto_library` rule defined elswhere, or a some other filegroup
213+
or label list. If the source is another `cc_proto_library` rule,
214+
specify that in the `deps` attribute to the calling
215+
`cc_proto_library` rule. Otherwise, pass it to the `cc_srcs` or
216+
perhaps `cc_deps` attribute to the calling `cc_proto_library` rule.
217+
Hopefully that made sense. It's tricky.
218+
147219
# Contributing
148220

149221
Contributions welcome; please create Issues or GitHub pull requests.

bzl/base/class.bzl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,59 @@ def post_execute(lang, self):
173173
pass
174174

175175

176+
def implement_compile_attributes(lang, self):
177+
"""Add attributes for the X_proto_compile rule"""
178+
179+
name = lang.name
180+
attrs = self["attrs"]
181+
182+
# Add "gen_java = X" option where X is True if this is the first
183+
# language specified.
184+
flag = "gen_" + name
185+
attrs[flag] = attr.bool(
186+
default = True,
187+
)
188+
189+
# Add a "gen_java_plugin_options=[]".
190+
opts = flag + "_plugin_options"
191+
attrs[opts] = attr.string_list()
192+
193+
# If there is a plugin binary, create this label now.
194+
if hasattr(lang, "protobuf") and hasattr(lang.protobuf, "executable"):
195+
attrs["gen_protobuf_" + name + "_plugin"] = attr.label(
196+
default = Label(lang.protobuf.executable),
197+
cfg = HOST_CFG,
198+
executable = True,
199+
)
200+
201+
# If this language supports gRPC, add this boolean flag in.
202+
# However, if we didn't load grpc, we don't actually want to
203+
# generate the label for the executable lest we actually need to
204+
# have the executable available. TODO: figure out how to write a
205+
# variable in the loading phase of workspace and read it here.
206+
207+
if hasattr(lang, "grpc"):
208+
attrs["gen_grpc_" + name] = attr.bool()
209+
if hasattr(lang.grpc, "executable"):
210+
attrs["gen_grpc_" + name + "_plugin"] = attr.label(
211+
default = Label(lang.grpc.executable),
212+
cfg = HOST_CFG,
213+
executable = True,
214+
)
215+
216+
217+
def implement_compile_outputs(lang, self):
218+
"""Add customizable outputs for the proto_compile rule"""
219+
if hasattr(lang, "protobuf") and hasattr(lang.protobuf, "outputs"):
220+
self["outputs"] += lang.protobuf.outputs
221+
if hasattr(lang, "grpc") and hasattr(lang.grpc, "outputs"):
222+
self["outputs"] += lang.grpc.outputs
223+
224+
225+
def implement_compile_output_to_genfiles(lang, self):
226+
self["output_to_genfiles"] = getattr(lang, "output_to_genfiles", self["output_to_genfiles"])
227+
228+
176229
CLASS = struct(
177230
name = "base",
178231

@@ -194,5 +247,10 @@ CLASS = struct(
194247
build_grpc_out = build_grpc_out,
195248
build_grpc_invocation = build_grpc_invocation,
196249
build_protoc_command = build_protoc_command,
250+
197251
post_execute = post_execute,
252+
253+
implement_compile_attributes = implement_compile_attributes,
254+
implement_compile_outputs = implement_compile_outputs,
255+
implement_compile_output_to_genfiles = implement_compile_output_to_genfiles,
198256
)

bzl/go/class.bzl

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,37 @@ load("//bzl:base/class.bzl", BASE = "CLASS")
22
load("//bzl:util.bzl", "invokesuper")
33

44

5-
def _build_protobuf_out(lang, self):
5+
def implement_compile_attributes(lang, self):
6+
"""Override attributes for the X_proto_compile rule"""
7+
invokesuper("implement_compile_attributes", lang, self)
8+
9+
attrs = self["attrs"]
10+
11+
# go_prefix is necessary for protoc-gen-go import mapping of dependent protos.
12+
attrs["go_prefix"] = attr.label(
13+
providers = ["go_prefix"],
14+
default = Label(
15+
"//:go_prefix",
16+
relative_to_caller_repository = True,
17+
),
18+
allow_files = False,
19+
cfg = HOST_CFG,
20+
)
21+
22+
23+
def build_protobuf_out(lang, self):
624
"""Override behavior to add a plugin option before building the --go_out option"""
725
if self.get("with_grpc", False):
826
self["protobuf_plugin_options"] = self.get("protobuf_plugin_options", []) + ["plugins=grpc"]
927
invokesuper("build_protobuf_out", lang, self)
1028

1129

12-
def _build_grpc_out(lang, self):
30+
def build_grpc_out(lang, self):
1331
"""Override behavior to skip the --grpc_out option (protoc-gen-go does not use it)"""
1432
pass
1533

1634

17-
def _build_imports(lang, self):
35+
def build_imports(lang, self):
1836
"""@Override: for all transitive packages source file names, provide import mapping."""
1937
invokesuper("build_imports", lang, self)
2038

@@ -26,12 +44,9 @@ def _build_imports(lang, self):
2644

2745
for dep in ctx.attr.deps:
2846
provider = dep.proto
29-
#print("proto provider: %s" % dir(provider))
3047
proto_packages = provider.transitive_packages
31-
#print("proto_packages: %s" % proto_packages)
3248
for pkg, srcs in proto_packages.items():
3349
target = pkg.rsplit(':') # [0] == ctx.label.package, [1] == ctx.label.name
34-
#print("target: %s" % target)
3550
for srcfile in srcs:
3651
src = srcfile.short_path
3752
dst = go_prefix + '/' + target[0]
@@ -40,6 +55,7 @@ def _build_imports(lang, self):
4055
dst += "/" + target[1][:-len(".pb")]
4156
self["protobuf_plugin_options"] = self.get("protobuf_plugin_options", []) + ["M%s=%s" % (src, dst)]
4257

58+
4359
CLASS = struct(
4460
parent = BASE,
4561
name = "go",
@@ -74,7 +90,8 @@ CLASS = struct(
7490
],
7591
),
7692

77-
build_protobuf_out = _build_protobuf_out,
78-
build_grpc_out = _build_grpc_out,
79-
build_imports = _build_imports,
93+
build_protobuf_out = build_protobuf_out,
94+
build_grpc_out = build_grpc_out,
95+
build_imports = build_imports,
96+
implement_compile_attributes = implement_compile_attributes,
8097
)

0 commit comments

Comments
 (0)