Skip to content

Commit 9cc8a57

Browse files
authored
Merge branch 'master' into fix-version-crash-mac
2 parents 4d2ee63 + a7bb35d commit 9cc8a57

6 files changed

Lines changed: 145 additions & 6 deletions

File tree

.github/workflows/mac.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,27 @@ jobs:
3939
build-with-python: true
4040
artifact-name: SCIRunMacInstaller-26-intel
4141

42+
# Oldest available macOS image, built with the GUI. Every other mac-gui job runs
43+
# on macOS-latest (currently the macos-26 image), and the only macOS 14 jobs are
44+
# headless -- but src/CMakeLists.txt skips ADD_SUBDIRECTORY(Interface) when
45+
# BUILD_HEADLESS is on, so without this job nothing compiles src/Interface for an
46+
# older macOS at all. That gap let a macOS-15-only libSystem symbol (quick_exit)
47+
# link cleanly on the 26 runners and abort at load on macOS 14 (PR #2564).
48+
#
49+
# Pinned deliberately to the oldest image rather than macOS-latest: running the
50+
# newest macOS is already covered several times over, and only the oldest one
51+
# exercises the lower deployment target. macOS-14 is deprecated by GitHub, so when
52+
# it is retired this should be bumped to the next-oldest image, not deleted.
53+
mac-gui-14-arm:
54+
uses: ./.github/workflows/reusable-build.yml
55+
with:
56+
runner: macOS-14
57+
qt-version: '6.10.2'
58+
scirun-qt-min-version: '6.3.1'
59+
variant: gui
60+
build-with-python: true
61+
run-smoke-test: true
62+
4263
mac-gui-nonpython:
4364
uses: ./.github/workflows/reusable-build.yml
4465
with:
@@ -69,13 +90,15 @@ jobs:
6990
runner: macOS-latest
7091
variant: headless
7192
build-testing: true
93+
run-smoke-test: true
7294

7395
mac-headless-14-arm:
7496
uses: ./.github/workflows/reusable-build.yml
7597
with:
7698
runner: macOS-14
7799
variant: headless
78100
build-testing: true
101+
run-smoke-test: true
79102

80103
mac-headless-14-arm-slim:
81104
uses: ./.github/workflows/reusable-build.yml
@@ -85,13 +108,15 @@ jobs:
85108
build-testing: true
86109
build-with-python: false
87110
run-unit-tests: true
111+
run-smoke-test: true
88112

89113
mac-headless-15-arm:
90114
uses: ./.github/workflows/reusable-build.yml
91115
with:
92116
runner: macOS-15
93117
variant: headless
94118
build-testing: true
119+
run-smoke-test: true
95120

96121
mac-headless-15-arm-slim:
97122
uses: ./.github/workflows/reusable-build.yml
@@ -101,13 +126,15 @@ jobs:
101126
build-testing: true
102127
build-with-python: false
103128
run-unit-tests: true
129+
run-smoke-test: true
104130

105131
mac-headless-15-intel:
106132
uses: ./.github/workflows/reusable-build.yml
107133
with:
108134
runner: macOS-15-intel
109135
variant: headless
110136
build-testing: true
137+
run-smoke-test: true
111138

112139
mac-headless-15-intel-slim:
113140
uses: ./.github/workflows/reusable-build.yml
@@ -117,10 +144,12 @@ jobs:
117144
build-testing: true
118145
build-with-python: false
119146
run-unit-tests: true
147+
run-smoke-test: true
120148

121149
mac-headless-26:
122150
uses: ./.github/workflows/reusable-build.yml
123151
with:
124152
runner: macOS-26
125153
variant: headless
126154
build-testing: true
155+
run-smoke-test: true

.github/workflows/reusable-build.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ on:
3838
required: false
3939
type: boolean
4040
default: false
41+
run-smoke-test:
42+
required: false
43+
type: boolean
44+
default: false
4145
run-regression-tests:
4246
required: false
4347
type: boolean
@@ -203,6 +207,34 @@ jobs:
203207
204208
Pop-Location
205209
210+
- name: Smoke test - executable loads and runs (Unix)
211+
if: ${{ inputs.run-smoke-test && runner.os != 'Windows' }}
212+
working-directory: bin/SCIRun
213+
# A successful build does NOT prove the binary can start. The dynamic loader
214+
# resolves linked libraries at load time, before main, so a reference to a
215+
# symbol the host OS lacks links cleanly and only aborts on execution.
216+
# Actually running the executable is what catches that class of failure;
217+
# --help suffices, because the loader has already resolved every dependency
218+
# by the time main runs, and --help routes to ConsoleApplication (see
219+
# src/Main/scirunMain.cc) so no window server is needed.
220+
#
221+
# Deliberately NO continue-on-error: unlike the test steps below, this step
222+
# must be able to fail the job, or it gates nothing.
223+
run: |
224+
set -eux
225+
# SCIRun_test is the unbundled executable, built on APPLE only
226+
# (src/Main/CMakeLists.txt); other platforms build SCIRun directly.
227+
if [ -x ./SCIRun_test ]; then
228+
APP=./SCIRun_test
229+
elif [ -x ./SCIRun ]; then
230+
APP=./SCIRun
231+
else
232+
echo "No SCIRun executable found in $(pwd)" >&2
233+
ls -la
234+
exit 1
235+
fi
236+
"$APP" --help
237+
206238
- name: Package (optional - Unix/macOS)
207239
if: ${{ inputs.artifact-name != '' && runner.os != 'Windows' }}
208240
shell: bash

