Skip to content

Add the platform-scenario axis and the 3.14 windows-msys (msys2/ucrt64) patch series - #14

Merged
ronaldtse merged 2 commits into
mainfrom
feat/windows-msys-port
Sep 12, 2026
Merged

ronaldtse merged 2 commits into
mainfrom
feat/windows-msys-port

Conversation

@ronaldtse

@ronaldtse ronaldtse commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Add the platform-scenario axis and the 3.14 windows-msys (msys2/ucrt64) patch series

The windows-msys port series, step 1. The catalog tip line (3.14.7) gains the msys2/ucrt64 port so tebako-runtime-python can build a ucrt64 runtime from our src release. The POSIX-only upstream tree cannot: its configure has no *-*-mingw* case at all and pyconfig.h.in never defines MS_WINDOWS for mingw — the full wall list is in tebako-runtime-python PR #2.

Draft — do not merge. The stacked factory PR (tebako-runtime-python, windows-2022 matrix return) references this mechanism and is expected red until this PR merges and the source release v0.3.0 is cut (the release cut is an owner step, required — the factory's contract.yml pin moves to v0.3.0 to fetch the new suffixed asset).

The patch series (patches/3.14/)

All four patches are ported from MSYS2 MINGW-packages (provenance cited in each header) and verified apply-clean against the real 3.14.7 tarball (tools/lint 3.14.7):

Patch MSYS2 origin What it does Wall it removes
pyport_ms_windows_msys.patch 0002 Include/pyport.h: derive MS_WINDOWS/MS_WIN64/MS_WIN32 from __MINGW32__/_WIN64/_WIN32; define the winapifamily partition macros + HAVE_WINDOWS_CONSOLE_IO pyconfig.h.in lacks MS_WINDOWS for mingw
configure_machdep_msys.patch 0004 configure.ac + hand-synced configure: *-*-mingw*ac_sys_system=MINGW, MACHDEP="win32", cross _host_cpu= arm zero mingw cases in configure
configure_c_locale_coercion_msys.patch 0052 default --without-c-locale-coercion on *-*-mingw* pylifecycle.c:258 setenv
pycore_time_timeval_msys.patch 0096 pycore_time.h: the struct timeval forward decl keys on MS_WINDOWS, not only __clang__ pytime.c timeval.tv_sec

The scenario axis (machinery the series rides on)

  • versions.yml entries may declare scenarios (absent = [linux-gnu]; linux-gnu is mandatory and stays the unsuffixed back-compat asset — the pristine upstream tree). 3.14.7 declares [linux-gnu, windows-msys]. Schema enforces uniqueness + the linux-gnu floor.
  • PatchManifest (patches/<line>/patch-<line>.yaml, ruby-factory conventions) + PatchSelection: per-line ordered series with per-patch platform gating. release_diff attributes upstream file changes to the affected scenarios, so a windows-only wall narrows the rebuild to the windows-msys leg.
  • tools/apply --platform applies a line's series onto a pristine tree; tools/prepare/tools/lint ride it (lint now apply-checks every declared scenario).
  • SmokePlan + tools/smoke_matrix + tools/compile_smoke: per-scenario native configure + wall-translation-unit compile probe. linux-gnu runs on the host; windows-msys is CI-only (no mingw on the maintainer macOS host — the windows-2022 smoke leg is the oracle).
  • Onboarder: a newly onboarded patch release inherits its line's newest sibling's scenarios so the release monitor never silently narrows a ported line back to linux-gnu-only. (Divergence note: the ruby factory's onboarder has the same latent gap — its human gate compensates; not fixed there.)

Workflows

  • New reusable _compile-smoke.yml: per-scenario compile smoke; windows-msys runs on windows-2022 via msys2/setup-msys2@66cd2cce… (pinned) with the ucrt64 toolchain.
  • lint.yml: PRs emit the smoke matrix (tools/versions --smoke) and run it.
  • release-src.yml: plan emits scenario legs; smoke gates builds; each scenario builds via tools/apply --platform; the windows asset ships as tfs-python-<v>-src-windows-msys.tar.gz (also fixes a real artifact-name collision); the unsuffixed linux-gnu asset is unchanged.

Bug fixes in the scenario machinery

  • release_diff#scenario_attribution used a non-capturing group — the suffix was never read and every upstream change attributed wide (all scenarios). Fixed with match?.
  • source_prep#audit resolved patch selection unguarded → SelectionError on lines with no patch directory (3.12/3.13). Fixed: unpatched lines audit against [].

Verification (this host)

  • rspec: 99 examples, 0 failures (incl. new PatchManifest / PatchSelection / SmokePlan specs and the scenario axes of versions/build_plan/release_diff/source_prep/onboarder)
  • tools/validate_manifests: OK
  • tools/lint 3.12.14 / 3.13.15 / 3.14.7: all "patches apply-clean"
  • tools/apply 3.14.7 --platform windows-msys: patched tree spot-checked (MS_WINDOWS defines, MACHDEP="win32", coercion default, pycore_time.h)
  • tools/compile_smoke 3.14.7 --platform linux-gnu: configure green, 3 wall TUs compile (macOS host)
  • tools/build_matrix v0.9.9 / tools/smoke_matrix v0.9.9: fail-closed full plan incl. tfs-python-3.14.7-src-windows-msys.tar.gz
  • actionlint: clean (one pre-existing SC2035 info, also on main)
  • Docs synced: README (status, scenario tarball contract, tooling, CI, local-dev bundler note), patches/README.md (real inventory), tools/build_matrix header

Required follow-up (owner)

Merge order: this PR → cut source release v0.3.0 (release-src workflow; smoke-gated) → the tebako-runtime-python windows PR goes green with no further edits.

Comment on lines +35 to +69
name: compile-smoke ${{ inputs.version }} (${{ inputs.platform }})
runs-on: ${{ inputs.runs_on }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Setup MSys2 (ucrt64)
if: inputs.platform == 'windows-msys'
uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
with:
msystem: ucrt64
path-type: minimal
update: true

# git for the apply step, make + the ucrt64 gcc for the compile,
# the msys ruby for the tooling. No openssl/zlib dev packages: the
# wall-TU set (pylifecycle/pytime/Programs/python) needs none.
- name: Install the ucrt64 toolchain
if: inputs.platform == 'windows-msys'
shell: msys2 {0}
run: pacman -S --needed --noconfirm git make ruby mingw-w64-ucrt-x86_64-toolchain

- name: Setup Ruby (the tooling's interpreter)
if: inputs.platform != 'windows-msys'
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
ruby-version: "3.2"

- name: compile_smoke (windows)
if: inputs.platform == 'windows-msys'
shell: msys2 {0}
run: tools/compile_smoke "${{ inputs.version }}" "$PWD/build/compile-smoke" --platform "${{ inputs.platform }}"

- name: compile_smoke (POSIX)
if: inputs.platform != 'windows-msys'
run: tools/compile_smoke "${{ inputs.version }}" "$PWD/build/compile-smoke" --platform "${{ inputs.platform }}"
Comment on lines +55 to +76
name: emit the compile-smoke matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.emit.outputs.matrix }}
leg_count: ${{ steps.emit.outputs.leg_count }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
ruby-version: "3.2"
- id: emit
run: |
matrix=$(tools/versions --smoke)
{
echo "matrix=$matrix"
# The count drives the smoke job's if: guard (an unguarded
# empty matrix poisons the run conclusion).
echo "leg_count=$(jq '.include | length' <<< "$matrix")"
} >> "$GITHUB_OUTPUT"
echo "compile-smoke legs: $(jq -r '.include[] | "\(.version)/\(.platform)"' <<< "$matrix" | paste -sd' ' -)"

smoke:
Comment on lines +77 to +87
name: smoke ${{ matrix.version }} (${{ matrix.platform }})
needs: smoke-legs
if: needs.smoke-legs.outputs.leg_count != '0'
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.smoke-legs.outputs.matrix) }}
uses: ./.github/workflows/_compile-smoke.yml
with:
version: ${{ matrix.version }}
platform: ${{ matrix.platform }}
runs_on: ${{ matrix.platform == 'windows-msys' && 'windows-2022' || 'ubuntu-latest' }}
Comment on lines +89 to +101
name: smoke ${{ matrix.version }} (${{ matrix.platform }})
needs: plan
if: needs.plan.outputs.build_count != '0'
if: needs.plan.outputs.smoke_count != '0'
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.smoke) }}
uses: ./.github/workflows/_compile-smoke.yml
with:
version: ${{ matrix.version }}
platform: ${{ matrix.platform }}
runs_on: ${{ matrix.platform == 'windows-msys' && 'windows-2022' || 'ubuntu-latest' }}

