Repository navigation
9.6: the release session — 2.0.0, checked on the Mac and on Windows (… #265
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |