@@ -41,11 +41,12 @@ jobs:
4141 sudo apt-get update
4242 sudo apt-get install -y --no-install-recommends \
4343 ca-certificates curl gnupg lsb-release \
44- build-essential cmake ninja-build pkg-config patchelf \
44+ build-essential cmake ninja-build pkg-config patchelf icoutils \
4545 libopencv-dev \
4646 libgl1-mesa-dev libglu1-mesa-dev \
47+ libx11-dev libxext-dev \
4748 libxinerama-dev libxcursor-dev libxi-dev libxrandr-dev \
48- libwayland-dev libxkbcommon-dev \
49+ libwayland-dev libxkbcommon-dev wayland-protocols \
4950 libusb-1.0-0-dev libudev-dev \
5051 libgtk-3-dev
5152
@@ -54,10 +55,26 @@ jobs:
5455 curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xFB0B24895113F120&options=mr" \
5556 | sudo gpg --dearmor -o /etc/apt/keyrings/librealsense.gpg
5657 sudo chmod 0644 /etc/apt/keyrings/librealsense.gpg
57- echo "deb [signed-by=/etc/apt/keyrings/librealsense.gpg] https://librealsense.intel.com/Debian/apt-repo $(lsb_release -cs) main" \
58+ # Intel only publishes the apt repo for select Ubuntu LTS codenames
59+ # (bionic/focal/jammy/noble). On a newer/interim release the repo 404s
60+ # and `apt-get update` fails, so fall back to the newest supported LTS
61+ # (its binaries are forward-compatible).
62+ RS_CODENAME="$(lsb_release -cs)"
63+ # Fall back to the newest supported LTS ONLY when the repo definitively
64+ # lacks this codename (Intel returns 403/404 for unsupported releases).
65+ # Retry first so a transient network error doesn't silently retarget the
66+ # distro — a real outage then fails loudly at apt-get update instead.
67+ RS_HTTP="$(curl -sSL -o /dev/null -w '%{http_code}' --retry 3 --retry-connrefused \
68+ "https://librealsense.intel.com/Debian/apt-repo/dists/${RS_CODENAME}/Release" || echo 000)"
69+ if [ "$RS_HTTP" = "403" ] || [ "$RS_HTTP" = "404" ]; then
70+ echo "librealsense apt repo has no '${RS_CODENAME}' (HTTP $RS_HTTP); falling back to noble"
71+ RS_CODENAME=noble
72+ fi
73+ echo "deb [signed-by=/etc/apt/keyrings/librealsense.gpg] https://librealsense.intel.com/Debian/apt-repo ${RS_CODENAME} main" \
5874 | sudo tee /etc/apt/sources.list.d/librealsense.list
5975 sudo apt-get update
60- sudo apt-get install -y --no-install-recommends librealsense2-dev librealsense2-utils
76+ sudo apt-get install -y --no-install-recommends \
77+ librealsense2-dev librealsense2-utils librealsense2-udev-rules
6178
6279 - name : Install macOS dependencies
6380 if : runner.os == 'macOS'
@@ -160,11 +177,53 @@ jobs:
160177
161178 # Binary + bundled libs under /usr/lib/<pkg>/ with $ORIGIN rpath.
162179 cp build/ir-tracking-app "$STAGE/usr/lib/$PKG/"
163- ldd build/ir-tracking-app \
164- | awk '/(librealsense|libopencv|libtbb|libusb-1\.0)/ { print $3 }' \
165- | grep -v '^$' | sort -u \
166- | while read -r lib; do cp -L "$lib" "$STAGE/usr/lib/$PKG/"; done
180+
181+ # Bundle the application's private compute libraries — RealSense,
182+ # OpenCV, and whatever THOSE pull in (TBB, protobuf, image codecs, ...)
183+ # — so the package never relies on the host shipping the exact sonames
184+ # we built against. Those drift across Ubuntu 22.04/24.04/26.04
185+ # (libtbb.so.2 -> .so.12, libprotobuf.so.23 absent on 26.04, etc.).
186+ #
187+ # We compute this set as the *dependency closure of the RealSense +
188+ # OpenCV libraries only*, not of the whole binary. That deliberately
189+ # excludes the GL / X11 / Wayland / GTK / C++-runtime stack (the binary
190+ # links those directly, but RealSense/OpenCV do not depend on them), so
191+ # we can never accidentally bundle e.g. libgtk-3 and break the user's
192+ # desktop integration. Those host libraries come from Depends instead.
193+ # (The previous hand-written allow-list missed libprotobuf.so.23 and
194+ # crashed on any host without it.)
195+ mapfile -t ROOTS < <(ldd build/ir-tracking-app \
196+ | awk '/=> \// && /lib(realsense2|opencv)/ { print $3 }' | sort -u)
197+ declare -A SEEN
198+ queue=("${ROOTS[@]}")
199+ while ((${#queue[@]})); do
200+ cur="${queue[0]}"; queue=("${queue[@]:1}")
201+ if [[ -n "${SEEN[$cur]:-}" ]]; then continue; fi
202+ SEEN["$cur"]=1
203+ while read -r dep; do
204+ if [[ -n "$dep" && -z "${SEEN[$dep]:-}" ]]; then queue+=("$dep"); fi
205+ done < <(ldd "$cur" 2>/dev/null | awk '/=> \//{print $3}')
206+ done
207+ # Copy the closure, except the host C/C++/system runtime (ABI-stable
208+ # and present on every target — bundling an older copy would be wrong).
209+ RUNTIME='^(ld-linux|libc|libm|libdl|libpthread|librt|libresolv|libstdc\+\+|libgcc_s|libatomic|libz|liblzma|libzstd|libbz2|libudev|libusb-1\.0|libcrypt)\.'
210+ for lib in "${!SEEN[@]}"; do
211+ base="$(basename "$lib")"
212+ if printf '%s' "$base" | grep -qE "$RUNTIME"; then continue; fi
213+ cp -L "$lib" "$STAGE/usr/lib/$PKG/"
214+ done
215+
216+ # $ORIGIN rpath on the binary AND every bundled lib. RUNPATH does not
217+ # propagate to a library's *own* dependencies, so without rpath on the
218+ # bundled .so files their private deps (libopencv_core -> libtbb.so.2,
219+ # libopencv_dnn -> libprotobuf.so.23) would be searched only on the
220+ # host and fail wherever those sonames are gone.
167221 patchelf --set-rpath '$ORIGIN' "$STAGE/usr/lib/$PKG/ir-tracking-app"
222+ shopt -s nullglob # don't iterate a literal '*.so*' if the closure were ever empty
223+ for so in "$STAGE/usr/lib/$PKG/"*.so*; do
224+ patchelf --set-rpath '$ORIGIN' "$so"
225+ done
226+ shopt -u nullglob
168227
169228 # Launcher shim on PATH.
170229 cat > "$STAGE/usr/bin/$PKG" <<SH
@@ -185,8 +244,52 @@ jobs:
185244 Terminal=false
186245 EOF
187246
188- # dpkg control metadata. Depends covers the runtime system libs we
189- # rely on (GTK for nfd, OpenGL, libusb for RealSense USB).
247+ # App icon for desktop launchers (the .desktop above references it by
248+ # name). Extract the 256px frame from the multi-resolution .ico.
249+ icotool -x -w 256 -h 256 \
250+ -o "$STAGE/usr/share/icons/hicolor/256x256/apps/$PKG.png" \
251+ resources/app_icon.ico
252+
253+ # RealSense udev rules so a NON-root user can access the camera. Without
254+ # these the USB device nodes are root-only and the app just reports
255+ # "No RealSense devices found" even with a camera attached. Shipped by
256+ # Intel's librealsense2-udev-rules (installed on the runner) and
257+ # reloaded by the postinst below.
258+ RULES_SRC="$(ls /lib/udev/rules.d/60-librealsense2-udev-rules.rules \
259+ /usr/lib/udev/rules.d/60-librealsense2-udev-rules.rules 2>/dev/null | head -1)"
260+ install -Dm644 "$RULES_SRC" \
261+ "$STAGE/lib/udev/rules.d/60-librealsense2-udev-rules.rules"
262+
263+ # postinst/postrm: reload udev so the rules take effect without a reboot.
264+ cat > "$STAGE/DEBIAN/postinst" <<'SH'
265+ #!/bin/sh
266+ set -e
267+ if command -v udevadm >/dev/null 2>&1; then
268+ udevadm control --reload-rules || true
269+ udevadm trigger || true
270+ fi
271+ # Refresh the icon cache / desktop database so the launcher entry and
272+ # icon appear without a re-login.
273+ command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -q -f /usr/share/icons/hicolor 2>/dev/null || true
274+ command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database -q /usr/share/applications 2>/dev/null || true
275+ exit 0
276+ SH
277+ cat > "$STAGE/DEBIAN/postrm" <<'SH'
278+ #!/bin/sh
279+ set -e
280+ if command -v udevadm >/dev/null 2>&1; then
281+ udevadm control --reload-rules || true
282+ fi
283+ exit 0
284+ SH
285+ chmod 0755 "$STAGE/DEBIAN/postinst" "$STAGE/DEBIAN/postrm"
286+
287+ # dpkg control metadata. Depends covers the host-provided runtime libs
288+ # we deliberately do NOT bundle (the GL/GTK/X11/C++-runtime/USB stack).
289+ # Names are the Ubuntu 22.04 ones; on 24.04/26.04 the t64 packages
290+ # (e.g. libgtk-3-0t64) satisfy them via Provides, so one .deb installs
291+ # on all three. libopengl0 provides libOpenGL.so.0, which the binary
292+ # links directly — its omission was the original launch crash.
190293 INSTALLED_KB=$(du -sk "$STAGE" --exclude=DEBIAN | cut -f1)
191294 cat > "$STAGE/DEBIAN/control" <<EOF
192295 Package: $PKG
@@ -196,7 +299,7 @@ jobs:
196299 Architecture: amd64
197300 Maintainer: Tianyu Song <tianyu@medivis.com>
198301 Homepage: https://github.com/stytim/RealSense-ToolTracker
199- Depends: libgtk-3-0, libgl1, libusb-1.0-0
302+ Depends: libc6, libstdc++6, libgcc-s1, libgtk-3-0, libgl1, libopengl0, libx11-6, libusb-1.0-0, libudev1, zlib1g
200303 Installed-Size: $INSTALLED_KB
201304 Description: Intel RealSense IR retro-reflective marker tool tracker
202305 Tracks passive IR sphere markers from an Intel RealSense camera and
0 commit comments