mirror of
https://github.com/openai/codex.git
synced 2026-09-11 20:36:49 +00:00
## Why Windows libffi builds invoke tools by name, and Cygwin provides a different `link.exe` from MSVC. Inherited search paths can select unintended tools or SDK inputs. ## What changed Add optional `--windows-build-inputs <json>` to `third_party/voice/build_native.py` to select installed tools and SDK directories for Windows MSVC targets. Validate the target, tool paths, architecture-specific assembler, SDK directories, and agreement with CLI tool arguments. Build the search path from the selection with MSVC ahead of Cygwin, reject shadowed tools, replace inherited `INCLUDE` and `LIB`, and remove `LIBPATH`. Retain the selection in build receipts. Builds without the option continue using the normal Visual Studio environment. ## Testing Add tests for linker precedence and subprocess selection, invalid or mismatched inputs, shadowed SDK tools, environment replacement, and receipt recording. GitOrigin-RevId: c80a6d9eb29556157ba1316d62c9b5083f4038b9
90 lines
3.6 KiB
Python
90 lines
3.6 KiB
Python
"""Select already provisioned Windows build tools without unrelated PATH entries.
|
|
|
|
This is an input-selection contract, not a sandbox or a hashed tool closure.
|
|
The selected tools still depend on their installed support files and Windows.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
def build_environment(path, target, selected_tools):
|
|
if path.stat().st_size > 65536:
|
|
raise ValueError("Windows build input document exceeds limits")
|
|
inputs = json.loads(path.read_text(encoding="utf-8"))
|
|
expected = {
|
|
"cc": "cl.exe",
|
|
"cxx": "cl.exe",
|
|
"link": "link.exe",
|
|
"lib": "lib.exe",
|
|
"dumpbin": "dumpbin.exe",
|
|
"assembler": "ml64.exe" if target.startswith("x86_64-") else "armasm64.exe",
|
|
"rc": "rc.exe",
|
|
"mt": "mt.exe",
|
|
"cmake": "cmake.exe",
|
|
"bootstrap_make": "nmake.exe",
|
|
"make": "make.exe",
|
|
"shell": "bash.exe",
|
|
"cygpath": "cygpath.exe",
|
|
"automake": "automake-1.18",
|
|
}
|
|
if (
|
|
target not in ("x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc")
|
|
or inputs.get("schemaVersion") != 1
|
|
or inputs.get("target") != target
|
|
or set(inputs.get("tools", {})) != {*expected, "pkg_config", "python"}
|
|
):
|
|
raise ValueError("Windows build inputs do not match the target and tool set")
|
|
tools = {}
|
|
for name, value in inputs["tools"].items():
|
|
tool = Path(value)
|
|
if (
|
|
not tool.is_absolute()
|
|
or not tool.is_file()
|
|
or ";" in str(tool)
|
|
or (name in expected and tool.name.lower() != expected[name])
|
|
):
|
|
raise ValueError(f"Invalid Windows build tool: {name}")
|
|
tools[name] = tool
|
|
for name, tool in selected_tools.items():
|
|
if not tools[name].samefile(tool):
|
|
raise ValueError(f"Windows build input disagrees with --{name}")
|
|
# libffi invokes cl/link/lib and its architecture's assembler by name.
|
|
# Keep MSVC ahead of Cygwin, which also installs a different link.exe.
|
|
directories = list(dict.fromkeys(tools[name].parent for name in expected))
|
|
directories += [tools[name].parent for name in ("pkg_config", "python")]
|
|
directories = list(dict.fromkeys(directories))
|
|
for name, filename in expected.items():
|
|
found = next(
|
|
(p / filename for p in directories if (p / filename).is_file()), None
|
|
)
|
|
if found is None or not found.samefile(tools[name]):
|
|
raise ValueError(f"Windows build tool is shadowed: {name}")
|
|
system = Path(inputs["systemRoot"])
|
|
command = system / "System32/cmd.exe"
|
|
if not system.is_absolute() or not command.is_file() or ";" in str(system):
|
|
raise ValueError(
|
|
"Windows build inputs require a valid system command directory"
|
|
)
|
|
directories.append(command.parent)
|
|
environment = {
|
|
"PATH": os.pathsep.join(map(str, directories)),
|
|
"SystemRoot": str(system),
|
|
"SYSTEMROOT": str(system),
|
|
"WINDIR": str(system),
|
|
"COMSPEC": str(command),
|
|
}
|
|
for name in ("INCLUDE", "LIB"):
|
|
values = inputs.get(name)
|
|
if not isinstance(values, list) or not 1 <= len(values) <= 64:
|
|
raise ValueError(
|
|
f"Windows build inputs require explicit {name} directories"
|
|
)
|
|
for value in values:
|
|
directory = Path(value)
|
|
if not directory.is_absolute() or not directory.is_dir() or ";" in value:
|
|
raise ValueError(f"Invalid Windows {name} directory")
|
|
environment[name] = os.pathsep.join(values)
|
|
return environment, inputs
|