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
exeSymLinkedis used forghcup 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.