Skip to content

Commit ea238ec

Browse files
committed
Take the heads' build and packaging from PlatformHeads
The viewer's native build was worked out here and copied into BuildMonitor, and fixes made here did not reach the copy: a Linux library that needed glibc 2.38, a raylib build whose window stayed blank, and libraries recorded as needed that a stock Ubuntu lacks were each fixed here and then found again there. PlatformHeads is the one copy, made from this one, and this takes its 0.1.0 in the three forms it comes in: - build-native.yml calls its reusable workflow in place of the jobs it had. - native/CMakeLists.txt fetches its CMake module, which pins raylib and Dear ImGui, switches off the raylib 6.0 options and links the library. What is left here is the viewer's own: its formats, its source file, and Threads and dl. - DiffEngine.csproj lists a PlatformHead for each RID and the package's targets publish and bundle them, in place of PublishViewerHeads and AddViewerToPackage, staged in obj\viewer as before. DiffEngineTray asks for the Windows heads by PlatformHeadsPublish. The Mac and Linux heads set PlatformHeadsNativeRuntimes in place of their own runtimes items. DE0001 is PH0001. native/build-linux.sh goes, since the workflow carries the script and PlatformHeads keeps it for running by hand. One thing that ships changes. The formats named are PNG, JPG, BMP and GIF, which is what the comment here always said, and the module leaves out every format not named. QOI and DDS were in the library only because raylib 6.0's CMake defaults every option to on, and the viewer hands raylib neither. Built in the container the linux-x64 library is 8 KB smaller for it, and with those two named it is byte for byte the one committed. The committed binaries are not changed here: build-native rebuilds them and opens the PR. A Release build makes all five packages with the entries Packaging.Tests has on record.
1 parent 87463ac commit ea238ec

10 files changed

Lines changed: 114 additions & 480 deletions

File tree

‎.github/workflows/build-native.yml‎

Lines changed: 19 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ name: Build native
77
# They are committed rather than built during a normal build so that a plain
88
# `dotnet build src --configuration Release` produces a shippable package on any machine, and
99
# contributors never need a C++ toolchain. Run this whenever native/ changes.
10+
#
11+
# The work is PlatformHeads' build-native workflow, which this one was moved into so that
12+
# BuildMonitor, built the same way, runs it too: the Linux library built in a manylinux_2_28
13+
# container and checked for the glibc it needs and the libraries it records as needed, the
14+
# universal dylib, and the pull request with the binaries. BuildMonitor had a copy of it, and
15+
# three fixes made here were waiting to be found again there.
16+
# The tag is the one native/CMakeLists.txt fetches and Directory.Packages.props names.
1017
on:
1118
push:
1219
paths:
@@ -27,210 +34,18 @@ concurrency:
2734
cancel-in-progress: true
2835

