#!/bin/bash # This is multilib-lite build script of xzm module for Porteus # Version 2026-08-16 # Copyright 2023-2030, Blaze, Dankov, Russia # All rights reserved. # # Redistribution and use of this script, with or without modification, is # permitted provided that the following conditions are met: # # 1. Redistributions of this script must retain the above copyright # notice, this list of conditions and the following disclaimer. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Root check if [ "$(whoami)" != "root" ]; then echo -e "\nOnly root can run this.\n" exit 1 fi BOLD=${BOLD:-"\e[1m"} CYAN=${CYAN:-"\e[96m"} GREEN=${GREEN:-"\e[92m"} RED=${RED:-"\e[31m"} YELLOW=${YELLOW:-"\e[93m"} RESET=${RESET:-"\e[0m"} # Detect Slackware version (stable or current) systemFullVersion=$(cat /etc/slackware-version 2>/dev/null) systemVersion=${systemFullVersion//* } if [[ "$systemFullVersion" == *"current"* ]] || [[ "$systemVersion" == *"+"* ]]; then export SLACKWAREVERSION=current export PORTEUSBUILD=current else export SLACKWAREVERSION=$systemVersion export PORTEUSBUILD=stable fi export SLACKBUILDVERSION=$SLACKWAREVERSION echo -e "\n${BOLD}Slackware base: ${CYAN}slackware-$SLACKWAREVERSION${RESET}" echo -e "${BOLD}Porteus build: ${CYAN}$PORTEUSBUILD${RESET}\n" PRGNAM=${PRGNAM:-0050-multilib-lite} BUILD=${BUILD:-1} TAG=${TAG:-bl} MIRROR=${MIRROR:-"http://mirror.yandex.ru/slackware"} SLACKDIR="$MIRROR/slackware-$SLACKWAREVERSION" BASE="$SLACKDIR/slackware" DATE=$(date +%Y%m%d) echo -e "${BOLD}Mirror: ${CYAN}$MIRROR${RESET}" echo -e "${BOLD}Base: ${CYAN}$BASE${RESET}\n" CWD=$(pwd) TMP=${TMP:-/tmp/SBo} PKG=$TMP/package-$PRGNAM OUTPUT=${OUTPUT:-/tmp} rm -rf $PKG mkdir -p $TMP $PKG $OUTPUT cd $TMP rm -rf $PRGNAM # Associative array: series -> package list (sorted alphabetically) declare -A PKG_LISTS=( [a]="aaa_glibc-solibs acl attr bzip2 dbus eudev openssl-solibs util-linux \ xz" [d]="gcc gcc-g++ llvm" [l]="alsa-lib alsa-plugins brotli cairo elfutils expat freetype fribidi \ gdk-pixbuf2 giflib glib2 gmp graphite2 gst-plugins-base gstreamer gtk+2 \ harfbuzz icu4c lame libasyncns libcap libedit libffi libidn2 \ libjpeg-turbo libnsl libogg libpcap libpng libpsl libsndfile libssh2 \ libtasn1 libtheora libtiff libunistring libunwind libusb libvorbis \ libxkbcommon libxml2 libxslt lz4 ncurses openal-soft opus orc pango pcre2 \ pulseaudio readline sdl SDL2 SDL2_image SDL2_mixer v4l-utils zlib zstd" [n]="curl gnutls libgcrypt libgpg-error libtirpc nettle nghttp2 p11-kit" [ap]="cups flac lm_sensors mpg123" [x]="fontconfig freeglut glu intel-gmmlib intel-media-driver intel-vaapi-driver \ libdisplay-info libdrm libglvnd libICE libpciaccess libSM libva libvdpau \ libX11 libXau libxcb libXcomposite libXcursor libXdamage libXdmcp libXext \ libXfixes libXft libXi libXinerama libXmu libXpm libXrandr libXrender \ libXScrnSaver libxshmfence libXt libXtst libXv libXxf86vm mesa pixman \ vulkan-sdk wayland xcb-util xcb-util-image xcb-util-keysyms \ xcb-util-renderutil xcb-util-wm" [xap]="sane" ) WGET_OPTS="--quiet --progress=bar:force:noscroll --max-redirect=20 --tries=2 --timeout=30" # ============================================================================== # Fetch fresh CHECKSUMS.md5 unconditionally to avoid 404s on updated packages # ============================================================================== CHECKSUMS_FILE="$TMP/CHECKSUMS.md5" echo -e "${CYAN}Fetching fresh CHECKSUMS.md5...${RESET}" rm -f "$CHECKSUMS_FILE" if ! wget $WGET_OPTS "$BASE/CHECKSUMS.md5" -O "$CHECKSUMS_FILE" 2>/tmp/wget.err; then echo -e "${RED}Failed to download CHECKSUMS.md5${RESET}" >&2 [ -s /tmp/wget.err ] && sed 's/^/ /' /tmp/wget.err >&2 exit 1 fi if [ ! -s "$CHECKSUMS_FILE" ]; then echo -e "${RED}CHECKSUMS.md5 is empty${RESET}" >&2 exit 1 fi echo -e "${GREEN}CHECKSUMS.md5 updated: $(du -h "$CHECKSUMS_FILE" | cut -f1)${RESET}\n" # Function to get package path from CHECKSUMS.md5 get_pkg_file() { local series="$1" name="$2" local checksums="$CHECKSUMS_FILE" # Escape regex special characters in package name local escaped_name escaped_name=$(printf '%s' "$name" | sed 's/[][\.*^$()+?{|]/\\&/g') local match match=$(grep -E -i "\./${series}/${escaped_name}-[0-9]" "$checksums" \ | grep -E -- "-(i[56]86|noarch)-" \ | grep '\.txz$' \ | tail -1 \ | awk '{print $2}' \ | sed 's|^\./||; s|.*/||') if [ -z "$match" ]; then local similar similar=$(grep -i "\./${series}/${escaped_name}" "$checksums" | grep '\.txz$' | head -2) if [ -n "$similar" ]; then echo -e "${YELLOW} (similar found in CHECKSUMS: $(echo "$similar" | awk '{print $2}' | sed 's|^\./||; s|.*/||' | tr '\n' ' '))${RESET}" >&2 fi fi echo "$match" } # Download packages cd $PKG downloaded=0 for series in "${!PKG_LISTS[@]}"; do for name in ${PKG_LISTS[$series]}; do file=$(get_pkg_file "$series" "$name") if [ -n "$file" ]; then echo -e "${CYAN}[$series]${RESET} $file" if wget $WGET_OPTS -O "$file" "$BASE/$series/$file" 2>/tmp/wget.err; then downloaded=$((downloaded + 1)) else echo -e "${RED}[WARN]${RESET} download failed: $file" [ -s /tmp/wget.err ] && sed 's/^/ /' /tmp/wget.err rm -f "$file" fi else echo -e "${RED}[WARN]${RESET} not found: $name" fi done done echo -e "\n${BOLD}Downloaded: ${GREEN}$downloaded${RESET} packages\n" if [ "$downloaded" -eq 0 ]; then echo -e "${RED}${BOLD}Error: no packages downloaded.${RESET}\n" exit 1 fi if [ -z "$(ls -A "$PKG"/*.txz 2>/dev/null)" ]; then echo -e "${RED}${BOLD}Error: no .txz files found in $PKG${RESET}\n" exit 1 fi # Determine architecture from downloaded packages ARCH="" for f in "$PKG"/*.txz; do if [ -f "$f" ]; then base=$(basename "$f") if [[ "$base" =~ -([^-]+)-[0-9]+\.txz$ ]]; then ARCH="${BASH_REMATCH[1]}" break fi fi done if [ -z "$ARCH" ]; then ARCH="i586" fi echo -e "${BOLD}Detected architecture: ${CYAN}$ARCH${RESET}\n" # Install all packages into $PKG ROOT="$PKG" installpkg "$PKG"/*.txz # ============================================================ # CLEANUP (lite module) # ============================================================ rm -f *.txz rm -rf $PKG/{KEEP,run,tmp,install} rm -rf $PKG/usr/{doc,man,info,src,libexec} rm -rf $PKG/usr/lib/{.build-id,cmake,pkgconfig,gtk-doc,girepository-1.0} rm -rf $PKG/usr/share/{doc,gtk-doc,help,i18n,locale,pkgconfig,gir-1.0} rm -rf $PKG/usr/share/{applications,icons,themes,pixmaps,wallpapers,sounds} rm -rf $PKG/{etc,run,dev,proc,sys} rm -rf $PKG/var/{log,cache,tmp} # Remove /usr/sbin entirely rm -rf $PKG/usr/sbin # Remove /usr/bin contents except vulkaninfo (needed for diagnostics) if [ -d "$PKG/usr/bin" ]; then find "$PKG/usr/bin" -mindepth 1 -maxdepth 1 ! -name 'vulkaninfo' -exec rm -rf {} + fi # Remove headers and static libs (runtime only) find $PKG -type f \( -name '*.a' -o -name '*.la' -o -name '*.o' -o -name '*.h' -o -name '*.pc' \) -delete find $PKG -type f \( -iname 'README*' -o -iname 'LICENSE*' -o -iname 'COPYING*' \ -o -iname 'ChangeLog*' -o -iname 'AUTHORS' -o -iname 'NEWS' \ -o -iname 'TODO' -o -iname '*.md' \) -delete find $PKG/usr/lib -type d -name 'python*' -prune -exec rm -rf {} + 2>/dev/null find $PKG -type d -name '.build-id' -prune -exec rm -rf {} + 2>/dev/null # ===== CLEANUP: vulkan-sdk ===== # Keep: libvulkan.so.1 (loader), vulkaninfo binary # Remove: validation layers, profiles layer, dev files, extra tools echo -e "${BOLD}Cleaning up vulkan-sdk (keeping only loader + vulkaninfo)...${RESET}" # Remove Vulkan validation and profiling layers (dev-only) find $PKG -type f -iname '*VkLayer*' -delete find $PKG -type f -iname '*VkLayer*.json' -delete # Remove Vulkan SDK documentation and samples rm -rf $PKG/usr/share/vulkan-sdk 2>/dev/null rm -rf $PKG/usr/share/doc/vulkan* 2>/dev/null # Remove extra vulkan-sdk tools (keep only vulkaninfo for diagnostics) for bin in apitrace vkconfig vkcube vkcubepp vkvia vktrace; do find $PKG -type f -name "$bin" -delete done # Remove Vulkan static libs if any find $PKG -type f \( -name 'libvulkan.a' -o -name 'libvulkan*.la' \) -delete # Remove Vulkan headers (already removed globally, but double-check) rm -rf $PKG/usr/include/vulkan 2>/dev/null # Ensure ICD directory from mesa is preserved mkdir -p $PKG/usr/share/vulkan/icd.d mkdir -p $PKG/usr/share/vulkan/explicit_layer.d mkdir -p $PKG/usr/share/vulkan/implicit_layer.d # Remove empty Vulkan dirs (except icd.d which may have mesa files) find $PKG/usr/share/vulkan -mindepth 1 -maxdepth 1 -type d -empty -delete 2>/dev/null # ===== END CLEANUP: vulkan-sdk ===== # Clean var/lib find $PKG/var/lib -mindepth 1 -maxdepth 1 -type d ! -name 'pkgtools' -exec rm -rf {} + 2>/dev/null find $PKG/var/lib/pkgtools -mindepth 1 -maxdepth 1 -type d ! -name 'packages' -exec rm -rf {} + 2>/dev/null # Remove top-level directories that are not lib, usr, var find $PKG -mindepth 1 -maxdepth 1 -type d ! -name 'lib' ! -name 'usr' ! -name 'var' -exec rm -rf {} + 2>/dev/null # Remove empty directories find $PKG -depth -type d -empty -delete # ============================================================ # CREATE SYMBOLIC LINKS (compatibility) # ============================================================ echo -e "${BOLD}Creating symbolic links for compatibility...${RESET}" # libudev.so.0 -> libudev.so.1 (Steam runtime expects old version) # Search in both /lib and /usr/lib, create symlink in same directory as source libudev_found="" libudev_dir="" for dir in "$PKG/lib" "$PKG/usr/lib"; do if [ -e "$dir/libudev.so.1" ] || [ -L "$dir/libudev.so.1" ]; then libudev_found="libudev.so.1" libudev_dir="$dir" break fi done if [ -n "$libudev_found" ] && [ -n "$libudev_dir" ]; then ln -sf "$libudev_found" "$libudev_dir/libudev.so.0" echo -e " ${GREEN}Created:${RESET} ${libudev_dir#$PKG}/libudev.so.0 -> $libudev_found" else echo -e " ${YELLOW}WARN:${RESET} libudev.so.1 not found in package" fi # ============================================================ # CHECKS # ============================================================ echo -e "\n${BOLD}Checking critical 32-bit libraries:${RESET}" check_lib() { local libname="$1" local found=0 for dir in "$PKG/lib" "$PKG/usr/lib"; do if [ -e "$dir/$libname" ] || [ -L "$dir/$libname" ]; then echo -e " ${GREEN}OK:${RESET} $libname (in ${dir#$PKG/})" found=1 break fi done if [ $found -eq 0 ]; then echo -e " ${RED}MISSING:${RESET} $libname" return 1 fi return 0 } missing=0 for lib in ld-linux.so.2 libc.so.6 libstdc++.so.6 libgcc_s.so.1; do check_lib "$lib" || missing=1 done # Check libLLVM (needed for llvmpipe) if ls "$PKG"/usr/lib/libLLVM*.so* >/dev/null 2>&1; then echo -e " ${GREEN}OK:${RESET} libLLVM" else echo -e " ${RED}MISSING:${RESET} libLLVM" missing=1 fi # Check libGL variants if [ -e "$PKG/usr/lib/libGL.so.1" ] || [ -L "$PKG/usr/lib/libGL.so.1" ] || \ [ -e "$PKG/usr/lib/libGLX_mesa.so.0" ] || [ -e "$PKG/usr/lib/libGLX.so.0" ]; then echo -e " ${GREEN}OK:${RESET} libGL variant found" else echo -e " ${RED}MISSING:${RESET} libGL variant" missing=1 fi # Check Vulkan loader echo -e "\n${BOLD}Checking Vulkan support:${RESET}" if [ -e "$PKG/usr/lib/libvulkan.so.1" ] || [ -L "$PKG/usr/lib/libvulkan.so.1" ]; then echo -e " ${GREEN}OK:${RESET} libvulkan.so.1" else echo -e " ${YELLOW}WARN:${RESET} libvulkan.so.1 not found (Vulkan may not work)" fi # Check Wine-specific libraries echo -e "\n${BOLD}Checking Wine-specific libraries:${RESET}" for lib in libxslt.so.1 libpcap.so.1; do check_lib "$lib" || missing=1 done # Check Steam-specific libraries (added packages) echo -e "\n${BOLD}Checking Steam-specific libraries:${RESET}" for lib in libpciaccess.so.0 libdisplay-info.so.4 libedit.so.0 libsensors.so.5; do check_lib "$lib" || missing=1 done # Check symbolic links echo -e "\n${BOLD}Checking symbolic links:${RESET}" libudev_link_found=0 for dir in "$PKG/lib" "$PKG/usr/lib"; do if [ -L "$dir/libudev.so.0" ]; then target=$(readlink -f "$dir/libudev.so.0" 2>/dev/null || readlink "$dir/libudev.so.0") echo -e " ${GREEN}OK:${RESET} ${dir#$PKG}/libudev.so.0 -> $(basename "$target")" libudev_link_found=1 break fi done if [ $libudev_link_found -eq 0 ]; then echo -e " ${YELLOW}WARN:${RESET} libudev.so.0 not found (Steam runtime compatibility)" fi if [ $missing -ne 0 ]; then echo -e "${RED}${BOLD}Error: critical libraries missing. Module not built.${RESET}\n" exit 1 fi echo -e "${GREEN}All critical libraries are present.${RESET}\n" # ============================================================ # BUILD MODULE # ============================================================ XZM_NAME="$PRGNAM-$PORTEUSBUILD-$DATE.xzm" XZM_FILE="$OUTPUT/$XZM_NAME" if ! dir2xzm "$PKG" "$XZM_FILE"; then echo -e "${RED}${BOLD}Error: dir2xzm failed${RESET}\n" exit 1 fi # Check result if [ -f "$XZM_FILE" ]; then SIZE=$(du -h "$XZM_FILE" | cut -f1) echo -e "\n${BOLD}Module $PRGNAM created: ${GREEN}${BOLD}$XZM_FILE${RESET}" echo -e "${BOLD}Size: ${CYAN}$SIZE${RESET}" echo -e "${BOLD}Move it to your modules folder to survive a reboot.${RESET}\n" else echo -e "\n${RED}${BOLD}Error. Module not built.${RESET}\n" fi