| # Copyright 2024 The Bazel Authors. All rights reserved. |
| # |
| # 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. |
| |
| """Internal only bootstrap level binary-like rule.""" |
| |
| load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") |
| |
| PyInterpreterProgramInfo = provider( |
| doc = "Information about how to run a program with an external interpreter.", |
| fields = { |
| "env": "dict[str, str] of environment variables to set prior to execution.", |
| "interpreter_args": "List of strings; additional args to pass " + |
| "to the interpreter before the main program.", |
| "main": "File; the .py file that is the entry point.", |
| }, |
| ) |
| |
| def _py_interpreter_program_impl(ctx): |
| # Bazel requires the executable file to be an output created by this target. |
| executable = ctx.actions.declare_file(ctx.label.name) |
| ctx.actions.symlink(output = executable, target_file = ctx.file.main) |
| execution_requirements = {} |
| execution_requirements.update([ |
| value.split("=", 1) |
| for value in ctx.attr.execution_requirements[BuildSettingInfo].value |
| if value.strip() |
| ]) |
| |
| return [ |
| DefaultInfo( |
| executable = executable, |
| files = depset([executable]), |
| runfiles = ctx.runfiles(files = [ |
| executable, |
| ]), |
| ), |
| PyInterpreterProgramInfo( |
| env = ctx.attr.env, |
| interpreter_args = ctx.attr.interpreter_args, |
| main = ctx.file.main, |
| ), |
| testing.ExecutionInfo( |
| requirements = execution_requirements, |
| ), |
| ] |
| |
| py_interpreter_program = rule( |
| doc = """ |
| Binary-like rule that doesn't require a toolchain because its part of |
| implementing build tools for the toolchain. This rule expects the Python |
| interprter to be externally provided. |
| |
| To run a `py_interpreter_program` as an action, pass it as a tool that is |
| used by the actual interpreter executable. This ensures its runfiles are |
| setup. Also pass along any interpreter args, environment, and requirements. |
| |
| ```starlark |
| ctx.actions.run( |
| executable = <python interpreter executable>, |
| args = ( |
| target[PyInterpreterProgramInfo].interpreter_args + |
| [target[DefaultInfo].files_to_run.executable] |
| ), |
| tools = target[DefaultInfo].files_to_run, |
| env = target[PyInterpreterProgramInfo].env, |
| execution_requirements = target[testing.ExecutionInfo].requirements, |
| ) |
| ``` |
| |
| """, |
| implementation = _py_interpreter_program_impl, |
| attrs = { |
| "env": attr.string_dict( |
| doc = "Environment variables that should set prior to running.", |
| ), |
| "execution_requirements": attr.label( |
| doc = "Execution requirements to set when running it as an action", |
| providers = [BuildSettingInfo], |
| ), |
| "interpreter_args": attr.string_list( |
| doc = "Args that should be passed to the interpreter.", |
| ), |
| "main": attr.label( |
| doc = "The entry point Python file.", |
| allow_single_file = True, |
| ), |
| }, |
| # This is set to False because this isn't a binary/executable in the usual |
| # Bazel sense (even though it sets DefaultInfo.files_to_run). It just holds |
| # information so that a caller can construct how to execute it correctly. |
| executable = False, |
| ) |