First run of the actual binary: ldd reported 'libR.so => not found'. patchelf --set-rpath replaces the existing rpath rather than appending, so setting it to $PREFIX/lib discarded the -Wl,-rpath the build had given the JASP binary for the bundled R. Every other bundled library resolved; only R went missing, because libR.so/libRblas.so/libRlapack.so live under R/lib/R/lib rather than in the bundled lib directory. The rpath now lists both. Two smaller things the same run surfaced: - makepkg ran its tidying steps on a tree that was already built and verified - removing libtool and static library files, purging, compressing man pages. For a -bin package that is a difference between what was tested and what gets installed. All of them are now disabled. - /opt/jasp-desktop/bin/JASP was not on PATH; the package now symlinks /usr/bin/jasp.
117 lines
4.7 KiB
Bash
Executable File
117 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# Make the staged JASP tree independent of Arch's rolling libraries.
|
|
#
|
|
# Copies every shared library JASP and its bundled R depend on into
|
|
# $PREFIX/lib, except a denylist of genuinely stable system components, then
|
|
# rewrites the rpath of every ELF object in the tree to look there first.
|
|
#
|
|
# The alternative — declaring versioned `depends` on icu, jsoncpp and friends —
|
|
# is worse on a rolling distro: when Arch ships the next ICU, pacman would
|
|
# refuse to upgrade it while JASP is installed, blocking the user's whole
|
|
# system upgrade. Bundling means we never constrain the user's system.
|
|
#
|
|
# $1 staged install tree (contains .$PREFIX)
|
|
# $2 install prefix, e.g. /opt/jasp-desktop
|
|
|
|
set -euo pipefail
|
|
|
|
STAGE_DIR="${1:?staged tree is required}"
|
|
PREFIX="${2:-/opt/jasp-desktop}"
|
|
LIBDIR="${STAGE_DIR}${PREFIX}/lib"
|
|
|
|
# Libraries that must keep coming from the system. Bundling any of these either
|
|
# breaks (glibc must match the running kernel/loader) or causes symbol clashes
|
|
# with the system Qt6 that JASP still links against.
|
|
#
|
|
# Qt6 itself is deliberately here: variant B keeps Qt as a system dependency and
|
|
# relies on Qt's binary-compatibility guarantee within Qt 6.
|
|
is_system_lib() {
|
|
case "$1" in
|
|
ld-linux*|libc.so*|libm.so*|libdl.so*|libpthread.so*|librt.so*|\
|
|
libresolv.so*|libnsl.so*|libutil.so*|libcrypt.so*|libanl.so*|\
|
|
libstdc++.so*|libgcc_s.so*|libatomic.so*) return 0 ;;
|
|
libQt6*) return 0 ;;
|
|
libGL*.so*|libEGL*.so*|libGLX*.so*|libOpenGL.so*|libGLdispatch.so*|\
|
|
libgbm.so*|libdrm*.so*|libvulkan.so*) return 0 ;;
|
|
libX11*.so*|libXext.so*|libXrender.so*|libXrandr.so*|libXi.so*|\
|
|
libXfixes.so*|libXcursor.so*|libXinerama.so*|libXcomposite.so*|\
|
|
libXdamage.so*|libXtst.so*|libXau.so*|libXdmcp.so*|libXss.so*|\
|
|
libxcb*.so*|libwayland*.so*|libxkbcommon*.so*) return 0 ;;
|
|
libdbus-1.so*|libsystemd.so*|libudev.so*) return 0 ;;
|
|
libasound.so*|libpulse*.so*) return 0 ;;
|
|
libfontconfig.so*|libfreetype.so*) return 0 ;;
|
|
libz.so*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
mkdir -p "$LIBDIR"
|
|
|
|
# Every ELF object in the tree: the JASP binary, libR, and the compiled .so of
|
|
# each of the several hundred R module packages.
|
|
mapfile -t elf_files < <(
|
|
find "$STAGE_DIR" -type f \( -name '*.so' -o -name '*.so.*' -o -perm -u+x \) \
|
|
-exec sh -c 'file -b "$1" | grep -q "^ELF" && echo "$1"' _ {} \;
|
|
)
|
|
echo ":: found ${#elf_files[@]} ELF objects in the staged tree"
|
|
|
|
# Resolving deps is iterative: a bundled library pulls in its own dependencies.
|
|
copied_any=1
|
|
round=0
|
|
while [[ "$copied_any" -eq 1 ]]; do
|
|
copied_any=0
|
|
round=$((round + 1))
|
|
|
|
mapfile -t scan < <(
|
|
printf '%s\n' "${elf_files[@]}"
|
|
find "$LIBDIR" -type f -name '*.so*' 2>/dev/null || true
|
|
)
|
|
|
|
for obj in "${scan[@]}"; do
|
|
[[ -f "$obj" ]] || continue
|
|
# ldd resolves against the build container, which is exactly the Arch
|
|
# userland we want to capture.
|
|
while read -r soname path; do
|
|
[[ -n "$path" && -f "$path" ]] || continue
|
|
# ldd reports some entries with an absolute path in the first column —
|
|
# the dynamic loader shows up as "/lib64/ld-linux-x86-64.so.2 => ...".
|
|
# Without this the denylist misses it and the copy below tries to create
|
|
# $LIBDIR/lib64/ld-linux-x86-64.so.2 in a directory that does not exist.
|
|
soname="${soname##*/}"
|
|
is_system_lib "$soname" && continue
|
|
[[ -f "$LIBDIR/$soname" ]] && continue
|
|
# Never bundle something that already lives inside our own tree.
|
|
case "$path" in "$STAGE_DIR"*|"$PREFIX"*) continue ;; esac
|
|
cp -L "$path" "$LIBDIR/$soname"
|
|
echo ":: bundled $soname"
|
|
copied_any=1
|
|
done < <(ldd "$obj" 2>/dev/null | awk '$2=="=>" && $3 ~ /^\// {print $1, $3}')
|
|
done
|
|
done
|
|
echo ":: bundling converged after $round round(s)"
|
|
|
|
chmod u+w "$LIBDIR"/*.so* 2>/dev/null || true
|
|
|
|
# An absolute rpath is safe here because the tarball always unpacks to $PREFIX;
|
|
# it avoids $ORIGIN arithmetic across bin/, R/lib/R/lib/ and the per-package
|
|
# libs/ directories, which all sit at different depths.
|
|
#
|
|
# Both directories must be listed. patchelf --set-rpath *replaces* the existing
|
|
# rpath rather than appending to it, so setting only $PREFIX/lib discards the
|
|
# -Wl,-rpath the build gave the JASP binary for the bundled R and leaves
|
|
# "libR.so => not found". libR.so, libRblas.so and libRlapack.so live under
|
|
# R/lib/R/lib, not in the bundled lib directory.
|
|
RPATH="${PREFIX}/lib:${PREFIX}/R/lib/R/lib"
|
|
echo ":: setting rpath to ${RPATH}"
|
|
patched=0
|
|
for obj in "${elf_files[@]}" "$LIBDIR"/*.so*; do
|
|
[[ -f "$obj" ]] || continue
|
|
if patchelf --set-rpath "$RPATH" "$obj" 2>/dev/null; then
|
|
patched=$((patched + 1))
|
|
fi
|
|
done
|
|
echo ":: rpath set on $patched objects"
|
|
|
|
echo ":: bundled library count: $(find "$LIBDIR" -name '*.so*' | wc -l)"
|
|
du -sh "$LIBDIR"
|