update new-module skill and record linking debug strategy in README
This commit is contained in:
@@ -106,6 +106,22 @@ There is **no `--dry-run` and no `--check-mode`** — to sanity-check config/pat
|
||||
prints "done", installing the modulefile over a broken PREFIX. Guard every critical
|
||||
hook step with `|| std::die 42 "<what and how to recover>"` and end `install` with an
|
||||
existence check of the file the modulefile needs.
|
||||
- **Shared-lib linking (compiled modules)**: RHEL bakes NO RPATH/RUNPATH into binaries
|
||||
and build-time `-L` paths are not recorded — a binary that links fine can fail at
|
||||
runtime with `cannot open shared object` / `GLIBCXX_... not found`. Diagnose:
|
||||
`objdump -p <bin|.so> | grep -E 'NEEDED|RPATH|RUNPATH'`, then `ldd` (look for
|
||||
"not found"). Runtime search order: DT_RPATH → LD_LIBRARY_PATH → DT_RUNPATH →
|
||||
ld.so.cache → /lib64,/usr/lib64. When the order bites:
|
||||
- built with a gcc module → ext needs newer libstdc++ than system /usr/lib64:
|
||||
add gcc to `runtime_deps:` (pymol-open-source) so the module's lib dir wins.
|
||||
- install may land in `$PREFIX/lib` (autotools/venv) OR `$PREFIX/lib64` (CMake
|
||||
GNUInstallDirs on RHEL) — check which exists, `prepend-path LD_LIBRARY_PATH`
|
||||
that one (ImageMagick). Or bake it at build: `LDFLAGS="-Wl,-rpath,$PREFIX/lib"`.
|
||||
- vendor binary WITH RPATH ignores LD_LIBRARY_PATH (patchelf/rebuild only); with
|
||||
RUNPATH, LD_LIBRARY_PATH wins — leftover loaded modules can shadow bundled
|
||||
libs (contaminated env again).
|
||||
- verify after install: `ldd` the main binary and any compiled Python ext (e.g.
|
||||
pymol `_cmd*.so`) — no "not found" lines (adxv README shows the pattern).
|
||||
- Interrupted downloads leave a **partial file in the distfiles dir which prep reuses
|
||||
silently** (no integrity check unless `shasums:` is set). Symptom: `gzip: unexpected
|
||||
end of file`. Fix: `rm` it there and re-run; verify big tarballs with `gzip -t` first;
|
||||
|
||||
@@ -90,6 +90,49 @@ rm -rf ~/.cache/Pmodules/distfiles
|
||||
common modbuild traps ("done" without success — no errexit in hooks,
|
||||
partial downloads reused silently, slow installs on shared /opt/psi).
|
||||
|
||||
## Debugging shared-library errors (lib vs lib64, RPATH)
|
||||
|
||||
Compiled modules often build fine and then fail at load/run time with
|
||||
`cannot open shared object file` or `GLIBCXX_x.y.z not found`. Diagnose
|
||||
with binutils (works on any binary or `.so`, e.g. `/bin/cmake`):
|
||||
|
||||
```bash
|
||||
objdump -p <binary-or-.so> | grep NEEDED # which libs it links
|
||||
objdump -p <binary-or-.so> | grep -E 'RPATH|RUNPATH' # usually EMPTY on RHEL
|
||||
ldd <binary-or-.so> # how each resolves; look for "not found"
|
||||
```
|
||||
|
||||
The dynamic linker searches in this order: `DT_RPATH` (if set) →
|
||||
`LD_LIBRARY_PATH` → `DT_RUNPATH` (if set) → `/etc/ld.so.cache` →
|
||||
`/lib64`, `/usr/lib64`. On RHEL, RPATH/RUNPATH are normally **not set**,
|
||||
and the build-time `-L` paths are not recorded in the binary — so build
|
||||
time and runtime resolve libraries completely independently.
|
||||
|
||||
Situations where the default order is not what you expect:
|
||||
|
||||
1. **Compiled with a newer-gcc module** → the binary needs a newer
|
||||
libstdc++, but at runtime the old system `/usr/lib64/libstdc++.so.6`
|
||||
resolves first (no RPATH) → `GLIBCXX_... not found`. Fix: add the gcc
|
||||
module to `runtime_deps:` so its lib dir is prepended to
|
||||
`LD_LIBRARY_PATH` (see `pymol-open-source/files/config.yaml`).
|
||||
2. **lib vs lib64 install dir**: system 64-bit libs live in `/usr/lib64`,
|
||||
but local installs may land in `$PREFIX/lib` (autotools default, venvs)
|
||||
or `$PREFIX/lib64` (CMake GNUInstallDirs picks lib64 on RHEL). Check
|
||||
which dir the install actually produced and prepend that one:
|
||||
`prepend-path LD_LIBRARY_PATH $PREFIX/lib` (see `ImageMagick/modulefile`).
|
||||
3. **Vendor binary WITH RPATH baked in**: `DT_RPATH` beats
|
||||
`LD_LIBRARY_PATH`, so the modulefile cannot override it — patchelf or
|
||||
rebuild are the only fixes. With `DT_RUNPATH` instead, it is the other
|
||||
way round: `LD_LIBRARY_PATH` wins, so a leftover loaded module can
|
||||
shadow the vendor's bundled libs (another contaminated-environment
|
||||
source — `module purge` first).
|
||||
4. Alternative to LD_LIBRARY_PATH in the modulefile: bake the path at
|
||||
build time with `LDFLAGS="-Wl,-rpath,$PREFIX/lib"`.
|
||||
|
||||
After install, `ldd` the main binary and any compiled Python extension
|
||||
(e.g. pymol's `_cmd*.so`) — no line may say "not found"
|
||||
(see `adxv/README.md`).
|
||||
|
||||
## Building with an AI agent
|
||||
|
||||
The repo ships the **new-module** skill at `.claude/skills/new-module/`
|
||||
|
||||
Reference in New Issue
Block a user