-
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
7193229
a74917a
9177885
4a9f718
0d24a30
813e911
2bd2a68
0c19d50
5ade215
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| "Module extensions for use under bzlmod" | ||
|
|
||
| load("//toolchain:platforms.bzl", "PROTOBUF_PLATFORMS") | ||
| load("//bazel/private:prebuilt_protoc_toolchain.bzl", "prebuilt_protoc_repo") | ||
|
|
||
| def create_all_toolchain_repos(name, version): | ||
| for platform in PROTOBUF_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, | ||
| ) | ||
|
|
||
| protoc = module_extension( | ||
| # TODO: replace version number here during release, maybe with git archive .gitattributes config | ||
| implementation = lambda module_ctx: create_all_toolchain_repos("prebuilt_protoc", "v33.0") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this have to know the version if there is only one set of hashes provided anyway? If you do want them, you could get the version from |
||
| ) | ||
| 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("//toolchain:platforms.bzl", "PROTOBUF_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", | ||
alexeagle marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| )) | ||
|
|
||
| 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 = PROTOBUF_PLATFORMS.keys(), | ||
| ), | ||
| "version": attr.string( | ||
| doc = "Release tag from protocolbuffers/protobuf repo, e.g. 'v25.3'", | ||
| mandatory = True, | ||
| ), | ||
| }, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| """Create lazy definitions 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. | ||
|
Comment on lines
+1
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be dropped in favor of the two line comment on the |
||
| 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. | ||
| """ | ||
|
|
||
| load("//toolchain:platforms.bzl", "PROTOBUF_PLATFORMS") | ||
| [ | ||
| toolchain( | ||
| name = "{}_toolchain".format(platform.replace("-", "_")), | ||
| exec_compatible_with = meta["compatible_with"], | ||
| # Toolchain resolution will only permit this toolchain if the config_setting for prefer_prebuilt_protoc is true, | ||
| target_settings = ["@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc.flag_set"], | ||
| # Bazel does not follow this attribute during analysis, so the referenced repo | ||
| # will only be fetched if this toolchain is selected. | ||
| toolchain = "@prebuilt_protoc.{}//:prebuilt_protoc_toolchain".format(platform.replace("-", "_")), | ||
| toolchain_type = "@com_google_protobuf//bazel/private:proto_toolchain_type", | ||
| ) | ||
| for platform, meta in PROTOBUF_PLATFORMS.items() | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| # Ignore the bazel symlinks | ||
| /bazel-* | ||
| bazel-* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # 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 | ||
| common --@protobuf//bazel/toolchains:prefer_prebuilt_protoc |
| 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"], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| """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 = "../..", | ||
| ) |
| 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| edition = "2023"; | ||
|
|
||
| package examples.without.cc.toolchain; | ||
|
|
||
| message EmptyMessage {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| "List of published platforms on protobuf GitHub releases" | ||
|
|
||
| # 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. | ||
| PROTOBUF_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", | ||
| ], | ||
| }, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use skylibs
modules.as_extensionso that this is marked reproducible and you getbazel mod tidyfixes foruse_repowhile developing protobuf.