Bundle our own R 4.5.3 instead of building against Arch's R

Arch ships R 4.6.1, but JASP pins its R packages against a frozen CRAN
snapshot (2026-03-31 for 0.98.1) that targets R 4.5. Configuring against
Arch's R fails at Rcpp 1.1.1, which cannot compile on R 4.6 since
R_NamespaceRegistry was removed; the Rcpp release that fixes it is newer
than the pinned snapshot, so the lockfile cannot reach it either.

Build R 4.5.3 into /opt/jasp-desktop/R and point R_HOME at it, matching
what upstream CI does. This drops 'r' from depends entirely, so an Arch R
upgrade can no longer break an installed JASP, and the rebuild triggers
become Qt6 and ICU - the libraries actually still linked against.
This commit is contained in:
agw
2026-09-01 21:40:00 +02:00
parent 3ad88f9b0d
commit d880e6748c
7 changed files with 130 additions and 64 deletions
+9 -8
View File
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
"""Decide whether a rebuild is needed.
A JASP binary is only valid for the R minor and Qt6 minor it was linked against,
so a rebuild is triggered by an upstream JASP release *or* by Arch moving R or
Qt6 — not just the former.
The build bundles its own R, so Arch's `r` is irrelevant here. What the binary
*is* still linked against is Qt6 and ICU, both of which break ABI across
releases — so a rebuild is triggered by an upstream JASP release or by Arch
moving either of those.
Writes GITHUB_OUTPUT-style key=value pairs to stdout (and to $GITHUB_OUTPUT).
"""
@@ -40,8 +41,8 @@ def main() -> int:
pinned_tag = os.environ.get("JASP_TAG", "").strip()
jasp_tag = pinned_tag or fetch_json(GH_LATEST)["tag_name"]
r_minor = minor(arch_version("r"))
qt_minor = minor(arch_version("qt6-base"))
icu_major = arch_version("icu").split(".")[0]
try:
state = json.loads(open(STATE_FILE).read())
@@ -53,16 +54,16 @@ def main() -> int:
reasons.append("manually forced")
if state.get("jasp_tag") != jasp_tag:
reasons.append(f"JASP {state.get('jasp_tag', 'none')} -> {jasp_tag}")
if state.get("r_minor") != r_minor:
reasons.append(f"R {state.get('r_minor', 'none')} -> {r_minor}")
if state.get("qt6_minor") != qt_minor:
reasons.append(f"Qt6 {state.get('qt6_minor', 'none')} -> {qt_minor}")
if state.get("icu_major") != icu_major:
reasons.append(f"ICU {state.get('icu_major', 'none')} -> {icu_major}")
needs_build = bool(reasons)
print(f"jasp_tag = {jasp_tag}")
print(f"r_minor = {r_minor}")
print(f"qt6_minor = {qt_minor}")
print(f"icu_major = {icu_major}")
print(f"decision = {'BUILD' if needs_build else 'up to date'}")
for reason in reasons:
print(f" reason: {reason}")
@@ -72,8 +73,8 @@ def main() -> int:
with open(out, "a") as fh:
fh.write(f"needs_build={'true' if needs_build else 'false'}\n")
fh.write(f"jasp_tag={jasp_tag}\n")
fh.write(f"r_minor={r_minor}\n")
fh.write(f"qt6_minor={qt_minor}\n")
fh.write(f"icu_major={icu_major}\n")
fh.write(f"reasons={'; '.join(reasons)}\n")
return 0
+7 -5
View File
@@ -63,17 +63,18 @@ def main() -> int:
f"/releases/download/{tag}/{info['tarball']}"
)
r_minor = info["r_minor"]
qt_minor = info["qt6_minor"]
icu_major = info["icu_major"]
substitutions = {
"@MAINTAINER@": args.maintainer,
"@JASP_VERSION@": jasp_version,
"@PKGREL@": str(pkgrel),
"@R_MINOR@": r_minor,
"@R_NEXT_MINOR@": next_minor(r_minor),
"@BUNDLED_R@": info["bundled_r_version"],
"@QT_MINOR@": qt_minor,
"@QT_NEXT_MINOR@": next_minor(qt_minor),
"@ICU_MAJOR@": icu_major,
"@ICU_NEXT_MAJOR@": str(int(icu_major) + 1),
"@DOWNLOAD_URL@": download_url,
"@SHA256@": info["sha256"],
}
@@ -95,8 +96,9 @@ def main() -> int:
json.dump({"tag": tag, "pkgrel": pkgrel, "download_url": download_url, **info}, fh, indent=2)
print(f"wrote {args.out} ({args.pkgname} {jasp_version}-{pkgrel})")
print(f" R pinned to >={r_minor} <{next_minor(r_minor)}")
print(f" Qt pinned to >={qt_minor} <{next_minor(qt_minor)}")
print(f" R bundled at {info['bundled_r_version']} (not an Arch dependency)")
print(f" Qt pinned to >={qt_minor} <{next_minor(qt_minor)}")
print(f" ICU pinned to >={icu_major} <{int(icu_major) + 1}")
return 0
+2 -1
View File
@@ -16,8 +16,9 @@ state = {
"jasp_tag": meta["jasp_tag"],
"jasp_version": meta["jasp_version"],
"pkgrel": meta["pkgrel"],
"r_minor": meta["r_minor"],
"bundled_r_version": meta["bundled_r_version"],
"qt6_minor": meta["qt6_minor"],
"icu_major": meta["icu_major"],
"sha256": meta["sha256"],
"download_url": meta["download_url"],
}