| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
`geom.tree().select()` silently returns zero results (or raises SWIG's "An unknown error occurred") in the Linux release packages, while the same commit built from source returns correct answers. `select_box()` agrees between both, and geometry conversion is bit-identical -- only operations that touch a stored TopoDS_Shape diverge. Cause is linkage, not code. 8bdaa8c narrowed the Rocky builds from `--shared` to `--ifcopenshell-shared`, which shares IfcOpenShell's own libraries but leaves every dependency static. OCCT is then compiled privately into each plug-in that uses it -- 15 of them, verified by their own copies of the BRepClass3d/BRepExtrema/Standard_Failure strings. That contradicts what the binaries already declare. tree.h:1748 casts a `conversion_result_shape*` to `open_cascade_shape*`, moves the TopoDS_Shape out of it and frees it; `open_cascade_shape` is defined once in ifcopenshell_geometry_kernel_opencascade.so and left undefined in ifcopenshell_geometry_tree_opencascade_brep.so for the loader to resolve. So the two plug-ins are designed to share one OCCT-based type system, yet static linking gives each its own Standard_Type registry and allocator. Shapes get read and released by a different OCCT instance than made them. Add `--opencascade-shared`, mirroring the existing `--ifcopenshell-shared` precedent, and use it on both Rocky workflows. It cannot be spelled `--occt-shared`: build-all.py parses any `occt-*` flag as a version override. BUILD_STATIC drives three things at once -- dependency link type, -fvisibility=hidden, and BUILD_SHARED_LIBS -- so making one dependency shared means overriding all three for it. Visibility is the subtle one: OCCT's Standard_EXPORT expands to nothing on Unix, so it relies on default visibility to export its API. Built shared under -fvisibility=hidden it exports almost nothing and its own libraries cannot resolve against each other (libTKMath.so fails to find NCollection_BaseAllocator::CommonBaseAllocator in libTKernel.so). Static archives are immune, which is why this surfaces only once OCCT goes shared. Compile OCCT with the pre-visibility flag set instead. Link the OCCT set with --as-needed. FindOpenCASCADE.cmake's config branch uses OCCT's *complete* module list, Visualization included, which against a static OCCT costs nothing -- an unreferenced module contributes no objects. Against a shared OCCT all 47 become hard DT_NEEDED entries, and libTKV3d pulls libGL.so.1 + libEGL.so.1, so `import ifcopenshell` fails on any headless machine with "libEGL.so.1: cannot open shared object file" even though nothing ever opens a window. Measured through the real find_package path with the LINK_GROUP workaround composed: 67 DT_NEEDED without the flag, 3 with it, TKV3d and TKOpenGl gone. The flag is deliberately left open rather than closed with -Wl,--no-as-needed. CMake emits the imported targets' INTERFACE_LINK_LIBRARIES -- where OCCT lists libGL/libEGL -- after that item, so closing the bracket switches the flag off immediately before the libraries it exists to exclude. Verified against the shipped artifact: closed, the kernel plug-in fell from 47 DT_NEEDED libTK entries to 14 and lost TKV3d, yet still carried a direct libEGL.so.1 and still failed to import on a headless server; open, the same 14 remain and libGL/libEGL are gone. None of the 14 retained modules depends on GL. Put the shared OCCT on LD_LIBRARY_PATH for the build itself. Nothing else points at it -- IfcOpenShell's libraries get INSTALL_RPATH=$ORIGIN and OCCT sits in its own dependency prefix -- so the post-build `import ifcopenshell` check fails the same way. This is build-time only; the shipped packages get libTK*.so* staged beside the payload with an $ORIGIN RUNPATH instead. Suffix OCCT's install directory with `-shared` when it applies. Static and shared installs are not interchangeable, but `build_dependency` skips any dependency whose install dir already exists and cache_dependencies.py keys its tarballs purely on that directory name -- so the static `cache-occt-7.8.1.tar.gz` restored from the build-outputs repo silently satisfied the build and BUILD_LIBRARY_TYPE was never applied. This is the same cache stickiness 8bdaa8c described, pointing the other way. The suffix makes the key configuration-aware, so it self-invalidates and the static tarball stays valid for builds that still want static. Packaging is the other half, and is why 8bdaa8c backed the flag out -- `stage_runtime_payload` only copies from install/ifcopenshell, so OCCT in install/occt-* was never staged and `--shared` "worked by accident" off cached static outputs. Stage libTK*.so* alongside, then give every staged library an $ORIGIN RUNPATH: the core libs currently carry dead build-machine RPATHs and the plug-ins carry none, resolving only because the Python wrapper pulls them in by SONAME first. Shared OCCT has no such first loader, since it is reached through the dlopen'd plug-ins. The packages shrink: the python zip goes from 109.4 MB to 85.6 MB, because the duplicated OCCT was 113 MB of the 287 MB unpacked payload (the eight geometry_writer_ifc* plug-ins alone were 4.6 MB each) against ~67 MB for one shared copy. Same argument as a91b1da ("Reduce Rocky package size") and 402591e. macOS and Windows are affected too but are not fixed here. Their packaging resolves via @loader_path install names and would need install_name_tool rewriting, which cannot be verified from Linux; adding the flag without that would ship a package that fails to load. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
I had a similar suggestion to Andrej, but if it's broken we don't need to retain the old behavior. I'd say, just remove all flags related to static/shared building from the build script and default to the new behavior. (Leave cmake as is, no need to remove options there). Also, I don't think these problems (multiple static libs compiled into multiple dynamic libraries loaded simultaneously) are unique to opencascade, just more apparent on our side because we have wider use across different dlls. Everything always shared seems to be the only sane default.
|
Sorry, something went wrong.
|
@Andrej730 I noticed you recently added the --occt-shared flag, what do you think about @aothms ' suggestion to go full shared on everything? Does this also mean that there will be a bazillion sofiles? |
Sorry, something went wrong.
I don't think it's a different order of magnitude. Worst offenders are definitely ifcopenshell itself (plugins) and opencascade. |
Sorry, something went wrong.
|
About --occt-shared - I was trying to test if this is enough to resolve all the issues I've met with OCCT recently, can confirm that it is. The reasoning in the first post is correct - each binary with static OCCT has its own Standard_Type registry, so e.g. if one binary is creating STANDARD_TYPE(Geom_Line), then only this binary would be able to recognize it as Geom_Line, any other would think it's a unknown entity. But agree, moving to full shared build seems to be the most straightforward way to resolve this, since we already have plugins, ifcopenshell libs as shared + OCCT needs it too, + potentially Boost, so we might as well reuse the infrastructure for managing and bundling shared libs for all other dependencies too, maybe it will save us from some side effects or reduce binaries size in some cases. And most .so files are coming from OCCT and ifcopenshell either way, as Thomas mentioned.
|
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
AI verdict below, out of my knowledge area, but I can confirm the bug and fix. I think @Andrej730 encountered something similar too.
geom.tree().select() silently returns zero results (or raises SWIG's "An unknown error occurred") in the Linux release packages, while the same commit built from source returns correct answers. select_box() agrees between both, and geometry conversion is bit-identical -- only operations that touch a stored TopoDS_Shape diverge.
Cause is linkage, not code. 8bdaa8c narrowed the Rocky builds from --shared to --ifcopenshell-shared, which shares IfcOpenShell's own libraries but leaves every dependency static. OCCT is then compiled privately into each plug-in that uses it -- 15 of them, verified by their own copies of the BRepClass3d/BRepExtrema/Standard_Failure strings.
That contradicts what the binaries already declare. tree.h:1748 casts a conversion_result_shape* to open_cascade_shape*, moves the TopoDS_Shape out of it and frees it; open_cascade_shape is defined once in ifcopenshell_geometry_kernel_opencascade.so and left undefined in ifcopenshell_geometry_tree_opencascade_brep.so for the loader to resolve. So the two plug-ins are designed to share one OCCT-based type system, yet static linking gives each its own Standard_Type registry and allocator. Shapes get read and released by a different OCCT instance than made them.
Add --opencascade-shared, mirroring the existing --ifcopenshell-shared precedent, and use it on both Rocky workflows. It cannot be spelled --occt-shared: build-all.py parses any occt-* flag as a version override.
BUILD_STATIC drives three things at once -- dependency link type, -fvisibility=hidden, and BUILD_SHARED_LIBS -- so making one dependency shared means overriding all three for it. Visibility is the subtle one: OCCT's Standard_EXPORT expands to nothing on Unix, so it relies on default visibility to export its API. Built shared under -fvisibility=hidden it exports almost nothing and its own libraries cannot resolve against each other (libTKMath.so fails to find
NCollection_BaseAllocator::CommonBaseAllocator in libTKernel.so). Static archives are immune, which is why this surfaces only once OCCT goes shared. Compile OCCT with the pre-visibility flag set instead.
Put the shared OCCT on LD_LIBRARY_PATH for the build itself. Nothing else points at it -- IfcOpenShell's libraries get INSTALL_RPATH=$ORIGIN and OCCT sits in its own dependency prefix -- so the post-build import ifcopenshell check fails with "libTKernel.so.7.8: cannot open shared object file". This is build-time only; the shipped packages get libTK*.so* staged beside the payload with an $ORIGIN RUNPATH instead.
Suffix OCCT's install directory with -shared when it applies. Static and shared installs are not interchangeable, but build_dependency skips any dependency whose install dir already exists and cache_dependencies.py keys its tarballs purely on that directory name -- so the static cache-occt-7.8.1.tar.gz restored from the build-outputs repo silently satisfied the build and BUILD_LIBRARY_TYPE was never applied. This is the same cache stickiness 8bdaa8c described, pointing the other way. The suffix makes the key configuration-aware, so it self-invalidates and the static tarball stays valid for builds that still want static.
Packaging is the other half, and is why 8bdaa8c backed the flag out -- stage_runtime_payload only copies from install/ifcopenshell, so OCCT in install/occt-* was never staged and --shared "worked by accident" off cached static outputs. Stage libTK*.so* alongside, then give every staged library an $ORIGIN RUNPATH: the core libs currently carry dead build-machine RPATHs and the plug-ins carry none, resolving only because the Python wrapper pulls them in by SONAME first. Shared OCCT has no such first loader, since it is reached through the dlopen'd plug-ins.
This shrinks the packages. Duplicated OCCT is 113 MB of the 287 MB unpacked python payload (the eight geometry_writer_ifc* plug-ins alone are 4.6 MB each); one shared copy of the 29 linked libTK is around 67 MB, and the plug-ins collapse to source-build sizes -- kernel_opencascade 15.4 MB -> 1.2 MB, tree_opencascade_brep 9.6 MB -> 248 KB. Same argument as a91b1da ("Reduce Rocky package size") and 402591e.
macOS and Windows are affected too but are not fixed here. Their packaging resolves via @loader_path install names and would need install_name_tool rewriting, which cannot be verified from Linux; adding the flag without that would ship a package that fails to load.