Build ThorVG natives #2
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
| # Builds ThorVG from source, once per RID, and stages each library at the path it will occupy | |
| # in this repository so the CD can merge the artifacts straight over the working tree. | |
| # | |
| # ThorVG publishes no binaries: every release carries a single source tarball. So unlike a | |
| # binding that downloads official libraries, this repository has to compile its own, and the | |
| # managed layer and the libraries must come from the same tag or the P/Invokes describe one | |
| # revision while the binary is another. | |
| name: Build ThorVG natives | |
| on: | |
| workflow_call: | |
| inputs: | |
| ref: | |
| description: 'ThorVG tag to build' | |
| required: false | |
| type: string | |
| default: 'v1.1.0' | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: 'ThorVG tag to build' | |
| required: false | |
| type: string | |
| default: 'v1.1.0' | |
| only-rid: | |
| description: 'Build a single RID (e.g. linux-arm64). Empty builds all four.' | |
| required: false | |
| type: string | |
| default: '' | |
| jobs: | |
| build: | |
| name: ${{ matrix.rid }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-24.04 | |
| rid: linux-x64 | |
| # A native ARM runner rather than a cross toolchain: meson cross-compilation needs a | |
| # cross file, and ThorVG ships none for linux-arm64. | |
| - os: ubuntu-24.04-arm | |
| rid: linux-arm64 | |
| - os: windows-latest | |
| rid: win-x64 | |
| - os: macos-latest | |
| rid: osx-arm64 | |
| steps: | |
| - name: Checkout ThorVG | |
| if: inputs.only-rid == '' || inputs.only-rid == matrix.rid | |
| uses: actions/checkout@v5 | |
| with: | |
| repository: thorvg/thorvg | |
| ref: ${{ inputs.ref || 'v1.1.0' }} | |
| - name: Setup Python | |
| if: inputs.only-rid == '' || inputs.only-rid == matrix.rid | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install meson and ninja | |
| if: inputs.only-rid == '' || inputs.only-rid == matrix.rid | |
| run: pip install meson ninja | |
| # ThorVG's own Windows CI does this; meson needs cl.exe on PATH. | |
| - name: Setup MSVC | |
| if: (inputs.only-rid == '' || inputs.only-rid == matrix.rid) && startsWith(matrix.rid, 'win-') | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| # -Dbindings=capi is the flag that matters: it is OFF by default, and without it the | |
| # library builds perfectly and exports not one tvg_* symbol. | |
| # -Dextra drops openmp, which would otherwise pull a libomp/vcomp runtime alongside the | |
| # library that the NuGet does not ship. | |
| # | |
| # Split by platform because bash on the Windows runner is Git bash, and it puts | |
| # C:\Program Files\Git\usr\bin ahead of the MSVC toolchain — meson then finds GNU link.exe | |
| # instead of MSVC's and refuses to configure. | |
| - name: Configure and build (Unix) | |
| if: (inputs.only-rid == '' || inputs.only-rid == matrix.rid) && !startsWith(matrix.rid, 'win-') | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| meson setup build \ | |
| --buildtype=release \ | |
| -Ddefault_library=shared \ | |
| -Dbindings=capi \ | |
| -Dengines=cpu \ | |
| -Dloaders=all \ | |
| -Dsavers=all \ | |
| -Dextra=lottie_exp \ | |
| -Dstatic=false \ | |
| -Dlog=false \ | |
| -Dtools='' \ | |
| -Dtests=false | |
| meson compile -C build | |
| - name: Configure and build (Windows) | |
| if: (inputs.only-rid == '' || inputs.only-rid == matrix.rid) && startsWith(matrix.rid, 'win-') | |
| shell: pwsh | |
| run: | | |
| meson setup build --buildtype=release -Ddefault_library=shared -Dbindings=capi -Dengines=cpu -Dloaders=all -Dsavers=all -Dextra=lottie_exp -Dstatic=false -Dlog=false -Dtools='' -Dtests=false | |
| if ($LASTEXITCODE -ne 0) { exit 1 } | |
| meson compile -C build | |
| if ($LASTEXITCODE -ne 0) { exit 1 } | |
| # The library is named after the major version — thorvg-1.dll, libthorvg-1.so.1, | |
| # libthorvg-1.1.dylib — and on Unix the plain name is a symlink, which does not survive | |
| # NuGet packaging. Copy the real file under a stable name so DllImport("thorvg") keeps | |
| # resolving when ThorVG goes to 2.x. | |
| - name: Stage into the runtimes layout | |
| if: inputs.only-rid == '' || inputs.only-rid == matrix.rid | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| native="out/Evergine.Bindings.ThorVG/runtimes/${{ matrix.rid }}/native" | |
| mkdir -p "$native" | |
| case "${{ matrix.rid }}" in | |
| win-*) | |
| cp "$(find build -name 'thorvg*.dll' -print -quit)" "$native/thorvg.dll" ;; | |
| linux-*) | |
| cp "$(readlink -f "$(find build -name 'libthorvg*.so*' -print -quit)")" "$native/libthorvg.so" ;; | |
| osx-*) | |
| cp "$(readlink -f "$(find build -name 'libthorvg*.dylib' -print -quit)")" "$native/libthorvg.dylib" ;; | |
| esac | |
| find out -type f -exec ls -l {} \; | |
| # Two ways this build fails without failing: -Dstatic or a missing -Ddefault_library=shared | |
| # yields only an archive, and a missing -Dbindings=capi yields a library with no C API at | |
| # all. Both would upload happily and be committed as a working platform. | |
| - name: Verify the library is real and exports the C API | |
| if: inputs.only-rid == '' || inputs.only-rid == matrix.rid | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| lib=$(find out -type f | head -1) | |
| size=$(stat -c%s "$lib" 2>/dev/null || stat -f%z "$lib") | |
| echo "$lib: $size bytes" | |
| [ "$size" -gt 300000 ] || { echo "::error::$lib is only $size bytes"; exit 1; } | |
| # The symbol table is captured before it is searched rather than piped into grep -q: | |
| # under `set -o pipefail`, grep -q exits at the first match, nm dies of SIGPIPE, and | |
| # the pipeline reports failure precisely when the symbol WAS found. | |
| case "${{ matrix.rid }}" in | |
| linux-*) symbols=$(nm -D --defined-only "$lib" || true) ;; | |
| osx-*) symbols=$(nm -gU "$lib" || true) ;; | |
| # No dumpbin under bash and no strings on the Windows image; an export name is | |
| # plain ASCII in the export table, so reading the file as text finds it. | |
| win-*) symbols=$(grep -a -o 'tvg_engine_init' "$lib" || true) ;; | |
| esac | |
| case "$symbols" in | |
| *tvg_engine_init*) echo "tvg_engine_init is exported." ;; | |
| *) echo "::error::tvg_engine_init is not exported by $lib -- was -Dbindings=capi lost?"; exit 1 ;; | |
| esac | |
| - name: Upload | |
| if: inputs.only-rid == '' || inputs.only-rid == matrix.rid | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: natives-${{ matrix.rid }} | |
| path: out/ | |
| # Not `warn`: an empty artifact merges over the tree as no change at all, and the | |
| # publish would ship the previous libraries against a newer header. | |
| if-no-files-found: error |