Haskell ghcup

ID

ghcup

Home page

https://www.haskell.org/ghcup/

Version requirement

>= 0.2.1

Platforms

🐧 Linux · 🍎 macOS · 🪟 Windows

Operations

installed · search · install · remove · sync · cleanup

purl types

pkg:ghcup

CLI name

ghcup

Forced environment

GHCUP_SKIP_UPDATE_CHECK=1 · NO_COLOR=1

Issues and PRs

📦 manager: ghcup

Source

meta_package_manager/managers/ghcup.py

Haskell toolchain installer, covering GHC and the tools built around it.

ghcup installs several kinds of tool side by side: GHC itself, plus cabal, hls, stack and whatever else its metadata offers. All of them are packages here, because ghcup list reports every kind in one flat listing whose every line names its own kind, and because install, rm, set and whereis all take the same <tool> <version> pair. Reporting only GHC versions would hide from the inventory tools mpm remains perfectly able to install and remove.

A package is therefore identified as <tool>-<version>, and split back on its first hyphen to rebuild the pair. First rather than last is load-bearing: a cross-compiling GHC renders its target into the version cell, so ghc-aarch64-unknown-linux-gnu-9.4.8 has to split into ghc and aarch64-unknown-linux-gnu-9.4.8, and that remainder is exactly the token ghcup’s own version parser accepts. No tool name contains a hyphen today.

Note

Every listing forces --show-revisions none. ghcup otherwise appends a -rN metadata-revision suffix to versions that have one, and that suffixed string is not a version ghcup rm will match: the inventory would then report packages that cannot be removed. The suffix appears only while a revision is pending, so the corruption is intermittent, which is worse than a consistent one.

Caution

Neither upgrade operation is declared, and neither is an oversight. ghcup upgrade upgrades the ghcup binary itself, not the tools it installs, so mapping it onto upgrade --all would replace the user’s package manager when they asked to upgrade their packages. And ghcup has no in-place upgrade for a tool at all: a newer GHC is a fresh side-by-side install that leaves the old one in place, which is what install already does.

Note

No outdated either, for a reason that follows from the package identity above rather than from any missing command. Since the version is part of the id, a package having a newer version is a contradiction: the newer version is a different package, installed alongside rather than over. A report pairing ghc-9.6.7 with a latest of 9.10.1 would also name an upgrade mpm cannot perform, both upgrade operations being absent. ghcup’s own new-version notice is no help here: it is prose on stderr, it deduplicates itself against a cache file so a second run prints nothing, and it fires as a side effect of unrelated commands.

Documentation: ghcup user guide.

What mpm adds to ghcup

Through mpm, ghcup gains --exact and --extended search, to narrow to exact names or match descriptions.

Bigger still, mpm reaches across every manager at once: mpm installed and mpm outdated cover ghcup alongside every other manager you run in one table, mpm upgrade --all updates them together, and mpm sbom exports the whole machine as one bill of materials.

Every mpm command also gains --dry-run and --plan previews, cross-scheme version comparison and purl identifiers. See manager augmentations for how each one is built.

Your ghcup commands, in mpm

You already know ghcup: each operation maps one-to-one onto mpm, in an interface shared by every manager.

To…

With ghcup

With mpm

List what’s installed

ghcup list --raw-format --show-revisions none --show-criteria installed

mpm --ghcup installed

Search for a package

ghcup list --raw-format --show-revisions none

mpm --ghcup search none

Install a package

ghcup install ghc 9.6.7

mpm install pkg:ghcup/ghc

Remove a package

ghcup rm ghc 9.6.7

mpm remove pkg:ghcup/ghc

Clear caches

ghcup gc --cache --tmpdirs

mpm --ghcup cleanup --cache

Prefix any command above with --dry-run to simulate the underlying manager calls without touching the system: the safe way to watch what mpm would do before trusting it.

Operations

Operation

Supported

Notes

installed

outdated

orphans

search

exact and extended search backfilled by mpm

install

upgrade

upgrade_all

remove

sync

cleanup

doctor

Selecting and configuring ghcup

Deselect ghcup for a single run with --no-ghcup, or persist the choice in your configuration:

[mpm]
ghcup = false

The arguments and environment variables listed in the box atop this page are forced on every ghcup call, so runs stay quiet, non-interactive and reproducible: the defaults you would set in CI anyway.

Keep it enabled but tune how mpm drives it with a per-manager override:

[mpm.managers.ghcup]
timeout = 900

mpm config-template ghcup prints every overridable attribute as a ready-to-paste block.

Recipes

A few jobs you would otherwise script around ghcup, one mpm command each:

  • Snapshot and clone a machine: mpm --ghcup dump ghcup.toml, then mpm restore ghcup.toml on the next one.

  • Export a compliance SBOM: mpm --ghcup sbom (CycloneDX by default, --spdx for SPDX).

Privilege escalation

mpm runs this manager as the current user and never prepends sudo by default. Flip the policy for its privileged operations with --sudo or the per-manager sudo override.

See privilege escalation for the full policy.

Cooldown

State of Haskell ghcup’s release-age gating, from the cooldown support table:

Status: ❌ None

A cooldown only pays off where a compromised release can be withdrawn while the clock runs, and can only be emulated where the registry dates its releases. From the retraction table:

  • Registry: Haskell toolchain bindists

  • Retraction: None published: bindists are static artifacts named by ghcup’s own metadata file, which carries no yank or unlist surface; withdrawing a release means the Haskell.org release team editing the metadata that describes it

  • Publish date: ✅ the metadata records a release day per version, which ghcup list renders in its notes column

With --cooldown set, mpm skips this manager’s install and upgrade operations rather than run them unguarded (fail-closed); --allow-unsupported-managers opts back in.

Version probe

The version is probed by running:

$ ghcup --numeric-version
0.2.6.2

and extracted with:

r"^(?P<version>\d+(?:\.\d+)+)"

Reference traces

Raw native outputs captured in the manager source: the reference mpm’s parsers were written against. If you know Haskell ghcup well and a transcript below looks wrong, or a newer release changed its output format, report it.

$ ghcup list --raw-format --show-revisions none --show-criteria installed
cabal 3.14.2.0 recommended
ghc 9.6.7 recommended,base-4.18.3.0
ghcup 0.2.6.2  stray

Feed any of these through mpm and the raw output becomes one uniform table, the same shape for every manager: filter it, project columns, or export it (mpm --ghcup installed --output json, or csv, toml, yaml), each package carrying a purl and a version comparable across managers.

Changelog

  • 7.7.0.dev0 (unreleased)

    • Add the Haskell ghcup toolchain installer with installed, search, install, remove, sync and cleanup support. Every tool it installs is a package, keyed as <tool>-<version> and split back on the first hyphen so cross-compiling GHCs survive. It declares neither upgrade operation: ghcup upgrade replaces the ghcup binary rather than the installed tools, and no in-place tool upgrade exists.