The seven-module build stalled at 6/7 on all three passes, deterministically,
with a cause unrelated to jaspMixedModels itself:
ERROR: failed to lock directory '.../LibraryR' for modifying
Error in library("devtools") : there is no package called 'devtools'
R leaves 00LOCK-<pkg> directories behind when an install is interrupted and
then refuses to modify the library at all, and the interrupted install had
also taken devtools with it. Every one of my killed containers poisoned the
library for the runs after it.
Clear stale locks and reinstall whatever is missing before building, rather
than assuming a clean library. This matters for the pipeline too: a job that
times out or gets OOM-killed would otherwise leave the runner unable to
build until someone cleaned up by hand.
465 lines
19 KiB
Bash
Executable File
465 lines
19 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.
|
|
#
|
|
# 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)
|
|
# R_VERSION R to build and bundle (default 4.5.3)
|
|
# GITHUB_PAT token for R package downloads from GitHub (optional but
|
|
# strongly recommended — several JASP modules are installed from
|
|
# GitHub and anonymous API rate limits will fail the build)
|
|
#
|
|
# Why we build our own R instead of using Arch's:
|
|
#
|
|
# JASP pins its R package set with renv lockfiles against a frozen CRAN snapshot
|
|
# (Modules/Rcpp_RInside.lock points at the 2026-03-31 Posit snapshot for 0.98.1).
|
|
# That set targets R 4.5. Building against Arch's R 4.6 fails immediately —
|
|
# Rcpp 1.1.1 does not compile there (R_NamespaceRegistry is gone in R 4.6), and
|
|
# the pinned snapshot predates the Rcpp release that fixes it. Upstream's own CI
|
|
# installs a specific R and points R_HOME at it, which is what we do here.
|
|
#
|
|
# Bundling R also means the package does not depend on Arch's `r` at all, so an
|
|
# Arch R bump can no longer break an installed JASP.
|
|
|
|
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)}"
|
|
R_VERSION="${R_VERSION:-4.5.3}"
|
|
|
|
SRC_DIR=/build/jasp-desktop
|
|
BUILD_DIR=/build/build
|
|
STAGE_DIR=/build/stage
|
|
PREFIX=/opt/jasp-desktop
|
|
R_HOME_DIR="${PREFIX}/R"
|
|
|
|
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 glpk jsoncpp libarchive openssl curl sqlite zlib \
|
|
readstat netcdf mpfr cairo pango fontconfig harfbuzz fribidi icu pcre2 \
|
|
readline bzip2 xz libpng libtiff libjpeg-turbo minizip \
|
|
libxkbcommon libxkbcommon-x11 libxt wget unzip python \
|
|
gsl libsodium gmp nlopt libxml2 fftw
|
|
|
|
# Stock makepkg.conf leaves MAKEFLAGS at -j1, which makes the AUR dependencies
|
|
# (JAGS especially) crawl.
|
|
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
|
|
|
|
# ------------------------------------------------------- R that we ship along
|
|
log "building R ${R_VERSION} into ${R_HOME_DIR}"
|
|
rm -rf "/build/R-${R_VERSION}"
|
|
cd /build
|
|
wget -q "https://cran.r-project.org/src/base/R-4/R-${R_VERSION}.tar.gz"
|
|
tar -xzf "R-${R_VERSION}.tar.gz"
|
|
cd "R-${R_VERSION}"
|
|
|
|
# --enable-R-shlib is required: JASP embeds R through RInside/libR.
|
|
# --libdir is pinned so R lands in lib/R rather than the lib64/R that Arch's
|
|
# config.site would otherwise pick, which keeps the rpath below predictable.
|
|
# The install prefix equals the final install path, so the rpath stays valid
|
|
# once the tarball is unpacked on the user's machine.
|
|
#
|
|
# --without-blas/--without-lapack is deliberate and load-bearing. With
|
|
# --with-blas, R links Arch's external BLAS and does NOT build libRblas.so or
|
|
# libRlapack.so. The JASP module bundles expect those files to exist inside
|
|
# R_HOME (that is how a conventionally built R, and the Flatpak, ship), so
|
|
# linking externally leaves the modules unable to resolve them. Reference BLAS
|
|
# is slower than OpenBLAS, but a working install beats a fast broken one.
|
|
./configure \
|
|
--prefix="${R_HOME_DIR}" \
|
|
--libdir="${R_HOME_DIR}/lib" \
|
|
--enable-R-shlib \
|
|
--enable-memory-profiling \
|
|
--without-blas \
|
|
--without-lapack \
|
|
--with-x=no \
|
|
--with-tcltk=no \
|
|
LDFLAGS="-Wl,-rpath,${R_HOME_DIR}/lib/R/lib"
|
|
make -j"$JOBS"
|
|
make install
|
|
|
|
# Guard the above: a silently external-BLAS build produces a JASP whose modules
|
|
# fail at runtime, which is exactly the class of failure this package exists to
|
|
# avoid. Better to stop here than ship it.
|
|
for lib in libR.so libRblas.so libRlapack.so; do
|
|
if [[ ! -f "${R_HOME_DIR}/lib/R/lib/${lib}" ]]; then
|
|
echo "error: ${lib} missing from the bundled R — R linked an external BLAS?" >&2
|
|
ls -la "${R_HOME_DIR}/lib/R/lib/" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
log "bundled R provides its own libR/libRblas/libRlapack"
|
|
|
|
export PATH="${R_HOME_DIR}/bin:${PATH}"
|
|
# Ask R where it actually put itself rather than assuming lib vs lib64.
|
|
# Deliberately NOT exported: R.cmake notes that a set R_HOME makes R print
|
|
# "WARNING: ignoring environment value of R_HOME", which corrupts the output it
|
|
# parses. CUSTOM_R_PATH is the supported way to point JASP at a specific R.
|
|
JASP_R_HOME="$("${R_HOME_DIR}/bin/R" RHOME)"
|
|
log "bundled R_HOME is ${JASP_R_HOME}"
|
|
"${R_HOME_DIR}/bin/R" --version | sed -n 1p || true
|
|
|
|
# ------------------------------------------------------------------- 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=OFF \
|
|
-DCUSTOM_R_PATH="${JASP_R_HOME}" \
|
|
-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 shells out to buildModuleBundlesLocally.R
|
|
# via system2(), which does NOT check exit status — so every failure below is
|
|
# silent and the script still exits 0. The guards after it are what actually
|
|
# catch a broken module build.
|
|
#
|
|
# GITHUB_PAT is required, not optional: buildModuleBundlesLocally.R does
|
|
# install_github('jasp-stats/jaspModuleTools'), and anonymous GitHub API rate
|
|
# limits make that fail. When it fails, no bundles are produced at all and the
|
|
# build otherwise looks successful.
|
|
if [[ -z "${GITHUB_PAT:-}" ]]; then
|
|
echo "error: GITHUB_PAT is required — jaspModuleTools is installed from GitHub" >&2
|
|
echo " without it the module build silently produces nothing" >&2
|
|
exit 1
|
|
fi
|
|
export GITHUB_PAT
|
|
export GITHUB_TOKEN="${GITHUB_PAT}"
|
|
|
|
log "building R modules — this is the slow part"
|
|
mkdir -p "$BUILD_DIR"/Modules/{binary_pkgs,manifests,module_libs,Tools}
|
|
cd "$SRC_DIR/Tools"
|
|
|
|
# buildModuleBundlesLocally.R calls devtools::install_github(), which since
|
|
# devtools 2.5.0 is deprecated and delegates to the `remotes` package — but
|
|
# remotes is no longer a hard dependency of devtools, so the call dies with
|
|
# "The package \"remotes\" is required." Pre-seed it into the same library the
|
|
# script uses, since that script does .libPaths(new=userLibrary) and would not
|
|
# see remotes anywhere else.
|
|
MODULE_LIBRARY="$SRC_DIR/Tools/ModuleBundleBuildDir/LibraryR"
|
|
MODULE_BUNDLES="$SRC_DIR/Modules/local"
|
|
|
|
log "pre-installing remotes into the module build library"
|
|
mkdir -p "$MODULE_LIBRARY"
|
|
Rscript -e "
|
|
lib <- '${MODULE_LIBRARY}'
|
|
.libPaths(new = lib)
|
|
install.packages('remotes', lib = lib, repos = 'https://cran.r-project.org')
|
|
if (!requireNamespace('remotes', lib.loc = lib, quietly = TRUE))
|
|
stop('failed to install remotes')
|
|
"
|
|
|
|
# Some of the versions JASP pins in its module renv.lock files predate the
|
|
# toolchain on a current Arch and cannot compile. Bump only those, by name, with
|
|
# the reason recorded — never a blanket "use latest", which would silently
|
|
# change the analysis code users get.
|
|
#
|
|
# later 1.4.6 bundles a tinycthread whose `#define once_flag pthread_once_t`
|
|
# collides with the C11 once_flag that glibc >= 2.43 now declares under
|
|
# C23, so it fails with "conflicting types for pthread_once_t".
|
|
# Fixed upstream in later 1.4.7 (r-lib/later#257). The 2026-04-22 RSPM
|
|
# snapshot the lockfiles already reference carries 1.4.8.
|
|
log "patching module lockfile pins that cannot build on this toolchain"
|
|
python3 - <<'PY'
|
|
import json, pathlib
|
|
|
|
OVERRIDES = {"later": "1.4.8"}
|
|
|
|
root = pathlib.Path("ModuleBundleBuildDir/Modules")
|
|
patched = 0
|
|
for lock in root.glob("*/renv.lock"):
|
|
data = json.loads(lock.read_text())
|
|
packages = data.get("Packages", {})
|
|
changed = False
|
|
for name, want in OVERRIDES.items():
|
|
pkg = packages.get(name)
|
|
if pkg and pkg.get("Version") != want:
|
|
print(f" {lock.parent.name}: {name} {pkg['Version']} -> {want}")
|
|
pkg["Version"] = want
|
|
# The recorded hash no longer matches the bumped version.
|
|
pkg.pop("Hash", None)
|
|
changed = True
|
|
if changed:
|
|
lock.write_text(json.dumps(data, indent=2) + "\n")
|
|
patched += 1
|
|
print(f"patched {patched} lockfiles")
|
|
PY
|
|
|
|
# renv restores packages concurrently and its ordering is not always
|
|
# dependency-correct: a module can be lazy-loaded while a package it needs is
|
|
# still being built, e.g. "forecast" failing with "there is no package called
|
|
# 'vctrs'" six log lines before vctrs finishes compiling. That aborts the whole
|
|
# sapply() loop, so a single race costs every remaining module.
|
|
#
|
|
# Retrying is the cheap fix: renv skips what is already installed, so each pass
|
|
# starts from more complete state and converges. Passes are capped so a genuine
|
|
# failure cannot loop forever.
|
|
# OpenMx instantiates enough Eigen templates that a single cc1plus can take
|
|
# several GB. At the top-level -j the compiler gets OOM-killed
|
|
# ("g++: fatal error: Killed signal terminated program cc1plus"), which is not
|
|
# a race — it stalls at the same module count on every retry. Compile R packages
|
|
# with less parallelism than the C++ build uses.
|
|
R_JOBS="${R_JOBS:-2}"
|
|
export MAKEFLAGS="-j${R_JOBS}"
|
|
# R leaves 00LOCK-<pkg> directories behind when an install is interrupted, and
|
|
# refuses to touch the library afterwards ("failed to lock directory ... for
|
|
# modifying"). A build that is killed mid-install - a timeout, an OOM, a stopped
|
|
# container - therefore poisons the library for every later run.
|
|
log "clearing stale R install locks"
|
|
rm -rf "${MODULE_LIBRARY}"/00LOCK-* 2>/dev/null || true
|
|
|
|
# The interrupted install can also leave the library without devtools, which
|
|
# buildModuleBundlesLocally.R calls unconditionally. Reinstall whatever is
|
|
# missing rather than assuming a clean slate.
|
|
Rscript -e "
|
|
lib <- '${MODULE_LIBRARY}'
|
|
dir.create(lib, recursive = TRUE, showWarnings = FALSE)
|
|
.libPaths(new = lib)
|
|
for (pkg in c('remotes', 'devtools')) {
|
|
if (!requireNamespace(pkg, lib.loc = lib, quietly = TRUE)) {
|
|
message('installing missing ', pkg)
|
|
install.packages(pkg, lib = lib, repos = 'https://cran.r-project.org')
|
|
}
|
|
if (!requireNamespace(pkg, lib.loc = lib, quietly = TRUE))
|
|
stop('failed to install ', pkg)
|
|
}
|
|
"
|
|
|
|
log "compiling R packages with -j${R_JOBS} (OpenMx needs the headroom)"
|
|
|
|
# Build only the modules JASP actually ships. Modules/modules-settings.json
|
|
# splits them into "common" (bundled with the application) and "extra"
|
|
# (fetched on demand at runtime, into ~/.local/share/JASP).
|
|
#
|
|
# buildAllDefaultJaspModules.R is a *developer* helper that builds 22 modules
|
|
# regardless of that split — its name says "all default modules", not "what is
|
|
# released". Building from it produces a ~9 GB tree, most of it modules the user
|
|
# would have downloaded on demand anyway, and drags in system dependencies that
|
|
# only the extras need (gsl for jaspNetwork, OpenMx for jaspSem).
|
|
mapfile -t COMMON_MODULES < <(
|
|
python3 -c "
|
|
import json
|
|
print('\n'.join(json.load(open('../Modules/modules-settings.json'))['common']))
|
|
"
|
|
)
|
|
log "shipping the ${#COMMON_MODULES[@]} common modules: ${COMMON_MODULES[*]}"
|
|
|
|
mkdir -p ModuleBundleBuildDir/Modules
|
|
for module in "${COMMON_MODULES[@]}"; do
|
|
if [[ ! -d "ModuleBundleBuildDir/Modules/${module}" ]]; then
|
|
log "cloning ${module}"
|
|
git clone --depth 1 "https://github.com/jasp-stats/${module}" \
|
|
"ModuleBundleBuildDir/Modules/${module}"
|
|
fi
|
|
done
|
|
|
|
EXPECTED_MODULES="${#COMMON_MODULES[@]}"
|
|
MODULE_PATHS=()
|
|
for module in "${COMMON_MODULES[@]}"; do
|
|
MODULE_PATHS+=("ModuleBundleBuildDir/Modules/${module}")
|
|
done
|
|
|
|
for attempt in 1 2 3; do
|
|
log "module build pass ${attempt}/3"
|
|
./buildModuleBundlesLocally.R "${MODULE_PATHS[@]}" || true
|
|
built=0
|
|
for module in "${COMMON_MODULES[@]}"; do
|
|
[[ -f "${MODULE_BUNDLES}/${module}.JASPModule" ]] && built=$((built + 1))
|
|
done
|
|
log "pass ${attempt} finished: ${built}/${EXPECTED_MODULES} common modules"
|
|
[[ "$built" -ge "$EXPECTED_MODULES" ]] && break
|
|
done
|
|
|
|
if [[ ! -d "$MODULE_LIBRARY/jaspModuleTools" ]]; then
|
|
echo "error: jaspModuleTools was not installed — install_github failed" >&2
|
|
echo " check the install_github output above: a missing 'remotes'," >&2
|
|
echo " a GitHub rate limit, or a PAT without read access all do this" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Guard on the expected count, not merely "non-empty". An earlier run shipped a
|
|
# package containing 2 of 22 modules because a non-empty check passed.
|
|
missing=()
|
|
for module in "${COMMON_MODULES[@]}"; do
|
|
[[ -f "${MODULE_BUNDLES}/${module}.JASPModule" ]] || missing+=("$module")
|
|
done
|
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
echo "error: ${#missing[@]} of ${EXPECTED_MODULES} common modules were not built" >&2
|
|
printf ' missing: %s\n' "${missing[@]}" >&2
|
|
echo " search the log above for 'failed to install'" >&2
|
|
exit 1
|
|
fi
|
|
log "all ${EXPECTED_MODULES} common modules built"
|
|
|
|
# -------------------------------------------------------------------- staging
|
|
log "staging install tree"
|
|
rm -rf "$STAGE_DIR"
|
|
mkdir -p "$STAGE_DIR"
|
|
DESTDIR="$STAGE_DIR" ninja -C "$BUILD_DIR" install
|
|
|
|
# The bundled R was installed straight to $PREFIX/R on the build host; copy it
|
|
# into the staged tree so it ends up in the tarball.
|
|
install -d "$STAGE_DIR$PREFIX"
|
|
cp -a "$R_HOME_DIR" "$STAGE_DIR$PREFIX/"
|
|
|
|
# Everything the module build produced has to travel with the tarball, or JASP
|
|
# falls back to downloading bundles at runtime — which is the failure mode this
|
|
# package is meant to remove.
|
|
#
|
|
# Modules/local the compiled module bundles themselves
|
|
# LibraryR the R library they were built with (jaspModuleTools, devtools
|
|
# and ~95 dependencies), compiled against the bundled R 4.5.3
|
|
#
|
|
# ModuleBundleBuildDir/Modules is deliberately NOT copied: it is the cloned
|
|
# module *sources* (no compiled objects at all), several hundred MB of git
|
|
# checkouts that would bloat the tarball without being usable at runtime.
|
|
install -d "$STAGE_DIR$PREFIX/Modules"
|
|
|
|
log "staging module bundles from ${MODULE_BUNDLES}"
|
|
cp -a "$MODULE_BUNDLES/." "$STAGE_DIR$PREFIX/Modules/"
|
|
|
|
log "staging module R library from ${MODULE_LIBRARY}"
|
|
install -d "$STAGE_DIR$PREFIX/ModuleLibrary"
|
|
cp -a "$MODULE_LIBRARY/." "$STAGE_DIR$PREFIX/ModuleLibrary/"
|
|
|
|
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 -name 'LICENSE*' \) -type f \
|
|
-exec install -m644 {} "$STAGE_DIR/usr/share/licenses/jasp-desktop-bin/" \;
|
|
|
|
find "$STAGE_DIR" -name '*.o' -delete 2>/dev/null || true
|
|
|
|
# Make the tree independent of Arch's rolling libraries (ICU, jsoncpp, glpk,
|
|
# JAGS, readstat, librdata ...). Qt6 stays a system dependency; BLAS/LAPACK now
|
|
# come from the bundled R itself.
|
|
log "bundling non-system shared libraries"
|
|
bash /work/build/bundle-libs.sh "$STAGE_DIR" "$PREFIX"
|
|
|
|
# Final sanity check on what is about to be shipped. Each of these has already
|
|
# been shipped missing once.
|
|
log "verifying the staged tree"
|
|
staged_r="$STAGE_DIR$PREFIX/R/lib/R/lib"
|
|
for lib in libR.so libRblas.so libRlapack.so; do
|
|
[[ -f "$staged_r/$lib" ]] || { echo "error: staged tree lacks $lib" >&2; exit 1; }
|
|
done
|
|
staged_bundles="$(find "$STAGE_DIR$PREFIX/Modules" -maxdepth 1 -name '*.JASPModule' 2>/dev/null | wc -l)"
|
|
[[ "$staged_bundles" -gt 0 ]] || { echo "error: staged tree has no module bundles" >&2; exit 1; }
|
|
staged_so="$(find "$STAGE_DIR$PREFIX" -name '*.so' 2>/dev/null | wc -l)"
|
|
log "staged: ${staged_bundles} module bundles, ${staged_so} shared objects"
|
|
du -sh "$STAGE_DIR$PREFIX"/* 2>/dev/null | sort -rh | head -8
|
|
|
|
# ------------------------------------------------------------------- metadata
|
|
pkgver_of() { pacman -Q "$1" 2>/dev/null | awk '{print $2}'; }
|
|
|
|
QT_FULL="$(pkgver_of qt6-base)"
|
|
QT_MINOR="$(echo "$QT_FULL" | grep -oE '^[0-9]+\.[0-9]+')"
|
|
QT_MAJOR="$(echo "$QT_FULL" | grep -oE '^[0-9]+')"
|
|
ICU_FULL="$(pkgver_of icu)"
|
|
ICU_MAJOR="$(echo "$ICU_FULL" | grep -oE '^[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},
|
|
"bundled_r_version": "${R_VERSION}",
|
|
"qt6_version": "${QT_FULL}",
|
|
"qt6_minor": "${QT_MINOR}",
|
|
"qt6_major": "${QT_MAJOR}",
|
|
"icu_version": "${ICU_FULL}",
|
|
"icu_major": "${ICU_MAJOR}",
|
|
"boost_version": "${BOOST_FULL}"
|
|
}
|
|
EOF
|
|
|
|
log "done"
|
|
cat "$OUT_DIR/build-info.json"
|