build:
…4) patch series

TODO.python/05 item 1: the catalog line gains the msys2/ucrt64 port so
tebako-runtime-python can build a ucrt64 runtime from our src release
(the POSIX line's configure has no mingw case at all — PR #2's wall).

patches/3.14/ — 4 patches, all ported from MSYS2 MINGW-packages
(provenance cited per header), apply-clean verified against the real
3.14.7 tarball:

- pyport_ms_windows_msys.patch (MSYS2 0002): pyport.h derives
  MS_WINDOWS/MS_WIN64/MS_WIN32 from __MINGW32__/_WIN64/_WIN32 and
  defines the winapifamily partition macros + HAVE_WINDOWS_CONSOLE_IO.
  Wall: pyconfig.h.in never defines MS_WINDOWS for mingw.
- configure_machdep_msys.patch (MSYS2 0004, configure hand-synced):
  *-*-mingw* gets ac_sys_system=MINGW, MACHDEP="win32", and the cross
  _host_cpu= arm. Wall: zero mingw cases in configure.
- configure_c_locale_coercion_msys.patch (MSYS2 0052): default
  --without-c-locale-coercion on mingw. Wall: pylifecycle.c:258 setenv.
- pycore_time_timeval_msys.patch (MSYS2 0096): the struct timeval
  forward decl is needed under MS_WINDOWS, not only __clang__.
  Wall: pytime.c timeval.tv_sec.

