ci: add Windows cargo-xwin release PoC

This commit is contained in:
Michael Bolin
2026-04-30 23:59:27 -07:00
parent 719431da6e
commit 57451d6bd7
9 changed files with 342 additions and 51 deletions

View File

@@ -153,6 +153,18 @@ common:ci-macos --config=remote
common:ci-macos --strategy=remote
common:ci-macos --strategy=TestRunner=darwin-sandbox,local
# On Windows, use Linux remote execution for build actions but keep test actions
# on the Windows runner so Bazel's normal test sharding and flaky-test retries
# still run against Windows binaries.
common:ci-windows-cross --config=ci-windows
common:ci-windows-cross --build_metadata=TAG_windows_cross_compile=true
common:ci-windows-cross --config=remote
common:ci-windows-cross --strategy=remote
common:ci-windows-cross --strategy=TestRunner=local
common:ci-windows-cross --local_test_jobs=8
common:ci-windows-cross --platforms=//:local_windows
common:ci-windows-cross --extra_execution_platforms=//:rbe,//:local_windows
# Linux-only V8 CI config.
common:ci-v8 --config=ci
common:ci-v8 --build_metadata=TAG_workflow=v8

View File

@@ -5,9 +5,9 @@ tool entries, such as Maven, that can change independently of this repo and
cause avoidable cache misses.
This script derives a smaller, cache-stable PATH that keeps the Windows
toolchain entries Bazel-backed CI tasks need: MSVC and Windows SDK paths, Git,
PowerShell, Node, Python, DotSlash, and the standard Windows system
directories.
toolchain entries Bazel-backed CI tasks need: MSVC and Windows SDK paths,
MinGW runtime DLL paths for gnullvm-built tests, Git, PowerShell, Node, Python,
DotSlash, and the standard Windows system directories.
`setup-bazel-ci` runs this after exporting the MSVC environment, and the script
publishes the result via `GITHUB_ENV` as `CODEX_BAZEL_WINDOWS_PATH` so later
steps can pass that explicit PATH to Bazel.
@@ -49,6 +49,8 @@ foreach ($pathEntry in ($env:PATH -split ';')) {
$pathEntry -like '*Microsoft Visual Studio*' -or
$pathEntry -like '*Windows Kits*' -or
$pathEntry -like '*Microsoft SDKs*' -or
$pathEntry -eq 'C:\mingw64\bin' -or
$pathEntry -like 'C:\msys64\*\bin' -or
$pathEntry -like 'C:\Program Files\Git\*' -or
$pathEntry -like 'C:\Program Files\PowerShell\*' -or
$pathEntry -like 'C:\hostedtoolcache\windows\node\*' -or
@@ -85,6 +87,12 @@ if ($pwshCommand) {
Add-StablePathEntry (Split-Path $pwshCommand.Source -Parent)
}
foreach ($mingwPath in @('C:\mingw64\bin', 'C:\msys64\mingw64\bin', 'C:\msys64\ucrt64\bin')) {
if (Test-Path $mingwPath) {
Add-StablePathEntry $mingwPath
}
}
if ($windowsAppsPath) {
Add-StablePathEntry $windowsAppsPath
}

View File

@@ -6,6 +6,7 @@ print_failed_bazel_test_logs=0
print_failed_bazel_action_summary=0
remote_download_toplevel=0
windows_msvc_host_platform=0
windows_cross_compile=0
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -25,6 +26,10 @@ while [[ $# -gt 0 ]]; do
windows_msvc_host_platform=1
shift
;;
--windows-cross-compile)
windows_cross_compile=1
shift
;;
--)
shift
break
@@ -37,7 +42,7 @@ while [[ $# -gt 0 ]]; do
done
if [[ $# -eq 0 ]]; then
echo "Usage: $0 [--print-failed-test-logs] [--print-failed-action-summary] [--remote-download-toplevel] [--windows-msvc-host-platform] -- <bazel args> -- <targets>" >&2
echo "Usage: $0 [--print-failed-test-logs] [--print-failed-action-summary] [--remote-download-toplevel] [--windows-msvc-host-platform] [--windows-cross-compile] -- <bazel args> -- <targets>" >&2
exit 1
fi
@@ -61,7 +66,11 @@ case "${RUNNER_OS:-}" in
ci_config=ci-macos
;;
Windows)
ci_config=ci-windows
if [[ $windows_cross_compile -eq 1 ]]; then
ci_config=ci-windows-cross
else
ci_config=ci-windows
fi
;;
esac
@@ -105,8 +114,8 @@ print_bazel_test_log_tails() {
while IFS= read -r target; do
failed_targets+=("$target")
done < <(
grep -E '^FAIL: //' "$console_log" \
| sed -E 's#^FAIL: (//[^ ]+).*#\1#' \
grep -E '^(FAIL: //|ERROR: .* Testing //)' "$console_log" \
| sed -E 's#^FAIL: (//[^ ]+).*#\1#; s#^ERROR: .* Testing (//[^ ]+) failed:.*#\1#' \
| sort -u
)
@@ -269,6 +278,13 @@ if [[ $remote_download_toplevel -eq 1 ]]; then
post_config_bazel_args+=(--remote_download_toplevel)
fi
if [[ "${RUNNER_OS:-}" == "Windows" && $windows_cross_compile -eq 1 && -z "${BUILDBUDDY_API_KEY:-}" ]]; then
# The Windows cross-compile config depends on remote execution. Fork PRs do
# not receive the BuildBuddy secret, so fall back to the existing local build
# shape and keep its lower concurrency cap.
post_config_bazel_args+=(--jobs=8)
fi
if [[ -n "${BAZEL_REPO_CONTENTS_CACHE:-}" ]]; then
# Windows self-hosted runners can run multiple Bazel jobs concurrently. Give
# each job its own repo contents cache so they do not fight over the shared
@@ -287,37 +303,78 @@ if [[ -n "${CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR:-}" ]]; then
fi
if [[ "${RUNNER_OS:-}" == "Windows" ]]; then
windows_action_env_vars=(
INCLUDE
LIB
LIBPATH
UCRTVersion
UniversalCRTSdkDir
VCINSTALLDIR
VCToolsInstallDir
WindowsLibPath
WindowsSdkBinPath
WindowsSdkDir
WindowsSDKLibVersion
WindowsSDKVersion
)
pass_windows_build_env=1
if [[ $windows_cross_compile -eq 1 && -n "${BUILDBUDDY_API_KEY:-}" ]]; then
# Remote build actions execute on Linux RBE workers. Passing the Windows
# runner's build environment there makes Bazel genrules try to execute
# C:\Program Files\Git\usr\bin\bash.exe on Linux.
pass_windows_build_env=0
fi
for env_var in "${windows_action_env_vars[@]}"; do
if [[ -n "${!env_var:-}" ]]; then
post_config_bazel_args+=("--action_env=${env_var}" "--host_action_env=${env_var}")
fi
done
if [[ $pass_windows_build_env -eq 1 ]]; then
windows_action_env_vars=(
INCLUDE
LIB
LIBPATH
UCRTVersion
UniversalCRTSdkDir
VCINSTALLDIR
VCToolsInstallDir
WindowsLibPath
WindowsSdkBinPath
WindowsSdkDir
WindowsSDKLibVersion
WindowsSDKVersion
)
for env_var in "${windows_action_env_vars[@]}"; do
if [[ -n "${!env_var:-}" ]]; then
post_config_bazel_args+=("--action_env=${env_var}" "--host_action_env=${env_var}")
fi
done
fi
if [[ -z "${CODEX_BAZEL_WINDOWS_PATH:-}" ]]; then
echo "CODEX_BAZEL_WINDOWS_PATH must be set for Windows Bazel CI." >&2
exit 1
fi
post_config_bazel_args+=(
"--action_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}"
"--host_action_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}"
"--test_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}"
)
if [[ $pass_windows_build_env -eq 1 ]]; then
post_config_bazel_args+=(
"--action_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}"
"--host_action_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}"
)
elif [[ $windows_cross_compile -eq 1 ]]; then
# Remote build actions run on Linux RBE workers. Give their shell snippets
# a Linux PATH while preserving CODEX_BAZEL_WINDOWS_PATH below for local
# Windows test execution.
post_config_bazel_args+=(
"--action_env=PATH=/usr/bin:/bin"
"--host_action_env=PATH=/usr/bin:/bin"
)
fi
if [[ $windows_cross_compile -eq 1 ]]; then
# Use a shell path that also exists on Linux RBE workers. Bazel runs local
# Windows actions from its execroot, and some Windows path normalization
# treats `/usr/bin/bash` as rooted on either the workspace or system drive.
# Put a Git Bash usr/bin shim on both drives so local TestRunner actions
# and Linux RBE actions can share one `--shell_executable` value.
bash_path="$(command -v bash)"
bash_dir="$(dirname "$bash_path")"
workspace_drive="$(pwd -W | sed 's#^\([A-Za-z]\):.*#\1#')"
system_drive="${SYSTEMDRIVE:-C:}"
system_drive="${system_drive%:}"
for drive in "${workspace_drive,,}" "${system_drive,,}"; do
portable_shell_dir="/${drive}/usr/bin"
mkdir -p "$portable_shell_dir"
cp -R "$bash_dir"/. "$portable_shell_dir/"
cp -f "$portable_shell_dir/bash.exe" "$portable_shell_dir/bash"
"$portable_shell_dir/bash" -lc 'command -v dirname >/dev/null'
done
post_config_bazel_args+=("--shell_executable=/usr/bin/bash")
fi
post_config_bazel_args+=("--test_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}")
fi
bazel_console_log="$(mktemp)"

View File

@@ -22,8 +22,6 @@ jobs:
# true test failure in a rust_test() marked "flaky" that gets run 3x.
# In that case, extra time generally does not give us more signal.
#
# Ultimately we need true distributed builds (e.g.,
# https://www.buildbuddy.io/docs/rbe-setup/) to speed things up.
timeout-minutes: 30
strategy:
fail-fast: false
@@ -53,7 +51,7 @@ jobs:
runs-on: ${{ matrix.os }}
# Configure a human readable name for each job
name: Local Bazel build on ${{ matrix.os }} for ${{ matrix.target }}
name: Bazel test on ${{ matrix.os }} for ${{ matrix.target }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -100,8 +98,11 @@ jobs:
--build_metadata=COMMIT_SHA=${GITHUB_SHA}
)
if [[ "${RUNNER_OS}" == "Windows" ]]; then
bazel_wrapper_args+=(--windows-msvc-host-platform)
bazel_test_args+=(--jobs=8)
bazel_wrapper_args+=(
--windows-msvc-host-platform
--windows-cross-compile
--remote-download-toplevel
)
fi
./.github/scripts/run-bazel-ci.sh \

View File

@@ -0,0 +1,182 @@
name: rust-release-windows-cross-compile-poc
on:
pull_request:
paths:
- ".github/workflows/rust-release-windows-cross-compile-poc.yml"
workflow_dispatch:
inputs:
release_lto:
description: Cargo release LTO mode
type: choice
default: thin
options:
- thin
- fat
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
CARGO_PROFILE_RELEASE_LTO: ${{ github.event_name == 'workflow_dispatch' && inputs.release_lto || 'thin' }}
CARGO_TERM_COLOR: always
CARGO_XWIN_VERSION: 0.22.0
WINDOWS_TARGET: x86_64-pc-windows-msvc
XWIN_ARCH: x86_64
XWIN_CACHE_DIR: ${{ github.workspace }}/.xwin-cache
XWIN_VARIANT: desktop
jobs:
build-windows-binaries:
name: Cross-compile Windows release binaries
runs-on: ubuntu-24.04
timeout-minutes: 90
defaults:
run:
working-directory: codex-rs
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Print runner specs
shell: bash
run: |
set -euo pipefail
cpu_model="$(lscpu | awk -F: '/Model name/ {gsub(/^[ \t]+/, "", $2); print $2; exit}')"
total_ram="$(awk '/MemTotal/ {printf "%.1f GiB\n", $2 / 1024 / 1024}' /proc/meminfo)"
echo "Runner: ${RUNNER_NAME:-unknown}"
echo "OS: $(uname -a)"
echo "CPU model: ${cpu_model}"
echo "Logical CPUs: $(nproc)"
echo "Total RAM: ${total_ram}"
echo "Disk usage:"
df -h .
- name: Install cross-linker dependencies
shell: bash
run: |
set -euo pipefail
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
clang \
cmake \
lld \
llvm \
nasm \
ninja-build \
pkg-config \
strace
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
targets: ${{ env.WINDOWS_TARGET }}
- name: Compute cache key inputs
id: lockhash
shell: bash
run: |
set -euo pipefail
echo "lock_hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "toolchain_hash=$(sha256sum rust-toolchain.toml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Restore cargo-xwin cache
id: cache_cargo_xwin_restore
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
${{ env.XWIN_CACHE_DIR }}
key: cargo-xwin-${{ runner.os }}-${{ env.WINDOWS_TARGET }}-${{ env.CARGO_XWIN_VERSION }}-${{ steps.lockhash.outputs.lock_hash }}-${{ steps.lockhash.outputs.toolchain_hash }}
restore-keys: |
cargo-xwin-${{ runner.os }}-${{ env.WINDOWS_TARGET }}-${{ env.CARGO_XWIN_VERSION }}-
- name: Install cargo-xwin
shell: bash
run: |
set -euo pipefail
if ! cargo xwin --version 2>/dev/null | grep -F "$CARGO_XWIN_VERSION" >/dev/null; then
cargo install cargo-xwin --locked --version "$CARGO_XWIN_VERSION"
fi
cargo xwin --version
- name: Cache MSVC CRT and Windows SDK
shell: bash
run: cargo xwin cache xwin
- name: Build codex-responses-api-proxy
shell: bash
run: |
set -euo pipefail
cargo xwin build \
--release \
--target "$WINDOWS_TARGET" \
--timings \
--bin codex-responses-api-proxy
- name: Build codex-app-server
shell: bash
run: |
set -euo pipefail
cargo xwin build \
--release \
--target "$WINDOWS_TARGET" \
--timings \
--bin codex-app-server
- name: Build codex
shell: bash
run: |
set -euo pipefail
cargo xwin build \
--release \
--target "$WINDOWS_TARGET" \
--timings \
--bin codex
- name: Verify Windows artifacts
shell: bash
run: |
set -euo pipefail
for binary in codex-responses-api-proxy codex-app-server codex; do
path="target/${WINDOWS_TARGET}/release/${binary}.exe"
ls -lh "$path"
file "$path"
done
- name: Upload Cargo timings
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: cargo-timings-rust-release-windows-cross-compile-poc-${{ env.WINDOWS_TARGET }}-primary-app-server
path: codex-rs/target/**/cargo-timings/cargo-timing.html
if-no-files-found: warn
- name: Upload Windows binaries
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: windows-cross-compile-poc-${{ env.WINDOWS_TARGET }}-primary-app-server
path: |
codex-rs/target/${{ env.WINDOWS_TARGET }}/release/codex-responses-api-proxy.exe
codex-rs/target/${{ env.WINDOWS_TARGET }}/release/codex-app-server.exe
codex-rs/target/${{ env.WINDOWS_TARGET }}/release/codex.exe
if-no-files-found: error
- name: Save cargo-xwin cache
if: always() && !cancelled() && steps.cache_cargo_xwin_restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
${{ env.XWIN_CACHE_DIR }}
key: cargo-xwin-${{ runner.os }}-${{ env.WINDOWS_TARGET }}-${{ env.CARGO_XWIN_VERSION }}-${{ steps.lockhash.outputs.lock_hash }}-${{ steps.lockhash.outputs.toolchain_hash }}

View File

@@ -30,6 +30,24 @@ platform(
parents = ["@platforms//host"],
)
platform(
name = "windows_x86_64_gnullvm",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
"@rules_rs//rs/experimental/platforms/constraints:windows_gnullvm",
],
)
platform(
name = "windows_x86_64_msvc",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
"@rules_rs//rs/experimental/platforms/constraints:windows_msvc",
],
)
alias(
name = "rbe",
actual = "@rbe_platform",

View File

@@ -133,7 +133,7 @@ index 85f31b7..7314584 100644
],
outs = [
"include/inspector/Debugger.h",
@@ -4426,15 +4426,18 @@ genrule(
@@ -4426,15 +4426,19 @@ genrule(
"src/inspector/protocol/Schema.cpp",
"src/inspector/protocol/Schema.h",
],
@@ -145,6 +145,7 @@ index 85f31b7..7314584 100644
+ --inspector_protocol_dir $$INSPECTOR_PROTOCOL_DIR \
--config $(location :src/inspector/inspector_protocol_config.json) \
--config_value protocol.path=$(location :include/js_protocol.pdl) \
+ --config_value crdtp.dir=third_party/inspector_protocol/crdtp \
--output_base $(@D)/src/inspector",
- local = 1,
message = "Generating inspector files",

View File

@@ -83,9 +83,21 @@ index 420df0b..6f47969 100644
return __libc_stack_end;
}
diff --git a/orig/v8-14.6.202.11/src/base/platform/platform-win32.cc b/mod/v8-14.6.202.11/src/base/platform/platform-win32.cc
index f5d9ddc..542ea1a 100644
index f5d9ddc..1c08b0f 100644
--- a/orig/v8-14.6.202.11/src/base/platform/platform-win32.cc
+++ b/mod/v8-14.6.202.11/src/base/platform/platform-win32.cc
@@ -20,7 +20,11 @@
#include <windows.h>
// This has to come after windows.h.
+#ifdef __MINGW32__
+#include <versionhelpers.h>
+#else
#include <VersionHelpers.h>
+#endif
#include <dbghelp.h> // For SymLoadModule64 and al.
#include <malloc.h> // For _msize()
#include <mmsystem.h> // For timeGetTime().
@@ -69,9 +69,7 @@ static_assert(offsetof(V8_CRITICAL_SECTION, SpinCount) ==
// Extra functions for MinGW. Most of these are the _s functions which are in
// the Microsoft Visual Studio C++ CRT.
@@ -171,7 +183,7 @@ diff --git a/orig/v8-14.6.202.11/src/heap/base/asm/x64/push_registers_masm.asm b
index d0d0563..72e230b 100644
--- a/orig/v8-14.6.202.11/src/heap/base/asm/x64/push_registers_masm.asm
+++ b/mod/v8-14.6.202.11/src/heap/base/asm/x64/push_registers_masm.asm
@@ -1,70 +1,30 @@
@@ -1,70 +1,47 @@
-;; Copyright 2020 the V8 project authors. All rights reserved.
-;; Use of this source code is governed by a BSD-style license that can be
-;; found in the LICENSE file.

View File

@@ -174,18 +174,18 @@ genrule(
name = "binding_cc",
srcs = ["@v8_crate_146_4_0//:binding_cc"],
outs = ["binding.cc"],
# Keep this as a literal shell snippet. The string-concatenated form looked
# cleaner in Starlark but produced a broken `sed` invocation in CI.
cmd = """
sed \
-e '/#include "v8\\/src\\/flags\\/flags.h"/d' \
-e 's|"v8/src/libplatform/default-platform.h"|"src/libplatform/default-platform.h"|' \
-e 's| namespace i = v8::internal;| (void)usage;|' \
-e '/using HelpOptions = i::FlagList::HelpOptions;/d' \
-e '/HelpOptions help_options = HelpOptions(HelpOptions::kExit, usage);/d' \
-e 's| i::FlagList::SetFlagsFromCommandLine(argc, argv, true, help_options);| v8::V8::SetFlagsFromCommandLine(argc, argv, true);|' \
$(location @v8_crate_146_4_0//:binding_cc) > "$@"
""",
cmd = " ".join([
"sed",
"-e '/#include \"v8\\/src\\/flags\\/flags.h\"/d'",
"-e 's|\"v8/src/libplatform/default-platform.h\"|\"src/libplatform/default-platform.h\"|'",
"-e 's| namespace i = v8::internal;| (void)usage;|'",
"-e '/using HelpOptions = i::FlagList::HelpOptions;/d'",
"-e '/HelpOptions help_options = HelpOptions(HelpOptions::kExit, usage);/d'",
"-e 's| i::FlagList::SetFlagsFromCommandLine(argc, argv, true, help_options);| v8::V8::SetFlagsFromCommandLine(argc, argv, true);|'",
"$(location @v8_crate_146_4_0//:binding_cc)",
">",
'"$@"',
]),
)
copy_file(