js8call/.github/workflows/scripts/JS8Call-Build-AppImage.sh
Chris-AC9KH 55ebf75966 Update to Qt 6.12.0-beta2
JS8Call-Build-AppImage.sh: Update Linux workflow script to include
                           libopengl-dev
tools/Linux-User-Build-JS8Call.sh: Update user build script to include
                                   libopengl-development package
2026-07-31 13:07:26 -05:00

278 lines
10 KiB
Bash
Executable file

# --- Build an AppImage for JS8Call ---
# --- Run on Ubuntu 24 Server ---
# --- Target: any Linux system with glibc 2.39 or later ---
# --- Must not run as root ---
if [ "$(id -u)" -eq 0 ]; then
echo "This script must NOT be run as root. Exiting."
exit 1
fi
set -euo pipefail
# --- Variables ---
JS8_ARCH=$(uname -m)
JS8_SOURCE="https://github.com/JS8Call-improved/JS8Call-improved.git"
JS8_BUILD_BRANCH="master"
# AppImage uses x86_64 and aarch64 — not Debian's amd64/arm64
if [ "$JS8_ARCH" = "x86_64" ]; then
APPIMAGE_ARCH="x86_64"
elif [ "$JS8_ARCH" = "aarch64" ]; then
APPIMAGE_ARCH="aarch64"
else
echo "Unsupported architecture: $JS8_ARCH"
exit 1
fi
# --- Tarball URLs ---
JS8LIB_URL="https://github.com/JS8Call-improved/js8lib/releases/download/lib%2F4.0/js8lib4.0-Linux_${JS8_ARCH}_pkg.tar.gz"
QT_URL="https://github.com/JS8Call-improved/js8lib/releases/download/lib%2F4.0/Qt6.12.0_Linux_${JS8_ARCH}_pkg.tar.gz"
# --- Directories ---
BUILD_DIR="$HOME/js8call-appimage-build"
STAGING_DIR="$BUILD_DIR/staging"
APPDIR="$STAGING_DIR/JS8Call.AppDir"
INSTALL_PREFIX="/usr/lib/js8call"
echo "######################################################################"
echo " Building JS8Call AppImage"
echo " Architecture: $JS8_ARCH"
echo "######################################################################"
sleep 2
# --- Install build dependencies ---
# These are build-time only and will NOT be bundled in the AppImage
echo "Installing build dependencies..."
sudo apt-get update
sudo apt-get install -y \
build-essential cmake ninja-build perl python3 git wget \
libssl-dev libfontconfig1-dev libfreetype-dev \
libharfbuzz-dev libjpeg-dev libpng-dev \
zlib1g-dev libbrotli-dev libdbus-1-dev libglib2.0-dev \
libatspi2.0-dev libgl-dev libegl-dev libgbm-dev \
libdrm-dev libinput-dev libvulkan-dev \
libopengl-dev \
libxkbcommon-dev libxkbcommon-x11-dev \
libxcb-util-dev libxcb-image0-dev libxcb-keysyms1-dev \
libxcb-render-util0-dev libxcb-icccm4-dev libxcb-cursor-dev \
libxrender-dev libxi-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
libwayland-dev wayland-protocols \
libpulse-dev \
libxcb-shape0-dev libxcb-randr0-dev libxcb-sync-dev \
libxcb-xfixes0-dev libxcb-xkb-dev libxcb-xinput-dev \
libasound2-dev || sudo apt-get install -y libasound2t64 \
clang-18 lld-18
# libjpeg-turbo ships as .so.62 on Debian but linuxdeploy-plugin-qt
# expects .so.8 — create a symlink if needed
if [ ! -f /usr/lib/${JS8_ARCH}-linux-gnu/libjpeg.so.8 ]; then
sudo ln -s /usr/lib/${JS8_ARCH}-linux-gnu/libjpeg.so.62 \
/usr/lib/${JS8_ARCH}-linux-gnu/libjpeg.so.8
fi
# --- Create build directory structure ---
echo "Creating build directory structure..."
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
mkdir -p "$STAGING_DIR"
mkdir -p "$APPDIR"
# --- Download linuxdeploy and its Qt plugin ---
# linuxdeploy handles all dependency walking and Qt plugin discovery,
# and builds the final AppImage — no separate appimagetool needed
echo "Downloading linuxdeploy..."
cd "$BUILD_DIR"
wget -q --show-progress \
"https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${APPIMAGE_ARCH}.AppImage" \
-O linuxdeploy.AppImage
wget -q --show-progress \
"https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${APPIMAGE_ARCH}.AppImage" \
-O linuxdeploy-plugin-qt.AppImage
chmod +x linuxdeploy.AppImage linuxdeploy-plugin-qt.AppImage
# --- Download and extract library tarballs ---
echo "Downloading js8lib and Qt..."
wget -q --show-progress "$JS8LIB_URL" -O js8lib.tar.gz
wget -q --show-progress "$QT_URL" -O Qt.tar.gz
# Extract to /usr/lib/js8call so cmake can find them at build time.
# This also serves as the source for bundling into the AppImage.
echo "Extracting libraries to $INSTALL_PREFIX..."
sudo mkdir -p "$INSTALL_PREFIX"
sudo tar -xzf js8lib.tar.gz -C /usr/lib/js8call --strip-components=1
sudo tar -xzf Qt.tar.gz -C /usr/lib/js8call
# Fix pkgconfig prefix paths to match this machine
for pc in /usr/lib/js8call/lib/pkgconfig/*.pc; do
sudo sed -i "s|prefix=.*js8call|prefix=/usr/lib/js8call|g" "$pc"
done
for pc in /usr/lib/js8call/Qt/lib/pkgconfig/*.pc; do
sudo sed -i "s|prefix=.*js8call/Qt|prefix=/usr/lib/js8call/Qt|g" "$pc"
done
# --- Fetch JS8Call source ---
echo "Fetching JS8Call source..."
cd "$BUILD_DIR"
# Check if running inside GitHub Actions
if [ -n "${GITHUB_WORKSPACE:-}" ]; then
echo "CI Environment Detected. Tracking local GitHub Workspace context..."
# Clone locally from the runner's pre-checked-out workspace.
git clone "$GITHUB_WORKSPACE" JS8Call-improved
cd JS8Call-improved
ACTIVE_BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-unknown}}"
echo "Active CI Target Branch: $ACTIVE_BRANCH"
else
# Local Fallback Setup
JS8_BRANCH="$JS8_BUILD_BRANCH"
echo "Local Environment Detected. Cloning remote fallback branch: $JS8_BRANCH"
git clone "$JS8_SOURCE" JS8Call-improved
cd JS8Call-improved
git checkout "$JS8_BRANCH"
ACTIVE_BRANCH="$JS8_BRANCH"
echo "Active Local Target Branch: $ACTIVE_BRANCH"
fi
# --- Determine version string ---
cd "$BUILD_DIR/JS8Call-improved"
# 1. Parse the exact VERSION declaration directly from CMakeLists.txt
CMAKE_VERSION=$(grep -oP 'VERSION\s+\K\d+\.\d+\.\d+' CMakeLists.txt | head -1 || echo "0.0.0")
# 2. Check if it's a dev build or an official release
if [ "$CMAKE_VERSION" = "0.0.0" ]; then
# Dev build: Fall back to the short git commit hash
export VERSION=$(git rev-parse --short HEAD)
else
# Release build: Prepend 'v' to the real version number from CMakeLists.txt
export VERSION="v$CMAKE_VERSION"
fi
# 3. Synchronize banner tracking variable
JS8_VERSION="$VERSION"
echo "AppImage Target Export Version: $JS8_VERSION"
# --- Build JS8Call ---
echo "Build version: $JS8_VERSION"
echo "######################################################################"
echo " Building JS8Call $JS8_VERSION AppImage on ($ACTIVE_BRANCH)"
echo " Architecture: $JS8_ARCH"
echo "######################################################################"
mkdir build && cd build
cmake \
-DCMAKE_C_COMPILER=clang-18 \
-DCMAKE_CXX_COMPILER=clang++-18 \
-DCMAKE_PREFIX_PATH="/usr/lib/js8call;/usr/lib/js8call/Qt" \
-DHAMLIB_ROOT="/usr/lib/js8call" \
-DCMAKE_BUILD_TYPE=Release \
..
cmake --build . --parallel $(nproc)
echo "######################################################################"
echo " JS8Call build successful"
echo "######################################################################"
sleep 2
# --- Populate the AppDir ---
# An AppDir mirrors the target filesystem layout:
#
# JS8Call.AppDir/
# AppRun ← entry point script (required, linuxdeploy writes this)
# js8call.desktop ← desktop entry (required, must be at root)
# js8call.svg ← icon (required, name must match Icon= in .desktop)
# usr/
# bin/JS8Call ← the compiled binary
# lib/ ← bundled .so files (populated by linuxdeploy)
echo "Populating AppDir..."
# Binary
mkdir -p "$APPDIR/usr/bin"
cp JS8Call "$APPDIR/usr/bin/"
# Desktop entry — must live at the ROOT of AppDir
cat > "$APPDIR/js8call.desktop" << EOF
[Desktop Entry]
Type=Application
Name=JS8Call
Exec=JS8Call
Icon=js8call
Terminal=false
Categories=HamRadio;Network;
EOF
# Icon — must be at the root of AppDir and match Icon= above
cp "$BUILD_DIR/JS8Call-improved/artwork/icon_128.svg" \
"$APPDIR/js8call.svg"
# libxcb-cursor is loaded by Qt's xcb platform plugin via dlopen at
# runtime — linuxdeploy won't find it by walking ldd output alone.
# Pre-seed it into the AppDir so linuxdeploy can walk its deps too.
mkdir -p "$APPDIR/usr/lib"
cp -L /usr/lib/${JS8_ARCH}-linux-gnu/libxcb-cursor.so.0 \
"$APPDIR/usr/lib/" 2>/dev/null || true
# AppStream metainfo — for software center integration
mkdir -p "$APPDIR/usr/share/metainfo"
cp "$BUILD_DIR/JS8Call-improved/.github/workflows/misc/JS8Call.appdata.xml" \
"$APPDIR/usr/share/metainfo/io.github.JS8Call_improved.JS8Call.appdata.xml"
# --- Run linuxdeploy ---
# linuxdeploy does the heavy lifting:
# - walks ldd dependencies of the binary and copies matching .so files
# - the Qt plugin (--plugin qt) uses qmake to find our private Qt
# installation and copies Qt libs, platform plugins, imageformats,
# iconengines etc. — everything Qt loads via dlopen at runtime
# - --output appimage calls appimagetool internally to produce the
# final compressed self-executing .AppImage file
#
# QMAKE points it at our private Qt 6.12.0, not the system Qt,
# so it bundles the right version
cd "$BUILD_DIR"
export EXTRA_PLATFORM_PLUGINS="libqwayland.so;libqoffscreen.so;libqeglfs.so;libqlinuxfb.so;libqminimalegl.so;libqminimal.so"
export EXTRA_QT_MODULES="waylandcompositor"
export QMAKE=/usr/lib/js8call/Qt/bin/qmake
./linuxdeploy.AppImage \
--appdir "$APPDIR" \
--executable "$APPDIR/usr/bin/JS8Call" \
--desktop-file "$APPDIR/js8call.desktop" \
--icon-file "$APPDIR/js8call.svg" \
--plugin qt \
--output appimage
# --- Strip debug symbols ---
# linuxdeploy copies libs with debug info intact — strip them down.
# 2>/dev/null suppresses "file format not recognized" on non-ELF files.
find "$APPDIR" -type f \( -name "*.so*" -o -name "JS8Call" \) \
-exec strip --strip-unneeded {} \; 2>/dev/null || true
# linuxdeploy names the output based on the .desktop file Name= field
# and the ARCH env var. Rename to our preferred convention.
APPIMAGE_FILE=$(ls "$BUILD_DIR"/JS8Call-*.AppImage 2>/dev/null | head -1)
if [ -z "$APPIMAGE_FILE" ]; then
echo "ERROR: No AppImage was produced by linuxdeploy." >&2
exit 1
fi
mv "$APPIMAGE_FILE" "$HOME/JS8Call-${JS8_VERSION}-${APPIMAGE_ARCH}.AppImage"
echo "######################################################################"
echo " DONE!"
echo " AppImage: $HOME/JS8Call-${JS8_VERSION}-${APPIMAGE_ARCH}.AppImage"
echo ""
echo " To run on any Linux system:"
echo " chmod +x JS8Call-${JS8_VERSION}-${APPIMAGE_ARCH}.AppImage"
echo " ./JS8Call-${JS8_VERSION}-${APPIMAGE_ARCH}.AppImage"
echo ""
echo " No installation required. The file is self-contained."
echo "######################################################################"