Skip to content

Simplify GHC packaging by extracting bootstrap and build tools#203

Open
0chroma wants to merge 3 commits into
mainfrom
feature/simplify-ghc-cabal
Open

Simplify GHC packaging by extracting bootstrap and build tools#203
0chroma wants to merge 3 commits into
mainfrom
feature/simplify-ghc-cabal

Conversation

@0chroma
Copy link
Copy Markdown
Contributor

@0chroma 0chroma commented May 28, 2026

Summary

This PR significantly simplifies the ghc package by extracting its inline bootstrap compiler and Haskell-based build tools (alex and happy) into their own dedicated, reusable packages.

Changes

  1. Created ghc-bootstrap (packages/ghc-bootstrap/build.ncl, packages/ghc-bootstrap/build.sh):
    • Packages the prebuilt GHC 9.8.1 binary for both x86_64 and aarch64 architectures.
    • Handles extraction, linking of libtinfo.so.6, and linker configuration in an isolated way.
  2. Created alex (packages/alex/build.ncl, packages/alex/build.sh):
    • Standalone package for the alex lexical analyzer generator, built from source using ghc-bootstrap.
  3. Created happy (packages/happy/build.ncl, packages/happy/build.sh):
    • Standalone package for the happy parser generator, built from source using ghc-bootstrap.
  4. Simplified ghc (packages/ghc/build.ncl, packages/ghc/build.sh):
    • Removed inline downloading and extraction of GHC 9.8.1 bootstrap binaries.
    • Removed inline source compilation of alex and happy.
    • Shrunk packages/ghc/build.sh from 130 lines to 63 lines.
    • The main ghc package now simply depends on ghc-bootstrap, alex, and happy as standard build dependencies.

Verification

  • Run min check --packages ghc-bootstrap,alex,happy,ghc to verify Nickel schemas and formatting.
  • Run min patched-build ghc-bootstrap to verify bootstrap compiler extraction and linking.
  • Run min patched-build alex and min patched-build happy to verify they compile successfully using ghc-bootstrap.

Summary by CodeRabbit

  • New Features

    • Added build specs to provide alex, happy, and a ghc-bootstrap toolchain so the toolchain and utilities are available as local build artifacts.
  • Refactor

    • GHC build now integrates local tooling instead of relying on remote prebuilt archives and uses a simplified bootstrap flow with the system toolchain.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

This PR extracts alex, happy, and ghc-bootstrap as local build packages with Nickel specs and Bash build scripts, integrates them into the packages/ghc build_deps, and refactors the GHC bootstrap script to use the available ghc from PATH with stubbed tools.

Changes

GHC Toolchain Refactor

Layer / File(s) Summary
alex BuildSpec and script
packages/alex/build.ncl, packages/alex/build.sh
Adds alex v3.5.1.0 build spec (Hackage source, sha256, build/runtime deps, output usr/bin/alex) and a Bash build script that compiles/uses Cabal Setup and stages outputs.
happy BuildSpec and script
packages/happy/build.ncl, packages/happy/build.sh
Adds happy v1.20.1.1 build spec (Hackage source, sha256, build/runtime deps, output usr/bin/happy) and a Bash build script that compiles/uses Cabal Setup and stages outputs.
ghc-bootstrap BuildSpec and script
packages/ghc-bootstrap/build.ncl, packages/ghc-bootstrap/build.sh
Adds ghc-bootstrap v9.8.1 spec selecting arch-specific tarball (extract=false), installs prebuilt GHC into $OUTPUT_DIR/usr, ensures libtinfo via host curses lib, and patches GHC settings to avoid ld.gold.
GHC build spec imports and dependency wiring
packages/ghc/build.ncl
Imports alex, ghc-bootstrap, and happy as local build packages and replaces prior Hackage/prebuilt GHC entries in build_deps, retaining hadrian-bootstrap-sources and reordering dependencies.
GHC build script refactoring
packages/ghc/build.sh
Creates stub-bin with no-op cabal/sphinx-build, uses ghc from PATH for Hadrian bootstrap, updates LD_LIBRARY_PATH handling for pseudostore libs, passes GHC=ghc to configure, and fixes an unclosed if with fi.

Sequence Diagram(s)

sequenceDiagram
  participant GHC_build_sh as packages/ghc/build.sh
  participant GHC_bootstrap_sh as packages/ghc-bootstrap/build.sh
  participant Alex_build_sh as packages/alex/build.sh
  participant Happy_build_sh as packages/happy/build.sh

  GHC_build_sh->>GHC_bootstrap_sh: extract/install prebuilt GHC -> install into $OUTPUT_DIR/usr
  GHC_build_sh->>Alex_build_sh: invoke alex build (version via build_args)
  GHC_build_sh->>Happy_build_sh: invoke happy build (version via build_args)
  GHC_build_sh->>GHC_bootstrap_sh: copy/fix libtinfo and patch settings
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • twitchyliquid64

