diff --git a/.github/scripts/rusty_v8_bazel.py b/.github/scripts/rusty_v8_bazel.py index 5ad6d3c408..712c2e3004 100644 --- a/.github/scripts/rusty_v8_bazel.py +++ b/.github/scripts/rusty_v8_bazel.py @@ -202,14 +202,51 @@ def single_bazel_output_file( return outputs[0] +def host_runnable_bazel_output_file( + platform: str, + label: str, + compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, +) -> Path: + outputs = ensure_bazel_output_files(platform, [label], compilation_mode, bazel_configs) + if len(outputs) == 1: + return outputs[0] + + runnable_outputs = [] + for output in outputs: + try: + result = subprocess.run( + [str(output), "--version"], + cwd=ROOT, + capture_output=True, + text=True, + ) + except OSError: + continue + if result.returncode == 0: + runnable_outputs.append(output) + + if len(runnable_outputs) != 1: + raise SystemExit( + f"expected exactly one host-runnable output for {label}, " + f"found {runnable_outputs} from {outputs}" + ) + return runnable_outputs[0] + + def merged_musl_archive( platform: str, lib_path: Path, compilation_mode: str = "fastbuild", bazel_configs: list[str] | None = None, ) -> Path: - llvm_ar = single_bazel_output_file(platform, LLVM_AR_LABEL, compilation_mode, bazel_configs) - llvm_ranlib = single_bazel_output_file( + llvm_ar = host_runnable_bazel_output_file( + platform, + LLVM_AR_LABEL, + compilation_mode, + bazel_configs, + ) + llvm_ranlib = host_runnable_bazel_output_file( platform, LLVM_RANLIB_LABEL, compilation_mode, diff --git a/.github/scripts/test_rusty_v8_bazel.py b/.github/scripts/test_rusty_v8_bazel.py index e86e82e8b2..95c3eac157 100644 --- a/.github/scripts/test_rusty_v8_bazel.py +++ b/.github/scripts/test_rusty_v8_bazel.py @@ -4,11 +4,64 @@ from __future__ import annotations import textwrap import unittest +from pathlib import Path +from unittest.mock import Mock +from unittest.mock import patch +import rusty_v8_bazel import rusty_v8_module_bazel class RustyV8BazelTest(unittest.TestCase): + @patch("rusty_v8_bazel.ensure_bazel_output_files") + @patch("rusty_v8_bazel.subprocess.run") + def test_host_runnable_bazel_output_file_selects_runnable_candidate( + self, + run: Mock, + ensure_outputs: Mock, + ) -> None: + amd64_tool = Path("/tmp/llvm-amd64/bin/llvm-ar") + arm64_tool = Path("/tmp/llvm-arm64/bin/llvm-ar") + ensure_outputs.return_value = [amd64_tool, arm64_tool] + run.side_effect = [ + OSError("Exec format error"), + Mock(returncode=0), + ] + + self.assertEqual( + arm64_tool, + rusty_v8_bazel.host_runnable_bazel_output_file( + "linux_arm64_musl", + "@llvm//tools:llvm-ar", + "opt", + ), + ) + + @patch("rusty_v8_bazel.ensure_bazel_output_files") + @patch("rusty_v8_bazel.subprocess.run") + def test_host_runnable_bazel_output_file_rejects_ambiguous_candidates( + self, + run: Mock, + ensure_outputs: Mock, + ) -> None: + amd64_tool = Path("/tmp/llvm-amd64/bin/llvm-ar") + arm64_tool = Path("/tmp/llvm-arm64/bin/llvm-ar") + ensure_outputs.return_value = [amd64_tool, arm64_tool] + run.side_effect = [ + Mock(returncode=0), + Mock(returncode=0), + ] + + with self.assertRaisesRegex( + SystemExit, + "expected exactly one host-runnable output", + ): + rusty_v8_bazel.host_runnable_bazel_output_file( + "linux_arm64_musl", + "@llvm//tools:llvm-ar", + "opt", + ) + def test_update_module_bazel_replaces_and_inserts_sha256(self) -> None: module_bazel = textwrap.dedent( """\