mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
macOS: re-enable Python wrapper + stage IfcViewerMinimal.app bundle
Three coupled fixes that close the macOS bring-up loop:
## 1. ifcwrap: fix INSTALL_RPATH on Apple
The ifcopenshell_wrapper Python module had `INSTALL_RPATH "$ORIGIN"` set
for "NOT WIN32 AND NOT WASM_BUILD" — but `$ORIGIN` is a Linux ld.so
placeholder, not a macOS dyld one. macOS dyld doesn't expand it; it
bakes the literal string `$ORIGIN` into LC_RPATH, which resolves to
nothing at runtime. The wrapper's hard-link `@rpath/ifcopenshell
.document.rdb.dylib` then fails to load even though INSTALL(TARGETS …
LIBRARY DESTINATION "${python_package_dir}/ifcopenshell") above had
already dropped the plug-in dylib right next to the wrapper.
Split the rpath assignment: `@loader_path` on Apple (the dyld
equivalent of `$ORIGIN`), `$ORIGIN` elsewhere.
This is what b0ef47819 (the build_osx IFCOS_BUILD_PYTHON_WRAPPER=off
gate) was working around. The gate is removed below.
## 2. ifcviewer-minimal: stage IfcOpenShell + wgpu_native into the .app
IfcViewerMinimal.app was building on macOS via the cmake
`BUILD_BONSAIVIEWER → BUILD_BONSAIVIEWER_WGPU` promotion, but had no
bundle staging — Contents/Frameworks/ only contained the Qt
frameworks macdeployqt deposited, so the .app would refuse to start
("Library not loaded: @rpath/libwgpu_native.dylib").
Mirror what src/bonsaiviewer/CMakeLists.txt does for BonsaiViewer.app:
* Set INSTALL_RPATH to `@executable_path/../Frameworks` so the exe
knows where to look for @rpath/* deps.
* install(FILES) libwgpu_native.dylib into the bundle's Frameworks/
(globbed from WGPU_NATIVE_LIB_DIR rather than hard-coded so it
covers any future versioned name).
* install(CODE) staging block that copies every `*.dylib` from
<prefix>/lib/ into the bundle's Frameworks/, excluding the
geometry-writer plug-ins (same EXCLUDE regex as BonsaiViewer.app —
viewer doesn't need OBJ/glTF/DAE/STP/IGS/SVG/TTL export converters).
Same long-form rationale + caveats apply (macdeployqt doesn't follow
non-Qt @rpath deps, lib-prefixed core libs vs ifcopenshell.* plug-in
naming split, Linux's equivalent lives in build_rocky.yml workflow
bash via patchelf + stage_runtime_payload). See src/bonsaiviewer/
CMakeLists.txt for the full version.
## 3. build_osx.yml: drop IFCOS_BUILD_PYTHON_WRAPPER=off
With (1) fixed, the Python wrapper smoke test should pass again. The
gate goes away; the comment block in build_osx.yml is replaced with a
short note pointing at the ifcwrap rpath fix as the underlying change
that re-enables this.
Together, (1)+(2)+(3) close the standalone IfcViewerMinimal-on-macOS
gap (task #43) and re-enable IfcOpenShell-Python on the macOS arm64 CI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -83,19 +83,19 @@ jobs:
|
||||
fi
|
||||
set -o pipefail
|
||||
export QT_DIR="$(brew --prefix qt)"
|
||||
# IFCOS_BUILD_PYTHON_WRAPPER=off skips IfcOpenShell-Python on
|
||||
# macOS until the plug-in refactor's CREATE_BUNDLE install rule
|
||||
# actually drops ifcopenshell.document.rdb.dylib into
|
||||
# site-packages/ifcopenshell/ (see the commit that added this
|
||||
# gate for the full history + the rocksdb/macdeployqt write-up).
|
||||
# --shared mirrors build_rocky.yml after 27249770e: builds
|
||||
# IfcOpenShell as shared libs so each plug-in dylib references
|
||||
# libIfcParse / libIfcGeom via @rpath instead of statically
|
||||
# embedding them — the dominant size win for BonsaiViewer.app
|
||||
# (per-plugin libs go from ~30-50 MB to a few MB).
|
||||
#
|
||||
# IfcOpenShell-Python is back on after the ifcwrap rpath fix:
|
||||
# INSTALL_RPATH "$ORIGIN" is a Linux-ism that macOS dyld bakes
|
||||
# in as a literal string, so `@rpath/ifcopenshell.document.rdb
|
||||
# .dylib` failed to resolve at import time. ifcwrap now sets
|
||||
# INSTALL_RPATH to "@loader_path" on Apple.
|
||||
CXXFLAGS="-O3" CFLAGS="-O3 ${DARWIN_C_SOURCE}" ADD_COMMIT_SHA=1 BUILD_CFG=Release \
|
||||
BUILD_BONSAIVIEWER=ON QT_DIR="${QT_DIR}" \
|
||||
IFCOS_BUILD_PYTHON_WRAPPER=off \
|
||||
python3 ./nix/build-all.py -v --diskcleanup --shared ${MAC_INTEL} \
|
||||
| tee build.log
|
||||
|
||||
|
||||
@@ -45,6 +45,16 @@ if(UNIX AND NOT APPLE AND WGPU_NATIVE_LIB_DIR)
|
||||
)
|
||||
endif()
|
||||
|
||||
# macOS install-time rpath — same as BonsaiViewer.app: dyld looks in
|
||||
# IfcViewerMinimal.app/Contents/Frameworks for @rpath/* link deps
|
||||
# (libwgpu_native.dylib, libIfcParse.dylib, libifcopenshell.geometry.dylib,
|
||||
# …). The actual files get staged below via install(CODE).
|
||||
if(APPLE)
|
||||
set_target_properties(IfcViewerMinimal PROPERTIES
|
||||
INSTALL_RPATH "@executable_path/../Frameworks"
|
||||
)
|
||||
endif()
|
||||
|
||||
# BUNDLE DESTINATION must be "." (install-prefix root) on macOS — Qt's
|
||||
# deploy generator emits `macdeployqt IfcViewerMinimal.app` (no
|
||||
# path prefix), which resolves only when the .app sits at the install
|
||||
@@ -56,3 +66,30 @@ install(TARGETS IfcViewerMinimal
|
||||
BUNDLE DESTINATION .
|
||||
)
|
||||
ifcopenshell_deploy_qt_runtime(IfcViewerMinimal)
|
||||
|
||||
# Mirror BonsaiViewer.app's bundle staging on macOS — see
|
||||
# src/bonsaiviewer/CMakeLists.txt for the long-form rationale (macdeployqt
|
||||
# doesn't follow non-Qt @rpath deps, the lib-prefixed core libs vs
|
||||
# ifcopenshell.* plug-in naming split, the geometry-writer EXCLUDE filter
|
||||
# that keeps the bundle small).
|
||||
if(APPLE)
|
||||
# libwgpu_native.dylib lives in WGPU_NATIVE_LIB_DIR (set by
|
||||
# src/ifcviewer/CMakeLists.txt). Glob it rather than hard-code the
|
||||
# filename so we cover both libwgpu_native.dylib (and any future
|
||||
# versioned name).
|
||||
file(GLOB _wgpu_native_dylib "${WGPU_NATIVE_LIB_DIR}/libwgpu_native.*")
|
||||
install(FILES ${_wgpu_native_dylib}
|
||||
DESTINATION "IfcViewerMinimal.app/Contents/Frameworks")
|
||||
|
||||
install(CODE [[
|
||||
set(_fw "${CMAKE_INSTALL_PREFIX}/IfcViewerMinimal.app/Contents/Frameworks")
|
||||
file(GLOB _ifc_dylibs "${CMAKE_INSTALL_PREFIX}/lib/*.dylib")
|
||||
list(FILTER _ifc_dylibs EXCLUDE REGEX "ifcopenshell\\.geometry\\.writer\\.")
|
||||
if(_ifc_dylibs)
|
||||
message(STATUS "Staging IfcOpenShell dylibs (linked core + plug-ins) into IfcViewerMinimal.app/Contents/Frameworks")
|
||||
file(COPY ${_ifc_dylibs} DESTINATION "${_fw}")
|
||||
else()
|
||||
message(WARNING "No IfcOpenShell *.dylib found in lib/ — IfcViewerMinimal.app will fail to launch (missing @rpath linked deps) or at IFC load time (missing plug-ins)")
|
||||
endif()
|
||||
]])
|
||||
endif()
|
||||
|
||||
@@ -233,9 +233,24 @@ IF(Python_Interpreter_FOUND OR PYTHON_MODULE_INSTALL_DIR)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT WASM_BUILD)
|
||||
set_target_properties(ifcopenshell_wrapper PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
)
|
||||
# On Linux $ORIGIN expands to the directory of the loaded
|
||||
# object (= site-packages/ifcopenshell/). macOS dyld doesn't
|
||||
# expand $ORIGIN; the equivalent placeholder is
|
||||
# @loader_path. Setting $ORIGIN on macOS bakes a literal
|
||||
# "$ORIGIN" into LC_RPATH which resolves to nothing at
|
||||
# runtime — the wrapper's `@rpath/ifcopenshell.document.rdb
|
||||
# .dylib` LC_LOAD_DYLIB then fails to find the plug-in even
|
||||
# though INSTALL(TARGETS … LIBRARY DESTINATION …/ifcopenshell)
|
||||
# above has dropped it right next to the wrapper.
|
||||
if(APPLE)
|
||||
set_target_properties(ifcopenshell_wrapper PROPERTIES
|
||||
INSTALL_RPATH "@loader_path"
|
||||
)
|
||||
else()
|
||||
set_target_properties(ifcopenshell_wrapper PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if (MSVC)
|
||||
|
||||
Reference in New Issue
Block a user