#!/usr/bin/env bash # # Minimal reproducible MSVC 6.0 compile probe for the Windows 9x port. # # Runs the external MSVC600 `cl.exe` under Wine against the CPython 3.11.16 # baseline in ./cpython, using the project-local compatibility headers in # ./compat/msvc600. Compile-only (/c): this probe measures how far the VC6 # front end gets through the public headers, it does not attempt to link. # # The MSVC600 bundle is an external, local-only prerequisite; nothing under # MSVC600/ is part of this project's source. # # Usage: # scripts/vc6-probe.sh # probe Programs/python.c # scripts/vc6-probe.sh path/to/other.c # probe another translation unit # # Environment: # MSVC600_ROOT override the MSVC600 location (default: /MSVC600) # WINEPREFIX override the Wine prefix (default: ~/.wine-win9xport) set -u # Absolute POSIX path -> Wine drive Z: path. winpath() { printf 'Z:%s' "${1//\//\\}"; } # Strip a literal (non-regex) prefix from every line of stdin. # # sed and bash glob patterns both treat backslashes in the *pattern* as # escape characters, so passing a Windows path (full of literal `\` # separators) as a sed/glob pattern silently fails to match: `\h`, `\u`, # etc. are consumed as (mostly no-op) escapes instead of literal # characters. Quoting the parameter inside a bash `${var//pattern/}` # expansion forces literal (non-glob) matching, which sidesteps the # problem entirely. strip_prefix() { local prefix="$1" line while IFS= read -r line || [ -n "$line" ]; do printf '%s\n' "${line//"$prefix"/}" done } run_probe() { REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" MSVC600_ROOT="${MSVC600_ROOT:-$REPO_ROOT/MSVC600}" BUILD_DIR="$REPO_ROOT/build/vc6-probe" SOURCE_REL="${1:-cpython/Programs/python.c}" SOURCE_ABS="$REPO_ROOT/$SOURCE_REL" CL="$MSVC600_ROOT/VC98/Bin/CL.EXE" VCVARS="$MSVC600_ROOT/VC98/Bin/VCVARS32.BAT" for required in "$CL" "$VCVARS" "$MSVC600_ROOT/VC98/Include" \ "$MSVC600_ROOT/Common/MSDev98/Bin" "$SOURCE_ABS"; do if [ ! -e "$required" ]; then echo "vc6-probe: missing prerequisite: $required" >&2 return 2 fi done if ! command -v wine >/dev/null 2>&1; then echo "vc6-probe: wine is not installed" >&2 return 2 fi export WINEPREFIX="${WINEPREFIX:-$HOME/.wine-win9xport}" export WINEDEBUG="${WINEDEBUG:--all}" # CL.EXE loads C1/C2 from VC98/Bin and MSPDB60.DLL from Common/MSDev98/Bin. export WINEPATH="$(winpath "$MSVC600_ROOT/VC98/Bin");$(winpath "$MSVC600_ROOT/Common/MSDev98/Bin")" mkdir -p "$BUILD_DIR" INCLUDE_ARGS=( "$(winpath "$REPO_ROOT/compat/msvc600")" "$(winpath "$REPO_ROOT/cpython/Include")" "$(winpath "$REPO_ROOT/cpython/PC")" "$(winpath "$MSVC600_ROOT/VC98/Include")" ) echo "== MSVC600 compile probe ==" echo "source : $SOURCE_REL" echo "prefix : $WINEPREFIX" echo CMD="call $(winpath "$VCVARS") && cd /d $(winpath "$REPO_ROOT/cpython") && cl /nologo" for include_arg in "${INCLUDE_ARGS[@]}"; do CMD+=" /I $include_arg" done CMD+=" /c $(winpath "$SOURCE_ABS") /Fo$(winpath "$BUILD_DIR")\\" wine cmd /c "$CMD" 2>&1 | grep -v '^[0-9a-f]\{4\}:err:' | strip_prefix "$(winpath "$REPO_ROOT")\\" status="${PIPESTATUS[0]}" echo echo "== cl.exe exit status: $status ==" return "$status" } # Allow `source scripts/vc6-probe.sh` (e.g. from tests) to pull in # winpath/strip_prefix without running the probe itself. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then run_probe "$@" exit "$?" fi