diff --git a/.github/scripts/audit_bazel_windows_hermeticity.py b/.github/scripts/audit_bazel_windows_hermeticity.py new file mode 100755 index 0000000000..45cc6a90db --- /dev/null +++ b/.github/scripts/audit_bazel_windows_hermeticity.py @@ -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 ''}" + 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} [ ...]", + 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:])) diff --git a/.github/scripts/run-bazel-ci.sh b/.github/scripts/run-bazel-ci.sh index 79aa630172..c2b53bd652 100755 --- a/.github/scripts/run-bazel-ci.sh +++ b/.github/scripts/run-bazel-ci.sh @@ -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 diff --git a/.github/scripts/run-bazel-query-ci.sh b/.github/scripts/run-bazel-query-ci.sh index 39fa04c47d..6868572af7 100755 --- a/.github/scripts/run-bazel-query-ci.sh +++ b/.github/scripts/run-bazel-query-ci.sh @@ -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 diff --git a/.github/scripts/run-bazel-windows-hermeticity-audit.sh b/.github/scripts/run-bazel-windows-hermeticity-audit.sh new file mode 100755 index 0000000000..75581e61f3 --- /dev/null +++ b/.github/scripts/run-bazel-windows-hermeticity-audit.sh @@ -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}" diff --git a/.github/scripts/sanitize-bazel-windows-environment.sh b/.github/scripts/sanitize-bazel-windows-environment.sh new file mode 100755 index 0000000000..50cc503d3a --- /dev/null +++ b/.github/scripts/sanitize-bazel-windows-environment.sh @@ -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" +} diff --git a/.github/scripts/test_audit_bazel_windows_hermeticity.py b/.github/scripts/test_audit_bazel_windows_hermeticity.py new file mode 100755 index 0000000000..1be7e4e9f1 --- /dev/null +++ b/.github/scripts/test_audit_bazel_windows_hermeticity.py @@ -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() diff --git a/.github/scripts/test_run_bazel_with_buildbuddy.py b/.github/scripts/test_run_bazel_with_buildbuddy.py index 0c76835de2..1ea73526c6 100644 --- a/.github/scripts/test_run_bazel_with_buildbuddy.py +++ b/.github/scripts/test_run_bazel_with_buildbuddy.py @@ -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( diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index adedb6f22c..9d54cfc86e 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -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 diff --git a/BUILD.bazel b/BUILD.bazel index 6e913825cb..ac5698d66c 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -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 = [ diff --git a/MODULE.bazel b/MODULE.bazel index eb284fd339..245542f3dc 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -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( diff --git a/codex-rs/v8-poc/BUILD.bazel b/codex-rs/v8-poc/BUILD.bazel index a1e875c5d8..883cdc9392 100644 --- a/codex-rs/v8-poc/BUILD.bazel +++ b/codex-rs/v8-poc/BUILD.bazel @@ -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"], ) diff --git a/defs.bzl b/defs.bzl index 7b423b6329..d4c527a9d5 100644 --- a/defs.bzl +++ b/defs.bzl @@ -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": [], }) diff --git a/patches/BUILD.bazel b/patches/BUILD.bazel index 8189f2d058..24e70e1a16 100644 --- a/patches/BUILD.bazel +++ b/patches/BUILD.bazel @@ -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", ]) diff --git a/patches/aws-lc-sys_windows_bazel_prebuilt_nasm.patch b/patches/aws-lc-sys_windows_bazel_prebuilt_nasm.patch new file mode 100644 index 0000000000..d49301ad05 --- /dev/null +++ b/patches/aws-lc-sys_windows_bazel_prebuilt_nasm.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 { + .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 diff --git a/patches/aws-lc-sys_windows_msvc_memcmp_probe.patch b/patches/aws-lc-sys_windows_msvc_memcmp_probe.patch deleted file mode 100644 index d244e9418f..0000000000 --- a/patches/aws-lc-sys_windows_msvc_memcmp_probe.patch +++ /dev/null @@ -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. diff --git a/patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch b/patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch deleted file mode 100644 index c0b129d1ca..0000000000 --- a/patches/aws-lc-sys_windows_msvc_prebuilt_nasm.patch +++ /dev/null @@ -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 { - .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() diff --git a/patches/ring_windows_msvc_include_dirs.patch b/patches/ring_windows_msvc_include_dirs.patch deleted file mode 100644 index 682252011f..0000000000 --- a/patches/ring_windows_msvc_include_dirs.patch +++ /dev/null @@ -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 - - diff --git a/patches/rules_rs_windows_exec_linker.patch b/patches/rules_rs_windows_exec_linker.patch deleted file mode 100644 index 3a93c01d46..0000000000 --- a/patches/rules_rs_windows_exec_linker.patch +++ /dev/null @@ -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)) diff --git a/patches/rules_rs_windows_gnullvm_link_flags.patch b/patches/rules_rs_windows_gnullvm_link_flags.patch new file mode 100644 index 0000000000..02ae9e2b18 --- /dev/null +++ b/patches/rules_rs_windows_gnullvm_link_flags.patch @@ -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, diff --git a/patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch b/patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch deleted file mode 100644 index 842098bb3a..0000000000 --- a/patches/rules_rust_windows_bootstrap_process_wrapper_linker.patch +++ /dev/null @@ -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" - diff --git a/patches/rules_rust_windows_exec_msvc_build_script_env.patch b/patches/rules_rust_windows_exec_msvc_build_script_env.patch deleted file mode 100644 index 38ead6a580..0000000000 --- a/patches/rules_rust_windows_exec_msvc_build_script_env.patch +++ /dev/null @@ -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 diff --git a/patches/rules_rust_windows_exec_std.patch b/patches/rules_rust_windows_exec_std.patch index e8e4c4e55c..3607526ac0 100644 --- a/patches/rules_rust_windows_exec_std.patch +++ b/patches/rules_rust_windows_exec_std.patch @@ -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 diff --git a/patches/rules_rust_windows_gnullvm_build_script.patch b/patches/rules_rust_windows_gnullvm_build_script.patch deleted file mode 100644 index dad436cd9f..0000000000 --- a/patches/rules_rust_windows_gnullvm_build_script.patch +++ /dev/null @@ -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). diff --git a/patches/rules_rust_windows_msvc_direct_link_args.patch b/patches/rules_rust_windows_msvc_direct_link_args.patch deleted file mode 100644 index fcf8a487be..0000000000 --- a/patches/rules_rust_windows_msvc_direct_link_args.patch +++ /dev/null @@ -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`. diff --git a/patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch b/patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch deleted file mode 100644 index 454e9d154f..0000000000 --- a/patches/rules_rust_windows_process_wrapper_skip_temp_outputs.patch +++ /dev/null @@ -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; - } diff --git a/patches/zstd-sys_windows_msvc_include_dirs.patch b/patches/zstd-sys_windows_msvc_include_dirs.patch deleted file mode 100644 index c45a360a46..0000000000 --- a/patches/zstd-sys_windows_msvc_include_dirs.patch +++ /dev/null @@ -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(""));