"🐰
I hop with a stub and a patch,
swapping tarballs for a local batch,
Alex and Happy spring to life,
bootstrap trimmed of extra strife,
building Haskell with a lighter hat."

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: extracting GHC bootstrap and build tools (alex, happy, ghc-bootstrap) into separate packages and simplifying the main ghc package.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/simplify-ghc-cabal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@0chroma 0chroma enabled auto-merge May 28, 2026 06:28
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/alex/build.sh`:
- Around line 1-12: The build script must change into the extracted, versioned
source directory before building and reference the version variable; update the
top of the script to cd into the directory named by $MINIMAL_ARG_VERSION (e.g.,
cd "${MINIMAL_ARG_VERSION}" or cd "$MINIMAL_ARG_VERSION") before invoking ghc
and the ./Setup commands (Setup.hs, ghc --make Setup.hs, ./Setup
configure/build/copy), ensuring $OUTPUT_DIR is still used for copy destdir.
- Around line 4-10: The build script always compiles "Setup.hs" even when only
"Setup.lhs" exists; update the logic to select the existing setup file and
compile that instead (e.g., determine a SETUP_FILE variable that is "Setup.hs"
if present, otherwise "Setup.lhs", and use that variable in the ghc --make and
./Setup commands). Ensure references to Setup.hs in the compile and run steps
are replaced by this selected setup filename so the path where only Setup.lhs is
provided succeeds.

In `@packages/ghc-bootstrap/build.sh`:
- Around line 27-35: The script hardcodes /usr/lib/libncursesw.so.6 when
creating the libtinfo.so.6 shim; update it to locate the real ncurses library
(search /usr/lib* and /usr/lib64 or use ldconfig -p/grep or gcc
-print-file-name) into a variable (e.g., NCURSES_PATH) and use that variable
when copying to "$OUTPUT_DIR/usr/lib/libtinfo.so.6" and to
"$GHC_LIB_DIR/libtinfo.so.6"; if no library is found, emit a clear error and
exit non‑zero. Ensure you reference OUTPUT_DIR, GHC_LIB_DIR and
MINIMAL_ARG_VERSION and replace the two cp -p /usr/lib/libncursesw.so.6
occurrences with cp -p "$NCURSES_PATH" ... .

In `@packages/ghc/build.sh`:
- Around line 42-46: The current export of LD_LIBRARY_PATH can produce a
trailing colon when LD_LIBRARY_PATH is unset, causing ld.so to treat it as the
current directory; update the export that sets LD_LIBRARY_PATH (the line using
PSEUDO_LIBS and export LD_LIBRARY_PATH) so it only inserts a ':' between
PSEUDO_LIBS and the existing LD_LIBRARY_PATH when LD_LIBRARY_PATH is non-empty
(i.e., append the old value conditionally via parameter expansion or an
if-check), ensuring no trailing empty entry is added.

In `@packages/happy/build.sh`:
- Around line 1-12: The build script doesn't cd into the extracted versioned
source directory nor reference the MINIMAL_ARG_VERSION variable; update
packages/happy/build.sh to first change directory into the extracted source
(e.g. cd "happy-$MINIMAL_ARG_VERSION" or whichever directory name pattern is
used) before running ghc and ./Setup commands, and replace any hardcoded paths
(if present) with references that use $MINIMAL_ARG_VERSION and keep OUTPUT_DIR
usage for copy/destdir; ensure the cd happens before checking/creating Setup.hs
and before running ghc and ./Setup configure/build/copy.
- Around line 4-10: The build script assumes Setup.hs is present when running
GHC; change the compile step to pick the existing setup file dynamically so
Setup.lhs is handled too: after the block that creates Setup.hs only when
neither file exists, invoke GHC with the actual setup filename (use Setup.lhs if
it exists, otherwise Setup.hs) for the "ghc --make" step, then continue to run
the generated ./Setup configure --prefix=/usr; update the reference to the
compiled filename in the subsequent commands to match the chosen source
(Setup.lhs or Setup.hs).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bbb8e42f-b3de-44f1-b199-9d03f99c6dec

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae4553 and cff927a.

📒 Files selected for processing (8)
  • packages/alex/build.ncl
  • packages/alex/build.sh
  • packages/ghc-bootstrap/build.ncl
  • packages/ghc-bootstrap/build.sh
  • packages/ghc/build.ncl
  • packages/ghc/build.sh
  • packages/happy/build.ncl
  • packages/happy/build.sh

Comment thread packages/alex/build.sh
Comment thread packages/alex/build.sh Outdated
Comment thread packages/ghc-bootstrap/build.sh Outdated
Comment thread packages/ghc/build.sh
Comment thread packages/happy/build.sh Outdated
Comment thread packages/happy/build.sh Outdated
@0chroma
Copy link
Copy Markdown
Contributor Author

0chroma commented May 28, 2026

/build

Replace glob pattern usr/bin/* with explicit binary enumeration
for the happy and alex packages to satisfy the enumerate bins checker.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ghc-bootstrap/build.sh`:
- Around line 58-68: The ldconfig pipeline in the NCURSES_PATH fallback can
cause the script to abort under set -euo pipefail; modify the assignment to
LDCONFIG_PATH so pipeline failures don't propagate (e.g. replace the pipeline
with a single awk that matches and exits: ldconfig -p | awk
'/libncursesw.so.6|libncurses.so.6/ {print $NF; exit}' or append "|| true" to
the command substitution), ensuring LDCONFIG_PATH becomes empty on no-match and
lets the subsequent if [ -f "$LDCONFIG_PATH" ] check run; update the block that
sets NCURSES_PATH from LDCONFIG_PATH accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f1f448cc-0e6e-401d-ae99-83d1d19f8c27