2936
jobs:
30-
build:
31-
name: ${{ matrix.rid }}
32-
runs-on: ${{ matrix.os }}
33-
timeout-minutes: 45
34-
strategy:
35-
fail-fast: false
36-
matrix:
37-
include:
38-
# No Windows entries. That head renders with WinForms and loads no native library.
39-
- rid: linux-x64
40-
os: ubuntu-24.04
41-
- rid: linux-arm64
42-
os: ubuntu-24.04-arm
43-
# One universal binary covers both macOS RIDs; it is copied into each below.
44-
- rid: osx
45-
os: macos-14
46-
steps:
47-
- name: Checkout
48-
uses: actions/checkout@v7
49-
50-
# In a manylinux_2_28 container rather than on the runner, for its glibc 2.28: the floor
51-
# .NET 10 itself supports, which is RHEL 8. A library records the glibc symbol versions of
52-
# the headers it was compiled against, and the loader refuses it anywhere older - built on
53-
# the runner's own Ubuntu 24.04 it needed GLIBC_2.38, so the viewer could not start on
54-
# Ubuntu 22.04, Debian 12, or RHEL 8 or 9, and every inline snapshot sent to it was lost.
55-
- name: Build
56-
if: runner.os == 'Linux'
57-
run: docker run --rm -v "$PWD:/src" -w /src "quay.io/pypa/manylinux_2_28_$(uname -m)" bash native/build-linux.sh
58-
59-
# The floor the container is there to hold, checked rather than trusted, since nothing else
60-
# would notice until a user on an older distribution did.
61-
- name: Check glibc floor
62-
if: runner.os == 'Linux'
63-
env:
64-
GLIBC_FLOOR: GLIBC_2.28
65-
run: |
66-
highest=$(objdump -T build/libdiffengine_viewer.so | grep -o 'GLIBC_[0-9][0-9.]*' | sort -uV | tail -n 1)
67-
echo "Highest glibc symbol version required: $highest"
68-
if [ "$(printf '%s\n' "$GLIBC_FLOOR" "$highest" | sort -V | tail -n 1)" != "$GLIBC_FLOOR" ]; then
69-
echo "::error::libdiffengine_viewer.so requires $highest, above the $GLIBC_FLOOR floor"
70-
exit 1
71-
fi
72-
# libGL.so.1 is on every machine with a GL driver; libOpenGL.so.0 is not
73-
if objdump -p build/libdiffengine_viewer.so | grep -q 'NEEDED.*libOpenGL'; then
74-
echo "::error::libdiffengine_viewer.so links libOpenGL.so.0 rather than libGL.so.1"
75-
exit 1
76-
fi
77-
78-
# Everything recorded as needed has to be on a machine for the library to load at all, so
79-
# the list is held to what any machine that runs .NET and has a display already has. It grew
80-
# once without anybody choosing it: the container's linker records every library it is
81-
# handed, used or not, and raylib hands on libSM and libICE, which a stock Ubuntu lacks.
82-
# The viewer could not start there, and nothing noticed until somebody ran it on one.
83-
- name: Check needed libraries
84-
if: runner.os == 'Linux'
85-
env:
86-
ALLOWED: >-
87-
libc.so.6 libm.so.6 libdl.so.2 libpthread.so.0 librt.so.1 libstdc++.so.6 libgcc_s.so.1
88-
libX11.so.6 libGL.so.1 ld-linux-x86-64.so.2 ld-linux-aarch64.so.1
89-
run: |
90-
needed=$(objdump -p build/libdiffengine_viewer.so | awk '$1 == "NEEDED" { print $2 }')
91-
echo "Needed:" $needed
92-
failed=0
93-
for library in $needed; do
94-
case " $ALLOWED " in
95-
*" $library "*) ;;
96-
*)
97-
echo "::error::libdiffengine_viewer.so needs $library, which is not in the list of libraries it may need"
98-
failed=1
99-
;;
100-
esac
101-
done
102-
exit $failed
103-
104-
# macOS draws with AppKit and Core Text rather than raylib and ImGui, so it is a Swift
105-
# package rather than a CMake project. Both --arch flags in one invocation produce a
106-
# universal binary, so there is no separate lipo step.
107-
#
108-
# Nothing of the Swift runtime is shipped: it has been part of macOS since 10.14.4, which is
109-
# why this dylib is a fraction of the size of the one it replaced.
110-
- name: Build
111-
if: matrix.rid == 'osx'
112-
run: swift build -c release --arch arm64 --arch x86_64 --package-path native/swift
113-
114-
- name: Collect
115-
shell: bash
116-
run: |
117-
# Laid out as src/{head}/runtimes/{rid}/native, so the propose job below can merge every
118-
# artifact straight into src and the binaries land in the head that loads them.
119-
collect() {
120-
mkdir -p "artifacts/$1/runtimes/$2/native"
121-
cp "$3" "artifacts/$1/runtimes/$2/native/"
122-
}
123-
case "${{ matrix.rid }}" in
124-
linux-*)
125-
# Already stripped, inside the container that owns the build directory
126-
collect DiffEngineViewer.Linux "${{ matrix.rid }}" build/libdiffengine_viewer.so
127-
;;
128-
osx)
129-
# swift build leaves a per architecture dylib in more than one place, so taking the
130-
# first one found gives a single slice binary that loads on half the Macs in the
131-
# world. Every candidate is checked, and if none is already universal they are
132-
# merged, so the only thing that can be collected is a fat binary.
133-
# The dSYM contains a DWARF file of the same name which is also universal, so the
134-
# arch check below would happily accept it and ship debug symbols as the library.
135-
candidates=$(find native/swift/.build -name libdiffengine_viewer.dylib -not -path '*.dSYM/*')
136-
if [ -z "$candidates" ]; then
137-
echo "::error::swift build produced no libdiffengine_viewer.dylib"
138-
exit 1
139-
fi
140-
141-
echo "$candidates" | while read -r candidate; do
142-
echo "$candidate: $(lipo -archs "$candidate" 2>/dev/null)"
143-
done
144-
145-
dylib=""
146-
for candidate in $candidates; do
147-
archs=$(lipo -archs "$candidate" 2>/dev/null || echo "")
148-
if [[ "$archs" == *x86_64* && "$archs" == *arm64* ]]; then
149-
dylib="$candidate"
150-
break
151-
fi
152-
done
153-
154-
if [ -z "$dylib" ]; then
155-
dylib=universal/libdiffengine_viewer.dylib
156-
mkdir -p universal
157-
# shellcheck disable=SC2086
158-
lipo -create $candidates -output "$dylib"
159-
fi
160-
161-
archs=$(lipo -archs "$dylib")
162-
echo "collecting $dylib: $archs"
163-
for arch in x86_64 arm64; do
164-
case " $archs " in
165-
*" $arch "*) ;;
166-
*) echo "::error::$dylib is missing the $arch slice"; exit 1 ;;
167-
esac
168-
done
169-
170-
strip -x "$dylib"
171-
# The dylib is universal, so both macOS RIDs get the same file.
172-
collect DiffEngineViewer.Mac osx-x64 "$dylib"
173-
collect DiffEngineViewer.Mac osx-arm64 "$dylib"
174-
;;
175-
esac
176-
ls -lhR artifacts
177-
178-
- name: Upload
179-
uses: actions/upload-artifact@v7
180-
with:
181-
name: native-${{ matrix.rid }}
182-
path: artifacts/
183-
retention-days: 7
184-
185-
propose:
186-
name: propose update
187-
needs: build
188-
# Also on push, not just manual dispatch. workflow_dispatch does not appear in the Actions UI
189-
# until the workflow is on the default branch, so on a feature branch a push is the only way
190-
# to trigger this, and leaving the binaries as bare artifacts to copy by hand is worse.
191-
#
192-
# Branches only. Never on pull_request, which would mean opening a PR from a PR, and never on
193-
# a tag: a tag checkout is a detached HEAD with no base branch to target, and proposing a
194-
# binaries PR against a release tag is meaningless anyway.
195-
if: >-
196-
github.event_name != 'pull_request' &&
197-
startsWith(github.ref, 'refs/heads/') &&
198-
(github.event_name != 'workflow_dispatch' || inputs.commit)
199-
runs-on: ubuntu-latest
37+
native:
38+
uses: SimonCropp/PlatformHeads/.github/workflows/build-native.yml@0.1.0
20039
permissions:
20140
contents: write
20241
pull-requests: write
203-
steps:
204-
- name: Checkout
205-
uses: actions/checkout@v7
206-
207-
- name: Download
208-
uses: actions/download-artifact@v4
209-
with:
210-
pattern: native-*
211-
merge-multiple: true
212-
path: src
213-
214-
- name: Show what changed
215-
run: |
216-
ls -lhR src/DiffEngineViewer.Linux/runtimes src/DiffEngineViewer.Mac/runtimes
217-
git status --short
218-
219-
# A PR rather than a direct push: these are binaries, so the diff is not reviewable and the
220-
# change deserves an explicit approval.
221-
- name: Open pull request
222-
uses: peter-evans/create-pull-request@v7
223-
with:
224-
# Scoped to the triggering branch, which is also the base, so a rebuild on a feature
225-
# branch does not collide with one on main.
226-
branch: native-binaries-${{ github.ref_name }}
227-
title: 'Rebuild native renderer binaries'
228-
commit-message: 'Rebuild native renderer binaries'
229-
body: |
230-
Rebuilt `diffengine_viewer` from `native/` for the four RIDs that load one.
231-
Windows is not among them: that head renders with WinForms.
232-
233-
Produced by the `build-native` workflow from ${{ github.sha }}.
234-
add-paths: |
235-
src/DiffEngineViewer.Linux/runtimes
236-
src/DiffEngineViewer.Mac/runtimes
42+
with:
43+
library: diffengine_viewer
44+
linux-runtimes: src/DiffEngineViewer.Linux/runtimes
45+
swift-package: native/swift
46+
mac-runtimes: src/DiffEngineViewer.Mac/runtimes
47+
# Also on push, not just manual dispatch. workflow_dispatch does not appear in the Actions
48+
# UI until the workflow is on the default branch, so on a feature branch a push is the only
49+
# way to trigger this, and leaving the binaries as bare artifacts to copy by hand is worse.
50+
propose: ${{ github.event_name != 'workflow_dispatch' || inputs.commit }}
51+
pull-request-title: Rebuild native renderer binaries

