#!/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 declare -A PKG_LISTS=( [a]="aaa_glibc-solibs bzip2 xz util-linux attr acl eudev dbus openssl-solibs" [d]="gcc gcc-g++ llvm" [l]="zlib lz4 zstd libffi expat glib2 pcre2 ncurses readline \ libcap icu4c libxml2 libxslt libpng libjpeg-turbo freetype \ cairo harfbuzz graphite2 pango fribidi gdk-pixbuf2 gtk+2 libtiff \ alsa-lib alsa-plugins openal-soft libsndfile libvorbis \ libogg libasyncns \ gstreamer gst-plugins-base orc opus libtheora \ SDL2 SDL2_image SDL2_mixer sdl \ libidn2 libunistring libtasn1 \ libunwind giflib \ libpsl libssh2 brotli libusb elfutils pulseaudio \ libxkbcommon v4l-utils libpcap gmp" [n]="libtirpc gnutls curl nghttp2 libgcrypt libgpg-error nettle p11-kit" [ap]="cups flac mpg123" [x]="libX11 libXext libXrender libXfixes libXcursor libXi libXrandr \ libXxf86vm libXinerama libXcomposite libXdamage libXScrnSaver \ libXtst libXft libXpm libXv libXmu libXt libxshmfence \ libxcb xcb-util xcb-util-image xcb-util-keysyms xcb-util-renderutil \ xcb-util-wm libXau libXdmcp wayland libSM libICE \ mesa pixman libdrm libva libvdpau libglvnd glu freeglut \ intel-vaapi-driver intel-media-driver \ vulkan-sdk \ fontconfig" [xap]="sane" ) WGET_OPTS="--quiet --progress=bar:force:noscroll --max-redirect=20 --tries=2 --timeout=30" # Function to get package path from CHECKSUMS.md5 get_pkg_file() { local series="$1" name="$2" local checksums="$TMP/CHECKSUMS.md5" if [ ! -f "$checksums" ] || [ ! -s "$checksums" ]; then echo -e "${CYAN}Downloading CHECKSUMS.md5...${RESET}" >&2 rm -f "$checksums" if ! wget $WGET_OPTS "$BASE/CHECKSUMS.md5" -O "$checksums" 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 rm -f "$checksums" return 1 fi if [ ! -s "$checksums" ]; then echo -e "${RED}CHECKSUMS.md5 is empty${RESET}" >&2 rm -f "$checksums" return 1 fi echo -e "${GREEN}CHECKSUMS.md5 downloaded: $(du -h "$checksums" | cut -f1)${RESET}" >&2 fi # 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: $(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 # ============================================================ # CHECKS # ============================================================ echo -e "${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 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