Skip to content

measure: icu with the toolset first on a kept PATH [m3-only] #5

measure: icu with the toolset first on a kept PATH [m3-only]

measure: icu with the toolset first on a kept PATH [m3-only] #5

Workflow file for this run

name: measure 734
# Readings for the design of #734 (.agents/docs/2026-09-28-build-cost-foreign-
# toolsets-and-library-surface-design.md). Every leg prints `READING <id>: ...`
# lines and never fails the job on the value it reads; a job fails only when the
# harness itself cannot run.
#
# M1 vcpkg on a Visual Studio machine: does selecting the instance through the
# environment (VCPKG_VISUAL_STUDIO_PATH) keep the ABI hash of a port?
# M2 vcpkg on a Visual Studio machine: a chain-loaded triplet (toolset
# "external") against an MSBuild port (libusb), a CMake port (zlib) as the
# control of the chain-loaded setup, and the hybrid with vcvars loaded.
# M3 vcpkg with Visual Studio masked and the managed msvc@14.44.35207 named by a
# chain-loaded triplet: zlib (CMake) and icu (make under msys).
# P3 the same toolset copied to another path: is the port restored from the
# binary cache (same ABI hash)?
# E2b mcpp 2026.9.28.2 + mcpp:plugins 0.16.0 + fmt: cxx_runtime x triplet.
# E4 the time of 1270 deploy edges on a first build.
on:
push:
branches: [ 'measure/734-readings' ]
workflow_dispatch:
env:
XLINGS_NON_INTERACTIVE: '1'
BASELINE: ee6a47dac031930715bea2e6dfb9ad60caaa5d83
jobs:
vcpkg-vs:
name: M1 M2 (Visual Studio)
if: "!contains(github.event.head_commit.message, '[m3-only]')"
runs-on: windows-2025
timeout-minutes: 90
defaults: { run: { shell: bash } }
steps:
- name: Locate Visual Studio and load its developer environment
shell: pwsh
run: |
$vsp = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
Write-Host "VS: $vsp"
$bat = Join-Path $vsp "VC\Auxiliary\Build\vcvars64.bat"
$lines = cmd /d /s /c "`"$bat`" >nul && set"
foreach ($l in $lines) {
$k, $v = $l -split '=', 2
if ($k -in 'INCLUDE', 'LIB', 'LIBPATH', 'Path') { Set-Item "env:$k" $v }
}
$out = @(
"VSP=$vsp",
"INCLUDE=$env:INCLUDE", "LIB=$env:LIB", "LIBPATH=$env:LIBPATH",
"MEASURE_CL=$((Get-Command cl.exe).Source)",
"MEASURE_RC=$((Get-Command rc.exe).Source)",
"MEASURE_MT=$((Get-Command mt.exe).Source)",
"MEASURE_VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT"
)
$out | ForEach-Object { Write-Host $_ }
$out | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
- name: Locate vcpkg
run: |
V="$(cygpath -u "$VCPKG_INSTALLATION_ROOT")/vcpkg.exe"
test -x "$V" || { echo "::error::no vcpkg at $VCPKG_INSTALLATION_ROOT"; exit 1; }
"$V" version | head -1
echo "V=$V" >> "$GITHUB_ENV"
mkdir -p "$RUNNER_TEMP/bincache"
echo "VCPKG_BINARY_SOURCES=clear;files,$(cygpath -w "$RUNNER_TEMP/bincache"),readwrite" >> "$GITHUB_ENV"
- name: Write the manifests and the chain-loaded triplets
run: |
set -e
T="$RUNNER_TEMP/triplets"; mkdir -p "$T"
base="set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)"
chain='set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/chain.cmake")
set(VCPKG_ENV_PASSTHROUGH_UNTRACKED INCLUDE LIB LIBPATH MEASURE_CL MEASURE_RC MEASURE_MT MEASURE_VCPKG_ROOT)'
printf '%s\n%s\n' "$base" "$chain" | sed 's/^ *//' > "$T/x64-windows-chain.cmake"
printf '%s\n%s\nset(VCPKG_LOAD_VCVARS_ENV ON)\n' "$base" "$chain" | sed 's/^ *//' > "$T/x64-windows-chainvc.cmake"
cat > "$T/chain.cmake" <<'EOF'
file(TO_CMAKE_PATH "$ENV{MEASURE_CL}" _cl)
file(TO_CMAKE_PATH "$ENV{MEASURE_RC}" _rc)
file(TO_CMAKE_PATH "$ENV{MEASURE_MT}" _mt)
file(TO_CMAKE_PATH "$ENV{MEASURE_VCPKG_ROOT}" _vr)
set(CMAKE_C_COMPILER "${_cl}")
set(CMAKE_CXX_COMPILER "${_cl}")
set(CMAKE_RC_COMPILER "${_rc}")
set(CMAKE_MT "${_mt}")
include("${_vr}/scripts/toolchains/windows.cmake")
EOF
sed -i 's/^ *//' "$T/chain.cmake"
cat "$T"/*.cmake
for p in zlib libusb; do
for leg in a b; do
d="$RUNNER_TEMP/m-$p-$leg"; mkdir -p "$d"
printf '{ "name": "m%s", "version": "0.1.0", "dependencies": ["%s"] }\n' "$leg" "$p" > "$d/vcpkg.json"
done
done
- name: M1 the instance named through the environment
continue-on-error: true
run: |
set +e +o pipefail
abi() { cat "$1"/vcpkg_installed/x64-windows/share/zlib/vcpkg_abi_info.txt 2>/dev/null | sha256sum | cut -c1-16; }
( cd "$RUNNER_TEMP/m-zlib-a" && "$V" install --triplet x64-windows ) > m1a.log 2>&1; echo "READING M1a: default instance, exit $?"
( cd "$RUNNER_TEMP/m-zlib-b" && VCPKG_VISUAL_STUDIO_PATH="$VSP" "$V" install --triplet x64-windows ) > m1b.log 2>&1; echo "READING M1b: VCPKG_VISUAL_STUDIO_PATH, exit $?"
echo "READING M1: abi_info a=$(abi "$RUNNER_TEMP/m-zlib-a") b=$(abi "$RUNNER_TEMP/m-zlib-b")"
echo "READING M1: leg b $(grep -iE 'Restored [0-9]+ package|Building zlib' m1b.log | head -2 | tr '\n' ' ')"
ls -R "$RUNNER_TEMP/bincache" | grep -i '\.zip' | head
- name: M2 chain-loaded triplets against CMake and MSBuild ports
continue-on-error: true
run: |
set +e +o pipefail
T=$(cygpath -w "$RUNNER_TEMP/triplets")
run() { # $1 port, $2 triplet, $3 label
d="$RUNNER_TEMP/m2-$1-$2"; rm -rf "$d"; mkdir -p "$d"
cp "$RUNNER_TEMP/m-$1-a/vcpkg.json" "$d/"
( cd "$d" && "$V" install --triplet "$2" --overlay-triplets="$T" ) > "m2-$1-$2.log" 2>&1
rc=$?
echo "READING M2 $3: $1 under $2 exit $rc"
[ $rc = 0 ] || { grep -iE 'error|PlatformToolset|external' "m2-$1-$2.log" | head -8; tail -5 "m2-$1-$2.log"; }
if [ $rc != 0 ]; then
for f in "$(cygpath -u "$VCPKG_INSTALLATION_ROOT")"/buildtrees/detect_compiler/*"$2"*.log \
"$(cygpath -u "$VCPKG_INSTALLATION_ROOT")"/buildtrees/"$1"/*"$2"*.log; do
[ -f "$f" ] || continue
echo "----- $f"; tail -40 "$f"; cp "$f" "m2-detail-$1-$2-$(basename "$f")" 2>/dev/null
done
rm -rf "$(cygpath -u "$VCPKG_INSTALLATION_ROOT")"/buildtrees/detect_compiler
fi
}
run libusb x64-windows "control"
run zlib x64-windows-chain "chain-loaded, CMake port"
run libusb x64-windows-chain "chain-loaded, MSBuild port"
run libusb x64-windows-chainvc "chain-loaded with vcvars, MSBuild port"
run zlib x64-windows-chainvc "chain-loaded with vcvars, CMake port"
- uses: actions/upload-artifact@v4
if: always()
with: { name: m1-m2-logs, path: '*.log' }
vcpkg-managed:
name: M3 P3 (Visual Studio masked, managed msvc)
runs-on: windows-2025
timeout-minutes: 150
defaults: { run: { shell: bash } }
steps:
- name: Mask Visual Studio
shell: pwsh
run: |
$ErrorActionPreference = 'Continue'
$vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswhere) { Rename-Item $vswhere "vswhere.exe.masked" }
Resolve-Path "C:\Program Files*\Microsoft Visual Studio\*\*\VC" -ErrorAction SilentlyContinue | ForEach-Object {
try { Rename-Item -LiteralPath $_.Path -NewName "VC.masked" -ErrorAction Stop }
catch { Write-Host " rename failed: $($_.Exception.Message)" }
}
foreach ($v in @('VSINSTALLDIR','VCINSTALLDIR','VCToolsInstallDir','VS170COMNTOOLS','VS160COMNTOOLS','VS150COMNTOOLS')) {
"$v=" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
}
$left = Get-ChildItem "C:\Program Files*\Microsoft Visual Studio\*\*\VC\Tools\MSVC" -Directory -ErrorAction SilentlyContinue
if ($left) { Write-Host "FAIL: VC tools still present after masking"; exit 1 }
- name: Install xlings
shell: pwsh
run: |
irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex
"$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
"$env:USERPROFILE\.xlings\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install msvc@14.44.35207 and derive its environment
run: |
set -e
( cd "$RUNNER_TEMP" && xlings install msvc@14.44.35207 -y ) > msvc-install.log 2>&1 || { tail -30 msvc-install.log; exit 1; }
X="$(cygpath -u "$USERPROFILE")/.xlings/data/xpkgs"
CL=$(find "$X"/xim-x-msvc -path '*Hostx64/x64/cl.exe' | head -1)
test -n "$CL" || { echo "::error::no cl.exe under $X/xim-x-msvc"; exit 1; }
TOOLS=${CL%/bin/Hostx64/x64/cl.exe}
WH=$(find "$X"/xim-x-windows-sdk -iname windows.h -path '*/um/*' | head -1)
test -n "$WH" || { echo "::error::no um/windows.h under $X/xim-x-windows-sdk"; ls "$X" | grep -i sdk; exit 1; }
INCV=$(dirname "$(dirname "$WH")"); VER=$(basename "$INCV"); SDK=$(dirname "$(dirname "$INCV")")
RC=$(find "$SDK" -ipath "*$VER*x64/rc.exe" | head -1); MT=$(find "$SDK" -ipath "*$VER*x64/mt.exe" | head -1)
echo "tools=$TOOLS sdk=$SDK ver=$VER rc=$RC mt=$MT"
w() { cygpath -w "$1"; }
echo "TOOLS=$TOOLS" >> "$GITHUB_ENV"; echo "SDK=$SDK" >> "$GITHUB_ENV"; echo "SDKVER=$VER" >> "$GITHUB_ENV"
echo "MEASURE_RC=$(w "$RC")" >> "$GITHUB_ENV"; echo "MEASURE_MT=$(w "$MT")" >> "$GITHUB_ENV"
echo "MEASURE_VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
mkdir -p "$RUNNER_TEMP/bincache"
echo "VCPKG_BINARY_SOURCES=clear;files,$(cygpath -w "$RUNNER_TEMP/bincache"),readwrite" >> "$GITHUB_ENV"
echo "V=$(cygpath -u "$VCPKG_INSTALLATION_ROOT")/vcpkg.exe" >> "$GITHUB_ENV"
- name: M3 and P3 chain-loaded managed toolset
continue-on-error: true
run: |
set +e +o pipefail
T="$RUNNER_TEMP/triplets"; mkdir -p "$T"
printf '%s\n' 'set(VCPKG_TARGET_ARCHITECTURE x64)' 'set(VCPKG_CRT_LINKAGE dynamic)' 'set(VCPKG_LIBRARY_LINKAGE dynamic)' \
'set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/chain.cmake")' \
'set(VCPKG_ENV_PASSTHROUGH_UNTRACKED INCLUDE LIB LIBPATH MEASURE_CL MEASURE_RC MEASURE_MT MEASURE_VCPKG_ROOT)' \
> "$T/x64-windows-chain.cmake"
printf '%s\n' 'file(TO_CMAKE_PATH "$ENV{MEASURE_CL}" _cl)' 'file(TO_CMAKE_PATH "$ENV{MEASURE_RC}" _rc)' \
'file(TO_CMAKE_PATH "$ENV{MEASURE_MT}" _mt)' 'file(TO_CMAKE_PATH "$ENV{MEASURE_VCPKG_ROOT}" _vr)' \
'set(CMAKE_C_COMPILER "${_cl}")' 'set(CMAKE_CXX_COMPILER "${_cl}")' \
'set(CMAKE_RC_COMPILER "${_rc}")' 'set(CMAKE_MT "${_mt}")' \
'include("${_vr}/scripts/toolchains/windows.cmake")' > "$T/chain.cmake"
setenv() { # $1 = the toolset directory
w() { cygpath -w "$1"; }
export MEASURE_CL="$(w "$1/bin/Hostx64/x64/cl.exe")"
I="$SDK/Include/$SDKVER"; L="$SDK/Lib/$SDKVER"
export INCLUDE="$(w "$1/include");$(w "$I/ucrt");$(w "$I/um");$(w "$I/shared");$(w "$I/winrt");$(w "$I/cppwinrt")"
export LIB="$(w "$1/lib/x64");$(w "$L/ucrt/x64");$(w "$L/um/x64")"
export LIBPATH="$(w "$1/lib/x64")"
}
run() { # $1 port, $2 label, $3 dir suffix
d="$RUNNER_TEMP/m3-$1-$3"; rm -rf "$d"; mkdir -p "$d"
printf '{ "name": "m", "version": "0.1.0", "dependencies": ["%s"] }\n' "$1" > "$d/vcpkg.json"
( cd "$d" && "$V" install --triplet x64-windows-chain --host-triplet x64-windows-chain --overlay-triplets="$(cygpath -w "$T")" ) > "m3-$1-$3.log" 2>&1
rc=$?
echo "READING $2: $1 exit $rc; $(grep -iE 'Restored [0-9]+ package' "m3-$1-$3.log" | head -1)"
[ $rc = 0 ] || { grep -iE 'error' "m3-$1-$3.log" | head -8; tail -5 "m3-$1-$3.log"; }
}
setenv "$TOOLS"
run zlib "M3 managed, CMake port" a
run icu "M3 managed, make port" a
# The make-based port finds link.exe on PATH; vcpkg keeps the caller's PATH
# only through VCPKG_KEEP_ENV_VARS.
( export PATH="$TOOLS/bin/Hostx64/x64:$SDK/bin/$SDKVER/x64:/c/Windows/system32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0"
export VCPKG_KEEP_ENV_VARS=PATH
run icu "M3 managed, make port, toolset first on a kept PATH" k )
# P3: the same toolset at another path; a restore means the same ABI hash.
cp -r "$TOOLS" "$RUNNER_TEMP/tc2"
setenv "$RUNNER_TEMP/tc2"
run zlib "P3 same toolset, other path" b
ls -R "$RUNNER_TEMP/bincache" | grep -ic '\.zip' | sed 's/^/READING P3: archives in the cache: /'
- uses: actions/upload-artifact@v4
if: always()
with: { name: m3-p3-logs, path: '*.log' }
mcpp-legs:
name: E2b E4 (mcpp 2026.9.28.2)
if: "!contains(github.event.head_commit.message, '[vcpkg-only]') && !contains(github.event.head_commit.message, '[m3-only]')"
runs-on: windows-2025
timeout-minutes: 120
defaults: { run: { shell: bash } }
steps:
- name: Install xlings
shell: pwsh
run: |
irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex
"$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
"$env:USERPROFILE\.xlings\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install mcpp 2026.9.28.2
run: |
set -e
( cd "$RUNNER_TEMP" && xlings install mcpp@2026.9.28.2 -y ) > mcpp-install.log 2>&1 || { tail -30 mcpp-install.log; exit 1; }
M="$(cygpath -u "$USERPROFILE")/.xlings/data/xpkgs/xim-x-mcpp/2026.9.28.2/bin/mcpp.exe"
"$M" --version
"$M" self config --mirror GLOBAL
echo "M=$M" >> "$GITHUB_ENV"
- name: E2b the C++ runtime contract against vcpkg's CRT linkage
continue-on-error: true
run: |
set +e +o pipefail
for crt in toolchain-coupled self-contained; do
for trip in x64-windows x64-windows-static-md; do
d="$RUNNER_TEMP/e2b-$crt-$trip"; mkdir -p "$d/src"
printf '[package]\nname = "crtprobe"\nversion = "0.1.0"\nstandard = "c++23"\n\n[toolchain]\nwindows = "llvm@22.1.8"\n\n[build]\ncxx_runtime = "%s"\n\n[build-dependencies]\nmcpp.plugins = { version = "0.16.0", features = ["deps-vcpkg"], host-module = true }\n' "$crt" > "$d/mcpp.toml"
printf '{ "name": "crtprobe", "version": "0.1.0", "dependencies": ["fmt"], "builtin-baseline": "%s" }\n' "$BASELINE" > "$d/vcpkg.json"
cat > "$d/build.mcpp" <<EOF
import std;
import mcpp;
import mcpp.deps.vcpkg;
int main() {
mcpp::deps::vcpkg::options o;
o.triplet = "$trip";
o.libraries = {"fmt"};
return mcpp::deps::vcpkg::use(o) ? 0 : 1;
}
EOF
sed -i 's/^ //' "$d/build.mcpp"
printf '#include <fmt/format.h>\n#include <string>\nint main() { std::string s = fmt::format("{}-{}", 4, 2); return s == "4-2" ? 0 : 3; }\n' > "$d/src/main.cpp"
( cd "$d" && "$M" build ) > "e2b-$crt-$trip.log" 2>&1; b=$?
r=skip
if [ $b = 0 ]; then ( cd "$d" && "$M" run ) >> "e2b-$crt-$trip.log" 2>&1; r=$?; fi
echo "READING E2b: cxx_runtime=$crt triplet=$trip build exit $b, run exit $r"
grep -iE 'LNK2038|mismatch|FAILIFMISMATCH|RuntimeLibrary|error' "e2b-$crt-$trip.log" | head -6
done
done
- name: E4 the time of deploy edges on a first build
continue-on-error: true
run: |
set +e +o pipefail
for n in 0 1270; do
d="$RUNNER_TEMP/e4-$n"; mkdir -p "$d/src" "$d/data"
for i in $(seq 1 $n); do echo "$i" > "$d/data/f$i.txt"; done
printf '[package]\nname = "e4"\nversion = "0.1.0"\nstandard = "c++23"\n' > "$d/mcpp.toml"
printf 'int main() { return 0; }\n' > "$d/src/main.cpp"
cat > "$d/build.mcpp" <<EOF
import std;
import mcpp;
int main() {
for (int i = 1; i <= $n; ++i) {
const std::string f = "data/f" + std::to_string(i) + ".txt";
mcpp::deploy(f.c_str(), "data");
}
return 0;
}
EOF
sed -i 's/^ //' "$d/build.mcpp"
for run in 1 2; do
rm -rf "$d/target"
s=$(date +%s.%N); ( cd "$d" && "$M" build ) > "e4-$n-$run.log" 2>&1; rc=$?; e=$(date +%s.%N)
log=$(find "$d/target" -name .ninja_log | head -1)
stats=$(awk -F'\t' 'NR>1 && $4 ~ /data[\\\/]f[0-9]+\.txt$/ {n++; s+=$2-$1; if(min==""||$1<min)min=$1; if($2>max)max=$2} END {printf "edges=%d sum=%.1fs span=%.1fs", n, s/1000, (max-min)/1000}' "$log" 2>/dev/null)
echo "READING E4: n=$n run=$run exit $rc wall=$(awk "BEGIN { printf \"%.1f\", $e - $s }")s $stats"
done
done
d="$RUNNER_TEMP/e4-1270"; rm -rf "$RUNNER_TEMP/e4-copy"
s=$(date +%s.%N); cp -r "$d/data" "$RUNNER_TEMP/e4-copy"; e=$(date +%s.%N)
echo "READING E4: one process copying the same 1270 files: $(awk "BEGIN { printf \"%.1f\", $e - $s }")s"
- uses: actions/upload-artifact@v4
if: always()
with: { name: e2b-e4-logs, path: '*.log' }