mirror of
https://github.com/openai/codex.git
synced 2026-09-11 20:36:49 +00:00
## What changed - Add explicit x64 and ARM64 targets for native builds, runtime preparation, and linking using the existing voice recipes. Each target requires native Windows execution of the matching architecture. - Declare compiler, SDK, Python, and CMake inputs; require an explicitly supplied Cygwin/pkgconf tool tree and a fixed `SystemRoot`. Validate installed tool selections against the manifest and declared files. - Pair Windows DLLs with SDK import libraries, preserve plugin and receipt runfiles, and omit Unix runtime-search flags. Use Python for portable payload copying. - Correct the MSVC ARM64 tool directory casing to `HostArm64` and document provisioning and build commands. The generic Rust-consumer aliases remain separate; native Windows Bazel execution and consumer validation are still needed to establish complete Windows voice support. ## Testing Add eight unit tests covering tool selection, path anchoring, invalid inputs, and DLL/import-library copying, plus x64 and ARM64 link smoke targets that reference `gst_version`. GitOrigin-RevId: fb2b4998e9ffb5b64ca830ebf4bb4633fc959eb9
171 lines
6.8 KiB
Python
171 lines
6.8 KiB
Python
"""Exercise declared tool selection and the platform-independent payload copy action."""
|
|
|
|
import copy
|
|
import errno
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
from bazel_copy import copy_payloads
|
|
from bazel_windows import selected_inputs
|
|
|
|
|
|
class WindowsInputsTests(unittest.TestCase):
|
|
def setUp(self):
|
|
temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(temporary.cleanup)
|
|
self.root = Path(temporary.name)
|
|
self.repository = self.root / "external/installed tools"
|
|
self.names = {
|
|
"shell": "cygwin/bin/bash.exe",
|
|
"make": "cygwin/bin/make.exe",
|
|
"cygpath": "cygwin/bin/cygpath.exe",
|
|
"automake": "cygwin/bin/automake-1.18",
|
|
"pkg_config": "pkgconf-image/PFiles64/pkgconf 3.0.6/pkgconf.exe",
|
|
}
|
|
for name in self.names.values():
|
|
path = self.repository / name
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_bytes(b"synthetic declared executable")
|
|
self.manifest = self.repository / "voice-tools.json"
|
|
self.metadata = {
|
|
"schemaVersion": 1,
|
|
"target": "aarch64-pc-windows-msvc",
|
|
"cygwinArchitecture": "x86_64",
|
|
"tools": self.names,
|
|
}
|
|
self.manifest.write_text(json.dumps(self.metadata))
|
|
self.config = {
|
|
"inputs": {
|
|
"schemaVersion": 1,
|
|
"target": "aarch64-pc-windows-msvc",
|
|
"systemRoot": "C:/Windows",
|
|
"tools": {
|
|
"cc": "external/msvc/cl.exe",
|
|
"pkg_config": str(
|
|
(self.repository / self.names["pkg_config"]).relative_to(
|
|
self.root
|
|
)
|
|
),
|
|
},
|
|
"INCLUDE": ["external/sdk/include"],
|
|
"LIB": ["external/sdk/lib"],
|
|
},
|
|
"manifest": str(self.manifest.relative_to(self.root)),
|
|
"installed_files": [
|
|
str((self.repository / name).relative_to(self.root))
|
|
for name in self.names.values()
|
|
],
|
|
}
|
|
|
|
def test_paths_are_anchored_before_recipe_changes_directory(self):
|
|
before = copy.deepcopy(self.config)
|
|
expected = {
|
|
**self.config["inputs"],
|
|
"tools": {
|
|
"cc": str(self.root / "external/msvc/cl.exe"),
|
|
**{
|
|
name: str(self.repository / path)
|
|
for name, path in self.names.items()
|
|
},
|
|
},
|
|
"INCLUDE": [str(self.root / "external/sdk/include")],
|
|
"LIB": [str(self.root / "external/sdk/lib")],
|
|
}
|
|
self.assertEqual(selected_inputs(self.config, self.root), expected)
|
|
self.assertEqual(self.config, before)
|
|
|
|
def test_manifest_cannot_select_a_file_omitted_from_action_inputs(self):
|
|
self.config["installed_files"].pop()
|
|
with self.assertRaisesRegex(ValueError, "selection differs: pkg_config"):
|
|
selected_inputs(self.config, self.root)
|
|
|
|
def test_manifest_cannot_escape_installed_tree(self):
|
|
outside = self.repository.parent / "outside.exe"
|
|
outside.write_bytes(b"not part of the installed support tree")
|
|
inside = self.repository / "cygwin/bin/other-bash.exe"
|
|
try:
|
|
inside.symlink_to(outside)
|
|
except OSError as error:
|
|
if error.errno not in (errno.EPERM, errno.EACCES):
|
|
raise
|
|
self.skipTest("creating symlinks requires OS permission")
|
|
self.config["installed_files"].append(str(inside.relative_to(self.root)))
|
|
self.metadata["tools"]["shell"] = str(inside.relative_to(self.repository))
|
|
self.manifest.write_text(json.dumps(self.metadata))
|
|
with self.assertRaisesRegex(ValueError, "selection differs: shell"):
|
|
selected_inputs(self.config, self.root)
|
|
|
|
def test_manifest_cannot_replace_the_build_script_executable(self):
|
|
alternative = self.repository / "pkgconf-image/another/pkgconf.exe"
|
|
alternative.parent.mkdir(parents=True)
|
|
alternative.write_bytes(b"different declared executable")
|
|
self.config["installed_files"].append(str(alternative.relative_to(self.root)))
|
|
self.metadata["tools"]["pkg_config"] = str(
|
|
alternative.relative_to(self.repository)
|
|
)
|
|
self.manifest.write_text(json.dumps(self.metadata))
|
|
with self.assertRaisesRegex(ValueError, "selection differs: pkg_config"):
|
|
selected_inputs(self.config, self.root)
|
|
|
|
def test_wrong_target_or_emulation_contract_is_rejected(self):
|
|
for key, value in (
|
|
("target", "x86_64-pc-windows-msvc"),
|
|
("cygwinArchitecture", "aarch64"),
|
|
):
|
|
with self.subTest(key=key):
|
|
self.manifest.write_text(json.dumps({**self.metadata, key: value}))
|
|
with self.assertRaisesRegex(ValueError, "selected target"):
|
|
selected_inputs(self.config, self.root)
|
|
|
|
def test_missing_declared_executable_does_not_fall_back_to_path(self):
|
|
(self.repository / self.names["shell"]).unlink()
|
|
with self.assertRaises(FileNotFoundError):
|
|
selected_inputs(self.config, self.root)
|
|
|
|
|
|
class LibraryCopiesTests(unittest.TestCase):
|
|
def test_import_library_and_dll_bytes_remain_distinct(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
root = Path(temporary)
|
|
dll = root / "source.dll"
|
|
library = root / "source.lib"
|
|
dll.write_bytes(b"DLL payload")
|
|
library.write_bytes(b"import library payload")
|
|
locator = root / "package/lib/search-path"
|
|
copy_payloads(
|
|
locator,
|
|
[
|
|
dll,
|
|
root / "package/bin/audio.dll",
|
|
library,
|
|
root / "package/lib/audio.lib",
|
|
],
|
|
)
|
|
self.assertTrue(locator.is_dir())
|
|
self.assertEqual(
|
|
{
|
|
path.relative_to(root / "package").as_posix(): path.read_bytes()
|
|
for path in (root / "package").rglob("*")
|
|
if path.is_file()
|
|
},
|
|
{
|
|
"bin/audio.dll": b"DLL payload",
|
|
"lib/audio.lib": b"import library payload",
|
|
},
|
|
)
|
|
|
|
def test_missing_payload_fails_the_action(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
root = Path(temporary)
|
|
with self.assertRaises(FileNotFoundError):
|
|
copy_payloads(
|
|
root / "lib/search-path",
|
|
[root / "missing.dll", root / "bin/audio.dll"],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|