diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56864f3..03464c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,10 @@ jobs: PYTHONOS_QEMU_ACCEL: tcg PYTHONOS_GUI_BOOT_TIMEOUT: "90" PYTHONOS_AUDIO_BOOT_TIMEOUT: "90" + # Both empty except on the macOS Intel runner, where the readers fall + # back to their defaults. + PYTHONOS_BUILD_JOBS: ${{ matrix.build_jobs }} + PYTHONOS_GUI_COMMAND_TIMEOUT: ${{ matrix.gui_command_timeout }} strategy: fail-fast: false matrix: @@ -41,6 +45,12 @@ jobs: # replace with macos-latest (Apple Silicon) without validating Docker. - name: macos-x86_64 runner: macos-15-intel + # One compiler at a time, and a longer GUI command budget: this + # runner builds and boots inside a QEMU-backed colima VM, so both + # the cross compiler and the guest are markedly slower than the + # Linux hosts. + build_jobs: "1" + gui_command_timeout: "240" qemu_bin: qemu-system-x86_64 artifact: pythonos-macos-x86_64 artifact_path: build/pythonos.iso diff --git a/GNUmakefile b/GNUmakefile index 2217773..14a5b29 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -464,7 +464,8 @@ docker-build: # ── CPython library (slow, cached — only rebuild if missing) ───────────────── $(LIBPYTHON): tools/setup_cpython.sh .docker-image - $(DOCKER_RUN) -e PYTHONOS_FREE_THREADING=$(PYTHONOS_FREE_THREADING) $(DOCKER_IMG) \ + $(DOCKER_RUN) -e PYTHONOS_FREE_THREADING=$(PYTHONOS_FREE_THREADING) \ + -e PYTHONOS_BUILD_JOBS=$(PYTHONOS_BUILD_JOBS) $(DOCKER_IMG) \ ./tools/setup_cpython.sh --build # ── Source file sets ($(wildcard) is evaluated by Make, not a subshell) ────── diff --git a/tests/gui_smoke_test.py b/tests/gui_smoke_test.py index 4c0cdb0..a064e57 100644 --- a/tests/gui_smoke_test.py +++ b/tests/gui_smoke_test.py @@ -31,9 +31,26 @@ QemuMonitor, parse_ppm, sample_pixel, color_close, ) +def _env_seconds(name: str, default: float) -> float: + """Read a timeout from the environment, falling back on anything unusable. + + A workflow that sets this from an unset matrix key passes an empty string + rather than leaving the variable out, and bare float("") raises. Treat + empty, non-numeric and non-positive values as "not configured" so a typo + cannot turn a timeout into a crash -- or into zero. + """ + raw = os.environ.get(name, "") + try: + value = float(raw) + except ValueError: + return default + return value if value > 0 else default + + ISO = sys.argv[1] if len(sys.argv) > 1 else "build/pythonos.iso" PORT = int(os.environ.get("PYTHONOS_GUI_HOST_PORT", "5559")) -BOOT_TIMEOUT = float(os.environ.get("PYTHONOS_GUI_BOOT_TIMEOUT", "30")) +BOOT_TIMEOUT = _env_seconds("PYTHONOS_GUI_BOOT_TIMEOUT", 30.0) +COMMAND_TIMEOUT = _env_seconds("PYTHONOS_GUI_COMMAND_TIMEOUT", 15.0) SERIAL_LOG = "/tmp/pythonos-gui-smoke.log" MONITOR_SOCK = "/tmp/pythonos-gui-smoke.mon.sock" @@ -84,7 +101,7 @@ def _send(s: socket.socket, line: str, wait: float = 2.5) -> str: chunks = [] # Cross-architecture TCG can exceed the old 1-2.5 second budgets. This is # a deadline, not a sleep: successful commands still return immediately. - deadline = time.time() + max(wait, float(os.environ.get("PYTHONOS_GUI_COMMAND_TIMEOUT", "15"))) + deadline = time.time() + max(wait, COMMAND_TIMEOUT) marker_seen = False complete = False while time.time() < deadline: diff --git a/tools/setup_cpython.sh b/tools/setup_cpython.sh index 588291f..5cdcc6f 100755 --- a/tools/setup_cpython.sh +++ b/tools/setup_cpython.sh @@ -404,7 +404,16 @@ if [[ "$BUILD_REQUESTED" == "1" ]]; then # PYTHON_FOR_FREEZE — use python3.14 for non-bootstrap freeze # -W Makefile -W Modules/config.c: additionally tell make both are up-to-date # so the self-regen recipe is skipped if possible. - make -j"$(nproc)" "$LIBPYTHON_ARCHIVE" \ + # PYTHONOS_BUILD_JOBS caps parallelism for hosts that cannot feed nproc + # compilers at once. The macOS Intel runner builds inside a 6 GB colima VM, + # where three concurrent cc1 processes on CPython's larger generated + # translation units crash the compiler -- a different file each time. + jobs="${PYTHONOS_BUILD_JOBS:-$(nproc)}" + case "$jobs" in + ''|*[!0-9]*|0) jobs="$(nproc)" ;; + esac + echo "[setup_cpython] building libpython with -j$jobs" + make -j"$jobs" "$LIBPYTHON_ARCHIVE" \ -W Makefile \ -W Modules/config.c \ 'FREEZE_MODULE_BOOTSTRAP=python3.14 ./Programs/_freeze_module.py' \