build-engine-windows #4
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-engine-windows | |
| # Builds OUR OWN WinCairo WebKit engine FROM SOURCE on a Windows runner, relocates | |
| # it into the Bunmaska engine-store layout, and proves it loads from the store — | |
| # the Windows peer of `build-engine.yml` (which relocates apt's WebKitGTK on Linux). | |
| # Windows has no system WebKit and we never ship Playwright's build, so the engine | |
| # is our own from-source binary. | |
| # | |
| # This recipe mirrors the one proven locally end-to-end (a real BrowserWindow loaded | |
| # the from-source engine from the store: STORE_ENGINE_OK). Notes baked in from that: | |
| # * WinCairo dropped MSVC — it builds with clang-cl, so we install LLVM 20. | |
| # * WebKit's code-gen needs gperf on PATH. | |
| # * A short WEBKIT_OUTPUTDIR avoids Windows' 250-char object-path limit in vcpkg. | |
| # * patch-webkit-wincairo.py routes ~300 serializer inputs through a response file | |
| # (otherwise the inline command line overflows cmd.exe's ~8191-char limit). | |
| # * `ninja -k 0` finishes past a broken dev-tooling target (compile_commands.json) | |
| # that no product DLL depends on; success = WebKit2.dll exists. | |
| # | |
| # Heavy (~hours, GBs): runs on demand / on the engine branch only. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| webkit_tag: | |
| description: 'WebKit git tag to build (STABLE train only - even minor, e.g. wpewebkit-2.52.5)' | |
| required: true | |
| default: 'wpewebkit-2.52.5' | |
| llvm: | |
| description: 'LLVM version for clang-cl (older WebKit trains may need an older clang)' | |
| required: false | |
| default: '20.1.8' | |
| push: | |
| branches: [feat/windows-engine] | |
| jobs: | |
| build: | |
| name: build WinCairo from source (windows) | |
| runs-on: windows-latest | |
| timeout-minutes: 350 | |
| env: | |
| # Hosted engines track the STABLE WPE train only (even minor; WSAs land | |
| # there). Development tags (odd minor) are for local experiments, never | |
| # the feed. | |
| WEBKIT_TAG: ${{ github.event.inputs.webkit_tag || 'wpewebkit-2.52.5' }} | |
| # Short build-output root (WebKit honours WEBKIT_OUTPUTDIR): vcpkg's ICU build | |
| # generates pathologically deep try-compile object paths that blow past | |
| # Windows' 250-char limit when nested under the workspace. | |
| WEBKIT_OUTPUTDIR: C:\wkb | |
| LLVM_VER: ${{ github.event.inputs.llvm || '20.1.8' }} | |
| LLVM_DIR: C:\llvm\clang+llvm-${{ github.event.inputs.llvm || '20.1.8' }}-x86_64-pc-windows-msvc | |
| # Publish steps are gated on these so forks / unconfigured runs still build | |
| # + prove the engine, skipping pack/sign/upload when the secrets are absent. | |
| HAVE_KEY: ${{ secrets.ENGINE_SIGNING_KEY != '' }} | |
| # R2_PUBLISH_TOKEN is R2-only, separate from the Pages-scoped | |
| # CLOUDFLARE_API_TOKEN deploy-web.yml uses (which has no R2 perms). | |
| HAVE_R2: ${{ secrets.R2_PUBLISH_TOKEN != '' && secrets.CLOUDFLARE_ACCOUNT_ID != '' }} | |
| steps: | |
| - name: Checkout bunmaska | |
| uses: actions/checkout@v4 | |
| with: | |
| path: bunmaska | |
| # Clone WebKit to a SHORT path (C:\WebKit) at the pinned tag; shallow (history | |
| # is enormous, the build only needs the tree at that revision). | |
| - name: Checkout WebKit at the pinned tag | |
| run: git clone --depth 1 --branch "$env:WEBKIT_TAG" https://github.com/WebKit/WebKit.git C:\WebKit | |
| shell: pwsh | |
| - name: Apply Bunmaska's WinCairo build patch (serializers response file) | |
| run: python bunmaska/tools/engine/patch-webkit-wincairo.py C:\WebKit | |
| shell: pwsh | |
| # cmake + VS are preinstalled; add ninja, perl, ruby, gperf. | |
| - name: Install build tools (ninja, perl, ruby, gperf) | |
| run: | | |
| choco install -y ninja strawberryperl ruby | |
| curl.exe -L -A "Mozilla/5.0" -o "$env:TEMP\gperf.zip" ` | |
| "https://master.dl.sourceforge.net/project/ezwinports/gperf-3.1-w32-bin.zip?viasf=1" | |
| Expand-Archive "$env:TEMP\gperf.zip" -DestinationPath C:\gperf -Force | |
| shell: pwsh | |
| # WinCairo builds with clang-cl (the find_library for clang_rt.builtins | |
| # resolves relative to the clang-cl compiler). Version is an input so an | |
| # older WebKit train can pin the clang it was contemporary with. | |
| - name: Install LLVM (clang-cl) | |
| run: | | |
| curl.exe -L -o "$env:TEMP\llvm.tar.xz" ` | |
| "https://github.com/llvm/llvm-project/releases/download/llvmorg-$env:LLVM_VER/clang%2Bllvm-$env:LLVM_VER-x86_64-pc-windows-msvc.tar.xz" | |
| New-Item -ItemType Directory -Force -Path C:\llvm | Out-Null | |
| tar.exe -xf "$env:TEMP\llvm.tar.xz" -C C:\llvm | |
| shell: pwsh | |
| - name: Set up MSVC (x64) | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| with: | |
| arch: x64 | |
| # build-webkit configures (auto-building the vcpkg deps) and compiles; it stops | |
| # on the broken dev-tooling target, so a follow-up `ninja -k 0` finishes the | |
| # product DLLs. clang-cl is the compiler (CC/CXX); gperf + clang on PATH. | |
| - name: Build WebKit (WinCairo, Release, clang-cl) | |
| working-directory: C:\WebKit | |
| env: | |
| CC: ${{ env.LLVM_DIR }}\bin\clang-cl.exe | |
| CXX: ${{ env.LLVM_DIR }}\bin\clang-cl.exe | |
| run: | | |
| $env:Path = "$env:LLVM_DIR\bin;C:\gperf\bin;$env:Path" | |
| perl Tools/Scripts/build-webkit --wincairo --release | |
| if (-not (Test-Path "$env:WEBKIT_OUTPUTDIR\Release\bin\WebKit2.dll")) { | |
| Write-Host "finishing past the dev-tooling target with ninja -k 0 ..." | |
| ninja -C "$env:WEBKIT_OUTPUTDIR\Release" -k 0 | |
| } | |
| if (-not (Test-Path "$env:WEBKIT_OUTPUTDIR\Release\bin\WebKit2.dll")) { | |
| Write-Error "WebKit2.dll was not produced"; exit 1 | |
| } | |
| shell: pwsh | |
| # A failed CMake probe (e.g. the atomics check) only explains itself in | |
| # CMakeError.log, which dies with the runner - surface it in the job log. | |
| - name: Dump CMake configure logs on failure | |
| if: failure() | |
| run: | | |
| foreach ($f in @("CMakeError.log", "CMakeOutput.log")) { | |
| $p = "$env:WEBKIT_OUTPUTDIR\Release\CMakeFiles\$f" | |
| if (Test-Path $p) { | |
| Write-Host "===== tail of $f =====" | |
| Get-Content $p -Tail 150 | |
| } | |
| } | |
| shell: pwsh | |
| - name: Compute the engine id from the tag | |
| id: id | |
| run: | | |
| $ver = "$env:WEBKIT_TAG" -replace '^(wpe|webkit)webkit-?|^webkitgtk-', '' | |
| "engine_id=webkit-2-$ver-bunmaska1-windows-x64" >> $env:GITHUB_OUTPUT | |
| shell: pwsh | |
| # Relocate the from-source closure (Release/bin) into the store, mark installed. | |
| - name: Relocate into the engine store | |
| run: | | |
| $store = "$env:RUNNER_TEMP\store" | |
| & bunmaska/tools/engine/build-wincairo-windows.ps1 ` | |
| -Source "$env:WEBKIT_OUTPUTDIR\Release\bin" -OutDir $store ` | |
| -EngineId "${{ steps.id.outputs.engine_id }}" | |
| New-Item -ItemType File -Force ` | |
| -Path "$store\${{ steps.id.outputs.engine_id }}\INSTALLATION_COMPLETE" | Out-Null | |
| shell: pwsh | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: bunmaska/.bun-version | |
| - name: Install bun deps | |
| working-directory: bunmaska | |
| run: bun install --frozen-lockfile | |
| # Prove the relocated engine works resolved purely from the store (no | |
| # BUNMASKA_WEBKIT_PATH): a real BrowserWindow + executeJavaScript. | |
| - name: Prove it loads from the store | |
| working-directory: bunmaska | |
| env: | |
| BUNMASKA_ENGINES_PATH: ${{ runner.temp }}\store | |
| BUNMASKA_WEBKIT_ID: ${{ steps.id.outputs.engine_id }} | |
| run: | | |
| $out = bun run tools/engine/windows-engine-load-probe.ts 2>&1 | Out-String | |
| Write-Host $out | |
| if ($out -notmatch 'STORE_ENGINE_OK') { Write-Error "engine did not load from the store"; exit 1 } | |
| shell: pwsh | |
| - name: Upload the engine artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ steps.id.outputs.engine_id }} | |
| path: ${{ runner.temp }}\store\${{ steps.id.outputs.engine_id }} | |
| retention-days: 7 | |
| # Pack + sign the engine into the 3-file feed layout, then publish to R2. | |
| # Gated on the signing key being present, so forks / unconfigured runs still | |
| # build + prove the engine without needing the release secret. | |
| - name: Pack + sign the engine for the feed | |
| if: ${{ env.HAVE_KEY == 'true' }} | |
| working-directory: bunmaska | |
| env: | |
| ENGINE_SIGNING_KEY: ${{ secrets.ENGINE_SIGNING_KEY }} | |
| run: | | |
| $key = "$env:RUNNER_TEMP\engine-signing-key.pem" | |
| Set-Content -Path $key -Value $env:ENGINE_SIGNING_KEY -NoNewline | |
| bun tools/engine/pack-engine.ts ` | |
| "$env:RUNNER_TEMP\store\${{ steps.id.outputs.engine_id }}" ` | |
| "$env:RUNNER_TEMP\feed" $key | |
| Remove-Item $key -Force | |
| shell: pwsh | |
| - name: Publish the signed engine to R2 | |
| if: ${{ env.HAVE_KEY == 'true' && env.HAVE_R2 == 'true' }} | |
| working-directory: bunmaska | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.R2_PUBLISH_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: | | |
| bun tools/engine/publish-engine-r2.ts ` | |
| "$env:RUNNER_TEMP\feed" "${{ steps.id.outputs.engine_id }}" --bucket bunmaska-engines | |
| shell: pwsh |