Root cause of the broken 'Sign in with Plex' and update links on portable builds: the bundle ships Ubuntu's libgio, whose compiled-in helper path (/usr/lib/x86_64-linux-gnu/glib-2.0/gio-launch-desktop) exists only on Debian-family hosts. glib then falls back to a bare PATH search, and distros like Fedora keep the helper in /usr/libexec outside PATH, so every URL/desktop-file spawn failed. Installing the RPM masked it only because system glib is built with the right path. Bundle the build host's gio-launch-desktop next to the libgio it was built with (bundle-libs.sh now fails the build if libgio is bundled and the helper cannot be found) and export GIO_LAUNCH_DESKTOP from plezy.sh, which glib checks before its compiled-in path. Covers the tarball and the deb/rpm/pacman packages alike, since they all ship the same bundle and launch through the wrapper. close #1477
203 lines
8.5 KiB
Bash
Executable File
203 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# bundle-libs.sh — Bundle shared libraries and GTK modules into a Flutter
|
|
# Linux bundle directory so the resulting tarball is portable.
|
|
#
|
|
# Usage: bundle-libs.sh <bundle-dir>
|
|
#
|
|
# The bundle directory should contain the main binary and a lib/ subdirectory.
|
|
# After running, lib/ will contain all required shared libraries (minus the
|
|
# exclusion list), GTK/GDK modules, and GSettings schemas.
|
|
|
|
set -euo pipefail
|
|
|
|
BUNDLE_DIR="$(realpath "$1")"
|
|
LIB_DIR="$BUNDLE_DIR/lib"
|
|
BINARY="$BUNDLE_DIR/plezy"
|
|
|
|
if [[ ! -f "$BINARY" ]]; then
|
|
echo "Error: binary not found at $BINARY" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── Exclusion list ──────────────────────────────────────────────────────────
|
|
# Libraries that must come from the host system.
|
|
EXCLUDE_PATTERN='linux-vdso\.so|ld-linux.*\.so|libc\.so|libm\.so|libpthread\.so|libdl\.so|librt\.so|libmvec\.so|libresolv\.so|libnss_.*\.so|libGL\.so|libEGL\.so|libGLX\.so|libGLESv2\.so|libGLdispatch\.so|libOpenGL\.so|libepoxy\.so|libvulkan\.so|libdrm\.so|libgbm\.so|libnvidia.*\.so|libcuda.*\.so|libasound\.so|libwayland.*\.so|libxcb.*\.so|libva.*\.so|libvdpau\.so|libX11\.so|libX11-xcb\.so|libXext\.so'
|
|
|
|
# ── Helper: collect ldd deps ───────────────────────────────────────────────
|
|
collect_deps() {
|
|
# Collect resolved library paths from ldd output for the given files.
|
|
# Filters out excluded libs and libs that don't exist on disk.
|
|
ldd "$@" 2>/dev/null \
|
|
| grep -oP '(?<==> )/\S+' \
|
|
| sort -u \
|
|
| grep -vE "$EXCLUDE_PATTERN" \
|
|
|| true
|
|
}
|
|
|
|
# ── Explicitly bundle dlopen'd libraries ───────────────────────────────────
|
|
# These are loaded at runtime via dlopen() and invisible to ldd.
|
|
bundle_dlopen_lib() {
|
|
local name="$1"
|
|
local path
|
|
path="$(ldconfig -p | grep -oP "/\S+/${name}[^\s]*" | head -1 || true)"
|
|
if [[ -n "$path" && -f "$path" ]]; then
|
|
local base
|
|
base="$(basename "$path")"
|
|
if [[ ! -f "$LIB_DIR/$base" ]]; then
|
|
echo "==> Bundling dlopen'd library: $path"
|
|
cp -L "$path" "$LIB_DIR/$base"
|
|
fi
|
|
else
|
|
echo "WARNING: Could not find $name" >&2
|
|
fi
|
|
}
|
|
|
|
echo "==> Discovering shared library dependencies..."
|
|
|
|
# Seed: the main binary + all .so files already in lib/
|
|
mapfile -t seed_files < <(find "$LIB_DIR" -name '*.so*' -type f 2>/dev/null; echo "$BINARY")
|
|
|
|
# Iteratively resolve until no new deps appear
|
|
declare -A seen
|
|
while true; do
|
|
mapfile -t deps < <(collect_deps "${seed_files[@]}")
|
|
new_files=()
|
|
for dep in "${deps[@]}"; do
|
|
base="$(basename "$dep")"
|
|
if [[ -z "${seen[$base]:-}" && ! -f "$LIB_DIR/$base" ]]; then
|
|
seen[$base]=1
|
|
cp -L "$dep" "$LIB_DIR/$base" 2>/dev/null || true
|
|
new_files+=("$LIB_DIR/$base")
|
|
fi
|
|
done
|
|
if [[ ${#new_files[@]} -eq 0 ]]; then
|
|
break
|
|
fi
|
|
echo " Bundled ${#new_files[@]} new libraries"
|
|
seed_files=("${new_files[@]}")
|
|
done
|
|
|
|
# ── GTK modules (loaded via dlopen, invisible to ldd) ──────────────────────
|
|
|
|
bundle_module_dir() {
|
|
local src_dir="$1" dest_dir="$2"
|
|
if [[ -d "$src_dir" ]]; then
|
|
echo "==> Bundling modules from $src_dir"
|
|
mkdir -p "$dest_dir"
|
|
find "$src_dir" -name '*.so' -exec cp -L {} "$dest_dir/" \;
|
|
# Resolve transitive deps of module .so files
|
|
mapfile -t mod_deps < <(collect_deps "$dest_dir"/*.so 2>/dev/null)
|
|
for dep in "${mod_deps[@]}"; do
|
|
base="$(basename "$dep")"
|
|
if [[ ! -f "$LIB_DIR/$base" ]]; then
|
|
cp -L "$dep" "$LIB_DIR/$base" 2>/dev/null || true
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
# gdk-pixbuf loaders
|
|
PIXBUF_MODULE_DIR="$(pkg-config --variable=gdk_pixbuf_moduledir gdk-pixbuf-2.0 2>/dev/null || true)"
|
|
PIXBUF_DEST="$LIB_DIR/gdk-pixbuf-2.0/2.10.0/loaders"
|
|
if [[ -n "$PIXBUF_MODULE_DIR" && -d "$PIXBUF_MODULE_DIR" ]]; then
|
|
bundle_module_dir "$PIXBUF_MODULE_DIR" "$PIXBUF_DEST"
|
|
# Regenerate loaders.cache with relative paths
|
|
GDK_PIXBUF_MODULEDIR="$PIXBUF_DEST" \
|
|
gdk-pixbuf-query-loaders "$PIXBUF_DEST"/*.so > "$LIB_DIR/gdk-pixbuf-2.0/2.10.0/loaders.cache" 2>/dev/null || true
|
|
fi
|
|
|
|
# GIO modules
|
|
GIO_MODULE_DIR="$(pkg-config --variable=giomoduledir gio-2.0 2>/dev/null || true)"
|
|
GIO_DEST="$LIB_DIR/gio/modules"
|
|
if [[ -n "$GIO_MODULE_DIR" && -d "$GIO_MODULE_DIR" ]]; then
|
|
bundle_module_dir "$GIO_MODULE_DIR" "$GIO_DEST"
|
|
fi
|
|
|
|
# gio-launch-desktop helper
|
|
# libgio spawns URI/desktop-file handlers through this binary. Its location is
|
|
# compiled into libgio as the *build host's* pkglibexecdir (an Ubuntu
|
|
# multiarch path), which does not exist on other distros, and glib's bare-name
|
|
# PATH fallback misses hosts that keep the helper in /usr/libexec (Fedora).
|
|
# Every launchUrl then failed (#1477). Ship the helper that matches the
|
|
# bundled libgio; plezy.sh points GIO_LAUNCH_DESKTOP at it.
|
|
if compgen -G "$LIB_DIR/libgio-2.0.so*" > /dev/null; then
|
|
GLIB_LIBDIR="$(pkg-config --variable=libdir glib-2.0 2>/dev/null || true)"
|
|
GIO_HELPER=""
|
|
for candidate in "${GLIB_LIBDIR:+$GLIB_LIBDIR/glib-2.0/gio-launch-desktop}" \
|
|
/usr/libexec/gio-launch-desktop \
|
|
/usr/lib/glib-2.0/gio-launch-desktop; do
|
|
if [[ -n "$candidate" && -x "$candidate" ]]; then
|
|
GIO_HELPER="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -z "$GIO_HELPER" ]]; then
|
|
echo "Error: libgio is bundled but no gio-launch-desktop helper was found on the build host." >&2
|
|
echo "Without it, URL launching breaks on every distro whose glib layout differs from the builder's." >&2
|
|
exit 1
|
|
fi
|
|
mkdir -p "$LIB_DIR/glib-2.0"
|
|
cp -a "$GIO_HELPER" "$LIB_DIR/glib-2.0/gio-launch-desktop"
|
|
echo "==> Bundled gio-launch-desktop from $GIO_HELPER"
|
|
fi
|
|
|
|
# GTK IM modules + print backends
|
|
GTK_LIBDIR="$(pkg-config --variable=libdir gtk+-3.0 2>/dev/null || true)"
|
|
if [[ -n "$GTK_LIBDIR" ]]; then
|
|
IM_MODULE_DIR="$GTK_LIBDIR/gtk-3.0/3.0.0/immodules"
|
|
IM_DEST="$LIB_DIR/gtk-3.0/3.0.0/immodules"
|
|
bundle_module_dir "$IM_MODULE_DIR" "$IM_DEST"
|
|
|
|
# Regenerate IM modules cache
|
|
if [[ -d "$IM_DEST" ]] && command -v gtk-query-immodules-3.0 &>/dev/null; then
|
|
gtk-query-immodules-3.0 "$IM_DEST"/*.so > "$IM_DEST/../immodules.cache" 2>/dev/null || true
|
|
fi
|
|
|
|
PRINT_MODULE_DIR="$GTK_LIBDIR/gtk-3.0/3.0.0/printbackends"
|
|
PRINT_DEST="$LIB_DIR/gtk-3.0/3.0.0/printbackends"
|
|
bundle_module_dir "$PRINT_MODULE_DIR" "$PRINT_DEST"
|
|
fi
|
|
|
|
# ── Resolve transitive deps of modules ────────────────────────────────────
|
|
# Module bundling above only resolves one level of deps. Re-run the iterative
|
|
# walk over everything now in lib/ to catch deeper transitive deps (e.g.
|
|
# GIO module → libsystemd → libselinux).
|
|
echo "==> Resolving transitive dependencies of bundled modules..."
|
|
mapfile -t seed_files < <(find "$LIB_DIR" -name '*.so*' -type f 2>/dev/null)
|
|
while true; do
|
|
mapfile -t deps < <(collect_deps "${seed_files[@]}")
|
|
new_files=()
|
|
for dep in "${deps[@]}"; do
|
|
base="$(basename "$dep")"
|
|
if [[ -z "${seen[$base]:-}" && ! -f "$LIB_DIR/$base" ]]; then
|
|
seen[$base]=1
|
|
cp -L "$dep" "$LIB_DIR/$base" 2>/dev/null || true
|
|
new_files+=("$LIB_DIR/$base")
|
|
fi
|
|
done
|
|
if [[ ${#new_files[@]} -eq 0 ]]; then
|
|
break
|
|
fi
|
|
echo " Bundled ${#new_files[@]} new libraries"
|
|
seed_files=("${new_files[@]}")
|
|
done
|
|
|
|
# ── GSettings schemas ─────────────────────────────────────────────────────
|
|
SCHEMAS_SRC="/usr/share/glib-2.0/schemas"
|
|
SCHEMAS_DEST="$BUNDLE_DIR/share/glib-2.0/schemas"
|
|
if [[ -d "$SCHEMAS_SRC" ]]; then
|
|
echo "==> Bundling GSettings schemas"
|
|
mkdir -p "$SCHEMAS_DEST"
|
|
cp "$SCHEMAS_SRC"/*.xml "$SCHEMAS_DEST/" 2>/dev/null || true
|
|
cp "$SCHEMAS_SRC"/*.override "$SCHEMAS_DEST/" 2>/dev/null || true
|
|
glib-compile-schemas "$SCHEMAS_DEST" 2>/dev/null || true
|
|
fi
|
|
|
|
# ── Strip debug symbols ───────────────────────────────────────────────────
|
|
echo "==> Stripping debug symbols..."
|
|
find "$LIB_DIR" -name '*.so*' -type f -exec strip --strip-unneeded {} \; 2>/dev/null || true
|
|
|
|
echo "==> Done. Bundled libraries in $LIB_DIR"
|
|
du -sh "$LIB_DIR"
|