Build every module locally: downloaded ones target the Flatpak runtime

Running the package surfaced what shipping only the seven common modules
actually costs. Opening Distributions made JASP download jaspDistributions at
runtime, and it failed to load:

  binary_pkgs/43a00e0b.../libjpeg.so.62: cannot open shared object file

Those bundles are built against the Flatpak runtime. libjpeg.so.62 is the
Debian-era soname; Arch has carried libjpeg.so.8 for years, and it is only
available here from the AUR. Installing libjpeg6-turbo fixed that one module,
but every downloaded module has its own dependency set built against the same
foreign runtime, so this is not a fixable class - it is found one soname at a
time, by users.

So build all of them here instead. common stays mandatory; extras are
best-effort, because a few need things Arch does not package at all
(jaspProcess wants V8) and losing one of those is better than losing the
build. The blockers that made me scope down to common in the first place -
gsl, OpenMx's memory, rstanarm, the renv ordering race - are all already
solved, so this is removing a restriction rather than starting over.
This commit is contained in:
agw
2026-09-03 21:45:00 +02:00
parent 2d5b6a8a0d
commit f0234708f3
+46 -17
View File
@@ -303,38 +303,58 @@ log "compiling R packages with -j${R_JOBS} (OpenMx needs the headroom)"
# 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).
# Build every module JASP ships a definition for, not just the "common" seven.
#
# The extras are normally downloaded at runtime — but those bundles are built
# against the Flatpak runtime, and on Arch they fail on sonames that do not
# exist here (the first one hit was libjpeg.so.62, which Arch replaced with
# libjpeg.so.8 long ago). A downloaded module is therefore never reliable in
# this package; anything the user might enable has to be built against the same
# Arch userland as the application itself.
#
# common modules are mandatory: without them the package is not JASP.
# extras are best-effort: a few need dependencies that are not packaged for
# Arch at all (jaspProcess wants V8), and losing one of those is better than
# losing the build.
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[*]}"
mapfile -t EXTRA_MODULES < <(
python3 -c "
import json
s = json.load(open('../Modules/modules-settings.json'))
print('\n'.join(m for m in s['extra'] if m != 'jaspTestModule'))
"
)
ALL_MODULES=("${COMMON_MODULES[@]}" "${EXTRA_MODULES[@]}")
log "building ${#ALL_MODULES[@]} modules (${#COMMON_MODULES[@]} common + ${#EXTRA_MODULES[@]} extra)"
mkdir -p ModuleBundleBuildDir/Modules
for module in "${COMMON_MODULES[@]}"; do
for module in "${ALL_MODULES[@]}"; do
if [[ ! -d "ModuleBundleBuildDir/Modules/${module}" ]]; then
log "cloning ${module}"
git clone --depth 1 "https://github.com/jasp-stats/${module}" \
"ModuleBundleBuildDir/Modules/${module}"
"ModuleBundleBuildDir/Modules/${module}" 2>/dev/null || log "no repo for ${module}, skipping"
fi
done
EXPECTED_MODULES="${#COMMON_MODULES[@]}"
MODULE_PATHS=()
for module in "${COMMON_MODULES[@]}"; do
MODULE_PATHS+=("ModuleBundleBuildDir/Modules/${module}")
for module in "${ALL_MODULES[@]}"; do
[[ -d "ModuleBundleBuildDir/Modules/${module}" ]] && \
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
for module in "${ALL_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
log "pass ${attempt} finished: ${built}/${#ALL_MODULES[@]} modules"
[[ "$built" -ge "${#ALL_MODULES[@]}" ]] && break
done
if [[ ! -d "$MODULE_LIBRARY/jaspModuleTools" ]]; then
@@ -346,17 +366,26 @@ 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=()
missing_common=()
for module in "${COMMON_MODULES[@]}"; do
[[ -f "${MODULE_BUNDLES}/${module}.JASPModule" ]] || missing+=("$module")
[[ -f "${MODULE_BUNDLES}/${module}.JASPModule" ]] || missing_common+=("$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
if [[ ${#missing_common[@]} -gt 0 ]]; then
echo "error: mandatory common modules were not built" >&2
printf ' missing: %s\n' "${missing_common[@]}" >&2
exit 1
fi
log "all ${EXPECTED_MODULES} common modules built"
missing_extra=()
for module in "${EXTRA_MODULES[@]}"; do
[[ -f "${MODULE_BUNDLES}/${module}.JASPModule" ]] || missing_extra+=("$module")
done
built_total=$(find "$MODULE_BUNDLES" -maxdepth 1 -name '*.JASPModule' | wc -l)
log "built ${built_total} modules; all ${#COMMON_MODULES[@]} common present"
if [[ ${#missing_extra[@]} -gt 0 ]]; then
log "extras that did not build (users will fall back to downloading these):"
printf ' %s\n' "${missing_extra[@]}"
fi
# -------------------------------------------------------------------- staging
log "staging install tree"