Skip to content

9.6: the release session — 2.0.0, checked on the Mac and on Windows (… #265

9.6: the release session — 2.0.0, checked on the Mac and on Windows (…

9.6: the release session — 2.0.0, checked on the Mac and on Windows (… #265

Workflow file for this run

name: build
# Builds the tap.python package on macOS and Windows.
# The embedded Python runtime is installed by scripts/install-runtime.*
# before configuring, since the externals link against it.
#
# The host-independent core (core/) is also built and tested on Linux against
# CPython 3.13, plain and under sanitizers — no Max, min-api or runtime install.
# The external and its mock-kernel unit test build on Linux too (Max does not
# run there, but the glue does), embedding the same CPython 3.13.
on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:
# Least privilege: the jobs only read the repository. Third-party actions are pinned by
# commit SHA (the tag each SHA was cut from is noted beside it).
permissions:
contents: read
# A newer push to the same branch or PR supersedes the run in progress.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
linux-core:
name: linux-core (${{ matrix.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: release
build_type: Release
sanitize: ""
- name: asan-ubsan
build_type: Debug
sanitize: "address,undefined"
- name: tsan
build_type: Debug
sanitize: "thread"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.13"
- name: Install the examples' dependencies into the embedded interpreter's prefix
run: 'python -m pip install --require-hashes --only-binary :all: -r scripts/requirements.lock'
# The sanitizer runtimes cannot map their shadow memory with the runner
# kernel's default ASLR entropy ("unexpected memory mapping").
- name: Reduce ASLR entropy for the sanitizers
if: matrix.sanitize != ''
run: sudo sysctl vm.mmap_rnd_bits=28
- name: Configure
run: >
cmake -S core -B build-core
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DPython3_EXECUTABLE="$(which python)"
-DTAP_PYTHON_SANITIZE="${{ matrix.sanitize }}"
- name: Build
run: cmake --build build-core --parallel
- name: Test
env:
TAP_PYTHON_TEST_REQUIRE_EXAMPLES: "1"
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
TSAN_OPTIONS: halt_on_error=1
run: ctest --test-dir build-core --output-on-failure --parallel 4
# The glue test under sanitizers as well (plan 9.3): tap.python's lock, and a reload racing a
# message on another thread, live in the glue, which the core's battery cannot see.
linux-max-glue:
name: linux-max-glue (${{ matrix.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: release
build_type: Release
sanitize: ""
- name: asan-ubsan
build_type: Debug
sanitize: "address,undefined"
- name: tsan
build_type: Debug
sanitize: "thread"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
submodules: recursive
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.13"
- name: Install the examples' dependencies into the embedded interpreter's prefix
run: 'python -m pip install --require-hashes --only-binary :all: -r scripts/requirements.lock'
# The sanitizer runtimes cannot map their shadow memory with the runner
# kernel's default ASLR entropy ("unexpected memory mapping").
- name: Reduce ASLR entropy for the sanitizers
if: matrix.sanitize != ''
run: sudo sysctl vm.mmap_rnd_bits=28
- name: Configure
run: >
cmake -S . -B build
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DPython3_EXECUTABLE="$(which python)"
-DTAP_PYTHON_SANITIZE="${{ matrix.sanitize }}"
- name: Build
run: cmake --build build --parallel
- name: Test
env:
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
TSAN_OPTIONS: halt_on_error=1
run: ctest --test-dir build --output-on-failure
# The Windows external delay-loads python3xx.dll (plan 4.2), which MSVC allows only while
# the external imports no CPython data symbols. The same headers make the same references
# here, so a regression fails this step by name rather than the Windows link.
- name: Verify the external imports no CPython data symbols
if: matrix.sanitize == ''
run: |
libpython=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR') + '/' + sysconfig.get_config_var('LDLIBRARY'))")
scripts/check-data-imports.sh externals/tap.python~.mxl_x86_64 "$libpython"
# What is written by hand, which no build makes and no test runs (plan 9.4): the reference
# pages, the guide and the tutorials well-formed and linked, the patchers' patch cords whole.
- name: Check the hand-made documentation
if: matrix.sanitize == ''
run: python scripts/check-docs.py
# The release packaging's license collection (plan 4.6), against this interpreter's own
# install, which is laid out like the macOS runtime and has the locked packages in it.
- name: Collect the licenses a package would ship
if: matrix.sanitize == ''
run: |
python scripts/assemble-package.py --licenses-only \
--support "$(python -c 'import sys; print(sys.base_prefix)')" --output build/licenses-check
cat build/licenses-check/licenses/README.md
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
submodules: recursive
# The runtime is exactly what the lock files pin, so it is cached on them (and on
# the installer itself); any change to either reinstalls it.
- name: Cache the Python runtime
id: runtime
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: support
key: runtime-macos-universal-${{ hashFiles('scripts/runtime.lock', 'scripts/requirements.lock', 'scripts/install-runtime.sh') }}
- name: Install Python runtime (universal libpython for linking)
if: steps.runtime.outputs.cache-hit != 'true'
run: ./scripts/install-runtime.sh --universal
- name: Configure (universal arm64 + x86_64)
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
- name: Build
run: cmake --build build --config Release
- name: Test
run: ctest --test-dir build --output-on-failure
- name: Verify externals are universal
run: |
set -e
ls -la externals
for mxo in externals/*.mxo; do
bin="$mxo/Contents/MacOS/$(basename "$mxo" .mxo)"
echo "== $mxo =="
lipo -info "$bin"
lipo -info "$bin" | grep -q "arm64" && lipo -info "$bin" | grep -q "x86_64" \
|| { echo "ERROR: $bin is not universal"; exit 1; }
done
# max-sdk-base leaves the identifier for Xcode to expand; the object's CMakeLists expands it
- name: Verify the bundle identifier is expanded
run: |
id=$(plutil -extract CFBundleIdentifier raw "externals/tap.python~.mxo/Contents/Info.plist")
echo "CFBundleIdentifier: $id"
[ "$id" = "com.74objects.tap.python-tilde" ] \
|| { echo "ERROR: expected com.74objects.tap.python-tilde"; exit 1; }
- name: Verify libpython resolves via a relative rpath only
run: |
bin="externals/tap.python~.mxo/Contents/MacOS/tap.python~"
otool -L "$bin"
otool -L "$bin" | grep -q "@rpath/libpython" \
|| { echo "ERROR: libpython is not linked via @rpath"; exit 1; }
rpaths=$(otool -l "$bin" | awk '/cmd LC_RPATH/ { getline; getline; print $2 }')
echo "rpaths: $rpaths"
echo "$rpaths" | grep -q "^@loader_path/" \
|| { echo "ERROR: the @loader_path rpath to support/lib is missing"; exit 1; }
if echo "$rpaths" | grep -q "^/"; then
echo "ERROR: an absolute build-machine path is embedded as an rpath"; exit 1
fi
# Weakly linked, so the external loads without a runtime and says what is missing (plan 4.2).
- name: Verify libpython is weakly linked
run: |
bin="externals/tap.python~.mxo/Contents/MacOS/tap.python~"
otool -l "$bin" | grep -A2 "cmd LC_LOAD_WEAK_DYLIB" | tee /dev/stderr | grep -q "@rpath/libpython" \
|| { echo "ERROR: libpython is not weakly linked"; exit 1; }
# The package as a release assembles it, which fails unless it is complete — init/tap.python.txt
# above all, the mapping without which Max does not find tap.python (plan 9.3).
- name: Assemble the package
run: support/bin/python3 scripts/assemble-package.py --platform macos --output build/package
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: tap-python-macos
path: externals/*.mxo
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
submodules: recursive
- name: Cache the Python runtime
id: runtime
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: support
key: runtime-windows-${{ hashFiles('scripts/runtime.lock', 'scripts/requirements.lock', 'scripts/install-runtime.ps1') }}
- name: Install Python runtime
if: steps.runtime.outputs.cache-hit != 'true'
run: powershell -ExecutionPolicy Bypass -File scripts\install-runtime.ps1
- name: Configure
run: cmake -S . -B build -A x64
- name: Build
run: cmake --build build --config Release
- name: Test
run: |
$env:PATH = "$PWD\support;$env:PATH" # so the test binary finds python3xx.dll
ctest --test-dir build --output-on-failure -C Release
- name: List externals
run: dir externals
# Delay-loaded, so the external loads without a runtime and says what is missing (plan 4.2).
- name: Verify python3xx.dll is delay-loaded
shell: pwsh
run: |
$dumpbin = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -find "VC\Tools\MSVC\*\bin\Hostx64\x64\dumpbin.exe" | Select-Object -First 1
$deps = & $dumpbin /dependents "externals\tap.python~.mxe64" | Out-String
Write-Output $deps
if ($deps -notmatch "(?s)delay load dependencies:.*python3\d+\.dll") {
Write-Error "python3xx.dll is not delay-loaded"; exit 1
}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: tap-python-windows
path: externals/*.mxe64