Skip to content

Commit

Permalink
Add bzlmod support (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeagle authored Nov 7, 2023
1 parent ad80bdc commit 1191ce8
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 3 deletions.
23 changes: 23 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"Bazel module definition for bzlmod"
module(
name = "rules_buf",
version = "0.0.0", # Replaced when publishing
compatibility_level = 1,
)

bazel_dep(name = "platforms", version = "0.0.4")
# Only needed because rules_proto doesn't provide the protoc toolchain yet.
# TODO: remove in the future
bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf")
bazel_dep(name = "rules_proto", version = "5.3.0-21.7")

# We depend on gazelle at runtime to generate our proto_library rules
bazel_dep(name = "gazelle", version = "0.33.0")

# ... and then reach inside to get the gazelle binary from this repository
non_module_deps = use_extension("@gazelle//internal/bzlmod:non_module_deps.bzl", "non_module_deps")
use_repo(non_module_deps, "bazel_gazelle_go_repository_tools")

ext = use_extension("//buf:extensions.bzl", "buf")
use_repo(ext, "rules_buf_toolchains")
register_toolchains("@rules_buf_toolchains//:all")
78 changes: 78 additions & 0 deletions buf/extensions.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Define module extensions for using rules_buf with bzlmod.
See https://bazel.build/docs/bzlmod#extension-definition
"""

load("//buf/internal:toolchain.bzl", "buf_download_releases")
load("//buf/internal:repo.bzl", "buf_dependencies")

# NB: this should be updated periodically
_DEFAULT_VERSION = "v1.27.0"
_DEFAULT_TOOLCHAIN_NAME = "rules_buf_toolchains"
_DEFAULT_DEPS = "buf_deps"

dependency = tag_class(attrs = {
"name": attr.string(doc = "name of resulting deps repo", default = _DEFAULT_DEPS),
"module": attr.string(doc = "A module name from the Buf Schema Registry, see https://buf.build/docs/bsr/module/manage"),
})

toolchains = tag_class(attrs = {
"name": attr.string(doc = "name of resulting buf toolchains repo", default = _DEFAULT_TOOLCHAIN_NAME),
"version": attr.string(doc = "Version of the buf tool, see https://github.com/bufbuild/buf/releases"),
})

def _extension_impl(module_ctx):
registrations = {}
dependencies = {}

# Iterate over the global modules registered either directly by the user
# or transitively by some other bazel module they use.
for mod in module_ctx.modules:

# collect all buf.dependency tags, group by name of resulting buf_dependencies repo
for dependency in mod.tags.dependency:
if dependency.name not in dependencies.keys():
dependencies[dependency.name] = []
dependencies[dependency.name].append(dependency.module)

# collect all toolchain versions, group by name of toolchain repo
for toolchains in mod.tags.toolchains:
if toolchains.name != _DEFAULT_TOOLCHAIN_NAME and not mod.is_root:
fail("""\
Only the root module may override the default name for the buf toolchains.
This prevents conflicting registrations in the global namespace of external repos.
""")
if toolchains.name not in registrations.keys():
registrations[toolchains.name] = []
registrations[toolchains.name].append(toolchains.version)

# Don't require that the user manually registers a toolchain
if len(registrations) == 0:
registrations = {_DEFAULT_TOOLCHAIN_NAME: [_DEFAULT_VERSION]}

for name, versions in registrations.items():
if len(versions) > 1:
# TODO: should be semver-aware, using MVS
selected = sorted(versions, reverse = True)[0]

# buildifier: disable=print
print("NOTE: buf toolchains {} has multiple versions {}, selected {}".format(name, versions, selected))
else:
selected = versions[0]
buf_download_releases(
name = name,
version = selected,
)

for name, modules in dependencies.items():
buf_dependencies(
name = name,
modules = modules,
)

buf = module_extension(
implementation = _extension_impl,
tag_classes = {
"dependency": dependency,
"toolchains": toolchains,
},
)
6 changes: 3 additions & 3 deletions buf/internal/toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def declare_buf_toolchains(os, cpu, rules_buf_repo_name):
native.toolchain(
name = cmd + "_toolchain",
toolchain = ":" + toolchain_impl,
toolchain_type = "@{}//tools/{}:toolchain_type".format(rules_buf_repo_name, cmd),
toolchain_type = "@@{}//tools/{}:toolchain_type".format(rules_buf_repo_name, cmd),
exec_compatible_with = [
"@platforms//os:" + os,
"@platforms//cpu:" + cpu,
Expand Down Expand Up @@ -202,7 +202,7 @@ def _buf_download_releases_impl(ctx):
)
return update_attrs(ctx.attr, ["version", "sha256"], {"version": version, "sha256": sha256})

_buf_download_releases = repository_rule(
buf_download_releases = repository_rule(
implementation = _buf_download_releases_impl,
attrs = {
"version": attr.string(
Expand All @@ -229,7 +229,7 @@ def rules_buf_toolchains(name = _TOOLCHAINS_REPO, version = None, sha256 = None,
repository_url: The repository url base used for downloads. Defaults to "https://github.com/bufbuild/buf/releases/download"
"""

_buf_download_releases(name = name, version = version, sha256 = sha256, repository_url = repository_url)
buf_download_releases(name = name, version = version, sha256 = sha256, repository_url = repository_url)

_register_toolchains(name, "buf")
_register_toolchains(name, "protoc-gen-buf-breaking")
Expand Down
1 change: 1 addition & 0 deletions examples/bzlmod/.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
common --enable_bzlmod
25 changes: 25 additions & 0 deletions examples/bzlmod/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load("@rules_buf//buf:defs.bzl", "buf_lint_test")
load("@rules_proto//proto:defs.bzl", "proto_library")

exports_files(["buf.yaml"], visibility = ["//visibility:public"])

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

proto_library(
name = "foo_proto",
srcs = ["file.proto"],
deps = [
# imports "validate/validate.proto"
"@buf_deps//validate:validate_proto",
":unused",
],
)

buf_lint_test(
name = "foo_proto_lint",
targets = [":foo_proto"],
config = "buf.yaml",
)
19 changes: 19 additions & 0 deletions examples/bzlmod/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"Bazel dependencies"
bazel_dep(name = "rules_buf", dev_dependency = True, version = "0.0.0")

local_path_override(
module_name = "rules_buf",
path = "../..",
)

buf = use_extension("@rules_buf//buf:extensions.bzl", "buf")

# Override the default version of buf
buf.toolchains(version = "v1.26.0")

# See https://buf.build/docs/build-systems/bazel#buf-dependencies
buf.dependency(module = "buf.build/envoyproxy/protoc-gen-validate:eac44469a7af47e7839a7f1f3d7ac004")
buf.dependency(module = "buf.build/acme/petapis:7abdb7802c8f4737a1a23a35ca8266ef")

# Allow references to labels under @buf_deps
use_repo(buf, "buf_deps")
1 change: 1 addition & 0 deletions examples/bzlmod/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Marker that this folder is the root of a Bazel workspace
4 changes: 4 additions & 0 deletions examples/bzlmod/buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
version: v1
lint:
use:
- IMPORT_USED
4 changes: 4 additions & 0 deletions examples/bzlmod/file.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
syntax = "proto3";

import "unused.proto";
import "validate/validate.proto";
1 change: 1 addition & 0 deletions examples/bzlmod/unused.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syntax = "proto3";

0 comments on commit 1191ce8

Please sign in to comment.