Skip to content

Commit cd1a713

Browse files
docs(chapel): PIC-from-source rebuild ADR + Justfile recipe (#150)
## Summary Wave-2 follow-on to PR #146. The metalayer is currently linked `--library --static` because the apt-distributed Chapel runtime ships only the `lib_pic-none` variant. This PR ships: - **ADR** at \`docs/decisions/2026-05-30-chapel-pic-rebuild.md\` — documents the constraint, the source-rebuild procedure, tradeoffs vs the current static form, and why the CI flip is deferred to a future registry-pushed container. - **\`just chapel-pic-from-source\`** — runnable recipe that downloads chapel-2.8.0, sets \`CHPL_LIB_PIC=pic\`, builds, and verifies the \`lib_pic-pic/\` runtime variant landed. Cache via \`ECHIDNA_CHAPEL_PIC_CACHE\`; version via \`CHPL_VERSION\`. **Not** invoked from CI. - Cross-link from the rehabilitation ADR's Wave-2 plan into this ADR. ## Why no CI flip The PIC runtime is required for \`--library --dynamic\` and the L2.5 multi-locale Chapel work, but **not** required for the current Rust link path (which uses \`libechidna_chapel.a\`). The ~30 min wall + ~5 GB disk cost of the source rebuild would dominate the chapel-ci budget for negligible gain until L2.5 starts — see the \"Tradeoffs\" table in the ADR. A registry-pushed Containerfile is the right amortisation, gated on L1 Cap'n Proto. ## Test plan - [x] \`just --list\` shows the new recipe - [x] Justfile parses (shebang-style recipe, same form as \`init\`/\`doctor\`/\`heal\`) - [ ] Recipe end-to-end execution (deliberately local-only; takes ~30 min) - [ ] CI: chapel-build + chapel-smoke remain green (unchanged from current main) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 12108ac commit cd1a713

3 files changed

Lines changed: 190 additions & 2 deletions

File tree

Justfile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,43 @@ bench-chapel-mrr:
534534
cd src/chapel && chpl -o bench_mrr bench_mrr.chpl && \
535535
./bench_mrr --verbose=false --timeout=10
536536

537+
# Rebuild Chapel 2.8.0 from source with CHPL_LIB_PIC=pic so that
538+
# `chpl --library --dynamic` can produce a shared-library form of the
539+
# metalayer. The apt deb ships only the `lib_pic-none` runtime
540+
# variant. ~25-35 min wall on a 4-core x86_64. ~5 GB disk under
541+
# ~/.cache/echidna/chapel-pic. Procedure + tradeoffs:
542+
# docs/decisions/2026-05-30-chapel-pic-rebuild.md. NOT a CI step.
543+
chapel-pic-from-source:
544+
#!/usr/bin/env bash
545+
set -euo pipefail
546+
CACHE_DIR="${ECHIDNA_CHAPEL_PIC_CACHE:-$HOME/.cache/echidna/chapel-pic}"
547+
CHPL_VERSION="${CHPL_VERSION:-2.8.0}"
548+
mkdir -p "$CACHE_DIR"
549+
cd "$CACHE_DIR"
550+
if [ ! -f "chapel-${CHPL_VERSION}.tar.gz" ]; then
551+
echo "Downloading chapel ${CHPL_VERSION}..."
552+
curl -fL "https://github.com/chapel-lang/chapel/releases/download/${CHPL_VERSION}/chapel-${CHPL_VERSION}.tar.gz" \
553+
-o "chapel-${CHPL_VERSION}.tar.gz"
554+
fi
555+
if [ ! -d "chapel-${CHPL_VERSION}" ]; then
556+
tar xf "chapel-${CHPL_VERSION}.tar.gz"
557+
fi
558+
cd "chapel-${CHPL_VERSION}"
559+
export CHPL_LIB_PIC=pic
560+
export CHPL_LLVM=bundled
561+
export CHPL_HOME="$PWD"
562+
source util/setchplenv.bash
563+
echo "Building chapel runtime with CHPL_LIB_PIC=pic (~30 min)..."
564+
make -j"$(nproc)"
565+
echo "Verifying lib_pic-pic variant landed..."
566+
if find lib -name 'lib_pic-pic' -type d | head -1 | grep -q .; then
567+
echo "OK: PIC runtime built at $CHPL_HOME"
568+
echo "Use: PATH=$CHPL_HOME/bin/linux64-x86_64:\$PATH chpl --library --dynamic ..."
569+
else
570+
echo "ERROR: lib_pic-pic variant not found after build" >&2
571+
exit 1
572+
fi
573+
537574
# Build Zig FFI bridge for Chapel (prerequisite for --features chapel)
538575
build-chapel-ffi:
539576
@echo "Building Zig FFI bridge..."
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<!-- SPDX-License-Identifier: MPL-2.0 -->
2+
3+
# 2026-05-30 — Chapel PIC rebuild from source for `--library --dynamic`
4+
5+
ADR-style addendum to [2026-05-30-chapel-rehabilitation.md][rehab].
6+
Documents the procedure and constraints for replacing the apt-shipped
7+
Chapel runtime with a position-independent variant so the metalayer can
8+
be linked as a shared library instead of a static archive.
9+
10+
[rehab]: ./2026-05-30-chapel-rehabilitation.md
11+
12+
## Status
13+
14+
Documented. Procedure is `just chapel-pic-from-source`; **not** invoked
15+
from any CI workflow because the build is ~30 min wall and ~5 GB disk,
16+
which would dominate the Chapel CI budget for negligible runtime gain
17+
until L2.5 multi-locale work needs the shared-library form.
18+
19+
## Context
20+
21+
The rehabilitation PR (#146) chose `--library --static` for
22+
`chapel-build` because the apt-distributed Chapel deb only ships one
23+
runtime variant — `lib_pic-none`:
24+
25+
```
26+
/usr/lib/chapel/2.8/runtime/lib/linux64/gnu/x86_64/loc-flat/
27+
comm-none/tasks-qthreads/launch-smp/tmr-generic/unwind-system/
28+
mem-jemalloc/atomics-cstdlib/lib_pic-none/san-none/libchpllaunch.a
29+
```
30+
31+
`chpl --library --dynamic` requires a `lib_pic-pic/` variant in the
32+
same path tree; without it, the link of `libchpl.a` into a `.so` fails
33+
with:
34+
35+
```
36+
relocation R_X86_64_TPOFF32 against symbol `chpl_task_root_uniqueId'
37+
can not be used when making a shared object; recompile with -fPIC
38+
```
39+
40+
The same constraint applies to the conda-forge `chapel` package, which
41+
also ships only the non-PIC runtime variant as of 2.8.0.
42+
43+
The only way to obtain a PIC runtime is to build Chapel from the
44+
release source with `CHPL_LIB_PIC=pic` set at `make` time. The variant
45+
ends up at the parallel `lib_pic-pic/` path and is auto-discovered by
46+
`chpl --library --dynamic` at link time.
47+
48+
## Decision
49+
50+
Ship a documented, reproducible Justfile recipe that performs the
51+
source rebuild on demand, but do **not** invoke it from CI and do **not**
52+
change the canonical `chapel-build` recipe. Local developers and the
53+
eventual L2.5 multi-locale workflow can opt in via `just chapel-pic-from-source`.
54+
55+
Rationale:
56+
57+
- The PIC rebuild is a one-shot ~30 min operation per CI runner, which
58+
cannot be amortised across PRs without a registry-pushed container
59+
image — see follow-up below.
60+
- The static-library form (`--library --static`) is correct for the
61+
current Rust-link path; Rust binaries already embed the Chapel
62+
runtime by linking against `libechidna_chapel.a`. Switching to
63+
`.so` linkage gains separability of the Chapel runtime but adds
64+
packaging complexity (now two artifacts to ship and version).
65+
- The PIC form is only **required** for L2.5 (multi-locale Chapel
66+
dispatch under PGAS), where the runtime needs to be loadable from
67+
multiple Chapel-distinct executables. That work is gated on L1
68+
Cap'n Proto, so the PIC runtime is a precondition we can satisfy
69+
asynchronously when L2.5 starts.
70+
71+
## Procedure
72+
73+
```bash
74+
just chapel-pic-from-source
75+
```
76+
77+
Equivalent to:
78+
79+
```bash
80+
mkdir -p ~/.cache/echidna/chapel-pic && cd ~/.cache/echidna/chapel-pic
81+
82+
# Source — 2.8.0 matches the local development install. Adjust if
83+
# CI moves to a newer point release.
84+
curl -fL https://github.com/chapel-lang/chapel/releases/download/2.8.0/chapel-2.8.0.tar.gz \
85+
-o chapel-2.8.0.tar.gz
86+
tar xf chapel-2.8.0.tar.gz
87+
cd chapel-2.8.0
88+
89+
# PIC runtime variant — the single env var that drives the additional
90+
# `lib_pic-pic/` subtree under `runtime/lib/.../`. LLVM is the default
91+
# back end on 2.8+; the apt deb also uses LLVM, so we match.
92+
export CHPL_LIB_PIC=pic
93+
export CHPL_LLVM=bundled # match apt deb
94+
export CHPL_HOME=$PWD
95+
source util/setchplenv.bash
96+
97+
# Build. This step is ~25-35 min wall on a 4-core x86_64 box.
98+
make -j$(nproc)
99+
100+
# Verify the PIC variant landed.
101+
find lib -name 'lib_pic-pic' -type d | head -1
102+
```
103+
104+
After this completes, `chpl` from `$CHPL_HOME/bin/linux64-x86_64/chpl`
105+
can produce shared-library output:
106+
107+
```bash
108+
$CHPL_HOME/bin/linux64-x86_64/chpl \
109+
--library --dynamic -I ../zig_ffi \
110+
-o libechidna_chapel parallel_proof_search.chpl chapel_ffi_exports.chpl
111+
```
112+
113+
## Tradeoffs
114+
115+
| Aspect | `--library --static` (current default) | `--library --dynamic` (PIC rebuild) |
116+
|---|---|---|
117+
| Build time, cold | ~10 s | ~30 min (one-time) + ~10 s |
118+
| Build time, warm | ~10 s | ~10 s |
119+
| Disk footprint | runtime in libchpl.a (~30 MB) | runtime in libchpl.so (~30 MB) + source tree (~5 GB) |
120+
| Rust link | static archive into executable | dynamic load at runtime |
121+
| Multi-process sharing | each process embeds runtime | runtime shared across processes |
122+
| L2.5 multi-locale | does not support | required |
123+
124+
## Follow-up
125+
126+
A registry-pushed Containerfile that pre-builds the PIC runtime would
127+
amortise the 30 min cost across CI runs, but requires:
128+
129+
- A `Containerfile.chapel-pic` under `.containerization/`.
130+
- A registry destination (ghcr.io/hyperpolymath/echidna-chapel-pic:2.8.0).
131+
- A CI job to build + push on chapel version bumps.
132+
133+
Tracked as a Wave-3 follow-up issue; the current PR does **not** add
134+
the container path because it would duplicate the in-flight L1 Cap'n
135+
Proto schema work and risk image-tag drift if Chapel point-releases
136+
between now and L2.5 starting.
137+
138+
## Verification
139+
140+
The recipe is not executed in CI for the reasons above. Local
141+
verification (one-time, by anyone who wants `--dynamic` linkage) is:
142+
143+
```bash
144+
just chapel-pic-from-source
145+
# Confirm:
146+
find ~/.cache/echidna/chapel-pic/chapel-2.8.0/lib -name 'lib_pic-pic' -type d
147+
# Expected output: at least one path containing `lib_pic-pic/`.
148+
```

docs/decisions/2026-05-30-chapel-rehabilitation.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ Wave 1 (this PR, echidna only):
106106
- `Justfile` recipes: `chapel-build`, `chapel-smoke`, `chapel-test`.
107107

108108
Wave 2 (separate PR, after Wave 1 green-on-main for ≥7 days):
109-
- Rebuild Chapel CI image with `CHPL_LIB_PIC=pic` runtime; revert
109+
- Rebuild Chapel from source with `CHPL_LIB_PIC=pic` runtime; revert
110110
the metalayer build to `--dynamic`; flip `rust-chapel-real` to
111-
strict.
111+
strict. Recipe + tradeoffs:
112+
[2026-05-30-chapel-pic-rebuild.md](./2026-05-30-chapel-pic-rebuild.md).
113+
CI flip deferred — the ~30 min source build dominates the chapel-ci
114+
budget; a registry-pushed container image is the L2.5 prerequisite.
112115
- Add the cancel-token thread through `tryProver` and switch the
113116
Chapel-side default to the speculative search.
114117
- Wire the `proven` and `docudactyl` parallel-dispatch tracks

0 commit comments

Comments
 (0)