Both jobs failed, at genuinely informative points: - linux-x64: DllNotFoundException loading lingua_physics — the .so committed for scripts/run-sample.sh's local-dev convenience was built on this dev machine's own (newer) glibc, and didn't load on ubuntu-latest's runner. liblingua_audio.so, built the same way, loaded fine there — this wasn't a generic "the file isn't where expected" problem, it was specific to what that one binary happened to require. Fixed by having CI rebuild and overwrite the native libs fresh for both platforms, every run, rather than trusting the committed one for anything but casual local use — only a binary built on the actual target platform is trustworthy on it. - win-x64: "WGL: The driver does not appear to support OpenGL" — from inside the --headless verification run. --headless only controls whether Engine.Host's own loop pumps a window; it does nothing to stop a *loaded* engine.windowing/engine.render from creating a real window and GL context regardless, which the full PhysicsDemo project always does. Fine on a real desktop, fatal on windows-latest's GPU-less runner. Fixed with a separate, minimal verification stage — engine. physics only, project.ci-headless.json/scene.ci-headless.json, no windowing/render/audio/game plugin at all — alongside the original full stage, which still produces the real uploaded artifact. engine.physics needs no display, so this is what CI can actually check without one; audio's own correctness is separately covered by Engine.Audio.Tests (forced onto miniaudio's null backend already) on both platforms. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N
154 lines
6.9 KiB
YAML
154 lines
6.9 KiB
YAML
name: Build
|
|
|
|
# M4's "done when": the build runs on both platforms with no editor
|
|
# plugins in the shipped binary. Neither half of that is verifiable from
|
|
# a single Linux dev machine — this workflow is what actually proves it,
|
|
# real builds on real ubuntu-latest and windows-latest runners, not just
|
|
# a cross-platform-looking csproj and hope.
|
|
|
|
on:
|
|
push:
|
|
branches: [master]
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
build:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: ubuntu-latest
|
|
rid: linux-x64
|
|
- os: windows-latest
|
|
rid: win-x64
|
|
runs-on: ${{ matrix.os }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: 9.0.x
|
|
|
|
- name: Build native physics shim (Box3D, pinned commit — see native/physics-native/CMakeLists.txt)
|
|
shell: bash
|
|
run: |
|
|
cmake -S native/physics-native -B native/physics-native/build -DCMAKE_BUILD_TYPE=Release
|
|
cmake --build native/physics-native/build --config Release
|
|
|
|
- name: Build native audio shim (miniaudio)
|
|
shell: bash
|
|
run: |
|
|
cmake -S native/audio-native -B native/audio-native/build -DCMAKE_BUILD_TYPE=Release
|
|
cmake --build native/audio-native/build --config Release
|
|
|
|
# win-x64's .dll can only ever be produced here — nothing else in
|
|
# this project can build or run one to check it. linux-x64's .so
|
|
# IS also committed (for scripts/run-sample.sh's convenience on a
|
|
# dev machine), but CI rebuilds and overwrites it fresh too, every
|
|
# run: a real failure found by this exact workflow, not a
|
|
# hypothetical one — the committed .so, built locally against a
|
|
# newer glibc than ubuntu-latest ships, loaded fine here and threw
|
|
# DllNotFoundException on the runner. Only a binary built on the
|
|
# actual target platform is trustworthy there.
|
|
- name: Place native libraries
|
|
shell: bash
|
|
run: |
|
|
mkdir -p "plugins/engine.physics/Engine.Physics/native/${{ matrix.rid }}"
|
|
mkdir -p "plugins/engine.audio/Engine.Audio/native/${{ matrix.rid }}"
|
|
find native/physics-native/build -iname "liblingua_physics.so" -o -iname "lingua_physics.dll" \
|
|
| xargs -I{} cp {} "plugins/engine.physics/Engine.Physics/native/${{ matrix.rid }}/"
|
|
find native/audio-native/build -iname "liblingua_audio.so" -o -iname "lingua_audio.dll" \
|
|
| xargs -I{} cp {} "plugins/engine.audio/Engine.Audio/native/${{ matrix.rid }}/"
|
|
ls "plugins/engine.physics/Engine.Physics/native/${{ matrix.rid }}"
|
|
ls "plugins/engine.audio/Engine.Audio/native/${{ matrix.rid }}"
|
|
|
|
- name: Build
|
|
run: dotnet build LinguaEngine.sln -c Release
|
|
|
|
# Nothing in the suite opens a real GL window or a real audio device
|
|
# (Engine.Audio.Tests forces miniaudio's null backend specifically so
|
|
# this works) — the whole thing runs headless on both runners.
|
|
- name: Test
|
|
run: dotnet test LinguaEngine.sln -c Release --no-build
|
|
|
|
- name: Stage the shippable build (engine.editor deliberately excluded)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
DIST="dist/${{ matrix.rid }}"
|
|
STAGE="$DIST/plugins"
|
|
mkdir -p "$STAGE"
|
|
|
|
for id in engine.windowing engine.assets engine.render engine.input engine.physics engine.audio; do
|
|
impl="$(awk -F. '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1)) substr($i,2)}}1' OFS=. <<<"$id")"
|
|
src="plugins/$id/$impl/bin/Release/net9.0"
|
|
mkdir -p "$STAGE/$id"
|
|
cp -r "$src/." "$STAGE/$id/"
|
|
cp "plugins/$id/plugin.json" "$STAGE/$id/"
|
|
done
|
|
|
|
mkdir -p "$DIST/engine"
|
|
cp -r src/Engine.Host/bin/Release/net9.0/. "$DIST/engine/"
|
|
|
|
GAME_DST="$DIST/samples/PhysicsDemo/GamePlugins/physics-demo-game"
|
|
mkdir -p "$GAME_DST"
|
|
cp -r samples/PhysicsDemo/GamePlugins/physics-demo-game/bin/Release/net9.0/. "$GAME_DST/"
|
|
cp samples/PhysicsDemo/GamePlugins/physics-demo-game/plugin.json "$GAME_DST/"
|
|
|
|
cp -r samples/PhysicsDemo/assets "$DIST/samples/PhysicsDemo/assets"
|
|
cp samples/PhysicsDemo/project.json samples/PhysicsDemo/scene.json "$DIST/samples/PhysicsDemo/"
|
|
|
|
# A deliberately minimal, engine.windowing/engine.render-free stage,
|
|
# separate from the full one above: found the hard way that --headless
|
|
# only controls whether Engine.Host's own loop pumps a window — it
|
|
# does nothing to stop a *loaded* engine.windowing/engine.render from
|
|
# creating a real window and GL context regardless, which the full
|
|
# PhysicsDemo project always does. That's fine on a real desktop; on
|
|
# windows-latest's GPU-less runner it's "WGL: The driver does not
|
|
# appear to support OpenGL," failing before physics ever got to run.
|
|
# engine.physics needs no display at all, so this checks only that.
|
|
- name: Stage a minimal headless-safe build for verification
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
VERIFY="verify/${{ matrix.rid }}"
|
|
mkdir -p "$VERIFY/plugins/engine.physics"
|
|
cp -r plugins/engine.physics/Engine.Physics/bin/Release/net9.0/. "$VERIFY/plugins/engine.physics/"
|
|
cp plugins/engine.physics/plugin.json "$VERIFY/plugins/engine.physics/"
|
|
|
|
mkdir -p "$VERIFY/engine"
|
|
cp -r src/Engine.Host/bin/Release/net9.0/. "$VERIFY/engine/"
|
|
|
|
mkdir -p "$VERIFY/samples/PhysicsDemo"
|
|
cp samples/PhysicsDemo/project.ci-headless.json "$VERIFY/samples/PhysicsDemo/project.json"
|
|
cp samples/PhysicsDemo/scene.ci-headless.json "$VERIFY/samples/PhysicsDemo/scene.json"
|
|
|
|
- name: Verify the shippable build actually runs (headless, real physics + real scene load, real P/Invoke)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
# Absolute paths only: Assembly.LoadFromAssemblyPath (which
|
|
# PluginHost.Load uses) throws on a relative one — found by
|
|
# hand running this exact staging locally before trusting it to
|
|
# a workflow run at all.
|
|
VERIFY="$(pwd)/verify/${{ matrix.rid }}"
|
|
cd "$VERIFY/samples/PhysicsDemo"
|
|
dotnet "$VERIFY/engine/Engine.Host.dll" run --headless \
|
|
--plugins "$VERIFY/plugins" --project project.json --scene scene.json \
|
|
--frames 200 --dump dump.json
|
|
cat dump.json
|
|
python3 -c "
|
|
import json
|
|
data = json.load(open('dump.json'))
|
|
box = next(o for o in data if o['name'] == 'DemoBox')
|
|
y = box['transform']['position'][1]
|
|
assert 0.9 < y < 1.1, f'DemoBox should have settled near y=1.0, got y={y}'
|
|
print(f'DemoBox settled at y={y:.4f} — physics ran for real on ${{ matrix.rid }}')
|
|
"
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: lingua-engine-${{ matrix.rid }}
|
|
path: dist/${{ matrix.rid }}
|