Files
jasp-desktop-bin/build/build-jasp.sh
T
agw 0d4db32d5c
Build JASP binary and publish to AUR / check (push) Successful in 10s
Build JASP binary and publish to AUR / build (push) Failing after 12s
Automated Arch binary builds of JASP for the AUR
Upstream JASP ships no Linux binary, so the existing AUR package builds
everything from source including several hundred R packages. This builds
that once in CI and publishes the result as jasp-desktop-bin.

JASP links against the system R and Qt6 rather than bundling them, so a
rebuild is triggered by an Arch r/qt6-base bump as well as by a new JASP
release, and the generated PKGBUILD pins depends to the versions actually
built against.
2026-09-01 20:30:00 +02:00

186 lines
6.4 KiB
Bash
Executable File

#!/bin/bash
# Build JASP Desktop on Arch Linux and emit a relocatable binary tarball.
#
# Runs as root inside an `archlinux:base-devel` container. The resulting tarball
# is what the AUR `jasp-desktop-bin` package downloads and unpacks, so this
# script also records the exact versions of the Arch libraries JASP ends up
# linked against — the PKGBUILD pins `depends` to them.
#
# JASP_TAG upstream tag to build, e.g. v0.98.1 (required)
# OUT_DIR where the tarball + metadata land (default /work/out)
# JOBS compile parallelism (default nproc)
#
# JASP links against the distro's R and Qt6 rather than bundling them
# (Tools/CMake/R.cmake only fetches R.framework on macOS), so a build is only
# valid for the R minor and Qt6 minor present at build time.
set -euo pipefail
JASP_TAG="${JASP_TAG:?JASP_TAG is required, e.g. v0.98.1}"
OUT_DIR="${OUT_DIR:-/work/out}"
JOBS="${JOBS:-$(nproc)}"
SRC_DIR=/build/jasp-desktop
BUILD_DIR=/build/build
STAGE_DIR=/build/stage
PREFIX=/opt/jasp-desktop
mkdir -p "$OUT_DIR" /build
log() { echo ":: $* "; }
# ---------------------------------------------------------------- system deps
log "installing Arch dependencies"
pacman -Syu --noconfirm --needed \
base-devel git cmake ninja gcc gcc-fortran make pkgconf patchelf \
autoconf automake libtool bison flex gettext blas lapack ccache \
qt6-base qt6-declarative qt6-svg qt6-positioning qt6-webchannel \
qt6-webengine qt6-5compat qt6-shadertools qt6-websockets qt6-tools \
qt6-httpserver vulkan-headers \
boost boost-libs r glpk jsoncpp libarchive openssl curl sqlite zlib \
readstat netcdf mpfr cairo fontconfig harfbuzz fribidi \
libpng libtiff libjpeg-turbo minizip libxkbcommon libxkbcommon-x11 \
wget unzip
# Stock makepkg.conf leaves MAKEFLAGS at -j1, which makes the AUR dependencies
# (JAGS especially) crawl. R's Makeconf picks this up for module builds too.
sed -i "s|^#\?MAKEFLAGS=.*|MAKEFLAGS=\"-j${JOBS}\"|" /etc/makepkg.conf
export MAKEFLAGS="-j${JOBS}"
# makepkg refuses to run as root.
if ! id builder &>/dev/null; then
useradd -m builder
echo "builder ALL=(ALL) NOPASSWD: ALL" >/etc/sudoers.d/builder
fi
# jags and librdata are AUR-only; build them the same way a user would.
build_aur() {
local pkg="$1"
log "building AUR package $pkg"
sudo -u builder bash -euc "
cd /home/builder
rm -rf '$pkg'
git clone --depth 1 'https://aur.archlinux.org/$pkg.git'
cd '$pkg'
makepkg -si --noconfirm --needed
"
}
build_aur jags
build_aur librdata-git
# JASP requires freexl >= 2.0.99; Arch ships 2.0.0, so use upstream's own fork
# exactly as the JASP CI does.
log "building jasp-stats/freexl"
rm -rf /build/freexl
git clone --depth 1 https://github.com/jasp-stats/freexl.git /build/freexl
cd /build/freexl
./configure --prefix=/usr
make -j"$JOBS"
make install
# ------------------------------------------------------------------- checkout
log "cloning jasp-desktop $JASP_TAG"
rm -rf "$SRC_DIR"
git clone --depth 1 --branch "$JASP_TAG" --recurse-submodules --shallow-submodules \
https://github.com/jasp-stats/jasp-desktop.git "$SRC_DIR"
cd "$SRC_DIR"
JASP_COMMIT="$(git rev-parse HEAD)"
# ---------------------------------------------------------------------- build
log "configuring"
export CMAKE_C_COMPILER_LAUNCHER=ccache
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
export CMAKE_Fortran_COMPILER_LAUNCHER=ccache
export CFLAGS="${CFLAGS:-} -fPIC"
export CXXFLAGS="${CXXFLAGS:-} -fPIC"
export FCFLAGS="${FCFLAGS:-} -fPIC"
export FFLAGS="${FFLAGS:-} -fPIC"
rm -rf "$BUILD_DIR"
cmake -S "$SRC_DIR" -B "$BUILD_DIR" -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
-DLINUX_LOCAL_BUILD=ON \
-Wno-dev
log "compiling JASP (-j$JOBS)"
ninja -C "$BUILD_DIR" -j"$JOBS"
# The R modules are the long pole: several hundred R packages compiled from
# source. buildAllDefaultJaspModules.R expects these directories to exist.
log "building R modules — this is the slow part"
mkdir -p "$BUILD_DIR"/Modules/{binary_pkgs,manifests,module_libs,Tools}
cd "$SRC_DIR/Tools"
Rscript buildAllDefaultJaspModules.R
# -------------------------------------------------------------------- staging
log "staging install tree"
rm -rf "$STAGE_DIR"
mkdir -p "$STAGE_DIR"
DESTDIR="$STAGE_DIR" ninja -C "$BUILD_DIR" install
install -d "$STAGE_DIR$PREFIX/Modules"
cp -a "$SRC_DIR/Tools/ModuleBundleBuildDir/Modules/." "$STAGE_DIR$PREFIX/Modules/"
if [[ -d "$BUILD_DIR/R/library" ]]; then
install -d "$STAGE_DIR$PREFIX/R/library"
cp -a "$BUILD_DIR/R/library/." "$STAGE_DIR$PREFIX/R/library/"
fi
# Desktop entry + icons, so the tarball is a complete install tree.
install -Dm644 /work/packaging/org.jaspstats.JASP.desktop \
"$STAGE_DIR/usr/share/applications/org.jaspstats.JASP.desktop"
if [[ -d "$STAGE_DIR$PREFIX/share/icons/hicolor" ]]; then
install -d "$STAGE_DIR/usr/share/icons/hicolor"
cp -a "$STAGE_DIR$PREFIX/share/icons/hicolor/." "$STAGE_DIR/usr/share/icons/hicolor/"
fi
install -d "$STAGE_DIR/usr/share/licenses/jasp-desktop-bin"
cd "$SRC_DIR"
find . -maxdepth 2 -name 'COPYING*' -o -maxdepth 2 -name 'LICENSE*' -type f \
| while read -r f; do install -m644 "$f" "$STAGE_DIR/usr/share/licenses/jasp-desktop-bin/"; done
# Strip build-host paths out of the staged tree so the tarball is relocatable.
find "$STAGE_DIR" -name '*.o' -delete 2>/dev/null || true
# ------------------------------------------------------------------- metadata
# Pin data: the PKGBUILD turns these into versioned `depends` entries, so an
# Arch R or Qt6 bump makes the package rebuild rather than silently break.
pkgver_of() { pacman -Q "$1" 2>/dev/null | awk '{print $2}'; }
R_FULL="$(pkgver_of r)"
R_MINOR="$(R --version | head -1 | grep -oE '[0-9]+\.[0-9]+' | head -1)"
QT_FULL="$(pkgver_of qt6-base)"
QT_MINOR="$(echo "$QT_FULL" | grep -oE '^[0-9]+\.[0-9]+')"
BOOST_FULL="$(pkgver_of boost-libs)"
JASP_VER="${JASP_TAG#v}"
TARBALL="jasp-desktop-${JASP_VER}-x86_64.tar.zst"
log "creating $TARBALL"
cd "$STAGE_DIR"
tar --zstd -cf "$OUT_DIR/$TARBALL" .
SHA256="$(sha256sum "$OUT_DIR/$TARBALL" | awk '{print $1}')"
SIZE="$(stat -c%s "$OUT_DIR/$TARBALL")"
cat >"$OUT_DIR/build-info.json" <<EOF
{
"jasp_version": "${JASP_VER}",
"jasp_tag": "${JASP_TAG}",
"jasp_commit": "${JASP_COMMIT}",
"tarball": "${TARBALL}",
"sha256": "${SHA256}",
"size_bytes": ${SIZE},
"r_version": "${R_FULL}",
"r_minor": "${R_MINOR}",
"qt6_version": "${QT_FULL}",
"qt6_minor": "${QT_MINOR}",
"boost_version": "${BOOST_FULL}"
}
EOF
log "done"
cat "$OUT_DIR/build-info.json"