blob: cd62a7190d37ec18f03790e4eec4096071c0ded5 [file] [log] [blame]
# 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,
)