From 845f73e1b958df155f6fb063a5f7fe1cdb99bc0e Mon Sep 17 00:00:00 2001 From: Ryan Northey Date: Sun, 27 Jul 2025 21:06:53 +0100 Subject: [PATCH] bazel: Add glint toolchain Signed-off-by: Ryan Northey --- bazel/WORKSPACE | 3 + bazel/deps.bzl | 13 +++- bazel/glint/BUILD | 1 + bazel/glint/IMPLEMENTATION_SUMMARY.md | 71 ++++++++++++++++++++ bazel/glint/SHA256_UPDATE.md | 41 +++++++++++ bazel/glint/archives.bzl | 31 +++++++++ bazel/glint/setup.bzl | 25 +++++++ bazel/packages.bzl | 11 +++ bazel/toolchains/glint/BUILD | 43 ++++++++++++ bazel/toolchains/glint/README.md | 44 ++++++++++++ bazel/toolchains/glint/current_toolchain.bzl | 21 ++++++ bazel/toolchains/glint/examples/BUILD | 59 ++++++++++++++++ bazel/toolchains/glint/glint_toolchain.bzl | 71 ++++++++++++++++++++ bazel/toolchains/glint/test/BUILD | 64 ++++++++++++++++++ bazel/toolchains/register.bzl | 2 + bazel/versions.bzl | 11 +++ 16 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 bazel/glint/BUILD create mode 100644 bazel/glint/IMPLEMENTATION_SUMMARY.md create mode 100644 bazel/glint/SHA256_UPDATE.md create mode 100644 bazel/glint/archives.bzl create mode 100644 bazel/glint/setup.bzl create mode 100644 bazel/toolchains/glint/BUILD create mode 100644 bazel/toolchains/glint/README.md create mode 100644 bazel/toolchains/glint/current_toolchain.bzl create mode 100644 bazel/toolchains/glint/examples/BUILD create mode 100644 bazel/toolchains/glint/glint_toolchain.bzl create mode 100644 bazel/toolchains/glint/test/BUILD diff --git a/bazel/WORKSPACE b/bazel/WORKSPACE index 107a9db41..a6a7828e9 100644 --- a/bazel/WORKSPACE +++ b/bazel/WORKSPACE @@ -14,3 +14,6 @@ load_packages() load("@toolshed_pip3//:requirements.bzl", "install_deps") install_deps() + +load("@crates//:defs.bzl", "crate_repositories") +crate_repositories() diff --git a/bazel/deps.bzl b/bazel/deps.bzl index 9e08672ab..b1fd92a6c 100644 --- a/bazel/deps.bzl +++ b/bazel/deps.bzl @@ -1,17 +1,21 @@ load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies") load("@rules_perl//perl:deps.bzl", "perl_register_toolchains", "perl_rules_dependencies") load("@rules_python//python:repositories.bzl", "py_repositories") +load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") load("@toolchains_llvm//toolchain:deps.bzl", "bazel_toolchain_dependencies") load("@toolchains_llvm//toolchain:rules.bzl", "llvm_toolchain") load("//:versions.bzl", "VERSIONS") load("//sysroot:sysroot.bzl", "setup_sysroots") load("//autotools:setup.bzl", "setup_autotools") +load("//glint:setup.bzl", "setup_glint") def resolve_dependencies( cmake_version=None, llvm_version=None, ninja_version=None, - setup_autotools_toolchain=True): + setup_autotools_toolchain=True, + setup_glint_toolchain=True, + setup_rust_toolchain=True): py_repositories() bazel_toolchain_dependencies() rules_foreign_cc_dependencies( @@ -22,6 +26,11 @@ def resolve_dependencies( ) perl_rules_dependencies() perl_register_toolchains() + + if setup_rust_toolchain: + rules_rust_dependencies() + rust_register_toolchains(versions = ["1.84.0"]) + setup_sysroots() llvm_toolchain( name = "llvm_toolchain", @@ -33,3 +42,5 @@ def resolve_dependencies( ) if setup_autotools_toolchain: setup_autotools() + if setup_glint_toolchain: + setup_glint() diff --git a/bazel/glint/BUILD b/bazel/glint/BUILD new file mode 100644 index 000000000..779d1695d --- /dev/null +++ b/bazel/glint/BUILD @@ -0,0 +1 @@ +licenses(["notice"]) # Apache 2 diff --git a/bazel/glint/IMPLEMENTATION_SUMMARY.md b/bazel/glint/IMPLEMENTATION_SUMMARY.md new file mode 100644 index 000000000..98b4f9295 --- /dev/null +++ b/bazel/glint/IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,71 @@ +# Glint Bazel Toolchain - Summary + +## What was implemented: + +1. **Added rules_rust to versions.bzl** + - Added rules_rust dependency (v0.58.0) + - Added placeholders for glint binary SHA256 hashes (to be updated after first release) + - Added VERSION_GLINT constant + +2. **Created glint toolchain structure** (`/bazel/toolchains/glint/`) + - `glint_toolchain.bzl`: Toolchain definition with GlintInfo provider + - `BUILD`: Toolchain targets (hermetic and preinstalled) + - `current_toolchain.bzl`: Helper rule for depending on current toolchain + - `README.md`: Documentation + - `test/BUILD`: Test to verify toolchain works + - `examples/BUILD`: Example usage in genrules + +3. **Created glint archive setup** (`/bazel/glint/`) + - `archives.bzl`: Downloads prebuilt binaries for amd64/arm64 + - `setup.bzl`: Main setup function + - `SHA256_UPDATE.md`: Instructions for updating hashes after release + +4. **Updated main Bazel configuration** + - `deps.bzl`: Added rules_rust dependencies and glint setup + - `packages.bzl`: Added rust crate repository setup + - `WORKSPACE`: Added crate repositories loading + - `toolchains/register.bzl`: Added glint toolchain registration + +5. **Created BUILD files for Rust code** + - `/rust/BUILD`: Exports Cargo files + - `/rust/glint/BUILD`: Defines glint_binary target for building from source + +6. **Fixed issues** + - Fixed Rust edition from "2024" to "2021" in glint's Cargo.toml + +## How it works: + +1. **For amd64/arm64 architectures**: Downloads prebuilt binaries from GitHub releases +2. **For other architectures**: Falls back to building from source using rules_rust +3. **Toolchain priority**: Hermetic (prebuilt/source) > Preinstalled + +## Next steps: + +1. **After the next bazel-bins release**: + - Download the released glint binaries + - Calculate SHA256 hashes + - Update `glint_amd64_sha256` and `glint_arm64_sha256` in versions.bzl + - Test the toolchain with: `bazel test //toolchains/glint/test:glint_toolchain_test` + +2. **Usage in other projects**: + ```python + # In WORKSPACE: + load("@envoy_toolshed//bazel:deps.bzl", "resolve_dependencies") + resolve_dependencies() + + # In BUILD files: + load("@envoy_toolshed//toolchains/glint:current_toolchain.bzl", "current_glint_toolchain") + + genrule( + name = "lint_files", + srcs = ["file.txt"], + outs = ["lint_report.json"], + cmd = "$(location @envoy_toolshed//toolchains/glint:current_glint_toolchain) $(SRCS) > $@", + tools = ["@envoy_toolshed//toolchains/glint:current_glint_toolchain"], + ) + ``` + +## Notes: +- Glint is a whitespace linter, not a grep tool +- It checks for trailing whitespace, tabs, and missing final newlines +- Use `--fix` flag to automatically fix issues diff --git a/bazel/glint/SHA256_UPDATE.md b/bazel/glint/SHA256_UPDATE.md new file mode 100644 index 000000000..7101e0e17 --- /dev/null +++ b/bazel/glint/SHA256_UPDATE.md @@ -0,0 +1,41 @@ +# Glint Toolchain Setup + +## SHA256 Hash Update Instructions + +After the first release with glint binaries: + +1. Download the binaries from the release: + ```bash + wget https://github.com/envoyproxy/toolshed/releases/download/bazel-bins-v0.1.11/glint-0.1.0-amd64 + wget https://github.com/envoyproxy/toolshed/releases/download/bazel-bins-v0.1.11/glint-0.1.0-arm64 + ``` + +2. Calculate the SHA256 hashes: + ```bash + sha256sum glint-0.1.0-amd64 + sha256sum glint-0.1.0-arm64 + ``` + +3. Update the hashes in `/bazel/versions.bzl`: + - `glint_amd64_sha256`: Update with the amd64 hash + - `glint_arm64_sha256`: Update with the arm64 hash + +4. Update the `bins_release` version if needed. + +## Testing the Toolchain + +After updating the hashes: + +```bash +cd bazel +bazel test //toolchains/glint/test:glint_toolchain_test +``` + +## Building from Source (Fallback) + +For architectures without prebuilt binaries: + +```bash +cd bazel +bazel build //rust/glint:glint_binary +``` diff --git a/bazel/glint/archives.bzl b/bazel/glint/archives.bzl new file mode 100644 index 000000000..3b94f432c --- /dev/null +++ b/bazel/glint/archives.bzl @@ -0,0 +1,31 @@ +"""Setup glint dependencies.""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file") +load("//:versions.bzl", "VERSION_GLINT", "VERSIONS") + +def _glint_archive(name, arch): + """Create a glint archive repository.""" + # Map arch names to match what the workflow produces + arch_map = { + "x86_64": "amd64", + "aarch64": "arm64", + } + release_arch = arch_map.get(arch, arch) + + # Download the binary directly (not a tar.xz) + http_file( + name = name, + urls = ["https://github.com/envoyproxy/toolshed/releases/download/bazel-bins-v%s/glint-%s-%s" % ( + VERSIONS["bins_release"], + VERSION_GLINT, + release_arch, + )], + sha256 = VERSIONS.get("glint_%s_sha256" % release_arch, ""), # Will be empty string until first release + downloaded_file_path = "glint", + executable = True, + ) + +def setup_glint_archives(): + """Set up glint archives for supported architectures.""" + _glint_archive("glint_amd64", "x86_64") + _glint_archive("glint_arm64", "aarch64") diff --git a/bazel/glint/setup.bzl b/bazel/glint/setup.bzl new file mode 100644 index 000000000..d3fb4d0dc --- /dev/null +++ b/bazel/glint/setup.bzl @@ -0,0 +1,25 @@ +"""Full glint setup for external repositories.""" + +load("//glint:archives.bzl", "setup_glint_archives") + +def setup_glint(register_toolchains = True): + """Set up glint for use in external repositories. + + This function: + 1. Downloads the prebuilt glint binaries for supported architectures + 2. Registers the toolchains + + For unsupported architectures, it will fall back to building from source. + + Args: + register_toolchains: Whether to register the glint toolchains (default: True) + """ + # Set up the archives for prebuilt binaries + setup_glint_archives() + + # Register toolchains + if register_toolchains: + native.register_toolchains( + "@envoy_toolshed//toolchains/glint:hermetic_glint_toolchain", + "@envoy_toolshed//toolchains/glint:preinstalled_glint_toolchain", + ) diff --git a/bazel/packages.bzl b/bazel/packages.bzl index cde78af7b..c62833522 100644 --- a/bazel/packages.bzl +++ b/bazel/packages.bzl @@ -1,5 +1,6 @@ load("@bazel_features//:deps.bzl", "bazel_features_deps") load("@rules_python//python:pip.bzl", "pip_parse") +load("@rules_rust//crate_universe:defs.bzl", "crates_repository") load("//:versions.bzl", "VERSIONS") def load_packages(): @@ -11,6 +12,16 @@ def load_packages(): ) bazel_features_deps() + # Rust crate dependencies for glint + crates_repository( + name = "crates", + cargo_lockfile = "@envoy_toolshed//rust:Cargo.lock", + manifests = [ + "@envoy_toolshed//rust:Cargo.toml", + "@envoy_toolshed//rust/glint:Cargo.toml", + ], + ) + def load_website_packages(): # Only call this if you wish to use the website functionality pip_parse( diff --git a/bazel/toolchains/glint/BUILD b/bazel/toolchains/glint/BUILD new file mode 100644 index 000000000..358cae46a --- /dev/null +++ b/bazel/toolchains/glint/BUILD @@ -0,0 +1,43 @@ +load(":glint_toolchain.bzl", "glint_toolchain") +load(":current_toolchain.bzl", "current_glint_toolchain") + +package(default_visibility = ["//visibility:public"]) + +# Toolchain type for glint +toolchain_type( + name = "glint_toolchain_type", +) + +# Preinstalled glint (fallback) +glint_toolchain( + name = "preinstalled_glint", + glint_path = "glint", +) + +toolchain( + name = "preinstalled_glint_toolchain", + toolchain = ":preinstalled_glint", + toolchain_type = ":glint_toolchain_type", +) + +# Hermetic glint toolchain - uses prebuilt binaries or builds from source +glint_toolchain( + name = "hermetic_glint", + glint = select({ + "@platforms//cpu:x86_64": "@glint_amd64//:glint", + "@platforms//cpu:aarch64": "@glint_arm64//:glint", + # Fall back to building from source for other architectures + "//conditions:default": "//rust/glint:glint_binary", + }), +) + +toolchain( + name = "hermetic_glint_toolchain", + toolchain = ":hermetic_glint", + toolchain_type = ":glint_toolchain_type", +) + +# Current toolchain for use in toolchains attribute +current_glint_toolchain( + name = "current_glint_toolchain", +) diff --git a/bazel/toolchains/glint/README.md b/bazel/toolchains/glint/README.md new file mode 100644 index 000000000..80e7f70ed --- /dev/null +++ b/bazel/toolchains/glint/README.md @@ -0,0 +1,44 @@ +# Glint Toolchain + +This directory contains the Bazel toolchain definition for glint, a whitespace linter written in Rust. + +## Usage + +The glint toolchain can be used in two ways: + +### 1. As a toolchain dependency + +Add the toolchain to your rule's `toolchains` attribute: + +```python +my_rule( + name = "example", + toolchains = ["//toolchains/glint:current_glint_toolchain"], +) +``` + +Then in your rule implementation: + +```python +load("//toolchains/glint:glint_toolchain.bzl", "get_glint_data") + +def _my_rule_impl(ctx): + glint_data = get_glint_data(ctx) + glint_path = glint_data.glint + # Use glint_path in your commands +``` + +### 2. Direct usage + +The toolchain will automatically use prebuilt binaries for x86_64 (amd64) and aarch64 (arm64) architectures. +For other architectures, it will fall back to building glint from source using Rust. + +## Architecture Support + +- **x86_64/amd64**: Downloads prebuilt binary from GitHub releases +- **aarch64/arm64**: Downloads prebuilt binary from GitHub releases +- **Other architectures**: Builds from source using rules_rust + +## Configuration + +The toolchain is automatically registered when using `setup_glint()` in your WORKSPACE file. diff --git a/bazel/toolchains/glint/current_toolchain.bzl b/bazel/toolchains/glint/current_toolchain.bzl new file mode 100644 index 000000000..d4401566d --- /dev/null +++ b/bazel/toolchains/glint/current_toolchain.bzl @@ -0,0 +1,21 @@ +"""Rule for depending on the current glint toolchain.""" + +def _current_glint_toolchain_impl(ctx): + """Implementation for current_glint_toolchain rule.""" + toolchain = ctx.toolchains["//toolchains/glint:glint_toolchain_type"] + if not toolchain: + fail("No glint toolchain found. Did you register the toolchain?") + + info = toolchain.glint_info + files = [] + if hasattr(info, "glint_file") and info.glint_file: + files.append(info.glint_file) + + return [ + DefaultInfo(files = depset(files)), + ] + +current_glint_toolchain = rule( + implementation = _current_glint_toolchain_impl, + toolchains = ["//toolchains/glint:glint_toolchain_type"], +) diff --git a/bazel/toolchains/glint/examples/BUILD b/bazel/toolchains/glint/examples/BUILD new file mode 100644 index 000000000..d7020526e --- /dev/null +++ b/bazel/toolchains/glint/examples/BUILD @@ -0,0 +1,59 @@ +# Example of using glint toolchain in a genrule + +load("//toolchains/glint:current_toolchain.bzl", "current_glint_toolchain") + +# Example: Use glint to check for whitespace issues +genrule( + name = "check_whitespace", + srcs = glob(["*.txt"]), + outs = ["whitespace_report.json"], + cmd = """ + # Use glint to check for whitespace issues + $(location //toolchains/glint:current_glint_toolchain) \ + $(SRCS) > $@ || \ + echo "Whitespace issues found (see report)" + """, + tools = ["//toolchains/glint:current_glint_toolchain"], +) + +# Example: Use glint to fix whitespace issues +genrule( + name = "fix_whitespace", + srcs = glob(["*.txt"]), + outs = [f.replace(".txt", "_fixed.txt") for f in glob(["*.txt"])], + cmd = """ + # Copy files and fix whitespace issues + for src in $(SRCS); do + base=$(basename $src .txt) + cp $src $base\_fixed.txt + done + $(location //toolchains/glint:current_glint_toolchain) \ + --fix *_fixed.txt || true + """, + tools = ["//toolchains/glint:current_glint_toolchain"], +) + +# Example data files for testing +filegroup( + name = "test_data", + srcs = [ + "example1.txt", + "example2.txt", + ], +) + +# Create test files +genrule( + name = "create_test_files", + outs = [ + "example1.txt", + "example2.txt", + ], + cmd = """ + echo 'This line has trailing spaces ' > $(location example1.txt) + echo -e 'This line has\ttabs' >> $(location example1.txt) + echo -n 'No final newline' >> $(location example1.txt) + echo 'Clean line' > $(location example2.txt) + echo 'Another clean line' >> $(location example2.txt) + """, +) diff --git a/bazel/toolchains/glint/glint_toolchain.bzl b/bazel/toolchains/glint/glint_toolchain.bzl new file mode 100644 index 000000000..033eab0e5 --- /dev/null +++ b/bazel/toolchains/glint/glint_toolchain.bzl @@ -0,0 +1,71 @@ +"""Glint toolchain definition.""" + +GlintInfo = provider( + doc = "Information about a glint installation", + fields = { + "glint": "Path to glint binary", + "glint_file": "File reference to glint binary", + }, +) + +def _glint_toolchain_impl(ctx): + """Implementation for glint toolchain rule.""" + + # Get file reference if provided + glint_file = ctx.file.glint if hasattr(ctx.file, "glint") else None + + # Get path - either from file reference or from path attribute + if glint_file: + glint_path = glint_file.path + else: + glint_path = ctx.attr.glint_path or "glint" + + # Build the info provider + info_fields = { + "glint": glint_path, + } + if glint_file: + info_fields["glint_file"] = glint_file + + return [ + platform_common.ToolchainInfo( + glint_info = GlintInfo(**info_fields), + ), + ] + +glint_toolchain = rule( + implementation = _glint_toolchain_impl, + attrs = { + "glint": attr.label( + doc = "Glint executable file", + allow_single_file = True, + executable = True, + cfg = "exec", + ), + "glint_path": attr.string( + doc = "Path to glint executable (for preinstalled toolchain)", + ), + }, + provides = [platform_common.ToolchainInfo], +) + +def get_glint_data(ctx): + """Get glint data from the toolchain. + + Args: + ctx: The rule context + + Returns: + A struct containing: + - glint: Path to glint binary + - glint_file: File reference to glint binary (if available) + """ + toolchain = ctx.toolchains["//toolchains/glint:glint_toolchain_type"] + if not toolchain: + fail("No glint toolchain found. Did you register the toolchain?") + + info = toolchain.glint_info + result = {"glint": info.glint} + if hasattr(info, "glint_file") and info.glint_file: + result["glint_file"] = info.glint_file + return struct(**result) diff --git a/bazel/toolchains/glint/test/BUILD b/bazel/toolchains/glint/test/BUILD new file mode 100644 index 000000000..2805b60a3 --- /dev/null +++ b/bazel/toolchains/glint/test/BUILD @@ -0,0 +1,64 @@ +load("//toolchains/glint:glint_toolchain.bzl", "get_glint_data") + +def _glint_test_impl(ctx): + """Test implementation that verifies glint toolchain is available.""" + glint_data = get_glint_data(ctx) + + # Create a test script that runs glint + test_script = ctx.actions.declare_file(ctx.label.name) + + script_content = """#!/bin/bash +set -e + +# Create a test file with whitespace issues +cat > test_file.txt << 'EOF' +This line has trailing whitespace +This line has a tab here +This file has no final newline +EOF + +# Check if glint is available and works +if command -v {glint} >/dev/null 2>&1; then + echo "✓ Glint found at: {glint}" + # Run glint on the test file (should find issues) + if {glint} test_file.txt 2>&1 | grep -q "files_with_issues"; then + echo "✓ Glint successfully detected whitespace issues" + exit 0 + else + echo "✗ Glint did not detect expected issues" + exit 1 + fi +else + echo "✗ Glint not found at expected path: {glint}" + exit 1 +fi +""".format(glint = glint_data.glint) + + ctx.actions.write( + output = test_script, + content = script_content, + is_executable = True, + ) + + runfiles = ctx.runfiles() + if hasattr(glint_data, "glint_file") and glint_data.glint_file: + runfiles = runfiles.merge(ctx.runfiles(files = [glint_data.glint_file])) + + return [ + DefaultInfo( + executable = test_script, + runfiles = runfiles, + ), + ] + +glint_test = rule( + implementation = _glint_test_impl, + test = True, + toolchains = ["//toolchains/glint:glint_toolchain_type"], +) + +# Actual test target +glint_test( + name = "glint_toolchain_test", + tags = ["manual"], # Manual because glint might not be built yet +) diff --git a/bazel/toolchains/register.bzl b/bazel/toolchains/register.bzl index 1d8748b47..440905306 100644 --- a/bazel/toolchains/register.bzl +++ b/bazel/toolchains/register.bzl @@ -3,4 +3,6 @@ def toolshed_toolchains(): native.register_toolchains( "@envoy_toolshed//toolchains/autotools:hermetic_autotools_toolchain", "@envoy_toolshed//toolchains/autotools:preinstalled_autotools_toolchain", + "@envoy_toolshed//toolchains/glint:hermetic_glint_toolchain", + "@envoy_toolshed//toolchains/glint:preinstalled_glint_toolchain", ) diff --git a/bazel/versions.bzl b/bazel/versions.bzl index 52fba2121..91b43ff8b 100644 --- a/bazel/versions.bzl +++ b/bazel/versions.bzl @@ -3,6 +3,7 @@ VERSION_AUTOCONF = "2.72" VERSION_AUTOMAKE = "1.17" VERSION_LIBTOOL = "2.5.4" VERSION_M4 = "1.4.19" +VERSION_GLINT = "0.1.0" VERSIONS = { "cmake": "3.23.2", @@ -15,6 +16,8 @@ VERSIONS = { "automake": VERSION_AUTOMAKE, "bins_release": "0.1.11", + "glint_amd64_sha256": "", # TODO: Update with actual sha256 after first release + "glint_arm64_sha256": "", # TODO: Update with actual sha256 after first release, "msan_libs_sha256": "38a90e3e015f3e762f51e66d6b3306e91916e03e69295f37b408ebaf80e12d05", "tsan_libs_sha256": "3c281d4c823634c74bcb83b6db4a75b5c3b4cbb9dee2346d313826d43392bfe7", "sysroot_amd64_sha256": "d9e5879125be147d91f6801176c10a98e2355685ced9ad5a047919dcdbfa93e9", @@ -134,4 +137,12 @@ VERSIONS = { "strip_prefix": "libtool-{version}", "build_file_content": """filegroup(name = "all", srcs = glob(["**"]), visibility = ["//visibility:public"])""", }, + + "rules_rust": { + "type": "github_archive", + "repo": "bazelbuild/rules_rust", + "version": "0.56.0", + "sha256": "f1306aac0b258b790df01ad9abc6abb0df0b65416c74b4ef27f4aab298780a64", + "url": "https://github.com/{repo}/releases/download/{version}/{name}-{version}.tar.gz", + }, }