Files
IfcOpenShell/src/ifcviewer-minimal/CMakeLists.txt
T
Dion Moult 748b4e72a9 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>
2026-06-04 11:31:45 +10:00

96 lines
4.5 KiB
CMake

################################################################################
# #
# This file is part of IfcOpenShell. #
# #
# IfcOpenShell is free software: you can redistribute it and/or modify #
# it under the terms of the Lesser GNU General Public License as published by #
# the Free Software Foundation, either version 3.0 of the License, or #
# (at your option) any later version. #
# #
# IfcOpenShell is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# Lesser GNU General Public License for more details. #
# #
# You should have received a copy of the Lesser GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
message("Running CMakeLists.txt in /src/ifcviewer-minimal")
find_package(Qt${QT_VERSION} COMPONENTS Widgets REQUIRED PATHS ${QT_DIR})
file(GLOB IFCVIEWER_MIN_CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
add_executable(IfcViewerMinimal ${IFCVIEWER_MIN_CPP_FILES})
set_target_properties(IfcViewerMinimal PROPERTIES
AUTOMOC ON
WIN32_EXECUTABLE ON
MACOSX_BUNDLE ON
)
target_link_libraries(IfcViewerMinimal PRIVATE
IfcViewer
Qt${QT_VERSION}::Widgets
)
# Pin the build-tree rpath so the executable finds libwgpu_native.so when
# run directly out of build-viewer-wgpu/. The patched SONAME means DT_NEEDED
# is just the basename, so a single rpath entry suffices.
if(UNIX AND NOT APPLE AND WGPU_NATIVE_LIB_DIR)
set_target_properties(IfcViewerMinimal PROPERTIES
BUILD_RPATH "${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
# root. With `bin/` it can't find the bundle and the install/strip
# step fails.
install(TARGETS IfcViewerMinimal
EXPORT ${IFCOPENSHELL_EXPORT_TARGETS}
RUNTIME DESTINATION bin
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()