📥 Commits

Reviewing files that changed from the base of the PR and between cff927a and 7b17dd4.

📒 Files selected for processing (7)
  • packages/alex/build.ncl
  • packages/alex/build.sh
  • packages/ghc-bootstrap/build.ncl
  • packages/ghc-bootstrap/build.sh
  • packages/ghc/build.sh
  • packages/happy/build.ncl
  • packages/happy/build.sh
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/alex/build.ncl
  • packages/ghc-bootstrap/build.ncl
  • packages/alex/build.sh

Comment on lines +58 to +68
if [ -z "$NCURSES_PATH" ]; then
if command -v ldconfig >/dev/null 2>&1; then
for libname in libncursesw.so.6 libncurses.so.6; do
LDCONFIG_PATH="$(ldconfig -p | grep "$libname" | head -n1 | awk '{print $NF}')"
if [ -f "$LDCONFIG_PATH" ]; then
NCURSES_PATH="$LDCONFIG_PATH"
break
fi
done
fi
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

ldconfig fallback can abort the script under set -euo pipefail, bypassing the clean error path.

The assignment at Line 61 runs a pipeline inside command substitution. Two cases break under set -euo pipefail:

  • No match: grep exits 1, pipefail propagates it, and set -e aborts the script here instead of falling through to the intended error message at Lines 70‑73.
  • Match present: head -n1 closes the pipe early, so grep can receive SIGPIPE (exit 141), again tripping pipefail/set -e even though a path was found.

This makes the last-resort fallback unreliable. Guard the pipeline so a non-zero status yields an empty value and lets control reach the explicit check.

🛡️ Proposed fix
     for libname in libncursesw.so.6 libncurses.so.6; do
-      LDCONFIG_PATH="$(ldconfig -p | grep "$libname" | head -n1 | awk '{print $NF}')"
+      LDCONFIG_PATH="$(ldconfig -p 2>/dev/null | grep -F "$libname" | head -n1 | awk '{print $NF}' || true)"
       if [ -f "$LDCONFIG_PATH" ]; then
         NCURSES_PATH="$LDCONFIG_PATH"
         break
       fi
     done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ghc-bootstrap/build.sh` around lines 58 - 68, The ldconfig pipeline
in the NCURSES_PATH fallback can cause the script to abort under set -euo
pipefail; modify the assignment to LDCONFIG_PATH so pipeline failures don't
propagate (e.g. replace the pipeline with a single awk that matches and exits:
ldconfig -p | awk '/libncursesw.so.6|libncurses.so.6/ {print $NF; exit}' or
append "|| true" to the command substitution), ensuring LDCONFIG_PATH becomes
empty on no-match and lets the subsequent if [ -f "$LDCONFIG_PATH" ] check run;
update the block that sets NCURSES_PATH from LDCONFIG_PATH accordingly.

@0chroma
Copy link
Copy Markdown
Contributor Author

0chroma commented May 30, 2026

/build

auto-merge was automatically disabled May 30, 2026 04:55

Pull request was closed

@bryan-minimal bryan-minimal reopened this May 30, 2026
@bryan-minimal
Copy link
Copy Markdown
Member

Hey @0chroma — good news, this is building now ✅

The minimal build check had gotten stuck on "Awaiting maintainer approval" after a buildbot restart caught it mid-flight (the build had actually already passed). I re-triggered it and it's running fresh now — it'll go green on its own shortly.

Two small things to get it merge-ready once it's green:

  • Rebase on main — the branch is a little behind.
  • It'll need one approving review.

Thanks for the GHC packaging cleanup! 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants