External Publication
Visit Post

[ANN] GHCup 0.2.0.0 RC (or beta, maybe)

Haskell Community [Unofficial] April 14, 2026
Source

This is maybe more of a BETA than a RELEASE CANDIDATE , but anyway.

With support from IOG, I have implemented some form of Intaller DSL · Issue #141 · haskell/ghcup-hs · GitHub

It’s not really a DSL though, but rather an “install specification”. That means GHCup can now install arbitrary tools. This moved us a bit on the “installer vs package manager” design spectrum. But it’s still an installer (for several reasons).

How to test

You may want to back up your ~/.ghcup directory, especially if you have compiled GHCs from source. But I encourage people to test this RC on their existing installation, since that may expose more bugs.

Run this (even with a pre-existing installation):

curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/haskell/ghcup-hs/refs/heads/installer-dsl2/scripts/bootstrap/bootstrap-haskell | BOOTSTRAP_HASKELL_NONINTERACTIVE=yes BOOTSTRAP_HASKELL_MINIMAL=yes sh

Then make sure you’re not using the vanilla channel. If in doubt, edit ~/.ghcup/config.yaml and ensure that you have this configuration:

url-source:
- GHCupURL

And now fire away:

ghcup install shellcheck --set latest
ghcup install ormolu     --set latest
ghcup install pandoc     --set latest
ghcup install hlint      --set latest

What’s the point?

One major point is to be able to install alternative compilers and GHC forks. It just so happens that this is the easiest approach.

How do I package tool XY?

The spec for hlint is as follows:

  hlint:
    3.10:
      viTags:
        - Latest
        - Recommended
      viArch:
        A_64:
          Linux_UnknownLinux:
            unknown_versioning:
              dlUri: https://github.com/ndmitchell/hlint/releases/download/v3.10/hlint-3.10-x86_64-linux.tar.gz
              dlHash: ccabc8802a58154699a3583b8dddc5ea2e6d65753a62c45c0e80088ebb16b42b
              dlSubdir: hlint-3.10
              dlInstallSpec
                exeRules:
                  - installSource: "hlint"
                    installDest: "bin/hlint"
                dataRules:
                  - installPattern: ["data/**"]
                exeSymLinked:
                 - linkName: "hlint-${PKGVER}" # the versioned binary
                   setName: "hlint"            # for 'ghcup set' only
                   target: "bin/hlint"         # linkName and setName both point to ~/.ghcup/hlint/3.10/bin/hlint
                   pVPMajorLinks: false        # whether to create tool-X.Y symlinks (useful for GHC)

This installs some executables and some data files and exeSymLinked tells ghcup how to expose binaries in ~/.ghcup/bin.

We can also execute configure and make, e.g. for something like GHC the dlInstallSpec would look like:

dlInstallSpec:
  exeRules: []
  dataRules: []
  configure:
    configFile: configure
    configArgs:
    - "--prefix=${PREFIX}"
  make:
    makeArgs:
    - DESTDIR=${TMPDIR}
    - install
  preserveMtimes: true
  exeSymLinked:
  - target: bin/ghc
    linkName: ghc-${PKGVER}
    pVPMajorLinks: true
    setName: ghc
  - target: bin/ghci
    linkName: ghci-${PKGVER}
    pVPMajorLinks: true
    setName: ghci
# and so forth...

There’s some things that packagers should know:

  • configure script must support --prefix
  • make must support DESTDIR
  • a makefile-only build system is possible too, then the Makefile also needs to support some way to pass the prefix
  • the first element in exeSymLinked is used for ghcup whereis
  • configure and make are not supposed to:
    • download from the internet
    • write anywhere outside of their current working dir and $TMPDIR
    • compile stuff from source

Bubblewrap support

Since users may in the future rely on third-party channels that support new tools, they need a way to protect themselves from buggy configure/Makefiles. This can be achieved like so in ~/.ghcup/config.yaml

build-wrapper:
  cmd: bwrap
  cmdArgs: [ "--ro-bind" , "/" , "/"
           , "--bind" , "/home/hasufell/.ghcup" , "/home/hasufell/.ghcup"
           , "--bind" , "/home/hasufell/.cabal" , "/home/hasufell/.cabal"
           , "--dev" , "/dev"
           , "--proc" , "/proc"
           , "--tmpfs" , "/tmp"
           ]

The .cabal bind mount is necessary for ghcup compile hls. Please adjust the paths to your own configuration. If you don’t compile hls from source, you can also specify --unshare-all.

Discussion in the ATmosphere

Loading comments...