aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-15 17:13:41 -0500
committerSulfurNitride <lukew19@proton.me>2026-05-16 12:57:25 -0500
commitfe424eef00692482b859c9b23e86b8fb814754fd (patch)
tree35e7330e6fee6120baf8475226377fcefaddfea4
parentf782b0ca53b2353011ebc517302f1509690040ee (diff)
package: prune unused PyQt6 modules and dedupe lib/
Shrinks the staging tree by ~27 MB (~250M -> ~223M) via two changes in docker/build-inner.sh: 1. PyQt6 prune. Allowlist QtCore/QtGui/QtWidgets/QtOpenGL/QtOpenGLWidgets (confirmed by full-repo grep of all shipped Python plugins). Drop the remaining ~30 .abi3.so bindings plus bindings/, uic/, lupdate/, every .pyi stub, every __pycache__. Pruning runs BEFORE the existing ldd-scan loop, so libQt6Designer/Help/Sql/Test/SvgWidgets/Bluetooth/... stop getting pulled into lib/ as a side effect. plugins/libs/PyQt6: 31M -> 14M. 2. Dedupe lib/. cp -Lf on globs like libboost_program_options.so* stages the unversioned soname and the versioned SO as identical real files. Adds a post-bundling pass that, for each *.so / *.so.N file, finds a byte-identical versioned twin via cmp -s and replaces it with a symlink. Skips existing symlinks. Fixes the boost_program_options, boost_thread, and xcb-cursor duplicates that have been here from the start. Also removes the now-orphan fluorine_vfs.dll / fluorine_vfs_hid.dll / libfluorine_vfs_preload.so staging block left behind after the VFS shim removal in 2ed0704f. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-rwxr-xr-xdocker/build-inner.sh92
1 files changed, 80 insertions, 12 deletions
diff --git a/docker/build-inner.sh b/docker/build-inner.sh
index 865e0b4..888894a 100755
--- a/docker/build-inner.sh
+++ b/docker/build-inner.sh
@@ -149,18 +149,6 @@ cp -f build/libs/uibase/src/libuibase.so "${OUT_DIR}/lib/"
cp -f build/libs/libbsarch/liblibbsarch.so "${OUT_DIR}/lib/"
cp -f build/libs/archive/src/libarchive.so "${OUT_DIR}/lib/"
cp -f build/libs/plugin_python/src/runner/librunner.so "${OUT_DIR}/lib/"
-[ -f build/src/src/libfluorine_vfs_preload.so ] && \
- cp -f build/src/src/libfluorine_vfs_preload.so "${OUT_DIR}/lib/"
-# PE-side VFS injector: cross-built with mingw, copied into wine/ where
-# Fluorine picks it up at prefix init and stages into c:\\windows\\system32\\.
-if [ -f build/src/src/fluorine_vfs.dll ]; then
- mkdir -p "${OUT_DIR}/wine"
- cp -f build/src/src/fluorine_vfs.dll "${OUT_DIR}/wine/"
-fi
-if [ -f build/src/src/fluorine_vfs_hid.dll ]; then
- mkdir -p "${OUT_DIR}/wine"
- cp -f build/src/src/fluorine_vfs_hid.dll "${OUT_DIR}/wine/"
-fi
if [ -f "libs/bsa_ffi/target/release/libbsa_ffi.so" ]; then
cp -f libs/bsa_ffi/target/release/libbsa_ffi.so "${OUT_DIR}/lib/"
fi
@@ -323,6 +311,37 @@ mkdir -p "${PYQT6_OUT}"
cp -a "${PYQT6_SRC}/." "${PYQT6_OUT}/"
# Remove PyQt6's bundled Qt — we already have Qt in lib/
rm -rf "${PYQT6_OUT}/Qt6"
+
+# Prune unused PyQt6 modules. Full-repo grep of libs/ + src/ confirms shipped
+# Python plugins only import the five modules below. Pruning happens BEFORE
+# the deps-scan loop further down so libQt6Designer/Help/Sql/Test/SvgWidgets/
+# Bluetooth/... stop getting copied into lib/ as a side effect.
+PYQT6_KEEP_MODULES="QtCore QtGui QtWidgets QtOpenGL QtOpenGLWidgets"
+rm -rf "${PYQT6_OUT}/bindings" "${PYQT6_OUT}/uic" "${PYQT6_OUT}/lupdate"
+find "${PYQT6_OUT}" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
+find "${PYQT6_OUT}" -type f -name '*.pyi' -delete 2>/dev/null || true
+for entry in "${PYQT6_OUT}"/*; do
+ [ -e "${entry}" ] || continue
+ base="$(basename "${entry}")"
+ case "${base}" in
+ __init__.py|py.typed) continue ;;
+ sip.cpython-*.so) continue ;;
+ Qt6) continue ;; # already removed; defensive
+ *.abi3.so)
+ mod="${base%.abi3.so}"
+ keep=0
+ for k in ${PYQT6_KEEP_MODULES}; do
+ [ "${mod}" = "${k}" ] && keep=1 && break
+ done
+ [ ${keep} -eq 0 ] && rm -f "${entry}"
+ ;;
+ *)
+ rm -rf "${entry}"
+ ;;
+ esac
+done
+echo "PyQt6 pruned to: ${PYQT6_KEEP_MODULES}"
+
# Patchelf all PyQt6 binding .so files to reach our lib/ via RPATH
# Path: plugins/libs/PyQt6/*.so → ../../.. = staging root → lib/
find "${PYQT6_OUT}" -name "*.so" -exec \
@@ -346,6 +365,55 @@ find "${PYQT6_OUT}" -name "*.so" | while read -r pyqt_so; do
done
done
+# ── Dedupe identical files in lib/ via symlinks ──
+# Source-side `cp -Lf` resolves symlinks, so unversioned (foo.so) and versioned
+# (foo.so.X.Y.Z) get staged as identical real files. Replace the unversioned
+# (or shorter-versioned) twin with a symlink to the longest real file.
+# MUST run after both the main dep-collection loop and the PyQt6 ldd scan
+# above; do not reorder.
+echo "Deduping lib/ via symlinks..."
+(
+ cd "${OUT_DIR}/lib" || exit 0
+ # Pass A: foo.so → foo.so.X[.Y[.Z]]
+ for f in *.so; do
+ [ -L "${f}" ] && continue
+ [ -f "${f}" ] || continue
+ target=""
+ for v in $(ls -1 "${f}".* 2>/dev/null | sort -r); do
+ [ -L "${v}" ] && continue
+ [ -f "${v}" ] || continue
+ if cmp -s "${f}" "${v}"; then
+ target="${v}"
+ break
+ fi
+ done
+ if [ -n "${target}" ]; then
+ rm -f "${f}"
+ ln -s "${target}" "${f}"
+ echo " link ${f} -> ${target}"
+ fi
+ done
+ # Pass B: foo.so.N → foo.so.N.M[.P] (handles e.g. libxcb-cursor.so.0 → .so.0.0.0)
+ for f in *.so.[0-9]*; do
+ [ -L "${f}" ] && continue
+ [ -f "${f}" ] || continue
+ target=""
+ for v in $(ls -1 "${f}".* 2>/dev/null | sort -r); do
+ [ -L "${v}" ] && continue
+ [ -f "${v}" ] || continue
+ if cmp -s "${f}" "${v}"; then
+ target="${v}"
+ break
+ fi
+ done
+ if [ -n "${target}" ]; then
+ rm -f "${f}"
+ ln -s "${target}" "${f}"
+ echo " link ${f} -> ${target}"
+ fi
+ done
+)
+
# ── Strip all MO2 binaries ──
echo "Stripping MO2 binaries..."
strip --strip-unneeded "${OUT_DIR}/ModOrganizer-core" 2>/dev/null || true