‎.github/workflows/build.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
if-no-files-found: ignore
7676
retention-days: 14
7777

78-
# DE0001 warns when a RID has no native renderer. Shipping a package that cannot run on a
78+
# PH0001 warns when a RID has no native renderer. Shipping a package that cannot run on a
7979
# platform it claims to support is worse than failing the release.
8080
- name: Verify every RID has a native renderer
8181
if: startsWith(github.ref, 'refs/tags/')

‎claude.md‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,23 @@ the comment there about not caching "nothing staged" asks for.
603603
gate's continuations would need.
604604

605605
**Native shim (`native/`), used by the Mac and Linux heads only:**
606-
- `raylib` and `imgui` are fetched by CMake (`FetchContent`), pinned by tag in
607-
`native/CMakeLists.txt`. Deliberately not submodules: nothing in a normal `dotnet build` touches
606+
- The build and packaging of the heads is PlatformHeads' (`D:\Code\PlatformHeads`,
607+
github.com/SimonCropp/PlatformHeads), which BuildMonitor shares: it was worked out here and
608+
copied there, and fixes made here did not reach the copy. Three pins name one tag of it and move
609+
together: the `uses:` of `.github/workflows/build-native.yml`, the `GIT_TAG` in
610+
`native/CMakeLists.txt`, and `PlatformHeads` in `Directory.Packages.props`. The workflow is its
611+
reusable `build-native`. Its CMake module pins raylib and Dear ImGui, carries the raylib 6.0
612+
option workarounds and links the library, and `native/CMakeLists.txt` names only the picture
613+
formats, the source file and the two libraries the viewer links beyond raylib. Its package's
614+
targets publish and bundle the `PlatformHead` items of `DiffEngine.csproj` (`PH0001` for a
615+
native head with no library, `PH0002` for an apphost that was not staged), staged in
616+
`obj\viewer`, where `DiffEngineTray.csproj` takes the two Windows heads from by calling
617+
`PlatformHeadsPublish`. A fix to any of that goes there, not here. To build the Linux library by
618+
hand, from the repository root: `docker run --rm -v "$PWD:/src" -v
619+
"D:/Code/PlatformHeads/native:/ph:ro" -w /src -e LIBRARY=diffengine_viewer
620+
"quay.io/pypa/manylinux_2_28_$(uname -m)" bash /ph/build-linux.sh`.
621+
- `raylib` and `imgui` are fetched by CMake (`FetchContent`), pinned by tag in PlatformHeads'
622+
module. Deliberately not submodules: nothing in a normal `dotnet build` touches
608623
this folder, so a recursive clone on every checkout would serve a path almost nobody takes.
609624
- Building it needs CMake 3.24+, a C++17 compiler and network access. Contributors do not need
610625
any of that, because the binaries are committed.

0 commit comments

Comments
 (0)