python-fcl: add build-python-fcl.yml for riscv64 wheels - #1770
Merged
Conversation
Builds eigen/libccd/octomap/fcl from source via build_dependencies/ install_linux.sh (mirrors upstream's own push.yml), then the Cython extension against them. Widens license-files to ship the vendored fcl/libccd/octomap licences alongside python-fcl's own.
All 4 matrix legs on PR #1770 failed identically at import with 'undefined symbol: typeinfo for fcl::CollisionGeometry<double>' (_ZTIN3fcl17CollisionGeometryIdEE). fcl's own CMake config sets no -fvisibility, and Debian's riscv64 buildd builds+tests the same upstream FCL 0.7.0 successfully, so this isn't a compiler/arch RTTI limitation. auditwheel's repair log shows zero libraries vendored (only a platform-tag relabel), unlike the official x86_64 wheel, which points at a link-time/dependency-resolution gap in our build rather than fcl's source. Trims the matrix to cp312 and adds before- build diagnostics (ldd + nm -D on the freshly built libfcl.so) to confirm whether the symbol is exported from the library itself. Also skips fcl's own ~25min test-binary build (BUILD_TESTING=OFF, not shipped in the wheel) to speed up iteration.
before-build confirmed libfcl.so exports the missing symbol (_ZTIN3fcl17CollisionGeometryIdEE, weak+global, verified via nm -D) and ldd resolves all of libfcl.so's own deps cleanly, so the gap is between the built Cython extension and that export, not the library. Dumps the extension .so's own ldd/NEEDED/RUNPATH/undefined-symbol state via find_spec (no import, so it doesn't trip the crash) before the pytest run that's expected to still fail.
find_spec('fcl.fcl') still imports the fcl package first (to locate
the submodule), tripping the same crash with none of the intended
diagnostics printed. importlib.metadata.distribution(...).files
lists the RECORD without importing anything, so use that to find the
.so path instead.
readelf -d on the built extension shows liboctomap correctly NEEDED and vendored, but libfcl.so is entirely absent from NEEDED despite being requested via setup.py's libraries=["fcl", "octomap"] and despite the extension having genuine unresolved references into it (nm -D --undefined-only lists exactly fcl::CollisionGeometry<double>'s ctor/dtor/typeinfo/vtable, which only exist via fcl's own `extern template ... FCL_EXPORT CollisionGeometry<double>` pattern). libfcl.so does export those symbols (weak+global, confirmed via before-build's nm -D --defined-only), so this looks like the image's --as-needed linker default dropping -lfcl. LDFLAGS=-Wl,--no-as-needed forces every requested -l onto NEEDED regardless.
… clean up
setup.py's get_libraries_dir() checks "LD_LIBRARY_PATH" in os.environ
and unconditionally splits its value; our build container exports it
empty (present, not unset), so "".split(":") appends an empty string
to library_dirs. distutils turns that into a bare -L with no argument,
which swallows the following -lfcl as -L's path instead of a library
request (confirmed via the actual printed link command: '-L -lfcl').
-loctomap, being last on the line, is unaffected - matching the
asymmetry seen in the wheel (liboctomap present and vendored, libfcl
entirely absent from NEEDED). That's the real source of the
'undefined symbol: typeinfo for fcl::CollisionGeometry<double>'
failure on every interpreter; it has nothing to do with riscv64's
toolchain or --as-needed (confirmed: forcing -Wl,--no-as-needed in
610ff2f made no difference, since -lfcl was never parsed as a
library flag to begin with).
0002-*.patch guards get_libraries_dir() (and the identical CPATH
pattern in get_include_dirs()) against an empty-but-present value.
Drops the now-unneeded LDFLAGS override and the debug-only
before-build/test-command introspection, and restores the full
cp312/cp313/cp314/cp314t matrix.
luhenry
added a commit
that referenced
this pull request
Sep 11, 2026
luhenry
added a commit
that referenced
this pull request
Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
python-fcl0.7.0.11Compiles a Cython wrapper over FCL, libccd and octomap, all three built from source at build time. Upstream publishes no riscv64 wheel.
Mirrors upstream's
push.yml, driven entirely throughpyproject.toml's[tool.cibuildwheel].Differs from upstream
before-allskips fcl's own test-binary build (-DBUILD_TESTING=OFF) - not shipped in the wheel, cuts ~25min per interpreter.Testing
License: Wheel bundles fcl, libccd and octomap (all BSD); upstream ships none of their licence text in any platform's wheel, so the build adds it.
Patches
0001-Widen-license-files-to-cover-the-vendored-fcl-libcc.patch- To upstream [same gap in every python-fcl wheel on PyPI, not riscv64-specific]. Without it dist-info/licenses ships only python-fcl's own LICENSE.0002-Guard-against-an-empty-but-present-LD_LIBRARY_PATH-.patch- To upstream [only reproduces when LD_LIBRARY_PATH is exported empty, which our build container does but upstream's own CI apparently does not]. Without it-lfclis silently swallowed as the path argument of a bare-L(from"".split(":")), so libfcl is never linked and the wheel fails to import withundefined symbol: typeinfo for fcl::CollisionGeometry<double>.Built on cp312; 15 passed.