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

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.

28 Likes

Thanks for the new features!

Say I want to package Agda, where could I maintain the yaml data for it?

For something like Agda, I’m willing to maintain that in the ghcup-metadata repo itself.

Can you point me to the proper releases and I will have a look.

1 Like

We publish binaries with our GitHub releases: Release v2.8.0 · agda/agda · GitHub (scroll all the way down to “Assets”).
After putting the binary in the path one can install it by calling agda –setup which extracts some embedded files into its versioned XDG app directory.

Versions before 2.8 do not support this method, so we would probably not add them GHCup.

I’ve added it here: Add Agda · haskell/ghcup-metadata@89d8d01 · GitHub

It should show up in the default channel when using the prerelease.

Once a proper release commences, I will move those tools to separate channels.

7 Likes