Scenario axis (the machinery the series rides on):

- versions.yml/schema: entries may declare `scenarios` (default
  [linux-gnu]; linux-gnu mandatory — it stays the unsuffixed
  back-compat asset). 3.14.7 declares [linux-gnu, windows-msys].
- PatchManifest (patches/<line>/patch-<line>.yaml) + PatchSelection:
  per-line ordered series with per-patch platform gating; release_diff
  attributes upstream file changes to the affected scenarios so a
  windows-only wall narrows the rebuild to the windows-msys leg.
- tools/apply: apply a line's series for one platform onto a pristine
  tree; tools/prepare and tools/lint ride it (lint now apply-checks
  every declared scenario).
- SmokePlan + tools/smoke_matrix + tools/compile_smoke: a native
  configure + wall-translation-unit compile probe per scenario —
  linux-gnu runs on the host, windows-msys is CI-only (msys2).
- Onboarder: a new patch release inherits its line's newest sibling's
  scenarios, so the monitor never silently narrows a ported line back
  to linux-gnu-only.

Workflows:

- _compile-smoke.yml (reusable): per-scenario compile smoke; the
  windows-msys leg runs on windows-2022 via msys2/setup-msys2
  (ucrt64 toolchain).
- lint.yml: PRs now run the smoke matrix (tools/versions --smoke).
- release-src.yml: the plan emits scenario legs; the smoke job gates
  the builds; each scenario builds via tools/apply --platform and
  ships a suffixed asset (tfs-python-<v>-src-windows-msys.tar.gz);
  the unsuffixed linux-gnu asset is byte-identical to before.

Bug fixes in the scenario machinery:

- release_diff#scenario_attribution used a non-capturing group, so
  the scenario suffix was never read and every change attributed wide.
- source_prep#audit resolved the patch selection unguarded, raising
  SelectionError on lines with no patch directory.
The windows-msys compile-smoke leg checks out on windows-2022, where
Git for Windows defaults to core.autocrlf=true: the patch series came
out CRLF, the pristine tarball tree is LF, and git apply failed
("patch failed: Include/pyport.h:78"). eol=lf attributes override
autocrlf per path. git add --renormalize confirms every tracked file
was already LF.
@ronaldtse
ronaldtse marked this pull request as ready for review September 12, 2026 22:17
@ronaldtse
ronaldtse merged commit 0fa9041 into main Sep 12, 2026
10 checks passed
ronaldtse added a commit to tamatebako/tebako-runtime-python that referenced this pull request Sep 13, 2026
* Return the windows-2022 leg on the source factory's scenario axis (TODO.python/05 item 2)

