|
| 1 | +# Copyright 2024 The Bazel Authors. All rights reserved. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | +"""Run a py_binary with altered config settings in an sh_test. |
| 15 | +
|
| 16 | +This facilitates verify running binaries with different configuration settings |
| 17 | +without the overhead of a bazel-in-bazel integration test. |
| 18 | +""" |
| 19 | + |
| 20 | +load("//python:py_binary.bzl", "py_binary") |
| 21 | + |
| 22 | +def _perform_transition_impl(input_settings, attr): |
| 23 | + settings = dict(input_settings) |
| 24 | + settings["//command_line_option:build_python_zip"] = attr.build_python_zip |
| 25 | + if attr.bootstrap_impl: |
| 26 | + settings["//python/config_settings:bootstrap_impl"] = attr.bootstrap_impl |
| 27 | + return settings |
| 28 | + |
| 29 | +_perform_transition = transition( |
| 30 | + implementation = _perform_transition_impl, |
| 31 | + inputs = [ |
| 32 | + "//python/config_settings:bootstrap_impl", |
| 33 | + ], |
| 34 | + outputs = [ |
| 35 | + "//command_line_option:build_python_zip", |
| 36 | + "//python/config_settings:bootstrap_impl", |
| 37 | + ], |
| 38 | +) |
| 39 | + |
| 40 | +def _transition_impl(ctx): |
| 41 | + default_info = ctx.attr.target[DefaultInfo] |
| 42 | + exe_ext = default_info.files_to_run.executable.extension |
| 43 | + if exe_ext: |
| 44 | + exe_ext = "." + exe_ext |
| 45 | + exe_name = ctx.label.name + exe_ext |
| 46 | + |
| 47 | + executable = ctx.actions.declare_file(exe_name) |
| 48 | + ctx.actions.symlink(output = executable, target_file = default_info.files_to_run.executable) |
| 49 | + |
| 50 | + default_outputs = [executable] |
| 51 | + |
| 52 | + # todo: could probably check target.owner vs src.owner to check if it should |
| 53 | + # be symlinked or included as-is |
| 54 | + # For simplicity of implementation, we're assuming the target being run is |
| 55 | + # py_binary-like. In order for Windows to work, we need to make sure the |
| 56 | + # file that the .exe launcher runs (the .zip or underlying non-exe |
| 57 | + # executable) is a sibling of the .exe file with the same base name. |
| 58 | + for src in default_info.files.to_list(): |
| 59 | + if src.extension in ("", "zip"): |
| 60 | + ext = ("." if src.extension else "") + src.extension |
| 61 | + output = ctx.actions.declare_file(ctx.label.name + ext) |
| 62 | + ctx.actions.symlink(output = output, target_file = src) |
| 63 | + default_outputs.append(output) |
| 64 | + |
| 65 | + return [ |
| 66 | + DefaultInfo( |
| 67 | + executable = executable, |
| 68 | + files = depset(default_outputs), |
| 69 | + runfiles = default_info.default_runfiles, |
| 70 | + ), |
| 71 | + testing.TestEnvironment( |
| 72 | + environment = ctx.attr.env, |
| 73 | + ), |
| 74 | + ] |
| 75 | + |
| 76 | +transition_binary = rule( |
| 77 | + implementation = _transition_impl, |
| 78 | + attrs = { |
| 79 | + "bootstrap_impl": attr.string(), |
| 80 | + "build_python_zip": attr.string(default = "auto"), |
| 81 | + "env": attr.string_dict(), |
| 82 | + "target": attr.label(executable = True, cfg = "target"), |
| 83 | + "_allowlist_function_transition": attr.label( |
| 84 | + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| 85 | + ), |
| 86 | + }, |
| 87 | + cfg = _perform_transition, |
| 88 | + executable = True, |
| 89 | +) |
| 90 | + |
| 91 | +def sh_py_run_test(*, name, sh_src, py_src, **kwargs): |
| 92 | + bin_name = "_{}_bin".format(name) |
| 93 | + native.sh_test( |
| 94 | + name = name, |
| 95 | + srcs = [sh_src], |
| 96 | + data = [bin_name], |
| 97 | + deps = [ |
| 98 | + "@bazel_tools//tools/bash/runfiles", |
| 99 | + ], |
| 100 | + env = { |
| 101 | + "BIN_RLOCATION": "$(rlocationpath {})".format(bin_name), |
| 102 | + }, |
| 103 | + ) |
| 104 | + |
| 105 | + transition_binary( |
| 106 | + name = bin_name, |
| 107 | + tags = ["manual"], |
| 108 | + target = "_{}_plain_bin".format(name), |
| 109 | + **kwargs |
| 110 | + ) |
| 111 | + |
| 112 | + py_binary( |
| 113 | + name = "_{}_plain_bin".format(name), |
| 114 | + srcs = [py_src], |
| 115 | + main = py_src, |
| 116 | + tags = ["manual"], |
| 117 | + ) |
0 commit comments