diff --git a/buf/BUILD.bazel b/buf/BUILD.bazel index 1d4bd07..4ab7902 100644 --- a/buf/BUILD.bazel +++ b/buf/BUILD.bazel @@ -16,6 +16,11 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") package(default_visibility = ["//visibility:public"]) + +toolchain_type( + name = "toolchain_type", +) + bzl_library( name = "defs", srcs = ["defs.bzl"], diff --git a/buf/internal/BUILD.dist.bazel b/buf/internal/BUILD.dist.bazel new file mode 100644 index 0000000..8689aaa --- /dev/null +++ b/buf/internal/BUILD.dist.bazel @@ -0,0 +1,27 @@ +load("@bazel_skylib//rules:native_binary.bzl", "native_binary") +load("@rules_buf//buf/internal:toolchain.bzl", "buf_toolchain") + +native_binary( + name = "buf", + src = "bin/buf%BIN_SUFFIX%", + out = "buf%BIN_SUFFIX%", +) + +native_binary( + name = "protoc_gen_buf_breaking", + src = "bin/protoc-gen-buf-breaking%BIN_SUFFIX%", + out = "protoc-gen-buf-breaking%BIN_SUFFIX%", +) + +native_binary( + name = "protoc_gen_buf_lint", + src = "bin/protoc-gen-buf-lint%BIN_SUFFIX%", + out = "protoc-gen-buf-lint%BIN_SUFFIX%", +) + +buf_toolchain( + name = "toolchain", + buf = ":buf", + protoc_breaking = ":protoc_gen_buf_breaking", + protoc_lint = ":protoc_gen_buf_lint", +) diff --git a/buf/internal/BUILD.toolchains.bazel b/buf/internal/BUILD.toolchains.bazel new file mode 100644 index 0000000..5cc0b59 --- /dev/null +++ b/buf/internal/BUILD.toolchains.bazel @@ -0,0 +1,24 @@ +OS = [ + "macos", + "linux", + "windows", +] + +ARCHS = [ + "x86_64", + "arm64", +] + +[ + toolchain( + name = "buf_toolchain_{}_{}".format(os, arch), + exec_compatible_with = [ + "@platforms//os:{}".format(os), + "@platforms//cpu:{}".format(arch), + ], + toolchain = "@%NAME%_dist_{}_{}//:toolchain".format(os, arch), + toolchain_type = "@rules_buf//buf:toolchain_type", + ) + for arch in ARCHS + for os in OS +] diff --git a/buf/internal/lint.bzl b/buf/internal/lint.bzl index 6947c1f..791b977 100644 --- a/buf/internal/lint.bzl +++ b/buf/internal/lint.bzl @@ -23,7 +23,7 @@ _DOC = """ For more info please refer to the [`buf_lint_test` section](https://docs.buf.build/build-systems/bazel#buf-lint-test) of the docs. """ -_TOOLCHAIN = str(Label("//tools/protoc-gen-buf-lint:toolchain_type")) +_TOOLCHAIN = "@rules_buf//buf:toolchain_type" def _buf_lint_test_impl(ctx): proto_infos = [t[ProtoInfo] for t in ctx.attr.targets] @@ -33,7 +33,7 @@ def _buf_lint_test_impl(ctx): files_to_include = [] if ctx.file.config != None: files_to_include.append(ctx.file.config) - return protoc_plugin_test(ctx, proto_infos, ctx.executable._protoc, ctx.toolchains[_TOOLCHAIN].cli, config, files_to_include) + return protoc_plugin_test(ctx, proto_infos, ctx.executable._protoc, ctx.toolchains[_TOOLCHAIN].buf.protoc_lint_tool, config, files_to_include) buf_lint_test = rule( implementation = _buf_lint_test_impl, diff --git a/buf/internal/toolchain.bzl b/buf/internal/toolchain.bzl index 33d3564..796f705 100644 --- a/buf/internal/toolchain.bzl +++ b/buf/internal/toolchain.bzl @@ -16,31 +16,43 @@ load("@bazel_tools//tools/build_defs/repo:utils.bzl", "update_attrs") -_TOOLCHAINS_REPO = "rules_buf_toolchains" - -_BUILD_FILE = """ -load(":toolchain.bzl", "declare_buf_toolchains") +_TOOLS = ["buf", "protoc-gen-buf-breaking", "protoc-gen-buf-lint"] -package(default_visibility = ["//visibility:public"]) +_TOOLCHAINS_REPO = "rules_buf_toolchains" -declare_buf_toolchains( - os = "{os}", - cpu = "{cpu}", - rules_buf_repo_name = "{rules_buf_repo_name}", - ) -""" +BufToolchainInfo = provider(fields = { + "buf_tool": "A File for a buf executable", + "protoc_breaking_tool": "A File for protoc-gen-buf-breaking executable", + "protoc_lint_tool": "A File for protoc-gen-buf-lint executable", +}) -_TOOLCHAIN_FILE = """ def _buf_toolchain_impl(ctx): - toolchain_info = platform_common.ToolchainInfo( - cli = ctx.executable.cli, + return platform_common.ToolchainInfo( + buf = BufToolchainInfo( + buf_tool = ctx.executable.buf, + protoc_breaking_tool = ctx.executable.protoc_breaking, + protoc_lint_tool = ctx.executable.protoc_lint, + ), ) - return [toolchain_info] -_buf_toolchain = rule( +buf_toolchain = rule( implementation = _buf_toolchain_impl, attrs = { - "cli": attr.label( + "buf": attr.label( + doc = "The buf cli", + executable = True, + allow_single_file = True, + mandatory = True, + cfg = "exec", + ), + "protoc_breaking": attr.label( + doc = "The buf cli", + executable = True, + allow_single_file = True, + mandatory = True, + cfg = "exec", + ), + "protoc_lint": attr.label( doc = "The buf cli", executable = True, allow_single_file = True, @@ -48,90 +60,39 @@ _buf_toolchain = rule( cfg = "exec", ), }, + provides = [ + platform_common.ToolchainInfo, + ], ) -def declare_buf_toolchains(os, cpu, rules_buf_repo_name): - for cmd in ["buf", "protoc-gen-buf-lint", "protoc-gen-buf-breaking"]: - ext = "" - if os == "windows": - ext = ".exe" - toolchain_impl = cmd + "_toolchain_impl" - _buf_toolchain( - name = toolchain_impl, - cli = str(Label("//:"+ cmd)), - ) - native.toolchain( - name = cmd + "_toolchain", - toolchain = ":" + toolchain_impl, - toolchain_type = "@{}//tools/{}:toolchain_type".format(rules_buf_repo_name, cmd), - exec_compatible_with = [ - "@platforms//os:" + os, - "@platforms//cpu:" + cpu, - ], - ) +def _buf_dist_toolchain_repo_impl(ctx): + manifest = json.decode(ctx.read(Label("@rules_buf_tool_manifest//:manifest.json"))) + bin_suffix = "" + if ctx.attr.os == "windows": + bin_suffix = ".exe" -""" + for tool in _TOOLS: + info = manifest[ctx.attr.os][ctx.attr.arch][tool] + out_name = "bin/" + tool + ctx.download(info["url"], output = out_name + bin_suffix, sha256 = info["sha256"]) -def _register_toolchains(repo, cmd): - native.register_toolchains( - "@{repo}//:{cmd}_toolchain".format( - repo = repo, - cmd = cmd, + ctx.template("BUILD.bazel", Label("@rules_buf//buf/internal:BUILD.dist.bazel"), substitutions = {"%BIN_SUFFIX%": bin_suffix}, executable = False) + +_buf_dist_toolchain_repo = repository_rule( + implementation = _buf_dist_toolchain_repo_impl, + attrs = { + "os": attr.string( + mandatory = True, + values = ["macos", "linux", "windows"], ), - ) + "arch": attr.string( + mandatory = True, + values = ["arm64", "x86_64"], + ), + }, +) -# Copied from rules_go: https://github.com/bazelbuild/rules_go/blob/bd44f4242b46e73fb2a81fc87ea4b52173bde84e/go/private/sdk.bzl#L240 -# -# NOTE: This doesn't check for windows/arm64 -# We can upgrade to repository_ctx.os.name and repository_ctx.os.arch once bazel 5.1 releases bazelbuild/bazel#14685 -def _detect_host_platform(ctx): - if ctx.os.name == "linux": - goos, goarch = "linux", "amd64" - res = ctx.execute(["uname", "-p"]) - if res.return_code == 0: - uname = res.stdout.strip() - if uname == "s390x": - goarch = "s390x" - elif uname == "i686": - goarch = "386" - - # uname -p is not working on Aarch64 boards - # or for ppc64le on some distros - res = ctx.execute(["uname", "-m"]) - if res.return_code == 0: - uname = res.stdout.strip() - if uname == "aarch64": - goarch = "arm64" - elif uname == "armv6l": - goarch = "arm" - elif uname == "armv7l": - goarch = "arm" - elif uname == "ppc64le": - goarch = "ppc64le" - - # Default to amd64 when uname doesn't return a known value. - - elif ctx.os.name == "mac os x": - goos, goarch = "darwin", "amd64" - - res = ctx.execute(["uname", "-m"]) - if res.return_code == 0: - uname = res.stdout.strip() - if uname == "arm64": - goarch = "arm64" - - # Default to amd64 when uname doesn't return a known value. - - elif ctx.os.name.startswith("windows"): - goos, goarch = "windows", "amd64" - elif ctx.os.name == "freebsd": - goos, goarch = "freebsd", "amd64" - else: - fail("Unsupported operating system: " + ctx.os.name) - - return goos, goarch - -def _buf_download_releases_impl(ctx): +def _buf_download_manifest_repo_impl(ctx): version = ctx.attr.version if not version: ctx.report_progress("Finding latest buf version") @@ -147,65 +108,92 @@ def _buf_download_releases_impl(ctx): versions = json.decode(versions_data) version = versions[0]["name"] - os, cpu = _detect_host_platform(ctx) - if os not in ["linux", "darwin", "windows"] or cpu not in ["arm64", "amd64"]: - fail("Unsupported operating system or cpu architecture ") - if os == "linux" and cpu == "arm64": - cpu = "aarch64" - if cpu == "amd64": - cpu = "x86_64" - - ctx.report_progress("Downloading buf release hash") + ctx.report_progress("Downloading and preparing manifest") ctx.download( url = [ "https://github.com/bufbuild/buf/releases/download/{}/sha256.txt".format(version), ], output = "sha256.txt", ) - ctx.file("WORKSPACE", "workspace(name = \"{name}\")".format(name = ctx.name)) - ctx.file("toolchain.bzl", _TOOLCHAIN_FILE) + + manifest = { + "macos": { + "arm64": {}, + "x86_64": {}, + }, + "linux": { + "arm64": {}, + "x86_64": {}, + }, + "windows": { + "arm64": {}, + "x86_64": {}, + }, + } + sha_list = ctx.read("sha256.txt").splitlines() + for sha_line in sha_list: + sha_line = sha_line.lower() if sha_line.strip(" ").endswith(".tar.gz"): continue - (sum, _, bin) = sha_line.partition(" ") - bin = bin.strip(" ") - lower_case_bin = bin.lower() - if lower_case_bin.find(os) == -1 or lower_case_bin.find(cpu) == -1: + + sum, _, bin = sha_line.split(" ") + + tool = None + + for maybe_tool in _TOOLS: + if bin.startswith(maybe_tool + "-"): + tool = maybe_tool + rest = bin[len(maybe_tool) + 1:] + break + + if not tool: + print("unknown tool found in manifest, skipping", bin) continue - output = lower_case_bin[:lower_case_bin.find(os) - 1] - if os == "windows": - output += ".exe" + os, _, arch = rest.partition("-") + arch = arch.removesuffix(".exe") - ctx.report_progress("Downloading " + bin) - download_info = ctx.download( - url = "https://github.com/bufbuild/buf/releases/download/{}/{}".format(version, bin), - sha256 = sum, - executable = True, - output = output, - ) + if os == "darwin": + os = "macos" - if os == "darwin": - os = "osx" + if arch == "aarch64": + arch = "arm64" + + manifest[os][arch][tool] = { + "url": "https://github.com/bufbuild/buf/releases/download/{version}/{tool}".format(version = version, tool = bin), + "sha256": sum, + } + + ctx.file("BUILD.bazel", content = "", executable = False) + ctx.file("manifest.json", content = json.encode_indent(manifest), executable = False) - ctx.file( - "BUILD", - _BUILD_FILE.format( - os = os, - cpu = cpu, - rules_buf_repo_name = Label("//buf/repositories.bzl").workspace_name, - ), - ) return update_attrs(ctx.attr, ["version"], {"version": version}) -_buf_download_releases = repository_rule( - implementation = _buf_download_releases_impl, +_buf_download_manifest_repo = repository_rule( + implementation = _buf_download_manifest_repo_impl, attrs = { "version": attr.string( - doc = "Buf release version", + doc = "A buf github release version", ), }, + doc = "Prepares a manifest.json file from a buf release checksum manifest. If no version is provided it will use Github releases to fetch the latest one", +) + +def _buf_toolchain_repo_impl(ctx): + ctx.template( + "BUILD.bazel", + Label("@rules_buf//buf/internal:BUILD.toolchains.bazel"), + substitutions = { + "%NAME%": ctx.attr.name, + }, + executable = False, + ) + +_buf_toolchain_repo = repository_rule( + implementation = _buf_toolchain_repo_impl, + doc = "Provides toolchain() targets for all execution platforms buf supports.", ) # buildifier: disable=unnamed-macro @@ -216,9 +204,18 @@ def rules_buf_toolchains(name = _TOOLCHAINS_REPO, version = None): name: The name of the toolchains repository. Defaults to "buf_toolchains" version: Release version, eg: `v.1.0.0-rc12`. If `None` defaults to latest """ - - _buf_download_releases(name = name, version = version) - - _register_toolchains(name, "buf") - _register_toolchains(name, "protoc-gen-buf-breaking") - _register_toolchains(name, "protoc-gen-buf-lint") + _buf_download_manifest_repo(name = "rules_buf_tool_manifest", version = version) + + # declare platform specific distribution and buf_toolchain repos for all the supported platforms. + # due to how toolchain resolution works the tool specific toolchain info is not loaded when registering a toolchain + for os in ["macos", "linux", "windows"]: + for arch in ["x86_64", "arm64"]: + dist_name = "{name}_dist_{os}_{arch}".format(name = name, os = os, arch = arch) + _buf_dist_toolchain_repo( + name = dist_name, + os = os, + arch = arch, + ) + + _buf_toolchain_repo(name = name) + native.register_toolchains("@{}//...".format(name)) diff --git a/tests/v1/BUILD.bazel b/tests/v1/BUILD.bazel new file mode 100644 index 0000000..63c6084 --- /dev/null +++ b/tests/v1/BUILD.bazel @@ -0,0 +1,14 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("//buf:defs.bzl", "buf_lint_test") + +proto_library( + name = "test_proto", + testonly = True, + srcs = ["test.proto"], + deps = [], +) + +buf_lint_test( + name = "test_proto_lint_test", + targets = [":test_proto"], +) diff --git a/tests/v1/test.proto b/tests/v1/test.proto new file mode 100644 index 0000000..c6bafc7 --- /dev/null +++ b/tests/v1/test.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package tests.v1; + +message Message1 { + +} \ No newline at end of file diff --git a/tools/buf/BUILD.bazel b/tools/buf/BUILD.bazel deleted file mode 100644 index 28ce096..0000000 --- a/tools/buf/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2021-2022 Buf Technologies, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -package(default_visibility = ["//visibility:public"]) - -toolchain_type(name = "toolchain_type") diff --git a/tools/protoc-gen-buf-breaking/BUILD.bazel b/tools/protoc-gen-buf-breaking/BUILD.bazel deleted file mode 100644 index 28ce096..0000000 --- a/tools/protoc-gen-buf-breaking/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2021-2022 Buf Technologies, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -package(default_visibility = ["//visibility:public"]) - -toolchain_type(name = "toolchain_type") diff --git a/tools/protoc-gen-buf-lint/BUILD.bazel b/tools/protoc-gen-buf-lint/BUILD.bazel deleted file mode 100644 index 28ce096..0000000 --- a/tools/protoc-gen-buf-lint/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2021-2022 Buf Technologies, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -package(default_visibility = ["//visibility:public"]) - -toolchain_type(name = "toolchain_type")