Skip to content

Fix six things in the native heads (#846) #84

Fix six things in the native heads (#846)

Fix six things in the native heads (#846) #84

Workflow file for this run

name: Build native
# Produces the diffengine_viewer binaries committed under
# src/DiffEngineViewer.{Linux,Mac}/runtimes/{rid}/native, in the head that loads them. There is no
# Windows equivalent: that head renders with WinForms.
#
# They are committed rather than built during a normal build so that a plain
# `dotnet build src --configuration Release` produces a shippable package on any machine, and
# contributors never need a C++ toolchain. Run this whenever native/ changes.
on:
push:
paths:
- 'native/**'
- '.github/workflows/build-native.yml'
pull_request:
paths:
- 'native/**'
workflow_dispatch:
inputs:
commit:
description: 'Open a PR with the rebuilt binaries'
type: boolean
default: true
concurrency:
group: build-native-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: ${{ matrix.rid }}
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
# No Windows entries. That head renders with WinForms and loads no native library.
- rid: linux-x64
os: ubuntu-24.04
- rid: linux-arm64
os: ubuntu-24.04-arm
# One universal binary covers both macOS RIDs; it is copied into each below.
- rid: osx
os: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
cmake ninja-build \
libx11-dev libxrandr-dev libxi-dev libxcursor-dev libxinerama-dev \
libgl1-mesa-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev
- name: Configure
if: matrix.rid != 'osx'
run: cmake -S native -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
- name: Build
if: matrix.rid != 'osx'
run: cmake --build build --config Release
# macOS draws with AppKit and Core Text rather than raylib and ImGui, so it is a Swift
# package rather than a CMake project. Both --arch flags in one invocation produce a
# universal binary, so there is no separate lipo step.
#
# Nothing of the Swift runtime is shipped: it has been part of macOS since 10.14.4, which is
# why this dylib is a fraction of the size of the one it replaced.
- name: Build
if: matrix.rid == 'osx'
run: swift build -c release --arch arm64 --arch x86_64 --package-path native/swift
- name: Collect
shell: bash
run: |
# Laid out as src/{head}/runtimes/{rid}/native, so the propose job below can merge every
# artifact straight into src and the binaries land in the head that loads them.
collect() {
mkdir -p "artifacts/$1/runtimes/$2/native"
cp "$3" "artifacts/$1/runtimes/$2/native/"
}
case "${{ matrix.rid }}" in
linux-*)
strip build/libdiffengine_viewer.so
collect DiffEngineViewer.Linux "${{ matrix.rid }}" build/libdiffengine_viewer.so
;;
osx)
# swift build leaves a per architecture dylib in more than one place, so taking the
# first one found gives a single slice binary that loads on half the Macs in the
# world. Every candidate is checked, and if none is already universal they are
# merged, so the only thing that can be collected is a fat binary.
# The dSYM contains a DWARF file of the same name which is also universal, so the
# arch check below would happily accept it and ship debug symbols as the library.
candidates=$(find native/swift/.build -name libdiffengine_viewer.dylib -not -path '*.dSYM/*')
if [ -z "$candidates" ]; then
echo "::error::swift build produced no libdiffengine_viewer.dylib"
exit 1
fi
echo "$candidates" | while read -r candidate; do
echo "$candidate: $(lipo -archs "$candidate" 2>/dev/null)"
done
dylib=""
for candidate in $candidates; do
archs=$(lipo -archs "$candidate" 2>/dev/null || echo "")
if [[ "$archs" == *x86_64* && "$archs" == *arm64* ]]; then
dylib="$candidate"
break
fi
done
if [ -z "$dylib" ]; then
dylib=universal/libdiffengine_viewer.dylib
mkdir -p universal
# shellcheck disable=SC2086
lipo -create $candidates -output "$dylib"
fi
archs=$(lipo -archs "$dylib")
echo "collecting $dylib: $archs"
for arch in x86_64 arm64; do
case " $archs " in
*" $arch "*) ;;
*) echo "::error::$dylib is missing the $arch slice"; exit 1 ;;
esac
done
strip -x "$dylib"
# The dylib is universal, so both macOS RIDs get the same file.
collect DiffEngineViewer.Mac osx-x64 "$dylib"
collect DiffEngineViewer.Mac osx-arm64 "$dylib"
;;
esac
ls -lhR artifacts
- name: Upload
uses: actions/upload-artifact@v4
with:
name: native-${{ matrix.rid }}
path: artifacts/
retention-days: 7
propose:
name: propose update
needs: build
# Also on push, not just manual dispatch. workflow_dispatch does not appear in the Actions UI
# until the workflow is on the default branch, so on a feature branch a push is the only way
# to trigger this, and leaving the binaries as bare artifacts to copy by hand is worse.
#
# Branches only. Never on pull_request, which would mean opening a PR from a PR, and never on
# a tag: a tag checkout is a detached HEAD with no base branch to target, and proposing a
# binaries PR against a release tag is meaningless anyway.
if: >-
github.event_name != 'pull_request' &&
startsWith(github.ref, 'refs/heads/') &&
(github.event_name != 'workflow_dispatch' || inputs.commit)
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download
uses: actions/download-artifact@v4
with:
pattern: native-*
merge-multiple: true
path: src
- name: Show what changed
run: |
ls -lhR src/DiffEngineViewer.Linux/runtimes src/DiffEngineViewer.Mac/runtimes
git status --short
# A PR rather than a direct push: these are binaries, so the diff is not reviewable and the
# change deserves an explicit approval.
- name: Open pull request
uses: peter-evans/create-pull-request@v7
with:
# Scoped to the triggering branch, which is also the base, so a rebuild on a feature
# branch does not collide with one on main.
branch: native-binaries-${{ github.ref_name }}
title: 'Rebuild native renderer binaries'
commit-message: 'Rebuild native renderer binaries'
body: |
Rebuilt `diffengine_viewer` from `native/` for the four RIDs that load one.
Windows is not among them: that head renders with WinForms.
Produced by the `build-native` workflow from ${{ github.sha }}.
add-paths: |
src/DiffEngineViewer.Linux/runtimes
src/DiffEngineViewer.Mac/runtimes