-
Couldn't load subscription status.
- Fork 15.9k
WIP: wire up prebuilt protoc toolchain #24115
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
Open
alexeagle
wants to merge
9
commits into
main
Choose a base branch
from
alexeagle/use_prebuilt_toolchain
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
7193229
feat: add toolchain providing pre-built protoc
alexeagle a74917a
wire as bzlmod extension
alexeagle 9177885
working now
alexeagle 4a9f718
cleanups
alexeagle 0d24a30
module extension private
alexeagle 813e911
move module extension to private
alexeagle 2bd2a68
simplify: no need for external 'hub' repo
alexeagle 0c19d50
Update bazel/private/prebuilt_protoc_toolchain.bzl
alexeagle 5ade215
Update BUILD
alexeagle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| "Module extensions for use under bzlmod" | ||
|
|
||
| load("@proto_bazel_features//:features.bzl", "bazel_features") | ||
| load("//bazel/toolchains:prebuilt_toolchains.bzl", "prebuilt_toolchains_repo", "PROTOC_PLATFORMS") | ||
| load("//bazel/private:prebuilt_protoc_toolchain.bzl", "prebuilt_protoc_repo") | ||
|
|
||
| DEFAULT_REPOSITORY = "prebuilt_protoc_hub" | ||
|
|
||
| def create_all_toolchain_repos(name, version): | ||
| for platform in PROTOC_PLATFORMS.keys(): | ||
| prebuilt_protoc_repo( | ||
| # We must replace hyphen with underscore to workaround rules_python py_proto_library constraint | ||
| name = ".".join([name, platform.replace("-", "_")]), | ||
| platform = platform, | ||
| version = version, | ||
| ) | ||
| # name will be mangled by bzlmod into apparent name, so pass an extra copy that is preserved | ||
| prebuilt_toolchains_repo(name = name, user_repository_name = name) | ||
|
|
||
| def _proto_extension_impl(module_ctx): | ||
| registrations = {} | ||
| root_name = None | ||
| for mod in module_ctx.modules: | ||
| for toolchain in mod.tags.prebuilt_toolchain: | ||
| if toolchain.name != DEFAULT_REPOSITORY and not mod.is_root: | ||
| fail("""\ | ||
| Only the root module may override the default name for the toolchain. | ||
| This prevents conflicting registrations in the global namespace of external repos. | ||
| """) | ||
|
|
||
| # Ensure the root wins in case of differences | ||
| if mod.is_root: | ||
| create_all_toolchain_repos(toolchain.name, toolchain.version) | ||
| root_name = toolchain.name | ||
| elif toolchain.name not in registrations.keys(): | ||
| registrations[toolchain.name] = toolchain | ||
| for name, toolchain in registrations.items(): | ||
| if name != root_name: | ||
| create_all_toolchain_repos(name, toolchain.version) | ||
|
|
||
| if bazel_features.external_deps.extension_metadata_has_reproducible: | ||
| return module_ctx.extension_metadata(reproducible = True) | ||
| else: | ||
| return None | ||
|
|
||
| protoc = module_extension( | ||
| implementation = _proto_extension_impl, | ||
| tag_classes = { | ||
| # buildifier: disable=unsorted-dict-items | ||
| "prebuilt_toolchain": tag_class(attrs = { | ||
| "name": attr.string( | ||
| doc = """\ | ||
| Base name for generated repositories, allowing more than one toolchain to be registered. | ||
| Overriding the default is only permitted in the root module. | ||
| """, | ||
| default = DEFAULT_REPOSITORY, | ||
| ), | ||
| "version": attr.string(doc = "A tag of protocolbuffers/protobuf repository."), | ||
| }), | ||
| }, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| "Repository rule that downloads a pre-compiled protoc from our official release for a single platform." | ||
|
|
||
| load(":prebuilt_tool_integrity.bzl", "RELEASED_BINARY_INTEGRITY") | ||
| load("//bazel/toolchains:prebuilt_toolchains.bzl", "PROTOC_PLATFORMS") | ||
|
|
||
| def release_version_to_artifact_name(release_version, platform): | ||
| # versions have a "v" prefix like "v28.0" | ||
| stripped_version = release_version.removeprefix("v") | ||
|
|
||
| # release candidate versions like "v29.0-rc3" have artifact names | ||
| # like "protoc-29.0-rc-3-osx-x86_64.zip" | ||
| artifact_version = stripped_version.replace("rc", "rc-") | ||
|
|
||
| return "{}-{}-{}.zip".format( | ||
| "protoc", | ||
| artifact_version, | ||
| platform, | ||
| ) | ||
|
|
||
| def _prebuilt_protoc_repo_impl(rctx): | ||
| release_version = rctx.attr.version | ||
| filename = release_version_to_artifact_name( | ||
| release_version, | ||
| rctx.attr.platform, | ||
| ) | ||
| rctx.download_and_extract( | ||
| url = "https://github.com/protocolbuffers/protobuf/releases/download/{}/{}".format( | ||
| release_version, | ||
| filename, | ||
| ), | ||
| sha256 = RELEASED_BINARY_INTEGRITY[filename], | ||
| ) | ||
|
|
||
| rctx.file("BUILD.bazel", """\ | ||
| # Generated by @protobuf//bazel/private:prebuilt_protoc_toolchain.bzl | ||
| load("@com_google_protobuf//bazel/toolchains:proto_toolchain.bzl", "proto_toolchain") | ||
|
|
||
| package(default_visibility = ["//visibility:public"]) | ||
|
|
||
| proto_toolchain( | ||
| name = "prebuilt_protoc_toolchain", | ||
| proto_compiler = "{protoc_label}", | ||
| ) | ||
| """.format( | ||
| protoc_label = ":bin/protoc.exe" if rctx.attr.platform.startswith("win") else ":bin/protoc", | ||
| )) | ||
|
|
||
| prebuilt_protoc_repo = repository_rule( | ||
| doc = "Download a pre-built protoc and create a concrete toolchains for it", | ||
| implementation = _prebuilt_protoc_repo_impl, | ||
| attrs = { | ||
| "platform": attr.string( | ||
| doc = "A platform that protobuf ships a release for", | ||
| mandatory = True, | ||
| values = PROTOC_PLATFORMS.keys(), | ||
| ), | ||
| "version": attr.string( | ||
| doc = "Release tag from protocolbuffers/protobuf repo, e.g. 'v25.3'", | ||
| mandatory = True, | ||
| ), | ||
| }, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| """Create a "hub" repository to reference the pre-built protoc toolchains. | ||
|
|
||
| Ensures that Bazel only downloads required binaries for selected toolchains. | ||
|
|
||
| This follows guidance here: | ||
| https://docs.bazel.build/versions/main/skylark/deploying.html#registering-toolchains | ||
| " | ||
| Note that in order to resolve toolchains in the analysis phase | ||
| Bazel needs to analyze all toolchain targets that are registered. | ||
| Bazel will not need to analyze all targets referenced by toolchain.toolchain attribute. | ||
| If in order to register toolchains you need to perform complex computation in the repository, | ||
| consider splitting the repository with toolchain targets | ||
| from the repository with <LANG>_toolchain targets. | ||
| Former will be always fetched, | ||
| and the latter will only be fetched when user actually needs to build <LANG> code. | ||
| " | ||
|
|
||
| The "complex computation" in our case is simply downloading our pre-built protoc binaries. | ||
| This guidance tells us how to avoid that: we put the toolchain targets in the alias repository | ||
| with only the toolchain attribute pointing into the platform-specific repositories. | ||
| """ | ||
|
|
||
| # Keys are chosen to match the filenames published on protocolbuffers/protobuf releases | ||
| # NB: keys in this list are nearly identical to /toolchain/BUILD.bazel#TOOLCHAINS | ||
| # Perhaps we should share code. | ||
| PROTOC_PLATFORMS = { | ||
| # "k8", # this is in /toolchain/BUILD.bazel but not a released platform | ||
| # "osx-universal_binary", # this is not in /toolchain/BUILD.bazel | ||
| # but also Bazel will never request it, as we have a darwin binary for each architecture | ||
| "linux-aarch_64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:linux", | ||
| "@platforms//cpu:aarch64", | ||
| ], | ||
| }, | ||
| "linux-ppcle_64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:linux", | ||
| "@platforms//cpu:ppc64le", | ||
| ], | ||
| }, | ||
| "linux-s390_64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:linux", | ||
| "@platforms//cpu:s390x", | ||
| ], | ||
| }, | ||
| "linux-x86_32": { | ||
| "compatible_with": [ | ||
| "@platforms//os:linux", | ||
| "@platforms//cpu:x86_32", | ||
| ], | ||
| }, | ||
| "linux-x86_64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:linux", | ||
| "@platforms//cpu:x86_64", | ||
| ], | ||
| }, | ||
| "osx-aarch_64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:macos", | ||
| "@platforms//cpu:aarch64", | ||
| ], | ||
| }, | ||
| "osx-x86_64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:macos", | ||
| "@platforms//cpu:x86_64", | ||
| ], | ||
| }, | ||
| "win32": { | ||
| "compatible_with": [ | ||
| "@platforms//os:windows", | ||
| "@platforms//cpu:x86_32", | ||
| ], | ||
| }, | ||
| "win64": { | ||
| "compatible_with": [ | ||
| "@platforms//os:windows", | ||
| "@platforms//cpu:x86_64", | ||
| ], | ||
| }, | ||
| } | ||
|
|
||
| prebuilt_toolchains_repo = repository_rule( | ||
| doc = """\ | ||
| Creates a single repository with toolchain definitions for all known platforms | ||
| that can be registered or selected. | ||
| """, | ||
| attrs = { | ||
| "user_repository_name": attr.string( | ||
| doc = """What the user chose for the base name. | ||
| Needed since bzlmod apparent name has extra tilde segments. | ||
| """, | ||
| mandatory = True, | ||
| ), | ||
| }, | ||
| implementation = lambda rctx: rctx.file("BUILD.bazel", """\ | ||
| # Generated by @protobuf//bazel/toolchains:prebuilt_toolchains.bzl | ||
| # | ||
| # These can be registered in the workspace file or passed to --extra_toolchains flag. | ||
| # By default all these toolchains are registered by the protoc module extension | ||
| # so users don't normally need to interact with these targets. | ||
|
|
||
| """ + "\n".join(["""\ | ||
| toolchain( | ||
| name = "{platform}_toolchain", | ||
| exec_compatible_with = {compatible_with}, | ||
| # Bazel does not follow this attribute during analysis, so the referenced repo | ||
| # will only be fetched if this toolchain is selected. | ||
| toolchain = "@{user_repository_name}.{platform}//:prebuilt_protoc_toolchain", | ||
| toolchain_type = "@com_google_protobuf//bazel/private:proto_toolchain_type", | ||
| ) | ||
| """.format( | ||
| platform = platform.replace("-", "_"), | ||
| user_repository_name = rctx.attr.user_repository_name, | ||
| compatible_with = meta["compatible_with"], | ||
| ) | ||
| for platform, meta in PROTOC_PLATFORMS.items()] | ||
| )), | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| # Ignore the bazel symlinks | ||
| /bazel-* | ||
| bazel-* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Simulate a non-functional CC toolchain | ||
| common --per_file_copt=external/.*protobuf.*@--THIS_CC_TOOLCHAIN_IS_BROKEN | ||
| common --host_per_file_copt=external/.*protobuf.*@--THIS_CC_TOOLCHAIN_IS_BROKEN | ||
| # But, users should be able to use pre-built protoc toolchains instead. | ||
| common --incompatible_enable_proto_toolchain_resolution |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| load("@protobuf//bazel:proto_library.bzl", "proto_library") | ||
|
|
||
| proto_library( | ||
| name = "empty_proto", | ||
| srcs = ["empty.proto"], | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| """Bazel module dependencies""" | ||
|
|
||
| module( | ||
| name = "com_google_protobuf-example-without-cc-toolchain", | ||
| version = "0.0.0", | ||
| compatibility_level = 1, | ||
| ) | ||
|
|
||
| bazel_dep(name = "protobuf") | ||
| local_path_override( | ||
| module_name = "protobuf", | ||
| path = "../..", | ||
| ) | ||
|
|
||
| protoc = use_extension("@protobuf//bazel:extensions.bzl", "protoc") | ||
| protoc.prebuilt_toolchain( | ||
| name = "prebuilt_protoc", | ||
| # FIXME: users shouldn't be able to vary the version here from the protobuf module | ||
alexeagle marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| version = "v33.0", | ||
| ) | ||
| use_repo(protoc, "prebuilt_protoc", "prebuilt_protoc.osx_aarch_64") | ||
| register_toolchains("@prebuilt_protoc//:all") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| This example demonstrates what happens when a Bazel user doesn't have a proper CC toolchain installed. | ||
| This case commonly happens in projects with no C++ code, so they don't have a hermetic method of building C++ code. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| edition = "2023"; | ||
|
|
||
| package examples.without.cc.toolchain; | ||
|
|
||
| message EmptyMessage {} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.