#!/usr/bin/env bash # Despite the script name, this doesn't necessarily create an "image"; # it sets up a compatibility script wrapper for the binary and arranges some resources. set -e export ROOT=$(echo $ROOT | grep . || pwd) export NCORES=$(nproc --all) CONFIG=Release SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)" POLICY_FILE="${REPO_ROOT}/scripts/appimage_lib_policy.sh" CHECK_SCRIPT="${REPO_ROOT}/scripts/check_appimage_libs.sh" if [ ! -f "${POLICY_FILE}" ]; then echo "Error: missing AppImage helper ${POLICY_FILE}" exit 1 fi # shellcheck source=/dev/null source "${POLICY_FILE}" while getopts ":ihR:" opt; do case ${opt} in i ) export BUILD_IMAGE="1" ;; h ) echo "Usage: ./build_linux_image.sh [-i][-R config]" echo " -i: Generate Appimage (optional)" echo " -R: Specify from which config to obtain the binary: Release, RelWithDebInfo, or Debug" exit 1 ;; R ) CONFIG=$OPTARG ;; esac done copy_directory_if_present() { local src_dir="$1" local dst_dir="$2" if [ -n "$src_dir" ] && [ -d "$src_dir" ]; then mkdir -p "$dst_dir" cp -a "$src_dir"/. "$dst_dir"/ fi } find_pkg_config_dir() { local variable="$1" shift local module for module in "$@"; do if pkg-config --exists "$module" 2>/dev/null; then pkg-config --variable="$variable" "$module" 2>/dev/null || true return 0 fi done return 1 } find_first_existing_file() { local path for path in "$@"; do if [ -f "$path" ]; then printf '%s\n' "$path" return 0 fi done return 1 } copy_shared_object_to_dir() { local src="$1" local dst_dir="$2" local src_real dst_name soname src_real="$(readlink -f "$src")" dst_name="$(basename "$src_real")" mkdir -p "$dst_dir" cp -fL "$src_real" "$dst_dir/$dst_name" if [ -L "$src" ]; then ln -snf "$dst_name" "$dst_dir/$(basename "$src")" fi soname="$(objdump -p "$src_real" 2>/dev/null | awk '$1 == "SONAME" { print $2; exit }')" if [ -n "$soname" ] && [ "$soname" != "$dst_name" ]; then ln -snf "$dst_name" "$dst_dir/$soname" fi } bundle_dependency_closure() { local dst_dir="$1" shift local -a queue=("$@") local target dep dep_real copied_path declare -A seen=() while [ ${#queue[@]} -gt 0 ]; do target="${queue[0]}" queue=("${queue[@]:1}") if [ ! -e "$target" ] || ! appimage_is_elf_file "$target"; then continue fi while IFS= read -r dep; do if [[ "$dep" == MISSING:* ]]; then echo "Error: missing runtime dependency ${dep#MISSING:} while bundling $target" exit 1 fi dep_real="$(readlink -f "$dep" 2>/dev/null || printf '%s' "$dep")" if appimage_is_host_library "$dep_real"; then continue fi if [ -n "${seen[$dep_real]}" ]; then continue fi seen[$dep_real]=1 copy_shared_object_to_dir "$dep" "$dst_dir" copied_path="$dst_dir/$(basename "$dep_real")" if [ -e "$copied_path" ]; then queue+=("$copied_path") fi done < <(appimage_list_direct_dependencies "$target") done } echo -n "[9/9] Generating Linux app..." # { if [ -d "package" ]; then rm -rf package fi APPDIR="package" BIN_DIR="$APPDIR/bin" LIB_DIR="$APPDIR/lib" PRIVATE_LIB_DIR="$LIB_DIR/orca-runtime" SHARE_DIR="$APPDIR/share" LIBEXEC_DIR="$APPDIR/libexec" BUNDLE_DESKTOP_STACK="${ORCA_BUNDLE_DESKTOP_STACK:-0}" GST_PLUGIN_DIR="$LIB_DIR/gstreamer-1.0" GIO_MODULE_DIR="$LIB_DIR/gio/modules" GDK_PIXBUF_DIR="$LIB_DIR/gdk-pixbuf-2.0/2.10.0/loaders" mkdir -p "$BIN_DIR" "$LIB_DIR" "$PRIVATE_LIB_DIR" "$SHARE_DIR" "$LIBEXEC_DIR" if [ "$BUNDLE_DESKTOP_STACK" = "1" ]; then mkdir -p "$GST_PLUGIN_DIR" "$GIO_MODULE_DIR" "$GDK_PIXBUF_DIR" fi cp -Rf ../resources "$APPDIR/resources" ORIGINAL_BINARY_LOCATION="" if [ -f "src/${CONFIG}/@SLIC3R_APP_CMD@" ]; then ORIGINAL_BINARY_LOCATION="$(realpath "src/${CONFIG}/@SLIC3R_APP_CMD@")" elif [ -f "src/@SLIC3R_APP_CMD@" ]; then ORIGINAL_BINARY_LOCATION="$(realpath "src/@SLIC3R_APP_CMD@")" else echo "Error: @SLIC3R_APP_CMD@ binary not found in any configuration directory" exit 1 fi cp -fl "${ORIGINAL_BINARY_LOCATION}" "$BIN_DIR/@SLIC3R_APP_CMD@" if [ "$BUNDLE_DESKTOP_STACK" = "1" ]; then GSTREAMER_PLUGIN_SOURCE_DIR="$(find_pkg_config_dir pluginsdir gstreamer-1.0 || true)" if [ -z "$GSTREAMER_PLUGIN_SOURCE_DIR" ]; then GSTREAMER_PLUGIN_SOURCE_DIR="$(find /usr/lib /usr/lib64 -maxdepth 4 -type d -name gstreamer-1.0 2>/dev/null | head -n1)" fi copy_directory_if_present "$GSTREAMER_PLUGIN_SOURCE_DIR" "$GST_PLUGIN_DIR" GIO_MODULE_SOURCE_DIR="$(find_pkg_config_dir giomoduledir gio-2.0 || true)" if [ -z "$GIO_MODULE_SOURCE_DIR" ]; then GIO_MODULE_SOURCE_DIR="$(find /usr/lib /usr/lib64 -maxdepth 5 -type d -path '*/gio/modules' 2>/dev/null | head -n1)" fi copy_directory_if_present "$GIO_MODULE_SOURCE_DIR" "$GIO_MODULE_DIR" GDK_PIXBUF_SOURCE_DIR="$(find_pkg_config_dir gdk_pixbuf_moduledir gdk-pixbuf-2.0 || true)" if [ -z "$GDK_PIXBUF_SOURCE_DIR" ]; then GDK_PIXBUF_SOURCE_DIR="$(find /usr/lib /usr/lib64 -maxdepth 6 -type d -path '*/gdk-pixbuf-2.0/*/loaders' 2>/dev/null | head -n1)" fi copy_directory_if_present "$GDK_PIXBUF_SOURCE_DIR" "$GDK_PIXBUF_DIR" GLIB_SCHEMAS_SOURCE_DIR="$(find_pkg_config_dir schemasdir gio-2.0 || true)" if [ -z "$GLIB_SCHEMAS_SOURCE_DIR" ]; then GLIB_SCHEMAS_SOURCE_DIR="/usr/share/glib-2.0/schemas" fi copy_directory_if_present "$GLIB_SCHEMAS_SOURCE_DIR" "$SHARE_DIR/glib-2.0/schemas" if [ -d "$SHARE_DIR/glib-2.0/schemas" ] && command -v glib-compile-schemas >/dev/null 2>&1; then glib-compile-schemas "$SHARE_DIR/glib-2.0/schemas" fi # Distros disagree on where helper executables live: pkg-config may point to libexec, # Debian/Ubuntu often ship the scanner under a multiarch libdir, and RPM distros may use lib64. GST_PLUGIN_SCANNER_SOURCE="$(find_first_existing_file \ "$(find_pkg_config_dir pluginscannerdir gstreamer-1.0 || true)/gst-plugin-scanner" \ /usr/libexec/gstreamer-1.0/gst-plugin-scanner \ /usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner \ /usr/lib/*/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner \ /usr/lib64/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner)" || true if [ -n "$GST_PLUGIN_SCANNER_SOURCE" ]; then mkdir -p "$LIBEXEC_DIR/gstreamer-1.0" cp -fL "$GST_PLUGIN_SCANNER_SOURCE" "$LIBEXEC_DIR/gstreamer-1.0/gst-plugin-scanner" fi # Prefer the canonical pkg-config-reported helper path, but keep PATH-based fallbacks # for distros exposing gdk-pixbuf-query-loaders or gdk-pixbuf-query-loaders-64 directly. GDK_PIXBUF_QUERY_LOADERS_SOURCE="$(find_first_existing_file \ "$(pkg-config --variable=gdk_pixbuf_query_loaders gdk-pixbuf-2.0 2>/dev/null || true)" \ "$(command -v gdk-pixbuf-query-loaders 2>/dev/null || true)" \ "$(command -v gdk-pixbuf-query-loaders-64 2>/dev/null || true)")" || true if [ -n "$GDK_PIXBUF_QUERY_LOADERS_SOURCE" ]; then cp -fL "$GDK_PIXBUF_QUERY_LOADERS_SOURCE" "$LIBEXEC_DIR/gdk-pixbuf-query-loaders" fi fi # WebKitGTK helper binaries are resolved from distro-specific absolute paths # baked into libwebkit2gtk. Bundling Ubuntu's WebKitGTK inside an AppImage # built with -g makes those helper paths unusable on Fedora and vice versa, # so rely on the host WebKitGTK runtime instead of copying the helper stack. mapfile -d '' BUNDLE_TARGETS < <(find "$BIN_DIR" "$LIB_DIR" "$LIBEXEC_DIR" -type f -print0 2>/dev/null) bundle_dependency_closure "$PRIVATE_LIB_DIR" "${BUNDLE_TARGETS[@]}" cat << EOF >"$LIBEXEC_DIR/@SLIC3R_APP_CMD@-env" #!/bin/sh set -e SELF_DIR=\$(CDPATH= cd -- "\$(dirname -- "\$0")" && pwd) APPDIR="\${APPDIR:-\$(CDPATH= cd -- "\$SELF_DIR/.." && pwd)}" USE_BUNDLED_WEBKITGTK_STACK=0 if [ -e "\$APPDIR/lib/libwebkit2gtk-4.1.so.0" ] || [ -e "\$APPDIR/lib/libwebkit2gtk-4.0.so.0" ]; then USE_BUNDLED_WEBKITGTK_STACK=1 fi export APPDIR PRIVATE_LIB_DIR="\$APPDIR/lib/orca-runtime" if [ -d "\$PRIVATE_LIB_DIR" ]; then export LD_LIBRARY_PATH="\$PRIVATE_LIB_DIR:\$APPDIR/bin\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}" else export LD_LIBRARY_PATH="\$APPDIR/bin\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}" fi has_host_runtime_library() { local lib_name path if command -v ldconfig >/dev/null 2>&1; then if ldconfig -p 2>/dev/null | grep -Fq " \$lib_name"; then return 0 fi fi for path in \ /lib /lib64 /usr/lib /usr/lib64 \ /usr/lib/* /usr/lib64/* /lib/* /lib64/*; do if [ -e "\$path/\$lib_name" ]; then return 0 fi done return 1 } target_missing_runtime_library() { local target_bin="\$1" local lib_name="\$2" if [ -z "\$target_bin" ] || [ ! -e "\$target_bin" ]; then return 1 fi if command -v ldd >/dev/null 2>&1; then if ldd "\$target_bin" 2>/dev/null | grep -Fq "\$lib_name => not found"; then return 0 fi fi return 1 } TARGET_BIN="\$1" if target_missing_runtime_library "\$TARGET_BIN" "libOpenGL.so.0" || ! has_host_runtime_library "libOpenGL.so.0"; then echo "Error: missing host OpenGL runtime library libOpenGL.so.0." >&2 echo "On Ubuntu/Pop!_OS/Debian, install: libopengl0 and libglu1-mesa" >&2 echo "On Arch/CachyOS, install: libglvnd" >&2 exit 1 fi if [ "\$USE_BUNDLED_WEBKITGTK_STACK" = "1" ]; then export LD_LIBRARY_PATH="\$APPDIR/lib\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}" export GST_PLUGIN_SYSTEM_PATH="" export GST_PLUGIN_PATH="\$APPDIR/lib/gstreamer-1.0\${GST_PLUGIN_PATH:+:\$GST_PLUGIN_PATH}" export GIO_EXTRA_MODULES="\$APPDIR/lib/gio/modules\${GIO_EXTRA_MODULES:+:\$GIO_EXTRA_MODULES}" export XDG_DATA_DIRS="\$APPDIR/share\${XDG_DATA_DIRS:+:\$XDG_DATA_DIRS}" if [ -d "\$APPDIR/share/glib-2.0/schemas" ]; then export GSETTINGS_SCHEMA_DIR="\$APPDIR/share/glib-2.0/schemas" fi if [ -x "\$APPDIR/libexec/gstreamer-1.0/gst-plugin-scanner" ]; then export GST_PLUGIN_SCANNER="\$APPDIR/libexec/gstreamer-1.0/gst-plugin-scanner" fi if [ -d "\$APPDIR/lib/gdk-pixbuf-2.0/2.10.0/loaders" ]; then export GDK_PIXBUF_MODULEDIR="\$APPDIR/lib/gdk-pixbuf-2.0/2.10.0/loaders" if [ -x "\$APPDIR/libexec/gdk-pixbuf-query-loaders" ]; then PIXBUF_CACHE_DIR="\${XDG_CACHE_HOME:-\$HOME/.cache}/@SLIC3R_APP_KEY@" PIXBUF_CACHE_FILE="\$PIXBUF_CACHE_DIR/gdk-pixbuf-loaders.cache" mkdir -p "\$PIXBUF_CACHE_DIR" if [ ! -s "\$PIXBUF_CACHE_FILE" ] || find "\$APPDIR/lib/gdk-pixbuf-2.0/2.10.0/loaders" -type f -newer "\$PIXBUF_CACHE_FILE" | grep -q .; then "\$APPDIR/libexec/gdk-pixbuf-query-loaders" "\$APPDIR/lib/gdk-pixbuf-2.0/2.10.0/loaders"/*.so > "\$PIXBUF_CACHE_FILE" 2>/dev/null || true fi if [ -s "\$PIXBUF_CACHE_FILE" ]; then export GDK_PIXBUF_MODULE_FILE="\$PIXBUF_CACHE_FILE" fi fi fi if [ -d "\$APPDIR/lib/webkit2gtk-4.1" ]; then export WEBKIT_EXEC_PATH="\$APPDIR/lib/webkit2gtk-4.1" elif [ -d "\$APPDIR/lib/webkit2gtk-4.0" ]; then export WEBKIT_EXEC_PATH="\$APPDIR/lib/webkit2gtk-4.0" fi if [ -d "\$APPDIR/lib/webkit2gtk-4.1/injected-bundle" ]; then export WEBKIT_INJECTED_BUNDLE_PATH="\$APPDIR/lib/webkit2gtk-4.1/injected-bundle" elif [ -d "\$APPDIR/lib/webkit2gtk-4.0/injected-bundle" ]; then export WEBKIT_INJECTED_BUNDLE_PATH="\$APPDIR/lib/webkit2gtk-4.0/injected-bundle" fi else if target_missing_runtime_library "\$TARGET_BIN" "libwebkit2gtk-4.1.so.0" || \ target_missing_runtime_library "\$TARGET_BIN" "libjavascriptcoregtk-4.1.so.0" || \ ! has_host_runtime_library "libwebkit2gtk-4.1.so.0" || \ ! has_host_runtime_library "libjavascriptcoregtk-4.1.so.0"; then echo "Error: missing host WebKitGTK 4.1 runtime libraries." >&2 echo "Install the distro package providing libwebkit2gtk-4.1.so.0 and libjavascriptcoregtk-4.1.so.0." >&2 echo "On Arch/CachyOS, install: webkit2gtk-4.1" >&2 exit 1 fi fi exec "\$@" EOF chmod ug+x "$LIBEXEC_DIR/@SLIC3R_APP_CMD@-env" cat << EOF >@SLIC3R_APP_CMD@ #!/bin/bash DIR=\$(dirname "\$(readlink -f "\$0")") export APPDIR="\${APPDIR:-\$DIR}" # FIXME: OrcaSlicer segfault workarounds # 1) OrcaSlicer will segfault on systems where locale info is not as expected (i.e. Holo-ISO arch-based distro) export LC_ALL=C if [ "\$XDG_SESSION_TYPE" = "wayland" ] && [ "\$ZINK_DISABLE_OVERRIDE" != "1" ]; then if command -v glxinfo >/dev/null 2>&1; then RENDERER=\$(glxinfo | grep "OpenGL renderer string:" | sed 's/.*: //') if echo "\$RENDERER" | grep -qi "NVIDIA"; then if command -v nvidia-smi >/dev/null 2>&1; then DRIVER_VERSION=\$(nvidia-smi --query-gpu=driver_version --format=csv,noheader | head -n1) DRIVER_MAJOR=\$(echo "\$DRIVER_VERSION" | cut -d. -f1) [ "\$DRIVER_MAJOR" -gt 555 ] && ZINK_FORCE_OVERRIDE=1 fi if [ "\$ZINK_FORCE_OVERRIDE" = "1" ]; then export __GLX_VENDOR_LIBRARY_NAME=mesa export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json export MESA_LOADER_DRIVER_OVERRIDE=zink export GALLIUM_DRIVER=zink export WEBKIT_DISABLE_DMABUF_RENDERER=1 fi fi fi fi exec "\$DIR/libexec/@SLIC3R_APP_CMD@-env" "\$DIR/bin/@SLIC3R_APP_CMD@" "\$@" EOF chmod ug+x @SLIC3R_APP_CMD@ cp -fl @SLIC3R_APP_CMD@ "$APPDIR/@SLIC3R_APP_CMD@" if [ -x "${CHECK_SCRIPT}" ] && [ -z "${ORCA_SKIP_APPIMAGE_AUDIT}" ]; then "${CHECK_SCRIPT}" "$APPDIR" "$BIN_DIR/@SLIC3R_APP_CMD@" fi # } &> $ROOT/Build.log # Capture all command output echo "done" if [[ -n "$BUILD_IMAGE" ]] then echo -n "Creating Appimage for distribution..." # { rm -rf package_appimage cp -Rf package package_appimage pushd package_appimage > /dev/null chmod +x ../build_appimage.sh if ../build_appimage.sh; then popd > /dev/null mv package_appimage/"@SLIC3R_APP_KEY@_Linux_V@SoftFever_VERSION@.AppImage" "@SLIC3R_APP_KEY@_Linux_V@SoftFever_VERSION@.AppImage" rm -fR package_appimage else popd > /dev/null fi # } &> $ROOT/Build.log # Capture all command output echo "done" fi