ci: enforce the hermetic Windows Bazel toolchain

This commit is contained in:
Adam Perry
2026-06-22 19:35:44 +00:00
parent 7835d1dc10
commit c1af3a2c5b
26 changed files with 1574 additions and 818 deletions
+912
View File
@@ -0,0 +1,912 @@
#!/usr/bin/env python3
"""Fail-closed policy audit for Windows Bazel build actions."""
import json
import os
import posixpath
import re
import sys
from collections.abc import Callable, Iterable, Mapping
from pathlib import Path, PurePosixPath
REPO_ROOT = Path(__file__).resolve().parents[2]
_REQUIRED_MNEMONICS = {
"ArgumentCommentLint",
"CargoBuildScriptRun",
"CppArchive",
"CppCompile",
"CppLink",
"Rustc",
"V8Mksnapshot",
}
_REQUIRED_TARGET_MARKERS = {
"aws-lc-sys": "crates__aws-lc-sys-",
"ring": "crates__ring-",
"stack protector probe": "//tools/windows-toolchain:stack-protector-probe",
"V8 consumer": "//codex-rs/v8-poc:v8-poc",
"zstd-sys": "crates__zstd-sys-",
}
_FORBIDDEN_ENVIRONMENT = {
"BAZEL_LLVM",
"BAZEL_LLVM_COV",
"BAZEL_LLVM_PROFDATA",
"BAZEL_SH",
"BAZEL_VC",
"BAZEL_VC_FULL_VERSION",
"BAZEL_VS",
"BAZEL_WINSDK_FULL_VERSION",
"CL",
"DEVENVDIR",
"EXTENSIONSDKDIR",
"FRAMEWORKDIR",
"FRAMEWORKDIR32",
"FRAMEWORKVERSION",
"FRAMEWORKVERSION32",
"INCLUDE",
"LIB",
"LIBPATH",
"LINK",
"NETFXSDKDIR",
"UCRTVERSION",
"UNIVERSALCRTSDKDIR",
"USE_CLANG_CL",
"VCINSTALLDIR",
"VCIDEINSTALLDIR",
"VCTOOLSINSTALLDIR",
"VCTOOLSREDISTDIR",
"VISUALSTUDIOVERSION",
"VSCMD_ARG_HOST_ARCH",
"VSCMD_ARG_TGT_ARCH",
"VSCMD_VER",
"VSCMD_START_DIR",
"VSINSTALLDIR",
"WINDOWSSDKBINPATH",
"WINDOWSLIBPATH",
"WINDOWSSDKDIR",
"WINDOWSSDKLIBVERSION",
"WINDOWSSDKVERSION",
"WINDOWSSDKVERBINPATH",
"_CL_",
"_LINK_",
}
_TOOL_ENVIRONMENT_SUFFIXES = {
"AR",
"AS",
"CARGO",
"CC",
"CMAKE",
"CXX",
"CXXFILT",
"DLLTOOL",
"GO",
"LD",
"MAKE",
"MESON",
"NASM",
"NINJA",
"NM",
"NODE",
"OBJCOPY",
"OBJDUMP",
"PERL",
"PKG_CONFIG",
"PROTOC",
"PYTHON",
"PYTHON3",
"RANLIB",
"RC",
"RUSTC",
"RUSTDOC",
"STRIP",
"WINDRES",
"YASM",
}
_TARGET_TOOL_ENVIRONMENT = {
"AR",
"AS",
"CC",
"CXX",
"DLLTOOL",
"LD",
"NM",
"OBJCOPY",
"OBJDUMP",
"RANLIB",
"RC",
"STRIP",
"WINDRES",
}
_FORBIDDEN_HOST_PATHS = (
re.compile(r"(?:^|/)(?:program files(?: \(x86\))?/)?microsoft visual studio/"),
re.compile(r"(?:^|/)windows kits/"),
re.compile(r"(?:^|/)microsoft sdks/"),
re.compile(r"(?:^|/)program files(?: \(x86\))?/llvm/"),
re.compile(r"(?:^|[^a-z0-9_])[a-z]:/llvm/(?:bin|lib)/"),
re.compile(r"(?:^|/)(?:msys64|mingw32|mingw64)/(?:[^/]*/)?bin/"),
re.compile(
r"(?:^|/)(?:usr|opt)/(?:[^/]+/)*bin/"
r"(?:ar|as|cc|clang(?:\+\+|-cl)?|cmake|g\+\+|gcc|go|ld(?:\.lld)?|"
r"lld-link|llvm-ar|make|nasm|ninja|node|perl|protoc|python(?:3)?|"
r"ranlib|windres)(?:\.exe)?(?:$|[\s;])"
),
re.compile(
r"(?:^|/)(?:hostedtoolcache|_work/_tool)/(?:windows/)?(?:python|node(?:js)?)/"
),
re.compile(r"(?:^|/)chocolatey/(?:bin|lib)/(?:llvm|mingw|python|node)"),
)
_BARE_COMPILER_COMMAND = re.compile(
r"(?:^|[\s;&|()=\"'])"
r"(?:ar|as|c\+\+|cargo|cc|clang(?:\+\+|-cl)?|cmake|dlltool|g\+\+|gcc|go|"
r"ld(?:\.lld)?|link|lld-link|llvm-ar|make|meson|nasm|ninja|nm|node|objcopy|"
r"perl|protoc|python(?:3)?|ranlib|rustc|rustdoc|strip|windres|yasm)"
r"(?:\.exe)?(?=$|[\s;&|()\"'])",
re.IGNORECASE,
)
_NESTED_TOOL_PREFIXES = (
"--ar=",
"--compiler=",
"--linker=",
"--node=",
"--python=",
"--script=",
"-clinker=",
)
class AuditFailure(RuntimeError):
pass
def _normalize(value: str) -> str:
value = value.strip().strip('"').replace("\\", "/")
for prefix in ("${pwd}/", "${exec_root}/"):
if value.lower().startswith(prefix):
value = value[len(prefix) :]
break
while value.startswith("./"):
value = value[2:]
return posixpath.normpath(value).lower()
def _path_entries(value: str) -> list[str]:
if ";" not in value:
return [_normalize(entry) for entry in value.split(":") if entry]
entries: list[str] = []
for entry in value.split(";"):
if re.match(r"^[A-Za-z]:[\\/]", entry) or ":" not in entry:
entries.append(_normalize(entry))
else:
entries.extend(_normalize(item) for item in entry.split(":") if item)
return [entry for entry in entries if entry]
def _expected_windows_paths(
environment: Mapping[str, str],
) -> tuple[list[str], list[str]] | None:
program_files = environment.get("ProgramFiles")
local_app_data = environment.get("LOCALAPPDATA")
windows_dir = environment.get("WINDIR") or environment.get("SystemRoot")
if not program_files or not local_app_data or not windows_dir:
return None
git_root = f"{program_files}/Git"
execution = [
f"{git_root}/usr/bin",
f"{windows_dir}/System32",
windows_dir,
]
test = [
f"{program_files}/PowerShell/7",
f"{git_root}/bin",
f"{git_root}/usr/bin",
f"{local_app_data}/Microsoft/WindowsApps",
f"{windows_dir}/System32/WindowsPowerShell/v1.0",
f"{windows_dir}/System32",
windows_dir,
]
return (
[_normalize(entry) for entry in execution],
[_normalize(entry) for entry in test],
)
def _is_tool_environment(key: str) -> bool:
upper = key.upper()
if upper.startswith("CARGO_TARGET_") and upper.endswith("_LINKER"):
return True
if upper.startswith(("CARGO_CFG_", "CARGO_FEATURE_")):
return False
tokens = upper.split("_")
return (
upper in _TOOL_ENVIRONMENT_SUFFIXES
or (tokens[0] in _TARGET_TOOL_ENVIRONMENT and len(tokens) > 1)
or (tokens[-1] in _TARGET_TOOL_ENVIRONMENT and len(tokens) > 1)
or (tokens[0] == "LLVM" and tokens[-1] in _TOOL_ENVIRONMENT_SUFFIXES)
)
def _artifact_paths(data: Mapping[str, object]) -> dict[int, str]:
fragments = {int(item["id"]): item for item in data["pathFragments"]} # type: ignore[index]
def fragment_path(fragment_id: int) -> str:
labels: list[str] = []
while fragment_id:
fragment = fragments[fragment_id]
labels.append(str(fragment["label"]))
fragment_id = int(fragment.get("parentId", 0))
return "/".join(reversed(labels))
return {
int(artifact["id"]): _normalize(fragment_path(int(artifact["pathFragmentId"])))
for artifact in data["artifacts"] # type: ignore[index]
}
def _action_input_resolver(
data: Mapping[str, object], artifacts: Mapping[int, str]
) -> Callable[[Mapping[str, object]], set[str]]:
dep_sets = {int(item["id"]): item for item in data["depSetOfFiles"]} # type: ignore[index]
memo: dict[int, set[int]] = {}
def expand(dep_set_id: int) -> set[int]:
if dep_set_id in memo:
return memo[dep_set_id]
dep_set = dep_sets[dep_set_id]
artifact_ids = {int(item) for item in dep_set.get("directArtifactIds", [])}
for transitive_id in dep_set.get("transitiveDepSetIds", []):
artifact_ids.update(expand(int(transitive_id)))
memo[dep_set_id] = artifact_ids
return artifact_ids
def resolve(action: Mapping[str, object]) -> set[str]:
artifact_ids: set[int] = set()
for dep_set_id in action.get("inputDepSetIds", []):
artifact_ids.update(expand(int(dep_set_id)))
return {artifacts[artifact_id] for artifact_id in artifact_ids}
return resolve
def _is_declared_tool(tool: str, inputs: set[str]) -> bool:
return _normalize(tool) in inputs
def _allowed_substrate_tools(environment: Mapping[str, str], mnemonic: str) -> set[str]:
allowed = {
"/bin/bash",
"/usr/bin/bash",
"bash",
"bash.exe",
"cmd.exe",
"sh",
"sh.exe",
}
paths = _expected_windows_paths(environment)
if paths:
program_files = environment["ProgramFiles"]
windows_dir = environment.get("WINDIR") or environment["SystemRoot"]
local_app_data = environment["LOCALAPPDATA"]
allowed.update(
{
f"{program_files}/Git/usr/bin/bash.exe",
f"{program_files}/Git/usr/bin/sh.exe",
f"{windows_dir}/System32/cmd.exe",
}
)
if mnemonic == "TestRunner":
allowed.update(
{
f"{program_files}/PowerShell/7/pwsh.exe",
f"{local_app_data}/Microsoft/WindowsApps/dotslash.exe",
f"{windows_dir}/System32/WindowsPowerShell/v1.0/powershell.exe",
}
)
return {_normalize(item) for item in allowed}
def _tool_candidates(
arguments: list[str],
env: Mapping[str, str],
*,
include_action_executable: bool = True,
) -> Iterable[tuple[str, str]]:
if arguments and include_action_executable:
yield ("action executable", arguments[0])
for index, argument in enumerate(arguments):
lower = argument.lower()
if argument == "--" and index + 1 < len(arguments):
yield ("wrapped executable", arguments[index + 1])
for prefix in _NESTED_TOOL_PREFIXES:
if lower.startswith(prefix):
yield (prefix.rstrip("="), argument[len(prefix) :])
if lower.startswith("--codegen=linker="):
yield ("rust linker", argument[len("--codegen=linker=") :])
if lower.startswith("-c") and "linker=" in lower:
yield ("rust linker", argument.split("linker=", 1)[1])
for key, value in env.items():
if value and _is_tool_environment(key):
yield (f"environment {key}", value.split()[0])
elif value and re.search(r"(?:^|[/\\])[^ ]+\.exe$", value, re.IGNORECASE):
yield (f"executable-valued environment {key}", value)
def _is_windows_gnullvm_platform(value: str) -> bool:
normalized = _normalize(value)
return normalized.endswith(("//:local_windows", "//:windows_x86_64_gnullvm"))
def _audit_path(
value: str,
mnemonic: str,
inputs: set[str],
environment: Mapping[str, str],
) -> str | None:
entries = _path_entries(value)
expected = _expected_windows_paths(environment)
build_paths = [["/usr/bin", "/bin"]]
test_paths: list[list[str]] = []
if expected:
build_paths.append(expected[0])
test_paths.append(expected[1])
allowed_suffixes = (
[*test_paths, *build_paths] if mnemonic == "TestRunner" else build_paths
)
input_directories = {_normalize(str(PurePosixPath(item).parent)) for item in inputs}
for suffix in allowed_suffixes:
if len(entries) < len(suffix) or entries[-len(suffix) :] != suffix:
continue
prefix = entries[: -len(suffix)]
if not prefix or (
mnemonic == "ArgumentCommentLint"
and all(entry in input_directories for entry in prefix)
):
return None
return f"{mnemonic} has non-frozen PATH: {value}"
def _forbidden_value(value: str) -> bool:
normalized = _normalize(value)
return any(pattern.search(normalized) for pattern in _FORBIDDEN_HOST_PATHS)
def audit_aquery_files(
paths: Iterable[Path], environment: Mapping[str, str] | None = None
) -> None:
if environment is None:
environment = os.environ
errors: list[str] = []
mnemonics: set[str] = set()
target_labels: set[str] = set()
saw_windows_build_script = False
saw_windows_lint = False
saw_windows_proc_macro = False
probe_compile = False
probe_link = False
probe_test = False
action_count = 0
for path in paths:
try:
data = json.loads(path.read_text(encoding="utf-8"))
artifacts = _artifact_paths(data)
resolve_inputs = _action_input_resolver(data, artifacts)
except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
raise AuditFailure(f"invalid aquery JSON in {path}: {error}") from error
target_by_id = {
int(target["id"]): str(target["label"])
for target in data.get("targets", [])
}
target_labels.update(target_by_id.values())
for action in data["actions"]:
action_count += 1
mnemonic = str(action.get("mnemonic", ""))
mnemonics.add(mnemonic)
arguments = [str(argument) for argument in action.get("arguments", [])]
param_arguments = [
str(argument)
for param_file in action.get("paramFiles", [])
for argument in param_file.get("arguments", [])
]
all_arguments = [*arguments, *param_arguments]
embedded_contents = [
str(action.get("templateContent", "")),
str(action.get("fileContents", "")),
*[
str(substitution.get("value", ""))
for substitution in action.get("substitutions", [])
],
]
env = {
str(item["key"]): str(item.get("value", ""))
for item in action.get("environmentVariables", [])
}
inputs = resolve_inputs(action)
identity = f"{path.name}:{mnemonic or '<unknown>'}"
target_label = target_by_id.get(int(action.get("targetId", 0)), "")
execution_platform = str(action.get("executionPlatform", ""))
windows_gnullvm_exec = _is_windows_gnullvm_platform(execution_platform)
if any("--crate-type=proc-macro" == argument for argument in all_arguments):
if (
windows_gnullvm_exec
and "--target=x86_64-pc-windows-gnullvm" in all_arguments
):
saw_windows_proc_macro = True
if mnemonic == "CargoBuildScriptRun" and windows_gnullvm_exec:
if (
env.get("HOST") == "x86_64-pc-windows-gnullvm"
and env.get("TARGET") == "x86_64-pc-windows-gnullvm"
):
saw_windows_build_script = True
if mnemonic == "ArgumentCommentLint" and windows_gnullvm_exec:
has_driver = any(
"argument-comment-lint-driver" in item
for item in [*all_arguments, *inputs]
)
has_rustc_driver = any("rustc_driver" in item for item in inputs)
if (
has_driver
and has_rustc_driver
and "--target=x86_64-pc-windows-gnullvm" in all_arguments
):
saw_windows_lint = True
for key in env:
if key.upper() in _FORBIDDEN_ENVIRONMENT:
errors.append(
f"{identity} forwards forbidden environment variable {key}"
)
for key, value in env.items():
if key.upper() == "PATH":
if path_error := _audit_path(value, mnemonic, inputs, environment):
errors.append(f"{identity}: {path_error}")
if _forbidden_value(value):
errors.append(
f"{identity} contains forbidden host path in {key}: {value}"
)
for argument in all_arguments:
if _forbidden_value(argument):
errors.append(
f"{identity} contains forbidden host path: {argument}"
)
if argument.lower().startswith("bash_bin_path="):
expected_paths = _expected_windows_paths(environment)
allowed_bash_paths = {"bash_bin_path=/bin/bash"}
if expected_paths:
program_files = environment["ProgramFiles"].replace("\\", "/")
allowed_bash_paths.add(
f"bash_bin_path={program_files}/Git/usr/bin/bash.exe".lower()
)
if argument.lower().replace("\\", "/") not in allowed_bash_paths:
errors.append(
f"{identity} uses non-frozen Bazel shell: {argument}"
)
for content in embedded_contents:
if not content:
continue
if _forbidden_value(content):
errors.append(f"{identity} writes a forbidden host path")
if re.search(
r"(?:--host|--target)[= ]\S*windows-msvc",
content,
re.IGNORECASE,
):
errors.append(f"{identity} writes an MSVC target command")
if content.lstrip().startswith(("#!", "set -")):
if match := _BARE_COMPILER_COMMAND.search(content):
errors.append(
f"{identity} writes a script that resolves a tool from PATH: "
f"{match.group(0).strip()}"
)
execution_platform_lower = execution_platform.lower()
msvc_target = any(
argument.lower().startswith(("--host=", "--target="))
and "windows-msvc" in argument.lower()
for argument in all_arguments
) or any(
key.upper() in {"CARGO_BUILD_TARGET", "HOST", "TARGET"}
and "windows-msvc" in value.lower()
for key, value in env.items()
)
msvc_target_env = env.get("CARGO_CFG_TARGET_ENV", "").lower() == "msvc"
if (
(
"windows" in execution_platform_lower
and "msvc" in execution_platform_lower
)
or msvc_target
or msvc_target_env
):
errors.append(f"{identity} selects an MSVC Windows platform or target")
tool_candidates = [
*_tool_candidates(arguments, env),
*_tool_candidates(
param_arguments,
{},
include_action_executable=False,
),
]
for description, tool in tool_candidates:
normalized = _normalize(tool)
if not normalized:
continue
if "windows-msvc" in normalized or "windows_msvc" in normalized:
errors.append(f"{identity} selects an MSVC {description}: {tool}")
continue
if _is_declared_tool(normalized, inputs):
continue
if normalized in _allowed_substrate_tools(environment, mnemonic):
continue
errors.append(f"{identity} uses undeclared {description}: {tool}")
if arguments and _normalize(arguments[0]) in _allowed_substrate_tools(
environment, mnemonic
):
shell_command = " ".join(arguments[1:])
if match := _BARE_COMPILER_COMMAND.search(shell_command):
errors.append(
f"{identity} resolves undeclared tool from PATH: {match.group(0).strip()}"
)
if target_label == "//tools/windows-toolchain:stack-protector-probe":
if mnemonic == "CppCompile":
required = {
"-fstack-protector-all",
"--sysroot=/dev/null",
"x86_64-w64-windows-gnu",
}
missing = required - set(all_arguments)
if missing:
errors.append(
f"{identity} stack-protector compile is missing: {sorted(missing)}"
)
else:
probe_compile = True
elif mnemonic == "CppLink":
required = {
"-fuse-ld=lld",
"--sysroot=/dev/null",
"x86_64-w64-windows-gnu",
}
required_inputs = {
"crt_objects_directory_windows",
"mingw_crt_library_search_directory",
"mingw_import_libraries_directory",
}
missing = required - set(all_arguments)
missing_inputs = {
marker
for marker in required_inputs
if not any(marker in input_path for input_path in inputs)
}
if missing or missing_inputs:
errors.append(
f"{identity} stack-protector link is missing flags {sorted(missing)} "
f"or declared runtime inputs {sorted(missing_inputs)}"
)
else:
probe_link = True
elif mnemonic == "TestRunner":
has_probe_runfiles = any(
input_path.endswith("/stack-protector-probe.exe.runfiles")
for input_path in inputs
)
has_probe_executable = (
len(arguments) > 1
and arguments[1]
== "tools/windows-toolchain/stack-protector-probe.exe"
)
if (
not _is_windows_gnullvm_platform(execution_platform)
or not has_probe_runfiles
or not has_probe_executable
):
errors.append(
f"{identity} stack-protector test lacks the fixed Windows gnullvm runfiles"
)
else:
probe_test = True
if action_count == 0:
errors.append("aquery audit received no actions")
for mnemonic in sorted(_REQUIRED_MNEMONICS - mnemonics):
errors.append(f"aquery coverage is missing {mnemonic} actions")
if not saw_windows_build_script:
errors.append(
"aquery coverage is missing a Windows-gnullvm build-script runner"
)
if not saw_windows_lint:
errors.append(
"aquery coverage is missing the Windows-gnullvm argument-comment lint driver"
)
if not saw_windows_proc_macro:
errors.append(
"aquery coverage is missing a Windows-gnullvm proc-macro Rustc action"
)
if not probe_compile:
errors.append("aquery coverage is missing the protected C compile probe")
if not probe_link:
errors.append("aquery coverage is missing the protected C link probe")
if not probe_test:
errors.append("aquery coverage is missing the protected C Windows run probe")
for description, marker in _REQUIRED_TARGET_MARKERS.items():
if not any(marker in label for label in target_labels):
errors.append(f"aquery coverage is missing {description}")
if errors:
raise AuditFailure(
"Windows Bazel hermeticity audit failed:\n- " + "\n- ".join(errors)
)
def _repo_files(repo_root: Path) -> Iterable[Path]:
for directory, directory_names, file_names in os.walk(repo_root):
directory_names[:] = [
name
for name in directory_names
if name
not in {
".cache",
".git",
"D:",
"__pycache__",
"bazel-out",
"node_modules",
"target",
}
and not name.startswith("bazel-")
]
for file_name in file_names:
yield Path(directory) / file_name
def _source_files(repo_root: Path) -> Iterable[Path]:
for path in _repo_files(repo_root):
relative = path.relative_to(repo_root)
if path.name in {
"BUILD",
"BUILD.bazel",
"MODULE.bazel",
"WORKSPACE",
"WORKSPACE.bazel",
}:
yield path
elif path.suffix == ".bzl" or relative == Path(".bazelrc"):
yield path
elif relative.parts[:2] == (".github", "workflows") and path.suffix in {
".yaml",
".yml",
}:
yield path
elif relative.parts[:1] == ("patches",) and path.suffix == ".patch":
yield path
elif relative.parts[:2] == (".github", "actions") and any(
marker in part
for marker in ("argument-comment-lint", "bazel")
for part in relative.parts[2:]
):
yield path
elif relative.parts[:2] == (".github", "scripts") and "bazel" in path.name:
if not path.name.startswith("test_"):
yield path
elif relative.parts[:1] in {("scripts",), ("tools",)} and "bazel" in path.name:
yield path
def audit_source_tree(repo_root: Path = REPO_ROOT) -> None:
errors: list[str] = []
self_paths = {
Path(".github/scripts/audit_bazel_windows_hermeticity.py"),
Path(".github/scripts/test_audit_bazel_windows_hermeticity.py"),
}
action_env_allowlist = {
'"--action_env=PATH=${windows_execution_path}"',
'"--host_action_env=PATH=${windows_execution_path}"',
}
repo_env_allowlist = {
"common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
"common --repo_env=BAZEL_NO_APPLE_CPP_TOOLCHAIN=1",
}
client_path_removal_allowlist = {
'*"/microsoft visual studio/"* | \\',
'*"/windows kits/"* | \\',
'*"/microsoft sdks/"* | \\',
'*"/program files/llvm/"* | \\',
'*"/program files (x86)/llvm/"* | \\',
'*"/msys64/"* | \\',
'*"/mingw32/"* | \\',
'*"/mingw64/"*)',
}
local_repository_occurrences: list[tuple[Path, str]] = []
repository_rule_occurrences: list[tuple[Path, str]] = []
for path in _source_files(repo_root):
relative = path.relative_to(repo_root)
if relative in self_paths:
continue
for line_number, raw_line in enumerate(
path.read_text(encoding="utf-8", errors="replace").splitlines(), 1
):
if (
path.suffix == ".patch"
and raw_line.startswith("-")
and not raw_line.startswith("---")
):
continue
line = (
raw_line[1:]
if path.suffix == ".patch" and raw_line.startswith("+")
else raw_line
)
lower = line.lower()
location = f"{relative}:{line_number}"
if "skip_incompatible_explicit_targets" in lower:
errors.append(
f"{location} skips explicitly requested incompatible targets"
)
if re.search(r"\b[A-Za-z_][A-Za-z0-9_]*\.which\s*\(", line):
errors.append(f"{location} discovers a tool from the host")
if _forbidden_value(line) and not (
relative
== Path(".github/scripts/sanitize-bazel-windows-environment.sh")
and line.strip() in client_path_removal_allowlist
):
errors.append(f"{location} hard-codes a forbidden host toolchain path")
if re.search(r"--(?:host_)?action_env=", line):
if (
relative != Path(".github/scripts/run-bazel-ci.sh")
or line.strip() not in action_env_allowlist
):
errors.append(
f"{location} forwards a new build-action environment variable"
)
if "--repo_env=" in line and (
relative != Path(".bazelrc") or line.strip() not in repo_env_allowlist
):
errors.append(
f"{location} forwards a new repository environment variable"
)
if "file://" in lower:
errors.append(f"{location} imports an unpinned local file")
if re.search(
r"\b(?:which|where(?:\.exe)?|get-command)\s+(?:clang|gcc|cl|link|lld|nasm|cmake|ninja|python|node)\b",
lower,
):
errors.append(f"{location} discovers a compiler or native-build tool")
if re.search(
r"\b(?:new_local_repository|local_repository|local_path_override)\b",
line,
):
local_repository_occurrences.append((relative, line.strip()))
if "repository_rule(" in line:
repository_rule_occurrences.append((relative, line.strip()))
expected_local_repository_occurrences = [
(
Path("MODULE.bazel"),
'new_local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")',
),
(Path("MODULE.bazel"), "new_local_repository("),
]
if local_repository_occurrences != expected_local_repository_occurrences:
errors.append(
"local repository policy changed; only the checked-in third_party/v8 repository is allowed"
)
module_text = (repo_root / "MODULE.bazel").read_text(encoding="utf-8")
if not re.search(
r'new_local_repository\(\s*name = "v8_targets",\s*build_file = "//third_party/v8:BUILD\.bazel",\s*path = "third_party/v8",\s*\)',
module_text,
):
errors.append(
"the v8_targets local repository must remain pinned to third_party/v8"
)
expected_repository_rule_occurrences = [
(Path("rbe.bzl"), "rbe_platform_repository = repository_rule("),
]
if repository_rule_occurrences != expected_repository_rule_occurrences:
errors.append(
"new custom repository rules are forbidden by Windows hermeticity policy"
)
sanitizer_source = 'source "${script_dir}/sanitize-bazel-windows-environment.sh"'
sanitizer_call = "sanitize_bazel_windows_environment"
sanitizer_path = Path(".github/scripts/sanitize-bazel-windows-environment.sh")
sanitizer_text = (repo_root / sanitizer_path).read_text(encoding="utf-8")
frozen_bazel_shell = r'export BAZEL_SH="${ProgramFiles//\\//}/Git/usr/bin/bash.exe"'
if sanitizer_text.count(frozen_bazel_shell) != 1:
errors.append(
f"{sanitizer_path} must freeze BAZEL_SH to the Git workflow shell"
)
for wrapper in (
Path(".github/scripts/run-bazel-ci.sh"),
Path(".github/scripts/run-bazel-query-ci.sh"),
):
wrapper_lines = (repo_root / wrapper).read_text(encoding="utf-8").splitlines()
if (
wrapper_lines.count(sanitizer_source) != 1
or wrapper_lines.count(sanitizer_call) != 1
):
errors.append(
f"{wrapper} must apply the fixed Windows environment sanitizer once"
)
text_suffixes = {
".bazel",
".bzl",
".json",
".md",
".patch",
".ps1",
".py",
".rs",
".sh",
".toml",
".yaml",
".yml",
}
for path in _repo_files(repo_root):
if (
path.relative_to(repo_root) in self_paths
or path.suffix not in text_suffixes
):
continue
for line_number, line in enumerate(
path.read_text(encoding="utf-8", errors="replace").splitlines(), 1
):
if "skip_incompatible_explicit_targets" in line.lower():
errors.append(
f"{path.relative_to(repo_root)}:{line_number} skips explicitly requested incompatible targets"
)
compute_path = repo_root / ".github/scripts/compute-bazel-windows-path.ps1"
allowed_environment_roots = {
"github_env",
"localappdata",
"programfiles",
"systemroot",
"windir",
}
for line_number, line in enumerate(
compute_path.read_text(encoding="utf-8").splitlines(), 1
):
for name in re.findall(r"\$env:([A-Za-z0-9_]+(?:\(x86\))?)", line):
if name.lower() not in allowed_environment_roots:
errors.append(
f"{compute_path.relative_to(repo_root)}:{line_number} extends the frozen path from {name}"
)
if errors:
raise AuditFailure(
"Windows Bazel source policy audit failed:\n- " + "\n- ".join(errors)
)
def main(argv: list[str]) -> int:
if not argv:
print(
f"usage: {Path(sys.argv[0]).name} <aquery.json> [<aquery.json> ...]",
file=sys.stderr,
)
return 2
try:
audit_source_tree()
audit_aquery_files(Path(argument) for argument in argv)
except (AuditFailure, OSError) as error:
print(error, file=sys.stderr)
return 1
print("Windows Bazel hermeticity audit passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
+8
View File
@@ -2,6 +2,10 @@
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${script_dir}/sanitize-bazel-windows-environment.sh"
sanitize_bazel_windows_environment
print_failed_bazel_test_logs=0
print_failed_bazel_action_summary=0
remote_download_toplevel=0
@@ -282,6 +286,10 @@ if [[ "${RUNNER_OS:-}" == "Windows" && -n "${BUILDBUDDY_API_KEY:-}" && ( $window
fi
fi
if [[ "${RUNNER_OS:-}" == "Windows" && ! ( -n "${BUILDBUDDY_API_KEY:-}" && ( $windows_cross_compile -eq 1 || "$ci_config" == "ci-windows-argument-lint" ) ) ]]; then
post_config_bazel_args+=("--shell_executable=${BAZEL_SH}")
fi
if [[ "${RUNNER_OS:-}" == "Windows" && $windows_cross_compile -eq 1 && -z "${BUILDBUDDY_API_KEY:-}" ]]; then
# The Windows cross-compile config depends on authenticated remote
# execution. When credentials are unavailable, spell out the equivalent
+4
View File
@@ -2,6 +2,10 @@
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${script_dir}/sanitize-bazel-windows-environment.sh"
sanitize_bazel_windows_environment
# Run target-discovery queries with the same startup settings as the main
# build/test invocation so they can reuse the same Bazel server. Queries only
# enumerate labels, so they intentionally do not select a CI build/test config
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 0 ]]; then
echo "This audit has no configurable arguments." >&2
exit 2
fi
if [[ "${RUNNER_OS:-}" != "Windows" ]]; then
echo "The Windows Bazel hermeticity audit must run on a Windows runner." >&2
exit 1
fi
if [[ -z "${RUNNER_TEMP:-}" ]]; then
echo "RUNNER_TEMP must be set." >&2
exit 1
fi
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "${repo_root}"
native_aquery="${RUNNER_TEMP}/windows-hermetic-native-aquery.json"
v8_aquery="${RUNNER_TEMP}/windows-hermetic-v8-aquery.json"
lint_aquery="${RUNNER_TEMP}/windows-hermetic-lint-aquery.json"
native_targets='deps(set(
@crates//:aws-lc-sys-0.39.0
@crates//:ring-0.17.14
@crates//:zstd-0.13.3
//codex-rs/codex-experimental-api-macros:codex-experimental-api-macros
//tools/windows-toolchain:stack-protector-probe
))'
v8_actions='mnemonic(
"(Action|CargoBuildScriptRun|CppArchive|CppLink|RunBinary|Rustc|V8Mksnapshot)",
deps(//codex-rs/v8-poc:v8-poc)
)'
./.github/scripts/run-bazel-ci.sh \
-- \
aquery \
--host_platform=//:local_windows \
--platforms=//:windows_x86_64_gnullvm \
--extra_execution_platforms=//:windows_x86_64_gnullvm \
--extra_toolchains=//:windows_gnullvm_tests_on_gnullvm_host_toolchain \
--include_aspects \
--include_param_files \
--output=jsonproto \
"--output_file=${native_aquery}" \
-- \
"${native_targets}"
./.github/scripts/run-bazel-ci.sh \
-- \
aquery \
--host_platform=//:local_windows \
--platforms=//:windows_x86_64_gnullvm \
--extra_execution_platforms=//:windows_x86_64_gnullvm \
--extra_toolchains=//:windows_gnullvm_tests_on_gnullvm_host_toolchain \
--include_aspects \
--include_param_files \
--output=jsonproto \
"--output_file=${v8_aquery}" \
-- \
"${v8_actions}"
./.github/scripts/run-bazel-ci.sh \
-- \
aquery \
--config=argument-comment-lint \
--host_platform=//:local_windows \
--platforms=//:windows_x86_64_gnullvm \
--extra_execution_platforms=//:windows_x86_64_gnullvm \
--extra_toolchains=//:windows_gnullvm_tests_on_gnullvm_host_toolchain \
--include_aspects \
--include_param_files \
--output=jsonproto \
"--output_file=${lint_aquery}" \
-- \
//codex-rs/codex-experimental-api-macros:codex-experimental-api-macros
python .github/scripts/audit_bazel_windows_hermeticity.py \
"${native_aquery}" \
"${v8_aquery}" \
"${lint_aquery}"
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# Remove compiler and SDK state before any Windows Bazel command starts or
# reuses a server. Callers cannot extend the fixed policy or supply replacement
# tools; the only value written is Bazel's path to the existing workflow shell.
sanitize_bazel_windows_environment() {
if [[ $# -ne 0 ]]; then
echo "sanitize_bazel_windows_environment does not accept arguments." >&2
return 2
fi
if [[ "${RUNNER_OS:-}" != "Windows" ]]; then
return
fi
if [[ -z "${ProgramFiles:-}" ]]; then
echo "ProgramFiles must be set for the Windows Bazel shell substrate." >&2
return 1
fi
unset \
AR ARFLAGS AS BAZEL_LLVM BAZEL_LLVM_COV BAZEL_LLVM_PROFDATA BAZEL_VC \
BAZEL_VC_FULL_VERSION BAZEL_VS BAZEL_WINSDK_FULL_VERSION CC CFLAGS CL \
CMAKE_GENERATOR CMAKE_GENERATOR_PLATFORM CMAKE_GENERATOR_TOOLSET \
CMAKE_TOOLCHAIN_FILE CPPFLAGS CXX CXXFLAGS DevEnvDir DLLTOOL \
ExtensionSdkDir FrameworkDir FrameworkDir32 FrameworkVersion \
FrameworkVersion32 INCLUDE LD LDFLAGS LIB LIBPATH LINK NETFXSDKDir NASM \
NM OBJCOPY OBJDUMP RANLIB RC RUSTC RUSTDOC RUSTFLAGS STRIP UCRTVersion \
UniversalCRTSdkDir USE_CLANG_CL VCIDEInstallDir VCINSTALLDIR \
VCToolsInstallDir VCToolsRedistDir VisualStudioVersion VSCMD_ARG_HOST_ARCH \
VSCMD_ARG_TGT_ARCH VSCMD_START_DIR VSCMD_VER VSINSTALLDIR WINDRES \
WindowsLibPath WindowsSdkBinPath WindowsSdkDir WindowsSDKLibVersion \
WindowsSDKVersion WindowsSdkVerBinPath YASM _CL_ _LINK_
# MSYS preserves the spelling of exported variable names. Clear alternate
# casing and target-qualified compiler variables without accepting a list
# from the caller.
mapfile -t client_environment_names < <(compgen -e)
for environment_name in "${client_environment_names[@]}"; do
uppercase_environment_name="${environment_name^^}"
case "${uppercase_environment_name}" in
AR | ARFLAGS | AS | BAZEL_LLVM* | BAZEL_VC | BAZEL_VC_FULL_VERSION | \
BAZEL_VS | BAZEL_WINSDK_FULL_VERSION | CC | CFLAGS | CL | CMAKE | \
CMAKE_* | CPPFLAGS | CXX | CXXFLAGS | DLLTOOL | GO | INCLUDE | LD | \
LDFLAGS | LIB | LIBPATH | LINK | LLVM_* | MAKE | MESON | MINGW* | \
MSVC* | NASM | NINJA | NM | OBJCOPY | OBJDUMP | PERL | PERL5LIB | \
PKG_CONFIG | PROTOC | RANLIB | RC | RUSTC | RUSTDOC | RUSTFLAGS | \
STRIP | UCRTVERSION | UNIVERSALCRTSDKDIR | USE_CLANG_CL | \
VCIDEINSTALLDIR | VCINSTALLDIR | VCTOOLS* | VISUALSTUDIOVERSION | \
VSCMD_* | VSINSTALLDIR | WINDRES | WINDOWSLIBPATH | WINDOWSSDK* | \
YASM | _CL_ | _LINK_ | CARGO_BUILD_TARGET | CARGO_TARGET_*_LINKER | \
AR_* | *_AR | AS_* | *_AS | CC_* | *_CC | CFLAGS_* | *_CFLAGS | \
CXX_* | *_CXX | CXXFLAGS_* | *_CXXFLAGS | LD_* | *_LD)
unset "${environment_name}"
;;
esac
done
path_separator=:
if [[ "${PATH}" == *";"* ]]; then
path_separator=";"
fi
IFS="${path_separator}" read -r -a client_path_entries <<< "${PATH}"
sanitized_client_path=""
for path_entry in "${client_path_entries[@]}"; do
[[ -n "${path_entry}" ]] || continue
normalized_path_entry="${path_entry//\\//}"
normalized_path_entry="${normalized_path_entry,,}"
case "${normalized_path_entry}/" in
*"/microsoft visual studio/"* | \
*"/windows kits/"* | \
*"/microsoft sdks/"* | \
*"/program files/llvm/"* | \
*"/program files (x86)/llvm/"* | \
*"/msys64/"* | \
*"/mingw32/"* | \
*"/mingw64/"*)
continue
;;
esac
if [[ -z "${sanitized_client_path}" ]]; then
sanitized_client_path="${path_entry}"
else
sanitized_client_path+="${path_separator}${path_entry}"
fi
done
if [[ -z "${sanitized_client_path}" ]]; then
echo "Windows Bazel client PATH is empty after removing compiler and SDK directories." >&2
return 1
fi
export PATH="${sanitized_client_path}"
export BAZEL_SH="${ProgramFiles//\\//}/Git/usr/bin/bash.exe"
}
+321
View File
@@ -0,0 +1,321 @@
#!/usr/bin/env python3
import json
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
import audit_bazel_windows_hermeticity as audit
WINDOWS_ENV = {
"CODEX_BAZEL_WINDOWS_EXECUTION_PATH": (
r"C:\Program Files\Git\usr\bin;C:\Windows\System32;C:\Windows"
),
"CODEX_BAZEL_WINDOWS_TEST_PATH": (
r"C:\Program Files\PowerShell\7;C:\Program Files\Git\bin;"
r"C:\Program Files\Git\usr\bin;C:\Users\runner\AppData\Local\Microsoft\WindowsApps;"
r"C:\Windows\System32\WindowsPowerShell\v1.0;C:\Windows\System32;C:\Windows"
),
"LOCALAPPDATA": r"C:\Users\runner\AppData\Local",
"ProgramFiles": r"C:\Program Files",
"WINDIR": r"C:\Windows",
}
def valid_aquery() -> dict[str, object]:
artifact_paths = [
"bazel-out/bin/process-wrapper.exe",
"bazel-out/bin/rustc.exe",
"bazel-out/bin/build-script-runner.exe",
"bazel-out/bin/build-script.exe",
"bazel-out/bin/argument-comment-lint-driver.exe",
"bazel-out/bin/rustc_driver-nightly.dll",
"external/llvm/bin/clang.exe",
"external/llvm/bin/clang++.exe",
"external/llvm/bin/llvm-ar.exe",
"external/v8/mksnapshot.exe",
"external/bazel_tools/tools/test/test-setup.sh",
"bazel-out/windows/bin/tools/windows-toolchain/stack-protector-probe.exe.runfiles",
"bazel-out/runtimes/crt_objects_directory_windows",
"bazel-out/runtimes/mingw/mingw_crt_library_search_directory",
"bazel-out/runtimes/mingw/mingw_import_libraries_directory",
]
targets = [
{"id": 1, "label": "//tools/windows-toolchain:stack-protector-probe"},
{"id": 2, "label": "//codex-rs/v8-poc:v8-poc"},
{
"id": 3,
"label": "@@rules_rs++crate+crates__aws-lc-sys-0.39.0//:aws-lc-sys",
},
{"id": 4, "label": "@@rules_rs++crate+crates__ring-0.17.14//:ring"},
{
"id": 5,
"label": "@@rules_rs++crate+crates__zstd-sys-2.0.16+zstd.1.5.7//:zstd-sys",
},
{
"id": 6,
"label": "//codex-rs/codex-experimental-api-macros:codex-experimental-api-macros",
},
]
platform = "//:windows_x86_64_gnullvm"
execution_path = WINDOWS_ENV["CODEX_BAZEL_WINDOWS_EXECUTION_PATH"]
lint_path = rf"bazel-out/bin;{execution_path}"
actions = [
{
"mnemonic": "Rustc",
"targetId": 6,
"executionPlatform": platform,
"arguments": [
"bazel-out/bin/process-wrapper.exe",
"--",
"bazel-out/bin/rustc.exe",
"--crate-type=proc-macro",
"--target=x86_64-pc-windows-gnullvm",
],
},
{
"mnemonic": "CargoBuildScriptRun",
"targetId": 3,
"executionPlatform": platform,
"arguments": [
"bazel-out/bin/build-script-runner.exe",
"--script=bazel-out/bin/build-script.exe",
],
"environmentVariables": [
{"key": "PATH", "value": execution_path},
{"key": "HOST", "value": "x86_64-pc-windows-gnullvm"},
{"key": "TARGET", "value": "x86_64-pc-windows-gnullvm"},
{"key": "CC", "value": "external/llvm/bin/clang.exe"},
],
},
{
"mnemonic": "ArgumentCommentLint",
"targetId": 6,
"executionPlatform": platform,
"arguments": [
"bazel-out/bin/process-wrapper.exe",
"--",
"bazel-out/bin/argument-comment-lint-driver.exe",
"--target=x86_64-pc-windows-gnullvm",
],
"environmentVariables": [{"key": "PATH", "value": lint_path}],
},
{
"mnemonic": "CppCompile",
"targetId": 1,
"executionPlatform": platform,
"arguments": [
"external/llvm/bin/clang.exe",
"-target",
"x86_64-w64-windows-gnu",
"--sysroot=/dev/null",
"-fstack-protector-all",
],
},
{
"mnemonic": "CppLink",
"targetId": 1,
"executionPlatform": platform,
"arguments": [
"external/llvm/bin/clang++.exe",
"-target",
"x86_64-w64-windows-gnu",
"--sysroot=/dev/null",
"-fuse-ld=lld",
],
},
{
"mnemonic": "CppArchive",
"targetId": 4,
"executionPlatform": platform,
"arguments": ["external/llvm/bin/llvm-ar.exe", "rcsD"],
},
{
"mnemonic": "V8Mksnapshot",
"targetId": 2,
"executionPlatform": platform,
"arguments": ["external/v8/mksnapshot.exe"],
},
{
"mnemonic": "TestRunner",
"targetId": 1,
"executionPlatform": platform,
"arguments": [
"external/bazel_tools/tools/test/test-setup.sh",
"tools/windows-toolchain/stack-protector-probe.exe",
],
"environmentVariables": [
{
"key": "PATH",
"value": WINDOWS_ENV["CODEX_BAZEL_WINDOWS_TEST_PATH"],
}
],
},
]
for action in actions:
action["inputDepSetIds"] = [1]
return {
"actions": actions,
"artifacts": [
{"id": index, "pathFragmentId": index}
for index in range(1, len(artifact_paths) + 1)
],
"depSetOfFiles": [
{"id": 1, "directArtifactIds": list(range(1, len(artifact_paths) + 1))}
],
"pathFragments": [
{"id": index, "label": path} for index, path in enumerate(artifact_paths, 1)
],
"targets": targets,
}
class WindowsBazelHermeticityAuditTest(unittest.TestCase):
def audit_graph(self, graph: dict[str, object]) -> None:
with TemporaryDirectory() as temp_dir:
path = Path(temp_dir) / "aquery.json"
path.write_text(json.dumps(graph), encoding="utf-8")
audit.audit_aquery_files([path], WINDOWS_ENV)
def assert_graph_fails(self, graph: dict[str, object], message: str) -> None:
with self.assertRaisesRegex(audit.AuditFailure, message):
self.audit_graph(graph)
def test_accepts_declared_windows_gnullvm_toolchain(self) -> None:
self.audit_graph(valid_aquery())
def test_rejects_bare_tool_even_when_same_basename_is_declared(self) -> None:
graph = valid_aquery()
graph["actions"][5]["arguments"][0] = "llvm-ar.exe" # type: ignore[index]
self.assert_graph_fails(graph, "undeclared action executable")
def test_rejects_forbidden_host_toolchain_path(self) -> None:
graph = valid_aquery()
graph["actions"][3]["arguments"][0] = ( # type: ignore[index]
r"C:\Program Files\LLVM\bin\clang.exe"
)
self.assert_graph_fails(graph, "forbidden host path")
def test_rejects_forbidden_environment_even_when_empty(self) -> None:
graph = valid_aquery()
graph["actions"][1]["environmentVariables"].append( # type: ignore[index]
{"key": "VCToolsInstallDir"}
)
self.assert_graph_fails(graph, "forbidden environment variable")
def test_rejects_path_extension_for_build_actions(self) -> None:
graph = valid_aquery()
graph["actions"][1]["environmentVariables"][0]["value"] += ( # type: ignore[index]
r";C:\extra\bin"
)
self.assert_graph_fails(graph, "non-frozen PATH")
def test_accepts_declared_lint_directory_before_linux_substrate(self) -> None:
graph = valid_aquery()
graph["actions"][2]["environmentVariables"][0]["value"] = ( # type: ignore[index]
"bazel-out/bin;/usr/bin:/bin"
)
self.audit_graph(graph)
def test_rejects_forbidden_path_in_param_file(self) -> None:
graph = valid_aquery()
graph["actions"][3]["paramFiles"] = [ # type: ignore[index]
{
"execPath": "bazel-out/compile.params",
"arguments": [r"-IC:\Program Files\LLVM\include"],
}
]
self.assert_graph_fails(graph, "forbidden host path")
def test_rejects_undeclared_linker_in_param_file(self) -> None:
graph = valid_aquery()
graph["actions"][0]["paramFiles"] = [ # type: ignore[index]
{
"execPath": "bazel-out/rustc.params",
"arguments": ["--codegen=linker=clang.exe"],
}
]
self.assert_graph_fails(graph, "undeclared rust linker")
def test_rejects_msvc_execution_platform(self) -> None:
graph = valid_aquery()
graph["actions"][5]["executionPlatform"] = "//:windows_x86_64_msvc" # type: ignore[index]
self.assert_graph_fails(graph, "MSVC Windows platform")
def test_rejects_missing_v8_action_coverage(self) -> None:
graph = valid_aquery()
graph["actions"] = [ # type: ignore[index]
action
for action in graph["actions"] # type: ignore[union-attr]
if action["mnemonic"] != "V8Mksnapshot"
]
self.assert_graph_fails(graph, "missing V8Mksnapshot")
def source_tree(self, temp_dir: str, build_contents: str = "") -> Path:
root = Path(temp_dir)
(root / ".github/scripts").mkdir(parents=True)
(root / ".github/scripts/compute-bazel-windows-path.ps1").write_text(
"$windowsDir = $env:WINDIR\n$programFiles = $env:ProgramFiles\n"
"$localAppData = $env:LOCALAPPDATA\n$githubEnv = $env:GITHUB_ENV\n",
encoding="utf-8",
)
for wrapper in ("run-bazel-ci.sh", "run-bazel-query-ci.sh"):
(root / ".github/scripts" / wrapper).write_text(
'source "${script_dir}/sanitize-bazel-windows-environment.sh"\n'
"sanitize_bazel_windows_environment\n",
encoding="utf-8",
)
(root / ".github/scripts/sanitize-bazel-windows-environment.sh").write_text(
r'export BAZEL_SH="${ProgramFiles//\\//}/Git/usr/bin/bash.exe"' + "\n",
encoding="utf-8",
)
(root / "BUILD.bazel").write_text(build_contents, encoding="utf-8")
(root / "MODULE.bazel").write_text(
'new_local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")\n'
"new_local_repository(\n"
' name = "v8_targets",\n'
' build_file = "//third_party/v8:BUILD.bazel",\n'
' path = "third_party/v8",\n'
")\n",
encoding="utf-8",
)
(root / "rbe.bzl").write_text(
"rbe_platform_repository = repository_rule(\n", encoding="utf-8"
)
return root
def test_source_policy_accepts_fixed_policy(self) -> None:
with TemporaryDirectory() as temp_dir:
audit.audit_source_tree(self.source_tree(temp_dir))
def test_source_policy_rejects_tool_discovery(self) -> None:
with TemporaryDirectory() as temp_dir:
root = self.source_tree(temp_dir, 'tool = rctx.which("clang.exe")\n')
with self.assertRaisesRegex(audit.AuditFailure, "discovers a tool"):
audit.audit_source_tree(root)
def test_source_policy_rejects_target_skipping_anywhere(self) -> None:
with TemporaryDirectory() as temp_dir:
root = self.source_tree(temp_dir)
(root / "notes.md").write_text(
"--skip_incompatible_explicit_targets\n", encoding="utf-8"
)
with self.assertRaisesRegex(audit.AuditFailure, "incompatible targets"):
audit.audit_source_tree(root)
def test_source_policy_rejects_new_build_environment_forwarding(self) -> None:
with TemporaryDirectory() as temp_dir:
root = self.source_tree(temp_dir, 'args = ["--action_env=CC"]\n')
with self.assertRaisesRegex(audit.AuditFailure, "build-action environment"):
audit.audit_source_tree(root)
def test_source_policy_rejects_new_repository_environment_forwarding(self) -> None:
with TemporaryDirectory() as temp_dir:
root = self.source_tree(temp_dir, 'args = ["--repo_env=CC"]\n')
with self.assertRaisesRegex(audit.AuditFailure, "repository environment"):
audit.audit_source_tree(root)
if __name__ == "__main__":
unittest.main()
@@ -22,7 +22,46 @@ class RunBazelWithBuildBuddyTest(unittest.TestCase):
fake_bazel_impl.write_text(
"#!/usr/bin/env python3\n"
"import json\n"
"import os\n"
"import sys\n"
"if os.environ.get('RUNNER_OS') == 'Windows':\n"
" forbidden = {'AR', 'ARFLAGS', 'AS', 'BAZEL_LLVM', "
"'BAZEL_LLVM_COV', 'BAZEL_LLVM_PROFDATA', 'BAZEL_VC', "
"'BAZEL_VC_FULL_VERSION', 'BAZEL_VS', 'BAZEL_WINSDK_FULL_VERSION', "
"'CC', 'CFLAGS', 'CL', 'CMAKE', 'CPPFLAGS', 'CXX', 'CXXFLAGS', "
"'DEVENVDIR', 'DLLTOOL', 'EXTENSIONSDKDIR', 'FRAMEWORKDIR', "
"'FRAMEWORKDIR32', 'FRAMEWORKVERSION', 'FRAMEWORKVERSION32', "
"'GO', 'INCLUDE', 'LD', 'LDFLAGS', 'LIB', 'LIBPATH', 'LINK', "
"'MAKE', 'MESON', 'NASM', 'NETFXSDKDIR', 'NINJA', 'NM', "
"'OBJCOPY', 'OBJDUMP', 'PERL', 'PERL5LIB', 'PKG_CONFIG', "
"'PROTOC', 'RANLIB', 'RC', 'RUSTC', 'RUSTDOC', 'RUSTFLAGS', "
"'STRIP', 'UCRTVERSION', 'UNIVERSALCRTSDKDIR', 'USE_CLANG_CL', "
"'VCIDEINSTALLDIR', "
"'USE_CLANG_CL', 'VCINSTALLDIR', 'VISUALSTUDIOVERSION', 'VSINSTALLDIR', "
"'WINDRES', 'WINDOWSLIBPATH', 'YASM', '_CL_', '_LINK_'}\n"
" tool_affixes = ('AR', 'AS', 'CC', 'CFLAGS', 'CXX', "
"'CXXFLAGS', 'LD')\n"
" present = []\n"
" for name in os.environ:\n"
" upper = name.upper()\n"
" if (upper in forbidden or upper.startswith(('CMAKE_', "
"'LLVM_', 'MINGW', 'MSVC', 'VCTOOLS', 'VSCMD_', 'WINDOWSSDK')) "
"or (upper.startswith('CARGO_TARGET_') and upper.endswith('_LINKER')) "
"or any(upper.startswith(f'{tool}_') or upper.endswith(f'_{tool}') "
"for tool in tool_affixes)):\n"
" present.append(name)\n"
" if present:\n"
" raise SystemExit(f'compiler environment was not cleared: {present}')\n"
" normalized_path = os.environ.get('PATH', '').lower().replace('\\\\', '/')\n"
" forbidden_paths = ('/microsoft visual studio/', '/windows kits/', "
"'/microsoft sdks/', '/program files/llvm/', '/program files (x86)/llvm/', "
"'/msys64/', '/mingw32/', '/mingw64/')\n"
" if any(path in normalized_path for path in forbidden_paths):\n"
" raise SystemExit(f'compiler path was not cleared: {normalized_path}')\n"
" expected_bazel_sh = os.environ['ProgramFiles'].replace('\\\\', '/') "
"+ '/Git/usr/bin/bash.exe'\n"
" if os.environ.get('BAZEL_SH') != expected_bazel_sh:\n"
" raise SystemExit(f'BAZEL_SH is not frozen: {os.environ.get(\"BAZEL_SH\")}')\n"
"print(json.dumps(sys.argv[1:]))\n",
encoding="utf-8",
)
@@ -190,6 +229,7 @@ class RunBazelWithBuildBuddyTest(unittest.TestCase):
"CODEX_BAZEL_WINDOWS_EXECUTION_PATH": r"C:\substrate\bin",
"CODEX_BAZEL_WINDOWS_TEST_PATH": r"C:\runtime\bin",
"INCLUDE": r"C:\Visual Studio\include",
"ProgramFiles": r"C:\Program Files",
"RUNNER_OS": "Windows",
},
"--windows-hybrid-execution",
@@ -222,7 +262,12 @@ class RunBazelWithBuildBuddyTest(unittest.TestCase):
{
"CODEX_BAZEL_WINDOWS_EXECUTION_PATH": r"C:\substrate\bin",
"CODEX_BAZEL_WINDOWS_TEST_PATH": r"C:\runtime\bin",
"CARGO_TARGET_X86_64_PC_WINDOWS_GNULLVM_LINKER": "system-linker",
"cc_x86_64_pc_windows_gnullvm": "system-cc",
"DevEnvDir": r"C:\Visual Studio\Common7\IDE",
"INCLUDE": r"C:\Visual Studio\include",
"PATH": os.environ["PATH"] + r":C:\Program Files\LLVM\bin",
"ProgramFiles": r"C:\Program Files",
"RUNNER_OS": "Windows",
},
"--windows-cross-compile",
@@ -233,6 +278,9 @@ class RunBazelWithBuildBuddyTest(unittest.TestCase):
)
self.assertIn("--jobs=8", command)
self.assertIn(
"--shell_executable=C:/Program Files/Git/usr/bin/bash.exe", command
)
self.assertIn("--host_platform=//:local_windows", command)
self.assertIn("--platforms=//:windows_x86_64_gnullvm", command)
self.assertIn(
+7
View File
@@ -407,6 +407,13 @@ jobs:
-- \
"${bazel_targets[@]}"
- name: Audit Windows Bazel toolchain hermeticity
if: runner.os == 'Windows'
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
shell: bash
run: ./.github/scripts/run-bazel-windows-hermeticity-audit.sh
- name: Upload Bazel execution logs
if: always() && !cancelled()
continue-on-error: true
-17
View File
@@ -22,14 +22,6 @@ platform(
parents = ["@platforms//host"],
)
platform(
name = "local_windows_msvc",
constraint_values = [
"@rules_rs//rs/experimental/platforms/constraints:windows_msvc",
],
parents = ["@platforms//host"],
)
platform(
name = "windows_x86_64_gnullvm",
constraint_values = [
@@ -39,15 +31,6 @@ platform(
],
)
platform(
name = "windows_x86_64_msvc",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
"@rules_rs//rs/experimental/platforms/constraints:windows_msvc",
],
)
toolchain(
name = "windows_gnullvm_tests_on_gnullvm_host_toolchain",
exec_compatible_with = [
+2 -19
View File
@@ -108,7 +108,7 @@ single_version_override(
patches = [
"//patches:rules_rs_build_script_deps_annotation.patch",
"//patches:rules_rs_windows_gnullvm_exec.patch",
"//patches:rules_rs_windows_exec_linker.patch",
"//patches:rules_rs_windows_gnullvm_link_flags.patch",
],
version = "0.0.58",
)
@@ -117,12 +117,7 @@ rules_rust = use_extension("@rules_rs//rs/experimental:rules_rust.bzl", "rules_r
rules_rust.patch(
patches = [
"//patches:rules_rust_repository_set_replacement.patch",
"//patches:rules_rust_windows_gnullvm_build_script.patch",
"//patches:rules_rust_windows_exec_msvc_build_script_env.patch",
"//patches:rules_rust_windows_bootstrap_process_wrapper_linker.patch",
"//patches:rules_rust_windows_build_script_runner_paths.patch",
"//patches:rules_rust_windows_msvc_direct_link_args.patch",
"//patches:rules_rust_windows_process_wrapper_skip_temp_outputs.patch",
"//patches:rules_rust_windows_exec_bin_target.patch",
"//patches:rules_rust_windows_exec_std.patch",
"//patches:rules_rust_windows_exec_rustc_dev_rlib.patch",
@@ -220,17 +215,6 @@ bazel_dep(name = "zstd", version = "1.5.7")
crate.annotation(
crate = "zstd-sys",
gen_build_script = "on",
patch_args = ["-p1"],
patches = [
"//patches:zstd-sys_windows_msvc_include_dirs.patch",
],
)
crate.annotation(
crate = "ring",
patch_args = ["-p1"],
patches = [
"//patches:ring_windows_msvc_include_dirs.patch",
],
)
crate.annotation(
build_script_env = {
@@ -240,8 +224,7 @@ crate.annotation(
patch_args = ["-p1"],
patches = [
"//patches:aws-lc-sys_memcmp_check.patch",
"//patches:aws-lc-sys_windows_msvc_prebuilt_nasm.patch",
"//patches:aws-lc-sys_windows_msvc_memcmp_probe.patch",
"//patches:aws-lc-sys_windows_bazel_prebuilt_nasm.patch",
],
)
crate.annotation(
+1 -4
View File
@@ -2,10 +2,7 @@ load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "v8-poc",
crate_features = select({
"@rules_rs//rs/experimental/platforms/constraints:windows_msvc": [],
"//conditions:default": ["sandbox"],
}),
crate_features = ["sandbox"],
crate_name = "codex_v8_poc",
deps_extra = ["@crates//:v8"],
)
+2 -10
View File
@@ -6,8 +6,8 @@ load("//bazel/rules/testing:foreign_platform_binary.bzl", "foreign_platform_bina
load("//bazel/rules/testing:hermetic_test_python.bzl", "HermeticTestPythonInfo")
load("//bazel/rules/testing/wine:wine_runtime.bzl", "WINE_TEST_TARGET_COMPATIBLE_WITH", "wine_test_runtime")
# Match Cargo's Windows linker behavior so Bazel-built binaries and tests use
# the same stack reserve on both Windows ABIs and resolve UCRT imports on MSVC.
# Match Cargo's Windows linker behavior so gnullvm-built binaries and tests use
# the same stack reserve as Cargo builds.
WINDOWS_GNULLVM_RUSTC_LINK_FLAGS = [
"-C",
"link-arg=-Wl,--stack,8388608", # 8 MiB
@@ -15,14 +15,6 @@ WINDOWS_GNULLVM_RUSTC_LINK_FLAGS = [
WINDOWS_RUSTC_LINK_FLAGS = select({
"@rules_rs//rs/experimental/platforms/constraints:windows_gnullvm": WINDOWS_GNULLVM_RUSTC_LINK_FLAGS,
"@rules_rs//rs/experimental/platforms/constraints:windows_msvc": [
"-C",
"link-arg=/STACK:8388608", # 8 MiB
"-C",
"link-arg=/NODEFAULTLIB:libucrt.lib",
"-C",
"link-arg=ucrt.lib",
],
"//conditions:default": [],
})
+2 -9
View File
@@ -1,29 +1,22 @@
exports_files([
"abseil_windows_gnullvm_thread_identity.patch",
"aws-lc-sys_memcmp_check.patch",
"aws-lc-sys_windows_msvc_prebuilt_nasm.patch",
"aws-lc-sys_windows_msvc_memcmp_probe.patch",
"aws-lc-sys_windows_bazel_prebuilt_nasm.patch",
"llvm_rusty_v8_custom_libcxx.patch",
"llvm_windows_arm64_powl.patch",
"llvm_windows_mingw_compat.patch",
"rules_rust_windows_bootstrap_process_wrapper_linker.patch",
"rules_rust_windows_build_script_runner_paths.patch",
"rules_rust_windows_exec_bin_target.patch",
"rules_rust_windows_exec_std.patch",
"rules_rust_windows_process_wrapper_skip_temp_outputs.patch",
"rules_rust_windows_msvc_direct_link_args.patch",
"rules_rust_windows_gnullvm_build_script.patch",
"rules_cc_rusty_v8_custom_libcxx.patch",
"rules_rust_repository_set_replacement.patch",
"rules_rs_build_script_deps_annotation.patch",
"rules_rs_windows_gnullvm_exec.patch",
"rules_rs_windows_exec_linker.patch",
"rules_rs_windows_gnullvm_link_flags.patch",
"rusty_v8_prebuilt_out_dir.patch",
"ring_windows_msvc_include_dirs.patch",
"v8_bazel_rules.patch",
"v8_module_deps.patch",
"v8_source_portability.patch",
"webrtc-sys_hermetic_darwin_sysroot.patch",
"windows-link.patch",
"zstd-sys_windows_msvc_include_dirs.patch",
])
@@ -0,0 +1,67 @@
# What: make Bazel Windows builds use aws-lc-sys's integrity-pinned NASM
# fallback and retain Bazel's declared manifest path.
# Why: build scripts must not probe PATH for an undeclared assembler, and
# canonicalizing the runfiles path loses the declared source-tree layout.
diff --git a/builder/main.rs b/builder/main.rs
index 51a9bc1..0d6f99f 100644
--- a/builder/main.rs
+++ b/builder/main.rs
@@ -723,14 +723,30 @@ fn get_crate_cflags() -> Option<String> {
.or(optional_env_optional_crate_target("CFLAGS"))
}
+pub(crate) fn is_bazel_windows_build_script() -> bool {
+ if target_os() != "windows" {
+ return false;
+ }
+
+ if optional_env("RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER").is_some() {
+ return true;
+ }
+
+ let cargo_manifest_dir = cargo_env("CARGO_MANIFEST_DIR");
+ Path::new(&cargo_manifest_dir)
+ .components()
+ .any(|component| component.as_os_str() == "bazel-out")
+}
+
fn use_prebuilt_nasm() -> bool {
+ let use_prebuilt_for_bazel_windows = is_bazel_windows_build_script();
target_os() == "windows"
&& target_arch() == "x86_64"
&& !is_no_asm()
- && !test_nasm_command() // NASM not found in environment
&& Some(false) != allow_prebuilt_nasm() // not prevented by environment
&& !is_disable_prebuilt_nasm() // not prevented by feature
// permitted by environment or by feature
+ && (use_prebuilt_for_bazel_windows || !test_nasm_command())
&& (Some(true) == allow_prebuilt_nasm() || is_prebuilt_nasm())
}
@@ -753,6 +769,10 @@ fn has_pregenerated() -> bool {
}
fn test_nasm_command() -> bool {
+ if is_bazel_windows_build_script() {
+ return false;
+ }
+
let status = execute_command("nasm".as_ref(), &["-version".as_ref()]).status;
if !status {
emit_warning("NASM command not found or failed to execute.");
@@ -817,8 +837,12 @@ fn main() {
initialize();
prepare_cargo_cfg();
- let manifest_dir = current_dir();
- let manifest_dir = dunce::canonicalize(Path::new(&manifest_dir)).unwrap();
+ let manifest_dir = if is_bazel_windows_build_script() {
+ PathBuf::from(cargo_env("CARGO_MANIFEST_DIR"))
+ } else {
+ let manifest_dir = current_dir();
+ dunce::canonicalize(Path::new(&manifest_dir)).unwrap()
+ };
let prefix_str = prefix_string();
let prefix = if is_no_prefix() {
None
@@ -1,38 +0,0 @@
diff --git a/builder/cc_builder.rs b/builder/cc_builder.rs
--- a/builder/cc_builder.rs
+++ b/builder/cc_builder.rs
@@ -667,12 +667,24 @@
if cargo_env("HOST") != target() {
return;
}
+
+ let bazel_execroot = Self::bazel_execroot(self.manifest_dir.as_path());
+ if bazel_execroot.is_some() && target().ends_with("windows-msvc") {
+ // This probe guards a GCC memcmp miscompile. Under Bazel's
+ // hermetic Windows/MSVC build-script toolchain we drive clang with
+ // MinGW-flavored CFLAGS, which is good enough for compiling
+ // aws-lc but not for linking and executing this standalone test
+ // binary. Skip the probe in that configuration instead of failing
+ // the whole build script.
+ emit_warning("Skipping memcmp probe for Bazel windows-msvc build scripts.");
+ return;
+ }
let basename = "memcmp_invalid_stripped_check";
let exec_path = out_dir().join(basename);
let memcmp_build = cc::Build::default();
let memcmp_compiler = memcmp_build.get_compiler();
if !memcmp_compiler.is_like_clang() && !memcmp_compiler.is_like_gnu() {
// The logic below assumes a Clang or GCC compiler is in use
return;
}
@@ -701,7 +713,7 @@
memcmp_compile_args.push(flag.into());
}
}
- if let Some(execroot) = Self::bazel_execroot(self.manifest_dir.as_path()) {
+ if let Some(execroot) = bazel_execroot {
// In Bazel build-script sandboxes, `cc` can pass `bazel-out/...` args
// relative to the execroot while the process runs from elsewhere.
// Normalize those args to absolute paths so this check can still link.
@@ -1,141 +0,0 @@
diff --git a/builder/cc_builder.rs b/builder/cc_builder.rs
index 95cacb0..d5d814b 100644
--- a/builder/cc_builder.rs
+++ b/builder/cc_builder.rs
@@ -20,9 +20,10 @@ mod win_x86_64;
use crate::nasm_builder::NasmBuilder;
use crate::{
cargo_env, disable_jitter_entropy, emit_warning, env_name_for_target, env_var_to_bool,
- execute_command, get_crate_cc, get_crate_cflags, get_crate_cxx, is_no_asm, out_dir,
- requested_c_std, set_env_for_target, target, target_arch, target_env, target_os, target_vendor,
- test_clang_cl_command, CStdRequested, EnvGuard, OutputLibType,
+ execute_command, get_crate_cc, get_crate_cflags, get_crate_cxx,
+ is_bazel_windows_msvc_build_script, is_no_asm, out_dir, requested_c_std, set_env_for_target,
+ target, target_arch, target_env, target_os, target_vendor, test_clang_cl_command,
+ CStdRequested, EnvGuard, OutputLibType,
};
use std::cell::Cell;
use std::collections::HashMap;
@@ -769,6 +770,30 @@ impl CcBuilder {
}
}
+fn configure_windows_msvc_clang_cl() {
+ let should_use_clang_cl = target_os() == "windows"
+ && target_env() == "msvc"
+ && (target_arch() == "aarch64" || is_bazel_windows_msvc_build_script());
+ if !should_use_clang_cl {
+ return;
+ }
+
+ let clang_cl = if test_clang_cl_command() {
+ Some(String::from("clang-cl"))
+ } else {
+ let system_clang_cl = PathBuf::from(r"C:\Program Files\LLVM\bin\clang-cl.exe");
+ system_clang_cl.is_file().then(|| system_clang_cl.display().to_string())
+ };
+ if let Some(clang_cl) = clang_cl {
+ if get_crate_cc().is_none() {
+ set_env_for_target("CC", &clang_cl);
+ }
+ if get_crate_cxx().is_none() {
+ set_env_for_target("CXX", &clang_cl);
+ }
+ }
+}
+
impl crate::Builder for CcBuilder {
fn check_dependencies(&self) -> Result<(), String> {
if OutputLibType::Dynamic == self.output_lib_type {
@@ -784,18 +809,12 @@ impl crate::Builder for CcBuilder {
return Err("cc_builder for libssl not supported".to_string());
}
+ configure_windows_msvc_clang_cl();
Ok(())
}
fn build(&self) -> Result<(), String> {
- if target_os() == "windows"
- && target_arch() == "aarch64"
- && target_env() == "msvc"
- && get_crate_cc().is_none()
- && test_clang_cl_command()
- {
- set_env_for_target("CC", "clang-cl");
- }
+ configure_windows_msvc_clang_cl();
println!("cargo:root={}", self.out_dir.display());
let sources = crate::cc_builder::identify_sources();
diff --git a/builder/main.rs b/builder/main.rs
index 51a9bc1..e714ba4 100644
--- a/builder/main.rs
+++ b/builder/main.rs
@@ -723,14 +723,35 @@ fn get_crate_cflags() -> Option<String> {
.or(optional_env_optional_crate_target("CFLAGS"))
}
+pub(crate) fn is_bazel_windows_build_script() -> bool {
+ if target_os() != "windows" {
+ return false;
+ }
+
+ if optional_env("RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER").is_some() {
+ return true;
+ }
+
+ let cargo_manifest_dir = cargo_env("CARGO_MANIFEST_DIR");
+ let manifest_dir = Path::new(&cargo_manifest_dir);
+ manifest_dir
+ .components()
+ .any(|component| component.as_os_str() == "bazel-out")
+}
+
+pub(crate) fn is_bazel_windows_msvc_build_script() -> bool {
+ target().ends_with("windows-msvc") && is_bazel_windows_build_script()
+}
+
fn use_prebuilt_nasm() -> bool {
+ let use_prebuilt_for_bazel_windows = is_bazel_windows_build_script();
target_os() == "windows"
&& target_arch() == "x86_64"
&& !is_no_asm()
- && !test_nasm_command() // NASM not found in environment
&& Some(false) != allow_prebuilt_nasm() // not prevented by environment
&& !is_disable_prebuilt_nasm() // not prevented by feature
// permitted by environment or by feature
+ && (use_prebuilt_for_bazel_windows || !test_nasm_command())
&& (Some(true) == allow_prebuilt_nasm() || is_prebuilt_nasm())
}
@@ -817,8 +838,12 @@ fn main() {
initialize();
prepare_cargo_cfg();
- let manifest_dir = current_dir();
- let manifest_dir = dunce::canonicalize(Path::new(&manifest_dir)).unwrap();
+ let manifest_dir = if is_bazel_windows_build_script() {
+ PathBuf::from(cargo_env("CARGO_MANIFEST_DIR"))
+ } else {
+ let manifest_dir = current_dir();
+ dunce::canonicalize(Path::new(&manifest_dir)).unwrap()
+ };
let prefix_str = prefix_string();
let prefix = if is_no_prefix() {
None
diff --git a/builder/nasm_builder.rs b/builder/nasm_builder.rs
index cf1f5c8..f683274 100644
--- a/builder/nasm_builder.rs
+++ b/builder/nasm_builder.rs
@@ -57,7 +57,7 @@ impl NasmBuilder {
return vec![];
}
- if test_nasm_command() {
+ if test_nasm_command() && !use_prebuilt_nasm() {
for src in &self.files {
let obj_name = src
.file_name()
@@ -1,100 +0,0 @@
diff --git a/build.rs b/build.rs
index 9843ad8aa..573075489 100644
--- a/build.rs
+++ b/build.rs
@@ -346,7 +346,29 @@ fn ring_build_rs_main(c_root_dir: &Path, core_name_and_version: &str) {
// we want to optimize for minimizing the build tools required: No Perl,
// no nasm, etc.
let generated_dir = if !is_git {
- c_root_dir.join(PREGENERATED)
+ let pregenerated_dir = c_root_dir.join(PREGENERATED);
+ if target.os == WINDOWS
+ && target.env == "msvc"
+ && env::var_os("RULES_RUST_BAZEL_BUILD_SCRIPT_RUNNER").is_some()
+ {
+ let mut pending = vec![(pregenerated_dir.clone(), out_dir.clone())];
+ while let Some((src_dir, dst_dir)) = pending.pop() {
+ fs::create_dir_all(&dst_dir).unwrap();
+ for entry in fs::read_dir(&src_dir).unwrap() {
+ let entry = entry.unwrap();
+ let src_path = entry.path();
+ let dst_path = dst_dir.join(entry.file_name());
+ if entry.file_type().unwrap().is_dir() {
+ pending.push((src_path, dst_path));
+ } else {
+ fs::copy(&src_path, &dst_path).unwrap();
+ }
+ }
+ }
+ out_dir.clone()
+ } else {
+ pregenerated_dir
+ }
} else {
generate_sources_and_preassemble(
&out_dir,
@@ -569,6 +591,15 @@ fn configure_cc(c: &mut cc::Build, target: &Target, c_root_dir: &Path, include_d
let _ = c.include(c_root_dir.join("include"));
let _ = c.include(include_dir);
+ let _ = c.include(c_root_dir.join("third_party").join("fiat"));
+ if compiler.is_like_msvc() {
+ let _ = c.include(c_root_dir.join("crypto").join("curve25519"));
+ let _ = c.include(c_root_dir.join("crypto").join("limbs"));
+ let _ = c.include(c_root_dir.join("crypto").join("fipsmodule").join("aes"));
+ let _ = c.include(c_root_dir.join("crypto").join("fipsmodule").join("bn"));
+ let _ = c.include(c_root_dir.join("crypto").join("fipsmodule").join("ec"));
+ let _ = c.include(c_root_dir.join("crypto").join("poly1305"));
+ }
for f in cpp_flags(&compiler) {
let _ = c.flag(f);
}
diff --git a/crypto/curve25519/curve25519.c b/crypto/curve25519/curve25519.c
index 99d7d7fbb..2f69ad560 100644
--- a/crypto/curve25519/curve25519.c
+++ b/crypto/curve25519/curve25519.c
@@ -47,11 +47,11 @@
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
-#include "../../third_party/fiat/curve25519_64.h"
+#include "curve25519_64.h"
#elif defined(OPENSSL_64_BIT)
-#include "../../third_party/fiat/curve25519_64_msvc.h"
+#include "curve25519_64_msvc.h"
#else
-#include "../../third_party/fiat/curve25519_32.h"
+#include "curve25519_32.h"
#endif
diff --git a/crypto/curve25519/curve25519_64_adx.c b/crypto/curve25519/curve25519_64_adx.c
index 88964a9dd..b660f55f4 100644
--- a/crypto/curve25519/curve25519_64_adx.c
+++ b/crypto/curve25519/curve25519_64_adx.c
@@ -19,5 +19,5 @@
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wsign-conversion"
-#include "../../third_party/fiat/curve25519_64_adx.h"
+#include "curve25519_64_adx.h"
#endif
diff --git a/crypto/fipsmodule/ec/p256.c b/crypto/fipsmodule/ec/p256.c
index 0117916da..4224d1192 100644
--- a/crypto/fipsmodule/ec/p256.c
+++ b/crypto/fipsmodule/ec/p256.c
@@ -50,11 +50,11 @@
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
-#include "../../../third_party/fiat/p256_64.h"
+#include "p256_64.h"
#elif defined(OPENSSL_64_BIT)
-#include "../../../third_party/fiat/p256_64_msvc.h"
+#include "p256_64_msvc.h"
#else
-#include "../../../third_party/fiat/p256_32.h"
+#include "p256_32.h"
#endif
@@ -1,69 +0,0 @@
# What: use Rust's bundled direct linker for Windows MSVC exec toolchains and
# preserve the Windows stdlib link flags the stable wrapper was dropping.
# Scope: Windows-only linker metadata for the generated `rules_rs` toolchains;
# Windows gnullvm keeps using the selected hermetic C++ toolchain driver.
diff --git a/rs/experimental/toolchains/declare_rustc_toolchains.bzl b/rs/experimental/toolchains/declare_rustc_toolchains.bzl
--- a/rs/experimental/toolchains/declare_rustc_toolchains.bzl
+++ b/rs/experimental/toolchains/declare_rustc_toolchains.bzl
@@ -58,6 +58,8 @@ def declare_rustc_toolchains(
rust_toolchain(
name = rust_toolchain_name,
rust_doc = "{}rustdoc".format(rustc_repo_label),
+ linker = "{}rust-lld".format(rustc_repo_label) if exec_triple.abi == "msvc" else None,
+ linker_type = "direct" if exec_triple.abi == "msvc" else None,
rust_std = select(rust_std_select),
rustc = "{}rustc".format(rustc_repo_label),
cargo = "{}cargo".format(cargo_repo_label),
@@ -104,7 +106,20 @@ def declare_rustc_toolchains(
"@platforms//os:nixos": ["-ldl", "-lpthread"],
"@platforms//os:openbsd": ["-lpthread"],
"@platforms//os:ios": ["-lSystem", "-lobjc", "-Wl,-framework,Security", "-Wl,-framework,Foundation", "-lresolv"],
- # TODO: windows
+ "@rules_rs//rs/experimental/platforms/constraints:windows_gnullvm": [
+ "-ladvapi32",
+ "-lws2_32",
+ "-luserenv",
+ "-lbcrypt",
+ ],
+ "@rules_rs//rs/experimental/platforms/constraints:windows_msvc": [
+ "advapi32.lib",
+ "ws2_32.lib",
+ "userenv.lib",
+ "Bcrypt.lib",
+ "ucrt.lib",
+ ],
+ # TODO: other platforms
"//conditions:default": [],
}),
default_edition = edition,
diff --git a/rs/private/rustc_repository.bzl b/rs/private/rustc_repository.bzl
--- a/rs/private/rustc_repository.bzl
+++ b/rs/private/rustc_repository.bzl
@@ -7,10 +7,26 @@ load(
)
load(":rust_repository_utils.bzl", "RUST_REPOSITORY_COMMON_ATTR", "download_and_extract")
+
+_WINDOWS_EXEC_LINKER_BUILD = """
+filegroup(
+ name = "rust-lld",
+ srcs = ["bin/lld-link.exe"],
+ visibility = ["//visibility:public"],
+)
+"""
def _rustc_repository_impl(rctx):
exec_triple = triple(rctx.attr.triple)
download_and_extract(rctx, "rustc", "rustc", exec_triple)
build_content = [BUILD_for_compiler(exec_triple)]
+ if exec_triple.abi == "msvc":
+ # Keep the `lld-link.exe` basename so the multi-call Rust LLD binary
+ # selects its COFF driver without importing a linker from PATH.
+ rctx.symlink(
+ "lib/rustlib/{}/bin/rust-lld.exe".format(exec_triple.str),
+ "bin/lld-link.exe",
+ )
+ build_content.append(_WINDOWS_EXEC_LINKER_BUILD)
if includes_rust_analyzer_proc_macro_srv(rctx.attr.version, rctx.attr.iso_date):
build_content.append(BUILD_for_rust_analyzer_proc_macro_srv(exec_triple))
rctx.file("BUILD.bazel", "\n".join(build_content))
@@ -0,0 +1,23 @@
# What: preserve the Windows system-library link flags that the stable wrapper
# was dropping for gnullvm Rust toolchains.
# Scope: Windows-gnullvm-only linker metadata for generated `rules_rs`
# toolchains; the selected hermetic C++ toolchain remains the linker driver.
diff --git a/rs/experimental/toolchains/declare_rustc_toolchains.bzl b/rs/experimental/toolchains/declare_rustc_toolchains.bzl
--- a/rs/experimental/toolchains/declare_rustc_toolchains.bzl
+++ b/rs/experimental/toolchains/declare_rustc_toolchains.bzl
@@ -104,7 +104,13 @@ def declare_rustc_toolchains(
"@platforms//os:nixos": ["-ldl", "-lpthread"],
"@platforms//os:openbsd": ["-lpthread"],
"@platforms//os:ios": ["-lSystem", "-lobjc", "-Wl,-framework,Security", "-Wl,-framework,Foundation", "-lresolv"],
- # TODO: windows
+ "@rules_rs//rs/experimental/platforms/constraints:windows_gnullvm": [
+ "-ladvapi32",
+ "-lws2_32",
+ "-luserenv",
+ "-lbcrypt",
+ ],
+ # TODO: other platforms
"//conditions:default": [],
}),
default_edition = edition,
@@ -1,21 +0,0 @@
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -472,7 +472,17 @@
)
ld_is_direct_driver = False
- if not ld or toolchain.linker_preference == "rust":
+ # Windows MSVC Rust targets can inherit a GNU-flavored C++ linker path
+ # (`clang++`) alongside MSVC-style arguments. Prefer the toolchain linker
+ # there so both exec helpers and ordinary MSVC Rust targets link through
+ # the Windows direct linker instead.
+ use_windows_rust_linker = (
+ toolchain.target_os.startswith("windows") and
+ toolchain.target_abi == "msvc" and
+ toolchain.linker != None
+ )
+
+ if not ld or toolchain.linker_preference == "rust" or use_windows_rust_linker:
ld = toolchain.linker.path
ld_is_direct_driver = toolchain.linker_type == "direct"
@@ -1,180 +0,0 @@
diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl
--- a/cargo/private/cargo_build_script.bzl
+++ b/cargo/private/cargo_build_script.bzl
@@ -125,9 +125,14 @@ def _rewrite_windows_exec_msvc_cc_args(toolchain, args):
if toolchain.target_flag_value != toolchain.exec_triple.str or not toolchain.exec_triple.str.endswith("-pc-windows-msvc"):
return args
- rewritten = []
+ rewritten = [
+ "-target",
+ toolchain.target_flag_value,
+ ]
skip_next = False
- for arg in args:
+ for index in range(len(args)):
+ arg = args[index]
+
if skip_next:
skip_next = False
continue
@@ -142,21 +147,58 @@ def _rewrite_windows_exec_msvc_cc_args(toolchain, args):
if arg == "-nostdlibinc" or arg.startswith("--sysroot"):
continue
- if "mingw-w64-" in arg or "mingw_import_libraries_directory" in arg or "mingw_crt_library_search_directory" in arg:
+ if arg.startswith("-fstack-protector") or arg.startswith("-D_FORTIFY_SOURCE="):
+ continue
+
+ if arg == "-isystem" and index + 1 < len(args):
+ path = args[index + 1]
+ if "mingw-w64-" in path or "mingw_import_libraries_directory" in path or "mingw_crt_library_search_directory" in path:
+ skip_next = True
+ continue
+
+ rewritten.append(arg)
+
+ return rewritten
+
+def _rewrite_windows_exec_msvc_link_args(toolchain, args):
+ """Translate GNU-flavored link args when exec-side build scripts target Windows MSVC."""
+ if toolchain.target_flag_value != toolchain.exec_triple.str or not toolchain.exec_triple.str.endswith("-pc-windows-msvc"):
+ return args
+
+ rewritten = []
+ skip_next = False
+ for index in range(len(args)):
+ arg = args[index]
+
+ if skip_next:
+ skip_next = False
+ continue
+
+ if arg == "--sysroot":
+ skip_next = True
+ continue
+
+ if arg.startswith("--sysroot="):
continue
- if arg.startswith("-fstack-protector"):
+ if arg == "-L" and index + 1 < len(args):
+ path = args[index + 1]
+ if "mingw_import_libraries_directory" in path or "mingw_crt_library_search_directory" in path:
+ skip_next = True
+ continue
+ rewritten.extend([arg, path])
+ skip_next = True
continue
- if arg.startswith("-D_FORTIFY_SOURCE="):
+ if arg.startswith("-L") and (
+ "mingw_import_libraries_directory" in arg or
+ "mingw_crt_library_search_directory" in arg
+ ):
continue
rewritten.append(arg)
- return [
- "-target",
- toolchain.target_flag_value,
- ] + rewritten
+ return rewritten
def get_cc_compile_args_and_env(cc_toolchain, feature_configuration):
"""Gather cc environment variables from the given `cc_toolchain`
@@ -487,15 +529,23 @@ def _cargo_build_script_impl(ctx):
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
linker, _, link_args, linker_env = get_linker_and_args(ctx, "bin", toolchain, cc_toolchain, feature_configuration, None)
env.update(**linker_env)
- env["LD"] = linker
- env["LDFLAGS"] = " ".join(_pwd_flags(link_args))
+ use_windows_exec_msvc_path_tools = (
+ toolchain.target_flag_value == toolchain.exec_triple.str and
+ toolchain.exec_triple.str.endswith("-pc-windows-msvc")
+ )
+
+ if not use_windows_exec_msvc_path_tools:
+ env["LD"] = linker
+ link_args = _rewrite_windows_exec_msvc_link_args(toolchain, link_args)
+ env["LDFLAGS"] = " ".join(_pwd_flags(link_args))
+
+ # Defaults for cxx flags.
+ env["CFLAGS"] = ""
+ env["CXXFLAGS"] = ""
- # Defaults for cxx flags.
env["ARFLAGS"] = ""
- env["CFLAGS"] = ""
- env["CXXFLAGS"] = ""
fallback_tools = []
- if not cc_toolchain:
+ if not cc_toolchain and not use_windows_exec_msvc_path_tools:
fallbacks = {
"AR": "_fallback_ar",
"CC": "_fallback_cc",
@@ -521,34 +571,35 @@ def _cargo_build_script_impl(ctx):
toolchain_tools.append(cc_toolchain.all_files)
- env["CC"] = cc_common.get_tool_for_action(
- feature_configuration = feature_configuration,
- action_name = ACTION_NAMES.c_compile,
- )
- env["CXX"] = cc_common.get_tool_for_action(
- feature_configuration = feature_configuration,
- action_name = ACTION_NAMES.cpp_compile,
- )
- env["AR"] = cc_common.get_tool_for_action(
- feature_configuration = feature_configuration,
- action_name = ACTION_NAMES.cpp_link_static_library,
- )
-
- # Many C/C++ toolchains are missing an action_config for AR because
- # one was never included in the unix_cc_toolchain_config.
- if not env["AR"]:
- env["AR"] = cc_toolchain.ar_executable
-
- cc_c_args = _rewrite_windows_exec_msvc_cc_args(toolchain, cc_c_args)
- cc_cxx_args = _rewrite_windows_exec_msvc_cc_args(toolchain, cc_cxx_args)
- # Populate CFLAGS and CXXFLAGS that cc-rs relies on when building from source, in particular
- # to determine the deployment target when building for apple platforms (`macosx-version-min`
- # for example, itself derived from the `macos_minimum_os` Bazel argument).
- env["CFLAGS"] = " ".join(_pwd_flags(cc_c_args))
- env["CXXFLAGS"] = " ".join(_pwd_flags(cc_cxx_args))
- # It may be tempting to forward ARFLAGS, but cc-rs is opinionated enough
- # that doing so is more likely to hurt than help. If you need to change
- # ARFLAGS, make changes to cc-rs.
+ if not use_windows_exec_msvc_path_tools:
+ env["CC"] = cc_common.get_tool_for_action(
+ feature_configuration = feature_configuration,
+ action_name = ACTION_NAMES.c_compile,
+ )
+ env["CXX"] = cc_common.get_tool_for_action(
+ feature_configuration = feature_configuration,
+ action_name = ACTION_NAMES.cpp_compile,
+ )
+ env["AR"] = cc_common.get_tool_for_action(
+ feature_configuration = feature_configuration,
+ action_name = ACTION_NAMES.cpp_link_static_library,
+ )
+
+ # Many C/C++ toolchains are missing an action_config for AR because
+ # one was never included in the unix_cc_toolchain_config.
+ if not env["AR"]:
+ env["AR"] = cc_toolchain.ar_executable
+
+ cc_c_args = _rewrite_windows_exec_msvc_cc_args(toolchain, cc_c_args)
+ cc_cxx_args = _rewrite_windows_exec_msvc_cc_args(toolchain, cc_cxx_args)
+ # Populate CFLAGS and CXXFLAGS that cc-rs relies on when building from source, in particular
+ # to determine the deployment target when building for apple platforms (`macosx-version-min`
+ # for example, itself derived from the `macos_minimum_os` Bazel argument).
+ env["CFLAGS"] = " ".join(_pwd_flags(cc_c_args))
+ env["CXXFLAGS"] = " ".join(_pwd_flags(cc_cxx_args))
+ # It may be tempting to forward ARFLAGS, but cc-rs is opinionated enough
+ # that doing so is more likely to hurt than help. If you need to change
+ # ARFLAGS, make changes to cc-rs.
# Inform build scripts of rustc flags
# https://github.com/rust-lang/cargo/issues/9600
+3 -4
View File
@@ -1,8 +1,7 @@
# What: expose an exec-side Rust standard library alongside the target stdlib.
# Why: mixed Windows toolchains compile repo crates for `windows-gnullvm`, but
# exec-side helper binaries (build.rs, runners) may need the host MSVC stdlib.
# The toolchain sysroot must therefore carry both stdlib trees so rustc can
# resolve the correct one for each `--target`.
# Why: Windows-gnullvm targets can use either Windows-gnullvm or Linux-RBE exec
# configurations. Helper binaries (build.rs, runners, and proc macros) need the
# exec triple's stdlib, so the sysroot must carry both trees when they differ.
diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl
--- a/rust/toolchain.bzl
@@ -1,57 +0,0 @@
diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl
--- a/cargo/private/cargo_build_script.bzl
+++ b/cargo/private/cargo_build_script.bzl
@@ -120,6 +120,44 @@
executable = True,
)
+def _rewrite_windows_exec_msvc_cc_args(toolchain, args):
+ """Translate GNU-flavored cc args when exec-side build scripts target Windows MSVC."""
+ if toolchain.target_flag_value != toolchain.exec_triple.str or not toolchain.exec_triple.str.endswith("-pc-windows-msvc"):
+ return args
+
+ rewritten = []
+ skip_next = False
+ for arg in args:
+ if skip_next:
+ skip_next = False
+ continue
+
+ if arg == "-target":
+ skip_next = True
+ continue
+
+ if arg.startswith("-target=") or arg.startswith("--target="):
+ continue
+
+ if arg == "-nostdlibinc" or arg.startswith("--sysroot"):
+ continue
+
+ if "mingw-w64-" in arg or "mingw_import_libraries_directory" in arg or "mingw_crt_library_search_directory" in arg:
+ continue
+
+ if arg.startswith("-fstack-protector"):
+ continue
+
+ if arg.startswith("-D_FORTIFY_SOURCE="):
+ continue
+
+ rewritten.append(arg)
+
+ return [
+ "-target",
+ toolchain.target_flag_value,
+ ] + rewritten
+
def get_cc_compile_args_and_env(cc_toolchain, feature_configuration):
"""Gather cc environment variables from the given `cc_toolchain`
@@ -503,6 +541,8 @@
if not env["AR"]:
env["AR"] = cc_toolchain.ar_executable
+ cc_c_args = _rewrite_windows_exec_msvc_cc_args(toolchain, cc_c_args)
+ cc_cxx_args = _rewrite_windows_exec_msvc_cc_args(toolchain, cc_cxx_args)
# Populate CFLAGS and CXXFLAGS that cc-rs relies on when building from source, in particular
# to determine the deployment target when building for apple platforms (`macosx-version-min`
# for example, itself derived from the `macos_minimum_os` Bazel argument).
@@ -1,93 +0,0 @@
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -501,11 +501,41 @@
filtered_args.append(version)
# Keep library search path flags
+ elif processed_arg == "-L" and i + 1 < len(link_args):
+ path = link_args[i + 1]
+ if ld_is_direct_driver and toolchain.target_os == "windows":
+ skip_next = True
+ continue
+ filtered_args.extend([processed_arg, path])
+ skip_next = True
+
elif processed_arg.startswith("-L"):
+ if ld_is_direct_driver and toolchain.target_os == "windows":
+ continue
filtered_args.append(processed_arg)
# Keep sysroot flags (as single or two-part arguments)
elif processed_arg == "--sysroot" or processed_arg.startswith("--sysroot="):
+ if ld_is_direct_driver and toolchain.target_os == "windows":
+ if processed_arg == "--sysroot" and i + 1 < len(link_args):
+ skip_next = True
+ continue
filtered_args.append(processed_arg)
if processed_arg == "--sysroot" and i + 1 < len(link_args):
# Two-part argument, keep the next arg too
@@ -2256,8 +2256,10 @@
use_pic,
ambiguous_libs,
get_lib_name,
+ for_windows = False,
for_darwin = False,
- flavor_msvc = False):
+ flavor_msvc = False,
+ use_direct_driver = False):
"""_summary_
Args:
@@ -2310,6 +2312,11 @@
):
return [] if for_darwin else ["-lstatic=%s" % get_lib_name(artifact)]
+ if for_windows and use_direct_driver and not artifact.basename.endswith(".lib"):
+ return [
+ "-Clink-arg={}".format(artifact.path),
+ ]
+
if flavor_msvc:
return [
"-lstatic=%s" % get_lib_name(artifact),
@@ -2346,7 +2353,7 @@
])
elif include_link_flags:
get_lib_name = get_lib_name_for_windows if flavor_msvc else get_lib_name_default
- ret.extend(portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, flavor_msvc = flavor_msvc))
+ ret.extend(portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = True, flavor_msvc = flavor_msvc, use_direct_driver = use_direct_driver))
# Windows toolchains can inherit POSIX defaults like -pthread from C deps,
# which fails to link with the MinGW/LLD toolchain. Drop them here.
@@ -2522,17 +2529,25 @@
else:
# For all other crate types we want to link C++ runtime library statically
# (for example libstdc++.a or libc++.a).
+ runtime_libs = cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration)
args.add_all(
- cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
+ runtime_libs,
map_each = _get_dirname,
format_each = "-Lnative=%s",
)
if include_link_flags:
- args.add_all(
- cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
- map_each = get_lib_name,
- format_each = "-lstatic=%s",
- )
+ if toolchain.target_os == "windows" and use_direct_link_driver:
+ for runtime_lib in runtime_libs.to_list():
+ if runtime_lib.basename.endswith(".lib"):
+ args.add(get_lib_name(runtime_lib), format = "-lstatic=%s")
+ else:
+ args.add(runtime_lib.path, format = "--codegen=link-arg=%s")
+ else:
+ args.add_all(
+ runtime_libs,
+ map_each = get_lib_name,
+ format_each = "-lstatic=%s",
+ )
def _get_dirname(file):
"""A helper function for `_add_native_link_flags`.
@@ -1,15 +0,0 @@
--- a/util/process_wrapper/main.rs
+++ b/util/process_wrapper/main.rs
@@ -213,6 +213,12 @@ fn consolidate_dependency_search_paths(
let file_name_lower = file_name
.to_string_lossy()
.to_ascii_lowercase();
+ if file_name_lower.contains(".tmp") || file_name_lower.ends_with(".rcgu.o") {
+ // MSVC link actions can leave transient `*.exe.tmp*` and
+ // per-codegen-unit `*.rcgu.o` outputs next to the final binary.
+ // Those are not stable linker search-path inputs.
+ continue;
+ }
if !seen.insert(file_name_lower) {
continue;
}
@@ -1,41 +0,0 @@
diff --git a/build.rs b/build.rs
index 912a3bf..e078f0e 100644
--- a/build.rs
+++ b/build.rs
@@ -85,6 +85,7 @@ fn flag_if_supported_with_fallbacks(config: &mut cc::Build, flags: &[&str]) {
}
fn compile_zstd() {
+ let target = env::var("TARGET").unwrap_or_default();
let mut config = cc::Build::new();
// Search the following directories for C files to add to the compilation.
@@ -164,6 +165,16 @@ fn compile_zstd() {
// Some extra parameters
config.include("zstd/lib/");
config.include("zstd/lib/common");
+ if target.contains("msvc") {
+ config.include("zstd/lib/compress");
+ config.include("zstd/lib/decompress");
+ #[cfg(feature = "zdict_builder")]
+ config.include("zstd/lib/dictBuilder");
+ #[cfg(feature = "legacy")]
+ config.include("zstd/lib/legacy");
+ #[cfg(feature = "seekable")]
+ config.include("zstd/contrib/seekable_format");
+ }
config.warnings(false);
config.define("ZSTD_LIB_DEPRECATED", Some("0"));
@@ -210,7 +221,9 @@ fn compile_zstd() {
// Hide symbols from resulting library,
// so we can be used with another zstd-linking lib.
- // See https://github.com/gyscos/zstd-rs/issues/58
- config.flag("-fvisibility=hidden");
+ // See https://github.com/gyscos/zstd-rs/issues/58.
+ if !target.contains("msvc") {
+ config.flag("-fvisibility=hidden");
+ }
config.define("XXH_PRIVATE_API", Some(""));
config.define("ZSTDLIB_VISIBILITY", Some(""));