@@ -266,8 +266,21 @@ jobs:
266266
267267 macos :
268268 if : ${{ github.event_name != 'workflow_dispatch' || inputs.only == '' || inputs.only == 'macos' }}
269- name : macOS (Homebrew)
270- runs-on : macos-latest
269+ name : macOS (${{ matrix.arch }})
270+ runs-on : ${{ matrix.os }}
271+ strategy :
272+ fail-fast : false
273+ matrix :
274+ include :
275+ - { os: macos-latest, arch: arm64, target: "11.0", experimental: false }
276+ # Catalina: the oldest macOS many 2012-2014 Intel Macs can run.
277+ - { os: macos-15-intel, arch: x86_64, target: "10.15", experimental: true }
278+ env :
279+ # Oldest macOS the app is built for (#133). Our code and every
280+ # bundled library are built to this; the minimum-macOS step fails
281+ # the job on any Mach-O above it.
282+ MACOSX_DEPLOYMENT_TARGET : ${{ matrix.target }}
283+ ECCE_DEPS : /Users/runner/ecce-deps
271284 # Bounded so a hung job cannot hold a runner slot for GitHub's
272285 # 6-hour default; see the concurrency note above.
273286 timeout-minutes : 90
@@ -278,22 +291,50 @@ jobs:
278291 # here to track the gap with a real, current compile log instead of
279292 # nothing -- it's expected to fail until #3 gets real porting work,
280293 # not a sign the workflow itself is broken.
281- continue-on-error : true
294+ # x86_64 may fail until it is proven; arm64 must be green.
295+ continue-on-error : ${{ matrix.experimental }}
282296 steps :
283297 - uses : actions/checkout@v4
284298
285- - name : Install build dependencies
286- run : |
287- brew install cmake ninja wxwidgets xerces-c mosquitto gcc libssh pkgconf
288- # The vendored Inventor core needs GLX and pre-C++17 code that
289- # Apple's compiler rejects; the Coin build (#166) skips it.
290- brew install coin3d || { echo "::error::Homebrew has no coin3d formula"; exit 1; }
299+ - name : Install build tools
300+ # Only tools: the libraries come from build-deps.sh, because
301+ # Homebrew bottles are built for the runner's own macOS.
302+ run : brew install cmake ninja pkgconf gcc bash
303+
304+ - name : Cache libraries built for macOS ${{ env.MACOSX_DEPLOYMENT_TARGET }}
305+ id : deps
306+ uses : actions/cache/restore@v4
307+ with :
308+ path : ${{ env.ECCE_DEPS }}
309+ key : macos-deps-${{ matrix.arch }}-${{ env.MACOSX_DEPLOYMENT_TARGET }}-${{ hashFiles('packaging/macos/build-deps.sh') }}
310+
311+ - name : Build libraries (wxWidgets, Coin, Xerces, Mosquitto, libssh, OpenSSL)
312+ if : steps.deps.outputs.cache-hit != 'true'
313+ run : packaging/macos/build-deps.sh "$ECCE_DEPS"
314+
315+ - name : Save the libraries
316+ if : steps.deps.outputs.cache-hit != 'true'
317+ uses : actions/cache/save@v4
318+ with :
319+ path : ${{ env.ECCE_DEPS }}
320+ key : ${{ steps.deps.outputs.cache-primary-key }}
291321
292322 - name : Configure
293323 # ECCE_HOME_DIR/ECCE_WRAPPER_DESTINATION point the install at a
294324 # staging tree inside the workspace for the run step below.
325+ # Homebrew's prefixes are ignored so nothing is picked up from
326+ # them by accident.
295327 run : |
296- cmake -G Ninja -B build-cmake -DECCE_USE_COIN=ON -DCMAKE_PREFIX_PATH="$(brew --prefix)" \
328+ export PATH="$ECCE_DEPS/bin:$PATH"
329+ export PKG_CONFIG_LIBDIR="$ECCE_DEPS/lib/pkgconfig"
330+ cmake -G Ninja -B build-cmake -DECCE_USE_COIN=ON \
331+ -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" \
332+ -DCMAKE_PREFIX_PATH="$ECCE_DEPS" \
333+ -DCMAKE_IGNORE_PREFIX_PATH="/opt/homebrew;/usr/local" \
334+ -DCMAKE_MAKE_PROGRAM="$(command -v ninja)" -DPKG_CONFIG_EXECUTABLE="$(command -v pkg-config)" \
335+ -DCMAKE_Fortran_COMPILER="$(ls $(brew --prefix)/bin/gfortran-* | head -1)" \
336+ -DFREETYPE_UNQUALIFIED_INCLUDE_DIR="$ECCE_DEPS/include/freetype2/freetype" \
337+ -DFREETYPE_ROOT_INCLUDE_DIR="$ECCE_DEPS/include/freetype2" \
297338 -DECCE_HOME_DIR="$PWD/stage/ecce" -DECCE_WRAPPER_DESTINATION="$PWD/stage/bin" \
298339 -DCMAKE_INSTALL_PREFIX="$PWD/stage/ecce"
299340
@@ -308,14 +349,78 @@ jobs:
308349 continue-on-error : true
309350 timeout-minutes : 25
310351 run : |
311- brew install bash || true
352+ export PATH="$ECCE_DEPS/sbin:$ECCE_DEPS/bin:$PATH"
312353 cmake --install build-cmake
313354 tests/macos/run.sh stage macos-run
314355 cat macos-run/summary.txt >> "$GITHUB_STEP_SUMMARY"
315356
316357 - uses : actions/upload-artifact@v4
317358 if : always()
318359 with :
319- name : macos-run
360+ name : macos-run-${{ matrix.arch }}
320361 path : macos-run/
321362 if-no-files-found : ignore
363+
364+ # ECCE.app in a .dmg (#133), then the start test again on a copy of
365+ # the app taken out of the mounted image, away from the build tree.
366+ # The two steps below fail the job when they fail. The job itself
367+ # is continue-on-error (the native build is still experimental), so
368+ # the workflow stays green but the job and these steps show red and
369+ # the summary says FAILED.
370+ - name : Package ECCE.app
371+ run : |
372+ export PATH="$ECCE_DEPS/bin:$PATH"
373+ packaging/macos/make-app.sh stage dist
374+
375+ # Oldest macOS the bundle can run on (#133): the step fails when any
376+ # Mach-O needs more than the deployment target or is not this runner's arch.
377+ - name : Minimum macOS of every Mach-O in the app
378+ run : |
379+ mkdir -p minos
380+ app=$(mktemp -d)
381+ mnt=$(mktemp -d)
382+ hdiutil attach dist/ECCE-*.dmg -mountpoint "$mnt" -nobrowse -quiet
383+ cp -R "$mnt/ECCE.app" "$app/"
384+ hdiutil detach "$mnt" -quiet
385+ python3 packaging/macos/minos.py "$app/ECCE.app" minos/minos.txt \
386+ --floor "$MACOSX_DEPLOYMENT_TARGET" --arch "${{ matrix.arch }}" > /dev/null || rc=$?
387+ echo "runner: $(sw_vers -productVersion) $(uname -m)" >> minos/minos.txt
388+ { echo '### Minimum macOS (ECCE.app)'; echo '```';
389+ sed -n '/^Mach-O files/,$p' minos/minos.txt; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
390+ exit ${rc:-0}
391+
392+ - name : Run the packaged app
393+ if : always() && hashFiles('dist/*.dmg') != ''
394+ timeout-minutes : 25
395+ run : |
396+ pkill -f "$PWD/stage" || true
397+ mnt=$(mktemp -d)
398+ hdiutil attach dist/ECCE-*.dmg -mountpoint "$mnt" -nobrowse -quiet
399+ appdir=$(mktemp -d)
400+ cp -R "$mnt/ECCE.app" "$appdir/"
401+ hdiutil detach "$mnt" -quiet
402+ # The build tree and the staging prefix must not be needed.
403+ mv build-cmake build-cmake.gone
404+ mv stage stage.gone
405+ ECCE_APP="$appdir/ECCE.app" tests/macos/run.sh "$appdir/ECCE.app/Contents/Resources" macos-app-run
406+ cat macos-app-run/summary.txt >> "$GITHUB_STEP_SUMMARY"
407+
408+ - name : Summary on failure
409+ if : failure()
410+ run : echo '### macOS packaging FAILED (see the failed step above)' >> "$GITHUB_STEP_SUMMARY"
411+
412+ - uses : actions/upload-artifact@v4
413+ if : always()
414+ with :
415+ name : ECCE-macos-dmg-${{ matrix.arch }}
416+ path : |
417+ dist/*.dmg
418+ minos/minos.txt
419+ if-no-files-found : ignore
420+
421+ - uses : actions/upload-artifact@v4
422+ if : always()
423+ with :
424+ name : macos-app-run-${{ matrix.arch }}
425+ path : macos-app-run/
426+ if-no-files-found : ignore
0 commit comments