diff --git a/news/4079.fixed.md b/news/4079.fixed.md new file mode 100644 index 0000000000..7e1c3237ae --- /dev/null +++ b/news/4079.fixed.md @@ -0,0 +1,4 @@ +(toolchain) Fixed a crash in {obj}`py_test` main validation +({obj}`validate_test_main`) on Windows: the interpreter used for the check +couldn't find its DLLs or its stdlib once relocated +([#4079](https://github.com/bazel-contrib/rules_python/issues/4079)). diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl index 0e7e292aae..0e9c315a73 100644 --- a/python/private/py_executable.bzl +++ b/python/private/py_executable.bzl @@ -64,7 +64,6 @@ load(":py_cc_link_params_info.bzl", "PyCcLinkParamsInfo") load(":py_executable_info.bzl", "PyExecutableInfo") load(":py_info.bzl", "PyInfo", "VenvSymlinkKind") load(":py_internal.bzl", "py_internal") -load(":py_interpreter_program.bzl", "PyInterpreterProgramInfo") load(":py_runtime_info.bzl", "DEFAULT_STUB_SHEBANG") load(":reexports.bzl", "BuiltinPyInfo", "BuiltinPyRuntimeInfo") load(":rule_builders.bzl", "ruleb") @@ -1348,47 +1347,33 @@ def _maybe_add_test_main_validation(ctx, main_py, output_groups): return exec_tools_toolchain = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE] - if exec_tools_toolchain == None or exec_tools_toolchain.exec_tools.exec_interpreter == None: + if ( + exec_tools_toolchain == None or + exec_tools_toolchain.exec_tools.exec_runtime == None + ): fail( "Validating py_test main modules requires the exec tools toolchain " + - "with an exec interpreter, but none was found. Either register one " + + "with an exec runtime, but none was found. Either register one " + "or set --@rules_python//python/config_settings:validate_test_main=disabled.", ) - exec_tools = exec_tools_toolchain.exec_tools validator = ctx.attr._validate_test_main - program_info = validator[PyInterpreterProgramInfo] - interpreter = exec_tools.exec_interpreter[DefaultInfo].files_to_run - validator_files_to_run = validator[DefaultInfo].files_to_run - validation_output = ctx.actions.declare_file(ctx.label.name + "_validate_test_main.txt") args = ctx.actions.args() - args.add_all(program_info.interpreter_args) - args.add(validator_files_to_run.executable) args.add("--src", main_py) args.add("--src_name", main_py.short_path) args.add("--label", str(ctx.label)) args.add("--output", validation_output) - execution_requirements = {} - if testing.ExecutionInfo in validator: - execution_requirements = validator[testing.ExecutionInfo].requirements - - ctx.actions.run( - executable = interpreter, + actions_run( + ctx, + executable = validator, arguments = [args], inputs = [main_py], outputs = [validation_output], - tools = [validator_files_to_run], mnemonic = "PyValidateTestMain", progress_message = "Validating py_test main %{label}", - env = program_info.env | { - "PYTHONNOUSERSITE": "1", - "PYTHONSAFEPATH": "1", - }, - execution_requirements = execution_requirements, - toolchain = EXEC_TOOLS_TOOLCHAIN_TYPE, ) if "_validation" in output_groups: output_groups["_validation"] = depset([validation_output], transitive = [output_groups["_validation"]]) diff --git a/tests/integration/BUILD.bazel b/tests/integration/BUILD.bazel index 13b9c2e855..2eae8dfecb 100644 --- a/tests/integration/BUILD.bazel +++ b/tests/integration/BUILD.bazel @@ -52,6 +52,7 @@ test_suite( "bzlmod_lockfile_test_bazel_9.1.0", "local_toolchains_test_bazel_self", "uv_lock_test_bazel_self", + "validate_test_main_test_bazel_self", ], ) diff --git a/tests/integration/runner.py b/tests/integration/runner.py index 9efcbebb89..23e41f6cf1 100644 --- a/tests/integration/runner.py +++ b/tests/integration/runner.py @@ -63,10 +63,10 @@ def describe(self) -> str: {env} \\ {args} RESULT: exit_code: {self.exit_code} -===== STDOUT START ===== -{self.stdout}{maybe_stdout_nl}===== STDOUT END ===== -===== STDERR START ===== -{self.stderr}{maybe_stderr_nl}===== STDERR END ===== +==================== STDOUT BEGIN ==================== +{self.stdout}{maybe_stdout_nl}==================== STDOUT END ==================== +==================== STDERR BEGIN ==================== +{self.stderr}{maybe_stderr_nl}==================== STDERR END ==================== """ @@ -74,7 +74,17 @@ class TestCase(unittest.TestCase): def setUp(self): super().setUp() self.repo_root = pathlib.Path(os.environ["BIT_WORKSPACE_DIR"]) - self.bazel = pathlib.Path(os.environ["BIT_BAZEL_BINARY"]) + bazel = pathlib.Path(os.environ["BIT_BAZEL_BINARY"]) + # Windows doesn't interpret shebangs, so prepend any script interpreter. + interpreter = [] + if os.name == "nt": + with bazel.open("rb") as f: + first_line = f.readline() + if first_line.startswith(b"#!"): + interpreter = first_line[2:].decode().split() + if interpreter and interpreter[0].endswith("/env"): + interpreter = interpreter[1:] + self.bazel_cmd = (*interpreter, str(bazel)) outer_test_tmpdir = pathlib.Path(os.environ["TEST_TMPDIR"]) self.test_tmp_dir = outer_test_tmpdir / "bit_test_tmp" # Put the global tmp not under the test tmp to better match how a real @@ -103,7 +113,7 @@ def run_bazel(self, *args: str, check: bool = True) -> ExecuteResult: Returns: An `ExecuteResult` from running Bazel """ - cmd_args = [str(self.bazel), *args] + cmd_args = [*self.bazel_cmd, *args] env = self.bazel_env _logger.info("executing: %s", shlex.join(cmd_args)) cwd = self.repo_root