The windows row comes back to the matrix (63e8336's descope), now
buildable: tamatebako/python's platform-scenario axis ships the
msys2/ucrt64 patch series as tfs-python-<base>-src-windows-msys.tar.gz
assets (tamatebako/python#14).

- .github/matrix.json: re-add the windows-2022 row, exactly as
  descoped. The windows-gated legs in _build-platform.yml (msys2
  provisioning, build, symbol provenance, boot smoke) and publish.yml's
  windows job need no edits — they light up on the row's legs.
- SourceFetcher: scenario-aware assets (the ruby factory's
  scenario_asset_names convention, minus ruby's msys two-pass split —
  CPython needs one tree per scenario). fetch/tarball_sha256 take an
  optional platform; a mingw host names the -windows-msys asset, POSIX
  keeps the unsuffixed back-compat asset. Builder threads @platform.
- compute_matrix.rb: jit lines skip windows (upstream's JIT target
  whitelist admits MSVC windows only — the musl skip's sibling), and a
  windows leg exists only when the pinned source_release's SHA256SUMS
  carries the line's windows-msys asset — missing asset = loud skip,
  not a red leg. The SHA256SUMS read is lazy and scoped to a windows
  env row surviving the filters (POSIX triggers stay fetch-free), and
  an unreadable pin surfaces as a named exit-64 error, never a stack
  trace.
- contract.yml: source_release v0.2.0 -> v0.3.0 (the release carrying
  the scenario axis). EXPECTED RED until tamatebako/python#14 merges
  and v0.3.0 is cut: the pinned SHA256SUMS 404s, and lint.yml's
  unfiltered compute_matrix fails named. Once v0.3.0 exists this PR
  goes green with no further edits (the #2 expected-red precedent).

Verified locally: rspec 14/0 (new source_fetcher_spec covers the
scenario grammar over a file:// mirror); check_contract green; POSIX
matrices byte-identical to main; PLATFORM_FILTER=windows against a
staged SHA256SUMS yields exactly the 3.14.7 leg with loud skips for
the other five catalog lines, and the all-missing case yields an empty
matrix with six loud skips; ruby -c sweep clean.

* contract: consume source release v0.3.1 (the errmap.h msys include fix)

v0.3.1 is the v0.3.0 catalog plus the MSYS2-MINGW-packages 0007 port
(tamatebako/python#16): PC/errmap.h is checked-in source and upstream's
Makefile grants -I$(srcdir)/PC to MSVC only; the windows-msys scenario
tarball now compiles Objects/exceptions.o through. The version catalog
is unchanged.

* contract: consume source release v0.3.2 (the msys compile-wall sweep)

v0.3.2 is the v0.3.1 catalog plus the MSYS2-MINGW-packages sweep for
the static ucrt64 shape (tamatebako/python#17): 18 more patches ported
— the observed round-2 walls (0054 -DVPATH on the sysmodule.o rule,
0001 --with-nt-threads default + the _POSIX_THREADS undefs) plus the
series audit's compile/link/module-reach set (0005 mingw configure
defaults — the alarm/dev-ptmx probe pins that walled the native ucrt64
smoke leg, 0008 nt module naming, 0016 frozenmain exe-link, 0017+0041
socketmodule + inet_pton probes, 0036 _signal -lws2_32, 0037
winconsoleio in the _io bootstrap line, 0045 ucrt _timezone spellings,
0056 -municode for _bootstrap_python, 0059 ssl/hashlib -l tails, 0060
winsock netdb probes, 0063 deterministic EXEEXT, 0075 POSIX-only
modules n/a, 0078 -Wno-error=incompatible-pointer-types for gcc>=14,
0097 mmapmodule SEH, 0100 _PTHREAD_NAME_MAXLEN) and the syslibs
deviation extended with 0010's -lversion -lshlwapi -lpathcch tail.
The version catalog is unchanged.

* contract: consume source release v0.3.3 (the mingw shared-build machinery)

v0.3.3 is the v0.3.2 catalog plus the MSYS2-MINGW-packages
shared-build port (tamatebako/python#18): 0009/0010 (declspec exports
+ the enable_shared mingw configure arms — LDLIBRARY=
libpython3.14.dll.a / DLLLIBRARY=libpython3.14.dll), the dynload_win
coherence cluster (0011 PYD_PLATFORM_TAG, 0012 dynload_win + PC/dl_nt.o,
0013 SHLIB_SUFFIX=.pyd, 0014 mingw SOABI/EXT_SUFFIX, 0015 MS_DLL_ID),
the two interpreter boot walls the PR's own smoke leg forced (0018:
winreg/msvcrt/_winapi as bootstrap-static modules — _bootstrap_external
imports winreg at top level on win32; 0034: sysmodule's winver/dllhandle
guard MS_COREDLL -> defined(MS_WINDOWS) && defined(Py_ENABLE_SHARED)),
0066 (the _Py_CheckPython3 declaration conflict), and 0039 (-municode
exe link + the win resources + pythonw.exe). Observable rename on
windows-msys: extension modules are .cp314-mingw_x86_64_ucrt_gnu.pyd.
The version catalog is unchanged.

* windows-msys: build --enable-shared + ship the runtime DLL (issue 40's answer)

The v0.3.3 source release carries the MSYS2-series machinery (0009/0010
shared build, 0011-0015, 0018, 0034, 0039, 0066); the first trp leg
against it (run 34745803168) failed exactly as predicted on the static
shape: the stdlib extensions link as loadable PE modules
(array.cp314-mingw_x86_64_ucrt_gnu.pyd, Makefile:3624) and PE modules
cannot carry undefined symbols — the link died on _Py_Dealloc & co
because --disable-shared gives them no libpython to resolve against.
On PE the answer is structural, not a patch: build --enable-shared and
ship the DLL.

- configure: --enable-shared on windows-msys (POSIX keeps
  --disable-shared, the shipped single-exe shape).
- substitute_makefiles: the exe link recipe substitution is now
  platform-shaped. The 0039 patch (win_resources_pythonw_msys) rewrites
  the rule to `...$(LINKFORSHARED) -municode -o $@ Programs/python.o ...
  $(SYSLIBS) python_exe.o` — the old POSIX regex never matched that
  line, so the prereq side rewrote python.o -> tebako_python.o while the
  recipe kept linking Programs/python.o: the driver link was SILENTLY
  lost (the fs TU compiled but never linked). The msys substitution
  strips -municode (the fs TU defines plain main; the mingw CRT demands
  wmain otherwise) and keeps python_exe.o (the version/icon resource).
  verify_substitutions now also requires the rewritten recipe line
  (-o $@ Programs/tebako_python.o) — the silent-loss class fails closed.
- abi: EXT_SUFFIX regex accepts the mingw spelling
  (cp314-mingw_x86_64_ucrt_gnu.pyd) alongside the POSIX cpython-* one.
- make: on msys pre-build the DLL target SERIALLY before the -j make.
  Upstream's `$(DLLLIBRARY) libpython$(LDVERSION).dll.a` two-target rule
  (-Wl,--out-implib=$@) is parallel-unsafe: observed locally, the
  interleaved import-library write replaced the 39 MB libpython3.14.dll
  with the 1.2 MB import stub. The serial pre-build materializes both
  files with the correct expansion; the parallel make then finds the
  rule up-to-date and never re-runs it.
- finalize stages libpython<X.Y>.dll beside the runtime package as
  <package>.dll (unique per leg — two same-ABI legs share the PE name
  and would collide in the merged release workspace); a missing DLL is
  a named error (exit 130), never a silent static regression. The image
  layout card declares runtime_dll (additive, schema_minor 3,
  spec 22 §2.1); PythonVersion#msys_dll_name stays the name's single
  owner. tools/boot_smoke materializes the PE-named copy next to the
  in-leg exe before any scenario boots it (the OS loader resolves it
  before any driver code runs).
- upload_release: expected_facets requires <package>.dll on windows —
  a leg that failed to stage it is an incomplete release, not an
  optional facet. (The dll manifest facet, install_as included, was
  already modeled; the comments claiming "nothing ships it today" are
  corrected.)
- The mingw support set never reaches the user: LIBS="-static-libgcc
  -l:libwinpthread.a" static-links libgcc/libwinpthread INTO the
  runtime's PE modules (ruby factory's proven Mlibs::MSYS_DLL_LIBRARIES
  recipe; LIBS lands in the link tail, after the objects, so the
  archives resolve). Verified against a local cross build of the v0.3.3
  tree: libpython3.14.dll (39,320,791 bytes) imports system DLLs only
  (KERNEL32/ADVAPI32/VERSION/WS2_32/bcrypt/api-ms-win-*) —
  libgcc_s_seh-1.dll and libwinpthread-1.dll are gone; the exe imports
  system + libpython3.14.dll only. A bare windows machine installs
  nothing (audience rule 1).
- README's windows-boundary section describes the shared shape.

Gates: rspec 14/0; check_contract.rb green; ruby -c sweep green. The
recipe substitutions were cross-checked by driving the real
exe_recipe_substitution against the real v0.3.3 Makefile.pre shapes
(msys probe tree + POSIX smoke trees): each pair matches its own shape
exactly once; the msys result line verified char-exact. trp#16 stays
draft — no merge without the owner's go-ahead.

* contract: consume source release v0.3.4 (the sysconfig posix-mode pair)

v0.3.4 (tamatebako/python#19) ports MSYS2 0003 (sys._is_mingw flags) +
0020 (mingw builds take the posix sysconfig path) — the answer to the
first --enable-shared leg's pybuilddir.txt wall (run 34748169561:
KeyError 'LIBPL'; upstream's nt path never merges the Makefile-parsed
build_time_vars). 0034's hunk 3 returns to MSYS2-verbatim context.
Windows-msys asset verified on the release (sha256
d5628491ee94b21dce0c92ba6913342cd68c3f99e7a569428ab16a4278803052).

* contract: consume source release v0.3.5 (the module-libs cluster)

v0.3.5 (tamatebako/python#20) ports MSYS2 0061 + 0073
(_multiprocessing: the win32 enable condition, then -lws2_32), 0074
(select: _MSC_VER → MS_WIN32 guards + -lws2_32), and 0076 (_ctypes:
-lole32 -loleaut32 -luuid) — the answer to the shared-extension link
wall the v0.3.4 leg hit (run 34750005177: undefined __WSAFDIsSet /
__imp_select / __imp_WSAGetLastError at the select.pyd and
_multiprocessing.pyd links; PE resolves every symbol at link time and
upstream's PY_STDLIB_MOD stanzas carry no per-module libs — MSVC gets
them via PCbuild). Windows-msys asset verified on the release (sha256
18f20a10922e55da3ce2213769cccf2db4a732b8898e2c128173051ed7b1f8b3;
the POSIX assets are byte-identical with v0.3.4's).

* contract: consume source release v0.3.6 (the mmap + legacy-DLL trio)

v0.3.6 (tamatebako/python#21) ports MSYS2 0062 (mmap builds on win32 —
its absence killed ensurepip in `make install` on the v0.3.5 leg, run
34753278108: pip's vendored cachecontrol imports mmap), 0067
(PYTHONLEGACYWINDOWSDLLLOADING — the bpo-36085 DEFAULT_DIRS search
never consults PATH, so the build-tree check removed
_decimal/_zstd/_testinternalcapi whose dep DLLs live in
<ucrt64>/bin), and 0064 (have_dynamic_loading for the dlopen-gated
test modules); 0076 returns to MSYS2-verbatim context. Windows-msys
asset verified on the release (sha256
20ec675d03bb3612e4c69026fa723d97cdd323a2faf38a22bee32105236f1c23;
the POSIX assets are byte-identical with v0.3.5's).

* windows-msys: install with prefix=/A/t + stage pip explicitly (round-6)

CPython's install rules spell every destination $(DESTDIR)$(dir) with no
separator; with the baked drive-qualified prefix (A:/t) that concatenates
into one junk component (stage + "A:/t/..." — the failing recipes are
mostly `-`-prefixed, so make exited green over the wreckage and
staged_prefix_tree's "no A/t tree" was the first truth, exit 105 on run
34756006093). The msys install now overrides prefix to the POSIX spelling
/A/t — every install dir var derives unexpanded from $(prefix) (autoconf
convention), so the override flows to every rule — and skips ensurepip:
pip's --root rebase drive-strips the built python's BUILD-TREE prefix,
never the staged one, so its pip landed under <stage>/<build-tree path>.

place_pip stages pip explicitly instead: the bundled wheel's own
installer run into the staged lib/pythonX.Y/site-packages (the series'
0026 site.py branch puts the runtime's site-packages there — verified
against the applied tree), mirroring ensurepip's -c invocation but with
--target deciding placement. The paths ride argv so the msys runtime
translates them for the native exe (a path embedded in the -c payload
crosses verbatim), and --no-compile mirrors ensurepip: .pyc records
written here would spell the stage paths — dead links in the mounted
image.

POSIX legs keep the old recipe exactly (ensurepip still rides the
install). Recorded follow-up: whether ensurepip's --root placement ever
landed pip in the POSIX staged trees either (the build-tree prefix
rebase exists there too) — checked separately, not in this commit.

* ci: spell the provenance exe with .exe on the windows leg (round-7)

msys stat() transparency lets foo.exe satisfy `[ -s foo ]`, so the
step's extensionless-or-.exe dance always kept the extensionless
spelling; the native nm/objdump receive the argument verbatim, get
ENOENT, and the script's discarded stderr read back as a stripped exe
(five failed provenance checks on run 34758424181 — the exe itself was
fine: the same build's place_pip had just run it). The name carries
.exe on this leg by construction (Platform#exe_suffix) — spell it out,
the ruby factory's ruby.exe spelling. The script now also prints nm's
own diagnostic when the symbol-table capture comes back empty, so a
silent nm failure never reads as "stripped" again.

* ci: capture-then-test the disassembly greps (the SIGPIPE false negative)

grep -q exits on its first match; under pipefail the still-writing
printf then dies of SIGPIPE and the pipeline reads 141 — a false
"not found". POSIX legs never noticed: their objdump honors
--disassemble=main tightly (a screenful, under the pipe buffer). A
binutils PE objdump's span runs from the symbol to the next data
island — 4,700 lines on the windows leg — so grep matched early,
printf was killed, and run 34759975017 failed the forwarding check
while its own evidence printout showed main calling
tebako_driver_boot at 140da1789. Same discipline as the nm side
already documented in this script: capture first, then test.

* windows-msys: bare boot pins driver + getpath warnings, not exit status (round-9)

Run 34760958198's windows leg failed the bare scenario on its POSIX
assertions: the child printed 'unreached' and exited 0, with only the
driver warning and getpath's "Could not find platform independent
libraries <prefix>" on stderr.

Mechanism, established against the 3.14.7 sources: the encodings import
at startup is unconditional (pylifecycle.c _PyUnicode_InitEncodings ->
unicodeobject.c _PyCodec_InitRegistry, fatal on failure), so a child
that ran user code necessarily FOUND a stdlib. The channel is upstream:
Modules/getpath.py's winreg section (os_name == 'nt' and
use_environment and winreg) appends the HKCU/HKLM
SOFTWARE\Python\PythonCore\<ver>\PythonPath registry values to sys.path
— and the GHA runner has a registered host python. Bare boot on windows
is therefore environment-dependent by upstream design: exit status and
the encodings fatal are not assertable there.

The msys bare scenario now pins the deterministic surface instead: the
driver's bare-boot warning (ours, every platform) plus getpath's
platstdlib warning (the prefix fell back to the baked-in mount root,
which never exists on the runner). POSIX keeps the strict trio
(non-zero exit + driver warning + encodings fatal) — no registry
fallback exists there.

---------

Co-authored-by: tebako-ci <tebako@ribose.com>
@ronaldtse
ronaldtse deleted the feat/windows-msys-port branch September 15, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants