mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
ifcopenshell plug-in loader: stable anchor + bundle-aware fallbacks
# What Two upstream-shaped fixes to ifcopenshell's runtime plug-in discovery so the schema/serializer plug-ins are findable in deployment layouts other than a flat \`<prefix>/lib/\` (specifically: macOS .app bundles). ## (1) Stable anchor variable instead of a function pointer \`schema_plugin_directory()\` used \`&load_schema_plugins\` as the anchor whose containing module \`dladdr\` is asked to resolve. Function addresses are not reliably equal to a single canonical location across toolchains — on macOS arm64 with BonsaiViewer.app, \`&load_schema_plugins\` took the address of a PLT/stub inside the consumer binary rather than the actual symbol inside \`libIfcParse.dylib\`. \`dladdr\` then dutifully returned the consumer's path and the loader started searching \`BonsaiViewer.app/Contents/MacOS/\` for plug-ins that were never installed there. A variable doesn't suffer from this — it has exactly one canonical address inside its defining dylib. Add \`ifcopenshell_libifcparse_anchor\` (exported via IFC_PARSE_API) and use \`&that\` instead. Standard pattern used by Boost.DLL, GStreamer, \`_dyld_get_image_*\`, etc. ## (2) Bundle-aware fallback search paths The primary search path is \`dirname(libIfcParse)\`. That works for flat installs (Linux \`lib/\`, Windows \`bin/\`) where plug-ins are siblings of libIfcParse. macOS app bundles split the layout: \`macdeployqt\` puts non-Qt @rpath deps in \`Contents/Frameworks/\`, Apple convention asks for \`Contents/PlugIns/\`, and some install rules co-locate libIfcParse with the exe in \`Contents/MacOS/\`. Plug-ins typically end up in a sibling directory, not the same one. In \`add_search_paths_or_default\`, after registering the primary path, also register \`<parent>/PlugIns\`, \`<parent>/Frameworks\`, and \`<parent>/MacOS\` on Apple platforms. \`discover_exact\` short-circuits on the first hit so duplicates and missing directories are harmless. # What this does NOT do The plug-in dylibs still need to actually be inside the app bundle somewhere for these fallbacks to find them — the upstream install rules (\`install(TARGETS …)\` in \`src/ifcparse/CMakeLists.txt\`, \`src/serializers/CMakeLists.txt\`, etc.) put them in \`<prefix>/lib/\` which lives outside \`BonsaiViewer.app\`. That side of the fix is a follow-up — either an explicit bundle-aware install destination on the plug-in targets, or an install(CODE) sweep that mirrors them into the bundle. # What this also un-does Reverts the BonsaiViewer-only \`install(CODE)\` hack that was about to copy \`ifcopenshell.*.dylib\` from \`lib/\` into \`BonsaiViewer.app/Contents/MacOS/\` — superseded by the loader-side fix above, which lets us put the plug-ins anywhere sane inside the bundle without further consumer-side stitching. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+13
-1
@@ -205,8 +205,20 @@ ifcopenshell::plugin::metadata ifcopenshell::schema_plugin_metadata(const std::s
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// Stable anchor symbol inside libIfcParse for plugin::module_directory()'s
|
||||
// dladdr/GetModuleHandleEx lookup. A variable (vs. a function pointer) has
|
||||
// exactly one canonical address inside its defining module — function
|
||||
// pointers can resolve to a PLT/stub copy in the consumer binary on some
|
||||
// toolchains (observed on macOS arm64: `&load_schema_plugins` resolved
|
||||
// inside BonsaiViewer.exe instead of libIfcParse.dylib, so dladdr returned
|
||||
// the exe's directory and the plugin search path ended up at
|
||||
// BonsaiViewer.app/Contents/MacOS/ instead of wherever libIfcParse — and
|
||||
// therefore the schema plug-ins — actually lived).
|
||||
extern "C" IFC_PARSE_API char ifcopenshell_libifcparse_anchor;
|
||||
IFC_PARSE_API char ifcopenshell_libifcparse_anchor = 0;
|
||||
|
||||
std::filesystem::path ifcopenshell::schema_plugin_directory() {
|
||||
return plugin::module_directory(reinterpret_cast<const void*>(&ifcopenshell::load_schema_plugins));
|
||||
return plugin::module_directory(&ifcopenshell_libifcparse_anchor);
|
||||
}
|
||||
|
||||
void ifcopenshell::load_schema_plugins(schema_registry& registry) {
|
||||
|
||||
Reference in New Issue
Block a user