Skip to content

feat: bit more configurable proto toolchain #1718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 26, 2025
Merged
56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ with the following `scala_toolchains()` parameters:
```py
scala_toolchains(
scala_proto = True,
scala_proto_enable_all_options = True,
scala_proto_options = [],
)
```

Expand Down Expand Up @@ -819,6 +819,60 @@ under WORKSPACE](#6.5.0), with the maximum dependency versions specified in
that section. While this may continue to work for some time, it is not
officially supported.

### `scala_proto_toolchain` changes and new `scalapb_toolchain` macro

`scala_proto_toolchain` has a more flexible plugin configuration schema. The
new `generators` and `generators_opts` attributes replace the following
attributes:

- `with_grpc`
- `with_flat_package`
- `with_single_line_to_string`
- `main_generator`
- `named_generators`

Now each generator (plugin) will get a corresponding name
that can be used for further plugin options setup:

```py
scala_proto_toolchain(
name = "example",
generators = {
"scala": "scripts.ScalaPbCodeGenerator",
"jvm_extra_protobuf_generator": "scalarules.test.extra_protobuf_generator.ExtraProtobufGenerator",
},
generators_opts = {
"scala": [
"grpc",
"single_line_to_proto_string",
],
"jvm_extra_protobuf_generator": [
"grpc",
"single_line_to_proto_string",
],
},
)
```

`scalapb_grpc_deps` no longer exists since it's now the user's responsibility
to configure dependencies based on the provided generators and their options.

The new `scalapb_toolchain` convenience macro wraps `scala_proto_toolchain`
to provide the default [ScalaPB](https://scalapb.github.io/) implementation:

```py
load("//scala_proto:scala_proto_toolchain.bzl", "scalapb_toolchain")

scalapb_toolchain(
name = "my_toolchain",
opts = [
"grpc",
"single_line_to_proto_string",
],
visibility = ["//visibility:public"],
)
```

### Removal of `bind()` aliases for `twitter_scrooge` dependencies

`rules_scala` 7.x removes all of the obsolete [`bind()`][] aliases under
Expand Down
31 changes: 15 additions & 16 deletions docs/scala_proto_library.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ which adds a few dependencies needed for ScalaPB:
scala_toolchains(
# Other toolchains settings...
scala_proto = True,
scala_proto_options = [
"grpc",
"flat_package",
"scala3_sources",
],
)

scala_register_toolchains()
Expand Down Expand Up @@ -47,9 +52,13 @@ load(

scala_proto_toolchain(
name = "scala_proto_toolchain",
with_grpc = False,
with_flat_package = False,
with_single_line_to_string = False,
generators_opts = {
"scala": [
"grpc",
"flat_package",
"scala3_sources",
]
},
visibility = ["//visibility:public"],
)

Expand All @@ -66,12 +75,10 @@ toolchain(
| Attribute name | Description |
| ----------------------------- | ----------------------------------------------------- |
| name | `Name, required`<br/>A unique name for this toolchain. |
| with_grpc | `boolean, optional (default False)`<br/>Enables generation of grpc service bindings for services. |
| with_flat_package | `boolean, optional (default False)`<br/>When true, ScalaPB will not append the protofile base name to the package name. |
| with_single_line_to_string | `boolean, optional (default False)`<br/>Enables generation of toString() methods that use a single line format. |
| generators_opts | `List of strings, optional`<br/>Additional protobuf options like 'grpc', 'flat_package' or 'scala3_sources'. |
| blacklisted_protos | `List of labels, optional`<br/>List of protobuf targets to exclude from recursive building. |
| code_generator | `Label, optional (has default)`<br/>Which code generator to use. A sensible default is provided. |
| named_generators | `String dict, optional` |
| generators | `String dict, optional` |
| extra_generator_dependencies | `List of labels, optional` |
| scalac | `Label, optional (has default)`<br/>Target for scalac. A sensible default is provided. |

Expand All @@ -89,7 +96,6 @@ scala_proto_deps_toolchain(
visibility = ["//visibility:public"],
dep_providers = [
":my_compile_deps",
":my_grpc_deps",
],
)

Expand All @@ -105,17 +111,10 @@ declare_deps_provider(
deps = ["@dep1", "@dep2"],
visibility = ["//visibility:public"],
)

declare_deps_provider(
name = "my_grpc_deps",
deps_id = "scalapb_grpc_deps",
deps = ["@dep3", "@dep4"],
visibility = ["//visibility:public"],
)
```

### `scala_proto_deps_toolchain` Toolchain Attributes

| Attribute name | Description |
| ----------------------------- | ----------------------------------------------------- |
| dep_providers | `List of labels, optional (has default)`<br/>allows injection of gRPC (deps_id - `scalapb_grpc_deps`) and ScalaPB (deps_id `scalapb_compile_deps`) dependencies |
| dep_providers | `List of labels, optional (has default)`<br/>allows injection of gRPC (deps_id - `scalapb_worker_deps`) and ScalaPB (deps_id `scalapb_compile_deps`) dependencies |
9 changes: 4 additions & 5 deletions scala/toolchains.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def scala_toolchains(
scalafmt = False,
scalafmt_default_config_path = ".scalafmt.conf",
scala_proto = False,
scala_proto_enable_all_options = False,
scala_proto_options = [],
jmh = False,
twitter_scrooge = False,
twitter_scrooge_deps = {}):
Expand Down Expand Up @@ -84,9 +84,8 @@ def scala_toolchains(
scalafmt_default_config_path: the relative path to the default Scalafmt
config file within the repository
scala_proto: whether to instantiate the scala_proto toolchain
scala_proto_enable_all_options: whether to instantiate the scala_proto
toolchain with all options enabled; `scala_proto` must also be
`True` for this to take effect
scala_proto_options: protobuf options, like 'scala3_sources' or 'grpc';
`scala_proto` must also be `True` for this to take effect
jmh: whether to instantiate the Java Microbenchmarks Harness toolchain
twitter_scrooge: whether to instantiate the twitter_scrooge toolchain
twitter_scrooge_deps: dictionary of string to Label containing overrides
Expand Down Expand Up @@ -180,7 +179,7 @@ def scala_toolchains(
specs2 = specs2,
scalafmt = scalafmt,
scala_proto = scala_proto,
scala_proto_enable_all_options = scala_proto_enable_all_options,
scala_proto_options = scala_proto_options,
jmh = jmh,
twitter_scrooge = twitter_scrooge,
twitter_scrooge_deps = twitter_scrooge_deps,
Expand Down
17 changes: 5 additions & 12 deletions scala/toolchains_repo.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def _scala_toolchains_repo_impl(repository_ctx):
repo_attr = repository_ctx.attr
format_args = {
"rules_scala_repo": Label("//:all").repo_name,
"proto_enable_all_options": repo_attr.scala_proto_enable_all_options,
"proto_options": repo_attr.scala_proto_options,
}
toolchains = {}

Expand Down Expand Up @@ -96,9 +96,9 @@ _scala_toolchains_repo = repository_rule(
"scala_proto": attr.bool(
doc = "Instantiate the scala_proto toolchain",
),
"scala_proto_enable_all_options": attr.bool(
"scala_proto_options": attr.string_list(
doc = (
"Enable all scala_proto_options; " +
"Protobuf generator options; " +
"scala_proto must also be True for this to take effect"
),
),
Expand Down Expand Up @@ -216,21 +216,14 @@ load(

setup_scala_proto_toolchains(
name = "scala_proto",
enable_all_options = {proto_enable_all_options},
default_gen_opts = {proto_options},
)

declare_deps_provider(
name = "scalapb_compile_deps_provider",
deps_id = "scalapb_compile_deps",
visibility = ["//visibility:public"],
deps = DEFAULT_SCALAPB_COMPILE_DEPS,
)

declare_deps_provider(
name = "scalapb_grpc_deps_provider",
deps_id = "scalapb_grpc_deps",
visibility = ["//visibility:public"],
deps = DEFAULT_SCALAPB_GRPC_DEPS,
deps = DEFAULT_SCALAPB_COMPILE_DEPS + DEFAULT_SCALAPB_GRPC_DEPS,
)

declare_deps_provider(
Expand Down
3 changes: 1 addition & 2 deletions scala_proto/default/default_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ _DEFAULT_DEP_PROVIDER_FORMAT = (

def scala_proto_deps_providers(
compile = _DEFAULT_DEP_PROVIDER_FORMAT % "compile",
grpc = _DEFAULT_DEP_PROVIDER_FORMAT % "grpc",
worker = _DEFAULT_DEP_PROVIDER_FORMAT % "worker"):
return [compile, grpc, worker]
return [compile, worker]

DEFAULT_SCALAPB_COMPILE_DEPS = [
Label("//scala/private/toolchain_deps:scala_library_classpath"),
Expand Down
3 changes: 2 additions & 1 deletion scala_proto/private/scala_proto_aspect.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def _generate_sources(ctx, toolchain, proto):
args.use_param_file(param_file_arg = "@%s", use_always = True)
for gen, out in outputs.items():
args.add("--" + gen + "_out", out)
args.add("--" + gen + "_opt", toolchain.generators_opts)
if gen in toolchain.generators_opts:
args.add_all(toolchain.generators_opts[gen], format_each = "--{}_opt=%s".format(gen))
args.add_joined("--descriptor_set_in", descriptors, join_with = ctx.configuration.host_path_separator)
args.add_all(sources)

Expand Down
74 changes: 32 additions & 42 deletions scala_proto/scala_proto_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ load(
_scala_proto_deps_providers = "scala_proto_deps_providers",
)

def _generators(ctx):
return dict(
ctx.attr.named_generators,
scala = ctx.attr.main_generator,
)

def _generators_jars(ctx):
generator_deps = ctx.attr.extra_generator_dependencies + [
ctx.attr._main_generator_dep,
Expand All @@ -19,22 +13,6 @@ def _generators_jars(ctx):
for dep in generator_deps
])

def _generators_opts(ctx):
opts = []
if ctx.attr.with_grpc:
opts.append("grpc")
if ctx.attr.with_flat_package:
opts.append("flat_package")
if ctx.attr.with_single_line_to_string:
opts.append("single_line_to_proto_string")
return ",".join(opts)

def _compile_dep_ids(ctx):
deps = ["scalapb_compile_deps"]
if ctx.attr.with_grpc:
deps.append("scalapb_grpc_deps")
return deps

def _ignored_proto_targets_by_label(ctx):
return {p.label: p for p in ctx.attr.blacklisted_protos}

Expand All @@ -49,13 +27,14 @@ def _worker_flags(ctx, generators, jars):
return "--jvm_flags=" + " ".join(["-D%s=%s" % i for i in env.items()])

def _scala_proto_toolchain_impl(ctx):
generators = _generators(ctx)
generators = ctx.attr.generators
generators_jars = _generators_jars(ctx)
compile_dep_ids = ["scalapb_compile_deps"]
toolchain = platform_common.ToolchainInfo(
generators = generators,
generators_jars = generators_jars,
generators_opts = _generators_opts(ctx),
compile_dep_ids = _compile_dep_ids(ctx),
generators_opts = ctx.attr.generators_opts,
compile_dep_ids = compile_dep_ids,
blacklisted_protos = _ignored_proto_targets_by_label(ctx),
protoc = ctx.executable.protoc,
scalac = ctx.attr.scalac.files_to_run,
Expand All @@ -66,35 +45,20 @@ def _scala_proto_toolchain_impl(ctx):
return [toolchain]

# Args:
# with_grpc: Enables generation of grpc service bindings for services
# with_flat_package: When true, ScalaPB will not append the protofile base name to the package name
# with_single_line_to_string: Enables generation of toString() methods that use the single line format
# blacklisted_protos: list of protobuf targets to exclude from recursive building
# code_generator: what code generator to use, usually you'll want the default
scala_proto_toolchain = rule(
implementation = _scala_proto_toolchain_impl,
attrs = {
"with_grpc": attr.bool(),
"with_flat_package": attr.bool(),
"with_single_line_to_string": attr.bool(),
"blacklisted_protos": attr.label_list(default = []),
"code_generator": attr.label(
executable = True,
cfg = "exec",
default = Label("//src/scala/scripts:scalapb_worker"),
allow_files = True,
),
# `scripts.ScalaPbCodeGenerator` and `_main_generator_dep` are currently
# necessary to support protoc-bridge < 0.9.8, specifically 0.7.14
# required by Scala 2.11. See #1647 and scalapb/ScalaPB#1771.
#
# If we drop 2.11 support, restore `scalapb.ScalaPbCodeGenerator` here,
# remove `_main_generator_dep`, and delete
# `//src/scala/scripts:scalapb_codegenerator_wrapper` and its files.
"main_generator": attr.string(
default = "scripts.ScalaPbCodeGenerator",
),
"named_generators": attr.string_dict(),
"generators": attr.string_dict(),
"generators_opts": attr.string_list_dict(),
"extra_generator_dependencies": attr.label_list(
providers = [JavaInfo],
),
Expand All @@ -121,6 +85,13 @@ scala_proto_toolchain = rule(
[proto rules documentation](https://docs.bazel.build/versions/master/be/protocol-buffer.html#proto_library)
""",
),
# `scripts.ScalaPbCodeGenerator` and `_main_generator_dep` are currently
# necessary to support protoc-bridge < 0.9.8, specifically 0.7.14
# required by Scala 2.11. See #1647 and scalapb/ScalaPB#1771.
#
# If we drop 2.11 support, restore `scalapb.ScalaPbCodeGenerator` here,
# remove `_main_generator_dep`, and delete
# `//src/scala/scripts:scalapb_codegenerator_wrapper` and its files.
"_main_generator_dep": attr.label(
default = Label(
"//src/scala/scripts:scalapb_codegenerator_wrapper",
Expand All @@ -132,6 +103,25 @@ scala_proto_toolchain = rule(
},
)

def scalapb_toolchain(name, opts = [], **kwargs):
"""Sets up a scala_proto_toolchain using ScalaPB.

Args:
name: A unique name for this target
opts: scalapb generator options like 'grpc' or 'flat_package'
kwargs: remaining arguments to `scala_proto_toolchain`
"""
scala_proto_toolchain(
name = name,
generators = {
"scala": "scripts.ScalaPbCodeGenerator",
},
generators_opts = {
"scala": opts,
},
**kwargs
)

def _scala_proto_deps_toolchain(ctx):
toolchain = platform_common.ToolchainInfo(
dep_providers = ctx.attr.dep_providers,
Expand Down
Loading