feat: add Haskell build toolchain (GHC, Cabal, Stack) - #178
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds BuildSpec entries and build/install scripts for ghc, cabal, and stack, and introduces project harnesses for cabal and stack with project-file matching predicates. ChangesBuild specs and build scripts (ghc, cabal, stack)
Project harnesses
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/ghc/build.sh (1)
14-14: ⚡ Quick winQuote command substitution to prevent word splitting.
Shellcheck flags that
$(nproc)should be quoted to prevent potential word splitting issues.♻️ Proposed fix
-./hadrian/build -j$(nproc) --flavour=quickest +./hadrian/build -j"$(nproc)" --flavour=quickest🤖 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/build.sh` at line 14, The build script calls ./hadrian/build -j$(nproc) which ShellCheck warns can suffer word splitting; update the invocation in build.sh to quote the command substitution (use -j"$(nproc)") so the processor count is treated as a single word and avoid potential splitting issues when invoking hadrian/build.
🤖 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/cabal/build.sh`:
- Line 2: The script currently starts with "set -ex"; replace that with "set
-euo pipefail" to enforce safer error handling (fail-on-error, undefined-vars,
and pipe failures). Update the top of the build.sh file (where "set -ex"
appears) to use "set -euo pipefail" and, if you still need command tracing, add
"-x" separately (e.g., export an extra DEBUG flag) rather than keeping "set
-ex".
In `@packages/ghc/build.sh`:
- Line 2: Replace the fragile "set -ex" at the top of the build script with the
safer strict mode: change the invocation on the line containing "set -ex" in
build.sh to use "set -euo pipefail" (optionally preserve debugging by adding -x
as "set -euo pipefail -x" if you still want trace output); this enables -e, -u
and pipefail to catch unset vars and pipeline failures per the coding
guidelines.
- Around line 8-11: Remove the unsupported configure flag
`--with-integer-simple` from the configure invocation in build.sh (the lines
that call ./configure with --disable-split-objs, --enable-shared, etc.);
instead, document or set the integer backend via the build system by setting
BIGNUM_BACKEND in mk/build.mk (for example BIGNUM_BACKEND=native or
BIGNUM_BACKEND=gmp) if a specific backend is required.
In `@packages/stack/build.sh`:
- Line 2: Replace the current shell options declaration "set -ex" in build.sh
with the safer strict mode "set -euo pipefail"; update any logic that relied on
"-x" (debug tracing) if necessary or re-enable tracing separately (e.g., via an
explicit debug flag), and ensure the change is applied at the top of the script
(affecting the existing set -ex line).
---
Nitpick comments:
In `@packages/ghc/build.sh`:
- Line 14: The build script calls ./hadrian/build -j$(nproc) which ShellCheck
warns can suffer word splitting; update the invocation in build.sh to quote the
command substitution (use -j"$(nproc)") so the processor count is treated as a
single word and avoid potential splitting issues when invoking hadrian/build.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6f93efa9-73c8-45a0-91ca-267d310cee7f
📒 Files selected for processing (8)
harnesses/cabal/harness.nclharnesses/stack/harness.nclpackages/cabal/build.nclpackages/cabal/build.shpackages/ghc/build.nclpackages/ghc/build.shpackages/stack/build.nclpackages/stack/build.sh
|
Looked at the hard-link error — turns out it's not a problem with your PR, but with how the orchestration extracts tarballs when Why the build failsGHC 9.10.1's source tarball uses tar hardlink entries to share The link target stored in each hardlink entry is the un-stripped path ( Workaround: do the extraction in build.sh (matches glibc / linux_headers / binutils / gcc)Most low-level toolchain packages already sidestep this by taking delivery of the raw {
url = "gs://minimal-staging-archives/linux-%{version}.tar.xz",
sha256 = "..."
# no extract = true, no strip_prefix
} | Source,And the build.sh for those packages opens with For your PR, the change is roughly: packages/ghc/build.ncl — drop {
url = "https://downloads.haskell.org/~ghc/%{version}/ghc-%{version}-src.tar.xz",
sha256 = "bf386a302d4ee054791ffd51748900f15d71760fd199157922d120cc1f89e2f7",
} | Source,packages/ghc/build.sh — extract first: #!/bin/sh
set -ex
tar xJf ghc-${version}-src.tar.xz
cd ghc-${version}
./boot
./configure ...(You'll need to thread This pattern is what glibc, linux_headers, binutils, and gcc all use — it uses the real Optional: keep
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/cabal/build.sh`:
- Around line 9-11: The current three cp glob lines in build.sh are brittle and
should be replaced with a reliable lookup using cabal itself: run `cabal
list-bin exe:cabal` (or `cabal v2-exec --which cabal` depending on environment)
to obtain the actual built executable path, capture that output into a variable,
verify it exists, and then copy it into "$OUTPUT_DIR"/usr/bin/; update the
script around the existing cp invocations in build.sh to call the lookup
command, check for failure, and fall back to error out instead of relying on the
dist-newstyle globs.
In `@packages/ghc/build.sh`:
- Around line 1-2: The script's shebang uses /bin/sh but relies on bash-only
'pipefail' via "set -euo pipefail"; change the shebang line in build.sh from
"#!/bin/sh" to "#!/usr/bin/env bash" so the shell supports 'pipefail' and keep
the existing "set -euo pipefail" line intact.
In `@packages/stack/build.sh`:
- Around line 9-11: Replace the fragile dist-newstyle glob copy with a stable
Cabal invocation: run `cabal list-bin exe:stack` to locate the built stack
binary, then copy that path to "$OUTPUT_DIR"/usr/bin/stack; ensure the script
checks the command exit status and fails with a clear error if `cabal list-bin`
doesn’t return a path. Update the build script lines referencing the old
`dist-newstyle` globs (and `cp -v ... "$OUTPUT_DIR"/usr/bin/`) to use the `cabal
list-bin exe:stack` output, verify the destination directory exists, and
preserve executable permissions.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a9082416-7860-42f0-8bb1-63bef55a0d86
📒 Files selected for processing (4)
packages/cabal/build.shpackages/ghc/build.nclpackages/ghc/build.shpackages/stack/build.sh
|
Good progress — the extract-in-build.sh refactor is working, the strip-prefix error is behind us. The new error is unrelated and easy to fix. The bug
FixDrop the cd ghc-$MINIMAL_ARG_VERSION
# Remove nofib benchmark suite before any build steps
rm -rf nofib
-# Generate configure script
-./boot
-
# Configure GHC
./configure --prefix=/usr \
--disable-split-objs \
--enable-shared \
--disable-tests \Heads-up on a separate sharp edgeThe ./configure --prefix=/usr \
--disable-split-objs \
--enable-shared \
- --disable-tests \
+ --disable-testsAlso: |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/ghc/build.sh (1)
6-7: ⚡ Quick winQuote path arguments derived from env vars.
Please quote
MINIMAL_ARG_VERSION/OUTPUT_DIRexpansions to prevent accidental word-splitting and globbing in shell path arguments.Suggested patch
-tar -xof ghc-$MINIMAL_ARG_VERSION-src.tar.xz -cd ghc-$MINIMAL_ARG_VERSION +tar -xof "ghc-${MINIMAL_ARG_VERSION}-src.tar.xz" +cd "ghc-${MINIMAL_ARG_VERSION}" @@ -./hadrian/build install --root=$OUTPUT_DIR +./hadrian/build install --root="$OUTPUT_DIR"Also applies to: 19-19
🤖 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/build.sh` around lines 6 - 7, The shell script uses unquoted environment variable expansions (e.g., MINIMAL_ARG_VERSION and OUTPUT_DIR) which can cause word-splitting and globbing; update the references in build.sh (notably the tar and cd commands: tar -xof ghc-$MINIMAL_ARG_VERSION-src.tar.xz and cd ghc-$MINIMAL_ARG_VERSION) to use quoted expansions (e.g., "ghc-$MINIMAL_ARG_VERSION-src.tar.xz" and "ghc-$MINIMAL_ARG_VERSION"), and apply the same quoting pattern wherever OUTPUT_DIR or MINIMAL_ARG_VERSION is expanded in the script.
🤖 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.
Nitpick comments:
In `@packages/ghc/build.sh`:
- Around line 6-7: The shell script uses unquoted environment variable
expansions (e.g., MINIMAL_ARG_VERSION and OUTPUT_DIR) which can cause
word-splitting and globbing; update the references in build.sh (notably the tar
and cd commands: tar -xof ghc-$MINIMAL_ARG_VERSION-src.tar.xz and cd
ghc-$MINIMAL_ARG_VERSION) to use quoted expansions (e.g.,
"ghc-$MINIMAL_ARG_VERSION-src.tar.xz" and "ghc-$MINIMAL_ARG_VERSION"), and apply
the same quoting pattern wherever OUTPUT_DIR or MINIMAL_ARG_VERSION is expanded
in the script.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bf98bae5-2c10-45f7-bdb2-d666a0c85e72
📒 Files selected for processing (4)
packages/cabal/build.shpackages/ghc/build.nclpackages/ghc/build.shpackages/stack/build.sh
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/cabal/build.sh
- packages/ghc/build.ncl
- packages/stack/build.sh
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/build.sh`:
- Line 17: The hadrian build invocation is using invalid legacy flags; remove
the unsupported CLI options `--disable-split-objs` and `--enable-shared` from
the ./hadrian/build call and keep just the valid flags (e.g., `-j$(nproc)` and
`--flavour=quickest`), or if you need explicit split-object or shared-library
control, adjust the flavour in src/Settings/User.hs or use `--split-objects`/a
custom flavour instead; update the line that invokes ./hadrian/build
accordingly.
- Around line 5-8: The script currently uses the literal placeholder
`%{version}` in the tar and cd commands which won't be expanded at runtime;
update the tar extraction and directory navigation to use the build-args
environment variable MINIMAL_ARG_VERSION (e.g. replace ghc-%{version}-src.tar.xz
and ghc-%{version} with names built from MINIMAL_ARG_VERSION) so the tar command
and cd operate on the actual version passed via build_args; ensure you reference
MINIMAL_ARG_VERSION consistently and quote the derived filenames where
appropriate to handle any special characters.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 28b47621-b185-41b8-96aa-6105b252092d
📒 Files selected for processing (2)
packages/ghc/build.nclpackages/ghc/build.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/ghc/build.ncl
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@harnesses/cabal/harness.ncl`:
- Around line 9-14: matches_project_if_any currently only detects projects with
file_regexes."cabal.project" and misses single-package Cabal projects that only
have a *.cabal file; add a second predicate object to matches_project_if_any
with file_regexes."*.cabal" = "*" so the harness also matches projects
containing any .cabal file (update the matches_project_if_any list to include
both the existing file_regexes."cabal.project" and the new
file_regexes."*.cabal" predicate).
In `@packages/ghc/build.sh`:
- Around line 5-8: Update the tar invocation and directory change to quote
variable expansions and use modern tar flags: change the tar line that uses
ghc-$MINIMAL_ARG_VERSION-src.tar.xz to use tar -xf
"ghc-${MINIMAL_ARG_VERSION}-src.tar.xz" (or keep -o if no-same-owner semantics
are required) and update the cd to cd "ghc-${MINIMAL_ARG_VERSION}" so both the
archive path and extracted directory are quoted to prevent word-splitting;
ensure every occurrence of MINIMAL_ARG_VERSION in this script is quoted
similarly.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5f713155-b6bb-4aea-9f9d-c95ab6a473ed
📒 Files selected for processing (5)
harnesses/cabal/harness.nclharnesses/stack/harness.nclpackages/cabal/build.shpackages/ghc/build.shpackages/stack/build.sh
7e45e67 to
c793ab2
Compare
|
seems like there's a bug that I found somehow, talked with @twitchyliquid64 about it and he was able to repro |
9207710 to
c793ab2
Compare
Add GHC 9.10.1 package with source build support and prebuilt fallbacks for Amd64 and Arm64 architectures. Add Cabal 3.16.1.0 package for building Haskell projects with cabal-build. Add Stack 3.8.1 package for building Haskell projects with stack. Add cabal harness to detect projects with cabal.project files. Add stack harness to detect projects with stack.yaml files.
- Set extract = true (without strip_prefix) so the source tarball is
auto-extracted. Without strip_prefix, hardlink entries in the tarball
work correctly since paths aren't changed.
- Add cd ghc-%{version} before rm -rf nofib in build.sh, as the script
now runs from the sandbox root, not the extracted directory.
The extract = true setting caused the sandbox setup to fail with ENOENT for build.sh, likely due to hardlink entries in the source tarball. Revert to extract = false with manual tar extraction in build.sh, which avoids the issue entirely.
- ghc/build.sh: Use $MINIMAL_ARG_VERSION instead of NCL interpolation
(%{version}) which doesn't work in shell scripts
- ghc/build.sh: Remove --disable-split-objs --enable-shared from hadrian
command (not valid hadrian CLI flags)
- cabal/build.sh: Use cabal list-bin instead of hardcoded dist-newstyle paths
- stack/build.sh: Use cabal list-bin instead of hardcoded dist-newstyle paths
c793ab2 to
c5701d9
Compare
The hadrian install command was writing directly to /usr which is read-only in the sandbox. The bindist Makefile supports DESTDIR as a staging prefix, so prefixing the command with DESTDIR=$OUTPUT_DIR redirects all installs to the staging directory while keeping --prefix=/usr for correct runtime paths.
Cabal is needed by hadrian bootstrap and ./configure, but the cabal package depends on ghc, creating a circular dependency. Build cabal-install 3.10.3.0 from source using the bootstrap GHC, following the same pattern as alex and happy.
Cabal is needed by ./configure (AC_PATH_PROG) and hadrian bootstrap. Building cabal-install from source failed because the bootstrap GHC lacks its ~20 Haskell library dependencies. Instead, provide a stub binary to satisfy ./configure's existence check. Hadrian's bootstrap.py will download and build the real cabal via internet access.
- Add 'base' to runtime_deps (GHC is dynamically linked against glibc) - Declare needs for dns/internet (hadrian bootstrap downloads cabal) - Add 'Needs' to import line - Add standalone version test - Fix formatting (blank line after version var, typed needs block)
Session Summary: GHC Build Fixes (2026-05-21)ProblemThe Fixes Applied1. Install step writing to read-only
|
The amd64 bootstrap binary (ghc-9.8.2-x86_64) could not run on arm64, causing 'Exec format error' on that architecture. Add the aarch64 bootstrap tarball and detect the target arch at build time using uname -m, selecting the appropriate bootstrap binary and library path. Both amd64 and arm64 bootstrap binaries are available from the official GHC downloads site for 9.8.2.
The cabal package was failing because it tried to run 'cabal build' but no cabal binary existed in the build environment (chicken-and-egg problem). Fix by using Cabal's upstream bootstrap machinery: - Switch to cabal-install 3.12.1.0 for better GHC 9.10.1 compatibility - Add update_bootstrap_json.py to generate a GHC-accurate bootstrap JSON plan - Update dependency versions in the bootstrap plan for GHC 9.10.1 compatibility - Patch only local monorepo .cabal files (cabal-install, Cabal-syntax) for base bounds - Use python to run the upstream bootstrap/bootstrap.py script Also fix ghc runtime deps: - Add libffi, binutils, and linux_headers so GHC works as a compiler in dependent builds
The stack build script calls 'cabal build' but did not declare cabal as a build dependency, causing the build to fail with 'cabal: command not found'.
…stall The cabal bootstrap was failing because GHC could not find gcc at runtime. Add toolchain (which provides gcc) to ghc's runtime_deps so that any package using GHC as a compiler gets gcc injected into its build environment. Also run ghc-pkg recache after installation to prevent stale package database cache warnings in downstream builds.
Per AGENTS.md, runtime_deps are injected into the build environment, so duplicating them in build_deps is unnecessary. Remove: - gmp from cabal build_deps (present in runtime_deps) - binutils from ghc build_deps (present in runtime_deps) - toolchain from ghc build_deps (present in runtime_deps)
The stack source pins ghc-9.10.3 in cabal.project and uses cabal.config to pin exact dependency versions matching GHC 9.10.3. Our GHC package provides 9.10.1, causing cabal to fail with 'ghc-9.10.3 not found'. Fix by: - Patching cabal.project to use ghc-9.10.1 - Removing cabal.config which pins 229 exact dependency versions for GHC 9.10.3, letting cabal resolve compatible versions for 9.10.1 - Running cabal update before cabal build to fetch package index
Bump GHC to 9.10.3 to resolve Cabal version conflict with stack-3.9.3, which requires Cabal >=3.14 && <3.18. GHC 9.10.1 ships with Cabal 3.12, causing an unresolvable dependency conflict. Also revert stack/build.sh GHC version patch (no longer needed as GHC now matches the version stack expects natively).
Summary of Fixes for GHC, Cabal, and Stack Build FailuresI have successfully resolved the build failures for the Haskell toolchain ( Here is a summary of what was changed and why: 1. GHC (
|
Summary
Add Haskell build toolchain support with GHC, Cabal, and Stack packages along with corresponding harnesses for automatic project detection.
Context
The Minimal project previously had no support for Haskell projects. This PR adds the complete Haskell toolchain, enabling building of Haskell projects using either Cabal or Stack build systems.
Changes
packages/ghc/): Glasgow Haskell Compiler package with source build support and bootstrapping via official prebuilt binaries.packages/cabal/): Haskell build tool for projects usingcabal-build.packages/stack/): Haskell project manager and build system.harnesses/cabal/): Auto-detects Haskell projects withcabal.projector single-package*.cabalfiles.harnesses/stack/): Auto-detects Haskell projects withstack.yamlfiles.Key Implementation Details
build.sh. This completely replaces the experimentalreplace_on_cycle/prebuilt = truemechanism which was failing in sandbox staging withENOENTdue to complex symlinks/hardlinks in GHC's official binary distribution.make install_bin install_lib update_package_db) to a local prefix.alex(lexical analyzer) andhappy(parser generator) from source using the bootstrap GHC.needsblock as GHC builds fully offline in the sandbox.docsoutput block since--docs=noneis passed to Hadrian.source_provenancemetadata as the GitHub repository is an unofficial mirror of GitLab, conforming to strict repository standards.cabal.projectconfigurations and single-package*.caballayouts.set -euo pipefail/set -euo pipefail) and follow the repo's conventions.Use Cases
cabal.projector*.cabalpresent).stack.yamlpresent).Testing