| # Copyright 2026 Google LLC |
| # |
| # 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. |
| |
| """Defines a rule for running hermetic bazel builds as executable invocations.""" |
| |
| load( |
| "//tools/bazel/sh_wrapper:sh_wrapper.bzl", |
| "DataDepsInfo", |
| "ShellWrapperInfo", |
| ) |
| |
| def _bazel_invocation_impl(ctx): |
| """Implementation of the bazel_invocation rule. |
| |
| Returns: |
| DefaultInfo for bazel run. |
| ShellWrapperInfo so that it can be the innermost action |
| in a chain of wrapped commands (Workload Dimension). |
| DataDepsInfo for location expansion after composition. |
| """ |
| runner_script = ctx.executable._runner_script |
| |
| relevant_targets = ctx.attr.data + ctx.attr.bazel_bin + ctx.attr.srcs |
| |
| # Assemble the arguments that will be passed to the runner script. |
| runner_args = [] |
| for target in ctx.attr.bazel_bin: |
| if target[DefaultInfo].files_to_run.executable: |
| runner_args.extend(["--bazel_bin", target[DefaultInfo].files_to_run.executable.short_path]) |
| else: |
| for f in target[DefaultInfo].files.to_list(): |
| runner_args.extend(["--bazel_bin", f.short_path]) |
| |
| if ctx.file.workspace_file: |
| runner_args.extend(["--workspace_file", ctx.file.workspace_file.short_path]) |
| if ctx.file.build_file: |
| runner_args.extend(["--build_file", ctx.file.build_file.short_path]) |
| |
| for src in ctx.files.srcs: |
| runner_args.extend(["--src", src.short_path]) |
| |
| # These are all the files the test needs at runtime. |
| runfiles = ctx.runfiles( |
| files = [runner_script] + ctx.files.data + ctx.files.bazel_bin + ctx.files.srcs + |
| ([ctx.file.workspace_file] if ctx.file.workspace_file else []) + |
| ([ctx.file.build_file] if ctx.file.build_file else []), |
| transitive_files = depset([ctx.file._runfiles]), |
| ) |
| for target in relevant_targets: |
| runfiles = runfiles.merge(target[DefaultInfo].default_runfiles) |
| |
| # --- Provider 1: DefaultInfo (for standalone `bazel run/test`) --- |
| launcher = ctx.actions.declare_file(ctx.label.name + "_launcher.sh") |
| ctx.actions.write( |
| output = launcher, |
| content = """#!/bin/bash |
| exec "{runner}" {runner_args} -- {user_args} "$@" |
| """.format( |
| runner = runner_script.short_path, |
| runner_args = " ".join(["'{}'".format(arg) for arg in runner_args]), |
| user_args = " ".join(["'{}'".format(arg) for arg in ctx.attr.args]), |
| ), |
| is_executable = True, |
| ) |
| |
| default_info = DefaultInfo( |
| runfiles = runfiles, |
| executable = launcher, |
| ) |
| |
| # --- Provider 2: ShellWrapperInfo (for composition) --- |
| shell_wrapper_info = ShellWrapperInfo( |
| tokens = [runner_script.short_path] + runner_args + ["--"] + ctx.attr.args, |
| runfiles = runfiles, |
| position = "terminal", |
| ) |
| |
| data_deps_info = DataDepsInfo( |
| deps = depset(relevant_targets), |
| ) |
| |
| return [default_info, shell_wrapper_info, data_deps_info] |
| |
| bazel_invocation = rule( |
| doc = """This rule defines an executable invocation of a Bazel build. |
| It sets up a temporary workspace and runs a Bazel build command. |
| """, |
| implementation = _bazel_invocation_impl, |
| executable = True, |
| attrs = { |
| "workspace_file": attr.label(allow_single_file = True), |
| "build_file": attr.label(allow_single_file = True), |
| "srcs": attr.label_list(allow_files = True), |
| "data": attr.label_list(allow_files = True), |
| "bazel_bin": attr.label_list( |
| default = [Label("//prebuilt:bazel_bin")], |
| allow_files = True, |
| ), |
| "_runner_script": attr.label( |
| default = Label("//scripts/bazel_tests:run_bazel_test"), |
| executable = True, |
| cfg = "exec", |
| ), |
| "_runfiles": attr.label( |
| default = Label("@bazel_tools//tools/bash/runfiles"), |
| allow_single_file = True, |
| ), |
| }, |
| ) |