src/Core/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ SET(Core_Utils_HEADERS
3838
FileUtil.h
3939
Lockable.h
4040
Macros.h
41+
QuickExit.h
4142
Singleton.h
4243
StringContainer.h
4344
StringUtil.h

src/Core/Utils/QuickExit.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
For more information, please see: http://software.sci.utah.edu
3+
4+
The MIT License
5+
6+
Copyright (c) 2020 Scientific Computing and Imaging Institute,
7+
University of Utah.
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a
10+
copy of this software and associated documentation files (the "Software"),
11+
to deal in the Software without restriction, including without limitation
12+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
13+
and/or sell copies of the Software, and to permit persons to whom the
14+
Software is furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included
17+
in all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25+
DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
29+
#ifndef CORE_UTILS_QUICKEXIT_H
30+
#define CORE_UTILS_QUICKEXIT_H
31+
32+
#include <cstdlib>
33+
34+
namespace SCIRun
35+
{
36+
namespace Core
37+
{
38+
39+
/// Terminates the process immediately, skipping static destructors and atexit
40+
/// handlers, while still propagating @a code to the caller (e.g. CTest). Used in
41+
/// regression mode to avoid hangs and crashes in async teardown paths where
42+
/// streaming execution threads outlive the GUI objects.
43+
///
44+
/// Apple always takes _Exit so that one build runs on every supported macOS.
45+
/// quick_exit entered libSystem in macOS 15.0, which breaks both directions:
46+
///
47+
/// - Against SDKs older than 15, it is not declared at all. Both spellings fail to
48+
/// compile -- "quick_exit" as an undeclared identifier, "std::quick_exit" as a
49+
/// reference to an unresolved using declaration, since libc++'s <cstdlib> has no
50+
/// ::quick_exit to pull in.
51+
/// - Against the 15 SDK it is declared ("void quick_exit(int) __dead2;" in
52+
/// _stdlib.h) gated only on __DARWIN_C_LEVEL/C11 -- a compile-time feature gate
53+
/// carrying no availability annotation -- so the compiler can neither warn nor
54+
/// weak-link. The object gets a hard _quick_exit reference and aborts on load on
55+
/// macOS < 15 with "dyld: Symbol not found: _quick_exit".
56+
///
57+
/// The trigger is the SDK, not the host: a macOS 14 machine with Xcode 16 installed
58+
/// builds the broken binary. Gating on __MAC_OS_X_VERSION_MIN_REQUIRED does not help
59+
/// either, because the deployment target is not pinned -- it follows the build host,
60+
/// and so reads 150000 on exactly the builders that emit the hard reference.
61+
///
62+
/// The two branches are equivalent only so long as nothing registers at_quick_exit
63+
/// handlers: _Exit does not run them.
64+
[[noreturn]] inline void quickExit(int code)
65+
{
66+
#ifdef __APPLE__
67+
std::_Exit(code);
68+
#else
69+
std::quick_exit(code);
70+
#endif
71+
}
72+
73+
}}
74+
75+
#endif

src/Interface/Application/GuiCommands.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <numeric>
3232
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
3333
#include <Core/Application/Preferences/Preferences.h>
34+
#include <Core/Utils/QuickExit.h>
3435
#include <Interface/Application/SCIRunMainWindow.h>
3536
#include <Interface/Application/GuiCommands.h>
3637
#include <Interface/Application/GuiLogger.h>
@@ -268,10 +269,10 @@ bool NetworkFileProcessCommand::execute()
268269
if (failTestOnError() && Application::Instance().parameters()->isRegressionMode())
269270
{
270271
GuiLogger::logErrorStd("Regression import failed, exiting non-zero: " + filename);
271-
// Mirrors SCIRunMainWindow::exitApplication's regression-mode behavior
272-
// (which calls quick_exit) so the regression test reports a failure. Done
273-
// directly here because exitApplication is a private slot.
274-
std::quick_exit(1);
272+
// Mirrors SCIRunMainWindow::exitApplication's regression-mode behavior so the
273+
// regression test reports a failure. Done directly here because
274+
// exitApplication is a private slot.
275+
quickExit(1);
275276
}
276277
return false;
277278
}

src/Interface/Application/SCIRunMainWindowQtOverrides.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include <cstdlib>
2929
#include <Core/Application/Application.h>
30+
#include <Core/Utils/QuickExit.h>
3031
#include <Core/Application/Preferences/Preferences.h>
3132
#include <Core/Logging/Log.h>
3233
#include <Core/Utils/Legacy/MemoryUtil.h>
@@ -66,12 +67,12 @@ void SCIRunMainWindow::exitApplication(int code)
6667
if (Application::Instance().parameters()->saveViewSceneScreenshotsOnQuit())
6768
{ networkEditor_->saveImages(); }
6869
returnCode_ = code;
69-
// In regression mode, use quick_exit to avoid hangs/crashes in async teardown
70+
// In regression mode, exit immediately to avoid hangs/crashes in async teardown
7071
// paths where streaming execution threads outlive the GUI objects (e.g. the
7172
// async streaming test networks). The exit code is still propagated to CTest.
7273
if (Application::Instance().parameters()->isRegressionMode())
7374
{
74-
std::quick_exit(code);
75+
quickExit(code);
7576
}
7677
close();
7778
qApp->exit(code);

0 commit comments

Comments
 (0)