#!/usr/bin/env bash # # Regression tests for scripts/vc6-probe-core.sh and, through it, the VC6 # _Py_ATOMIC_VC6_X86 branch added to # cpython/Include/internal/pycore_atomic.h. # # Skipped (not failed) when wine + MSVC600 are unavailable, matching # scripts/vc6-probe.test.sh's convention, since CI/dev boxes without the # external MSVC600 bundle cannot exercise this. # # Usage: scripts/vc6-probe-core.test.sh set -u REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PROBE="$REPO_ROOT/scripts/vc6-probe-core.sh" fail=0 pass() { echo "ok - $1"; } bad() { echo "not ok - $1"; fail=1; } MSVC600_ROOT="${MSVC600_ROOT:-$REPO_ROOT/MSVC600}" if command -v wine >/dev/null 2>&1 && [ -e "$MSVC600_ROOT/VC98/Bin/CL.EXE" ]; then # --- positive case: the isolated atomics smoke fixture compiles clean --- out="$("$PROBE" scripts/fixtures/vc6_atomic_smoke.c 2>&1)" status=$? if [ "$status" -eq 0 ]; then pass "pycore_atomic.h VC6 branch: atomics smoke fixture compiles (exit 0)" else bad "pycore_atomic.h VC6 branch: atomics smoke fixture compiles (exit $status)" echo "$out" fi if printf '%s' "$out" | grep -qi 'error'; then bad "atomics smoke fixture: no compiler errors in output" else pass "atomics smoke fixture: no compiler errors in output" fi if printf '%s' "$out" | grep -q 'intrin.h'; then bad "atomics smoke fixture: does not hit the intrin.h blocker" else pass "atomics smoke fixture: does not hit the intrin.h blocker" fi # --- negative case: a deliberately invalid atomics usage still fails --- BAD_SRC="$REPO_ROOT/scripts/fixtures/_vc6_atomic_smoke_bad.c" cat > "$BAD_SRC" <<'EOF' #include #include "pycore_atomic.h" int bad(void) { return this_identifier_does_not_exist; } EOF trap 'rm -f "$BAD_SRC"' EXIT out_bad="$("$PROBE" scripts/fixtures/_vc6_atomic_smoke_bad.c 2>&1)" status_bad=$? if [ "$status_bad" -ne 0 ]; then pass "probe-core reports nonzero exit for a broken translation unit" else bad "probe-core reports nonzero exit for a broken translation unit (got: $status_bad)" fi if printf '%s' "$out_bad" | grep -q 'error C'; then pass "probe-core output surfaces the compiler diagnostic" else bad "probe-core output surfaces the compiler diagnostic" fi # --- reference case: Modules/main.c reaches past pycore_atomic.h --- # main.c is expected to fail today (pycore_interp.h needs , # a separate, unrelated VC6 gap) but must not fail *at* # pycore_atomic.h/intrin.h; that would mean the atomics branch # regressed even though the isolated fixture above passed. out_main="$("$PROBE" cpython/Modules/main.c 2>&1)" if printf '%s' "$out_main" | grep -q 'pycore_atomic.h.*intrin.h\|intrin.h.*No such file'; then bad "Modules/main.c probe: does not regress to the pycore_atomic.h/intrin.h blocker" else pass "Modules/main.c probe: does not regress to the pycore_atomic.h/intrin.h blocker" fi # --- positive case: the isolated stdbool smoke fixture compiles clean --- out_bool="$("$PROBE" scripts/fixtures/vc6_stdbool_smoke.c 2>&1)" status_bool=$? if [ "$status_bool" -eq 0 ]; then pass "compat/msvc600/stdbool.h: stdbool smoke fixture compiles (exit 0)" else bad "compat/msvc600/stdbool.h: stdbool smoke fixture compiles (exit $status_bool)" echo "$out_bool" fi if printf '%s' "$out_bool" | grep -qi 'error'; then bad "stdbool smoke fixture: no compiler errors in output" else pass "stdbool smoke fixture: no compiler errors in output" fi # --- negative case: a deliberately invalid bool usage still fails --- BAD_BOOL_SRC="$REPO_ROOT/scripts/fixtures/_vc6_stdbool_smoke_bad.c" cat > "$BAD_BOOL_SRC" <<'EOF' #include int bad(void) { return this_identifier_does_not_exist; } EOF trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC"' EXIT out_bool_bad="$("$PROBE" scripts/fixtures/_vc6_stdbool_smoke_bad.c 2>&1)" status_bool_bad=$? if [ "$status_bool_bad" -ne 0 ]; then pass "probe-core reports nonzero exit for a broken stdbool translation unit" else bad "probe-core reports nonzero exit for a broken stdbool translation unit (got: $status_bool_bad)" fi if printf '%s' "$out_bool_bad" | grep -q 'error C'; then pass "probe-core output surfaces the compiler diagnostic (stdbool case)" else bad "probe-core output surfaces the compiler diagnostic (stdbool case)" fi # --- reference case: Modules/main.c reaches past pycore_interp.h's # include. It is expected to still fail today -- the next, # unrelated blocker is VC6's C89-only parser rejecting C99-style mixed # declarations-and-code in several pycore_*.h inline functions (e.g. # Include/internal/pycore_code.h's _PyLocals_GetKind(), which declares # `char *ptr` after a preceding `assert(...)` statement) -- but it must # not fail *at* anymore; that would mean this slice # regressed even though the isolated fixture above passed. if printf '%s' "$out_main" | grep -q "Cannot open include file: 'stdbool.h'"; then bad "Modules/main.c probe: does not regress to the pycore_interp.h/stdbool.h blocker" else pass "Modules/main.c probe: does not regress to the pycore_interp.h/stdbool.h blocker" fi # --- positive case: the isolated mixed-declarations smoke fixture # compiles clean (pycore_code.h, pycore_dict.h, pycore_list.h, # pycore_call.h all hoisted their C99 mid-block declarations) --- out_decls="$("$PROBE" scripts/fixtures/vc6_mixed_decls_smoke.c 2>&1)" status_decls=$? if [ "$status_decls" -eq 0 ]; then pass "mixed-declarations header slice: smoke fixture compiles (exit 0)" else bad "mixed-declarations header slice: smoke fixture compiles (exit $status_decls)" echo "$out_decls" fi if printf '%s' "$out_decls" | grep -qi 'error'; then bad "mixed-declarations smoke fixture: no compiler errors in output" else pass "mixed-declarations smoke fixture: no compiler errors in output" fi # --- negative case: a deliberately invalid mixed-declarations usage # still fails --- BAD_DECLS_SRC="$REPO_ROOT/scripts/fixtures/_vc6_mixed_decls_smoke_bad.c" cat > "$BAD_DECLS_SRC" <<'EOF' #include "Python.h" #include "pycore_code.h" int bad(void) { return this_identifier_does_not_exist; } EOF trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC" "$BAD_DECLS_SRC"' EXIT out_decls_bad="$("$PROBE" scripts/fixtures/_vc6_mixed_decls_smoke_bad.c 2>&1)" status_decls_bad=$? if [ "$status_decls_bad" -ne 0 ]; then pass "probe-core reports nonzero exit for a broken mixed-declarations translation unit" else bad "probe-core reports nonzero exit for a broken mixed-declarations translation unit (got: $status_decls_bad)" fi if printf '%s' "$out_decls_bad" | grep -q 'error C'; then pass "probe-core output surfaces the compiler diagnostic (mixed-declarations case)" else bad "probe-core output surfaces the compiler diagnostic (mixed-declarations case)" fi # --- reference case: Modules/main.c no longer fails inside any of the # four fixed pycore_*.h headers. It is expected to still fail today -- # Modules/main.c itself uses the same C99 mixed-declarations pattern # pervasively in its own function bodies, a much larger, whole-file # source rewrite that is out of scope for this header-only slice; see # PORT_STATUS.md's "Mixed-declarations header slice" section for the # documented boundary -- but it must not fail *inside* # pycore_code.h/pycore_dict.h/pycore_list.h/pycore_call.h anymore; # that would mean this slice regressed even though the isolated # fixture above passed. if printf '%s' "$out_main" | grep -qE 'pycore_(code|dict|list|call)\.h\([0-9]+\)'; then bad "Modules/main.c probe: does not regress to the pycore_code/dict/list/call.h mixed-declarations blocker" else pass "Modules/main.c probe: does not regress to the pycore_code/dict/list/call.h mixed-declarations blocker" fi # --- positive case: the isolated declaration-order smoke fixture # compiles clean. It exercises, self-contained, the same five hoist # shapes applied to Modules/main.c itself (bare hoist above an early # return, side-effecting-initializer hoist, a hoisted pair where fixing # the first declaration would otherwise turn the second into a new # violation, a nested-block hoist, and a platform-conditional same-name # hoist) so this guards the general transformation, independent of # main.c's own future edits. --- out_decl_order="$("$PROBE" scripts/fixtures/vc6_decl_order_smoke.c 2>&1)" status_decl_order=$? if [ "$status_decl_order" -eq 0 ]; then pass "Modules/main.c declaration-order slice: smoke fixture compiles (exit 0)" else bad "Modules/main.c declaration-order slice: smoke fixture compiles (exit $status_decl_order)" echo "$out_decl_order" fi if printf '%s' "$out_decl_order" | grep -qi 'error'; then bad "declaration-order smoke fixture: no compiler errors in output" else pass "declaration-order smoke fixture: no compiler errors in output" fi # --- negative case: a deliberately invalid declaration-order usage # still fails --- BAD_DECL_ORDER_SRC="$REPO_ROOT/scripts/fixtures/_vc6_decl_order_smoke_bad.c" cat > "$BAD_DECL_ORDER_SRC" <<'EOF' int bad(void) { return this_identifier_does_not_exist; } EOF trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC" "$BAD_DECLS_SRC" "$BAD_DECL_ORDER_SRC"' EXIT out_decl_order_bad="$("$PROBE" scripts/fixtures/_vc6_decl_order_smoke_bad.c 2>&1)" status_decl_order_bad=$? if [ "$status_decl_order_bad" -ne 0 ]; then pass "probe-core reports nonzero exit for a broken declaration-order translation unit" else bad "probe-core reports nonzero exit for a broken declaration-order translation unit (got: $status_decl_order_bad)" fi if printf '%s' "$out_decl_order_bad" | grep -q 'error C'; then pass "probe-core output surfaces the compiler diagnostic (declaration-order case)" else bad "probe-core output surfaces the compiler diagnostic (declaration-order case)" fi # --- reference case: Modules/main.c no longer fails on any C89 # mixed-declaration site anywhere in its own body (the C2146/C2065/ # C2275 "illegal use of this type as an expression" pattern tied to a # declaration keyword). At the time this check was written, main.c # still failed with a distinct, out-of-scope blocker instead (C99 # compound literals/designated initializers, see below); that blocker # is now also fixed (see the compound-literal checks further down), # but this check's job is narrower and unchanged: main.c must not show # the declaration-order-specific error codes; that would mean this # slice regressed even though the isolated fixture above passed. if printf '%s' "$out_main" | grep -qE "main\.c\([0-9]+\) : error C2146|main\.c\([0-9]+\) : error C2065.*undeclared identifier|main\.c\([0-9]+\) : error C2275"; then bad "Modules/main.c probe: does not regress to a C89 mixed-declaration error inside main.c's own body" else pass "Modules/main.c probe: does not regress to a C89 mixed-declaration error inside main.c's own body" fi # --- positive case: the isolated compound-literal/designated- # initializer smoke fixture compiles clean. It exercises, self- # contained (aside from the real Python.h/pycore_initconfig.h types # it needs), the three replacement patterns applied to main.c: calling # PyStatus_Ok() instead of the _PyStatus_OK() compound-literal macro, # explicit field assignment instead of the _PyCompilerFlags_INIT # compound-literal macro, and explicit field assignment instead of a # _PyArgv designated-initializer aggregate. --- out_compound="$("$PROBE" scripts/fixtures/vc6_compound_literal_smoke.c 2>&1)" status_compound=$? if [ "$status_compound" -eq 0 ]; then pass "compound-literal slice: smoke fixture compiles (exit 0)" else bad "compound-literal slice: smoke fixture compiles (exit $status_compound)" echo "$out_compound" fi if printf '%s' "$out_compound" | grep -qi 'error'; then bad "compound-literal smoke fixture: no compiler errors in output" else pass "compound-literal smoke fixture: no compiler errors in output" fi # --- negative case: a deliberately invalid compound-literal-fixture # usage still fails --- BAD_COMPOUND_SRC="$REPO_ROOT/scripts/fixtures/_vc6_compound_literal_smoke_bad.c" cat > "$BAD_COMPOUND_SRC" <<'EOF' #include "Python.h" #include "pycore_initconfig.h" int bad(void) { return this_identifier_does_not_exist; } EOF trap 'rm -f "$BAD_SRC" "$BAD_BOOL_SRC" "$BAD_DECLS_SRC" "$BAD_DECL_ORDER_SRC" "$BAD_COMPOUND_SRC"' EXIT out_compound_bad="$("$PROBE" scripts/fixtures/_vc6_compound_literal_smoke_bad.c 2>&1)" status_compound_bad=$? if [ "$status_compound_bad" -ne 0 ]; then pass "probe-core reports nonzero exit for a broken compound-literal translation unit" else bad "probe-core reports nonzero exit for a broken compound-literal translation unit (got: $status_compound_bad)" fi if printf '%s' "$out_compound_bad" | grep -q 'error C'; then pass "probe-core output surfaces the compiler diagnostic (compound-literal case)" else bad "probe-core output surfaces the compiler diagnostic (compound-literal case)" fi # --- reference case: Modules/main.c itself no longer shows any C2059 # "syntax error : '{'"/"syntax error : '.'" (the compound-literal/ # designated-initializer error signature), and -- since this was the # last recorded blocker for main.c as a compile-only (/c) translation # unit -- now compiles with exit status 0. This is a compile-only # result: no link or runtime success is claimed (vc6-probe-core.sh is # /c only; see its header comment). --- if printf '%s' "$out_main" | grep -q 'main\.c([0-9]*) : error C2059'; then bad "Modules/main.c probe: does not regress to the compound-literal/designated-initializer C2059 blocker" else pass "Modules/main.c probe: does not regress to the compound-literal/designated-initializer C2059 blocker" fi out_main_status="$("$PROBE" cpython/Modules/main.c >/dev/null 2>&1; echo $?)" if [ "$out_main_status" -eq 0 ]; then pass "Modules/main.c probe: compiles clean end-to-end (compile-only, exit 0)" else bad "Modules/main.c probe: compiles clean end-to-end (compile-only, got exit $out_main_status)" fi else echo "skip - wine/MSVC600 not available; skipping live probe-core integration checks" fi if [ "$fail" -ne 0 ]; then echo "FAILED" exit 1 fi echo "ALL TESTS PASSED"