% GHCup % Julian Ospald % June 07, 2024 # Introduction ## What is GHCup? ![](what.jpg) ## State of 2019 ::: incremental * stack is the only "Haskell Installer" * no unified alternative for cabal users * distro packages, nix, manual installs, ... * 🤮 ::: ## How it started ::: incremental * 🤹 small team at work (Capital Match), using different platforms - originally used stack - cabal distro packages constantly out of date * 🦾 first version was 165 LOC - Posix shell * ![](linux.png){#id .class height=32px} only supported linux and mac * ![](rust.png){#id .class height=32px} inspired by **rustup** ::: ## GHCup today [Haskell Survey 2022](https://taylor.fausak.me/2022/11/18/haskell-survey-results/#s2q1): ![](survey.png) . . . - over **17k** LOC Haskell - supports all platforms: Linux, Windows, macOS, FreeBSD ## GHCup and haskell.org The default installer: ![](downloads.png) . . . * provides infrastructure (home page, downloads) ## GHCup and the Haskell Foundation ::: incremental * affiliation: [https://haskell.foundation/affiliates/](https://haskell.foundation/affiliates/) * pays for CI * pays consultants to assist the project ::: # Philosophy ## What is a good installer? ::: incremental * it installs (just that) - everywhere * intuitive interface - `ghcup install ghc` * good documentation - but you shouldn't have to read it * get out of the way - has no self-fulfilling purpose ::: ## Goals and principles ::: incremental - ☮️ unification - unix principles - 🛠️ do one thing and do it well - ⚗️ pipes, compose stdout and stdin (re-usable) - 🎁 good re-distribution - 🛣️ user experience ::: ## What is GHCup (simplified)? ```sh curl -s -L 'https://downloads.haskell.org/~ghc/9.6.5/ghc-9.6.5-x86_64-fedora33-linux.tar.xz' | tar -xJ -C /tmp && cd /tmp/ghc-9.6.5-x86_64-unknown-linux/ && ./configure --prefix="$HOME/.local" && make install && rm -rf /tmp/ghc-9.6.5-x86_64-unknown-linux/ ``` ## What is GHCup really? * [https://hasufell.github.io/posts/2023-11-14-ghcup-is-not-an-installer.html](https://hasufell.github.io/posts/2023-11-14-ghcup-is-not-an-installer.html) ::: incremental * ![](open-box.png){#id .class width=32 height=32px} installer * ![](debian.png){#id .class width=32 height=32px} distribution channel * ![](feedback.png){#id .class width=32 height=32px} feedback channel * ![](qa.png){#id .class width=32 height=32px} testing/QA gateway * ![](user.png){#id .class width=32 height=32px} provider of sane defaults (e.g. "recommended" GHC version) * ![](chain-saw.png){#id .class width=32 height=32px} glue for holistic toolchain experience - VSCode, stack, cabal-install integration * ![](ghaction.png){#id .class width=32 height=32px} CI provisioning (e.g. github actions) ::: # Relationships ## Upstream (dependencies) - supported tools - GHC - Cabal - HLS - Stack . . . - decisions that affect us - release frequency - upstream CI - platform support - binary distributions (the `.tar.gz`/`.zip`) ## Downstream (dependents) - ![](haskell_logo.png){#id .class height=32px} Haskell developers - beginners, advanced, students, companies - ![](person.png){#id .class width=32 height=32px} end users (e.g. compiling pandoc from source) - ![](ghaction.png){#id .class width=32 height=32px} GitHub CI - GitHub images, Haskell repos - 🪞 mirrors - [sjtug](https://mirror.sjtu.edu.cn/docs/ghcup) - 🧰 tools - [vscode-haskell](https://github.com/haskell/vscode-haskell), [Haskell playground](https://play.haskell.org/), [nvim-lsp-installer](https://github.com/williamboman/nvim-lsp-installer) # The installer in detail ## How does it work? * **Architectural components** - ![](sh-file.png){#id .class height=32px} bootstrap scripts - ![](exe-file.png){#id .class height=32px} ghcup binary (compiled) - ![](config-file.png){#id .class height=32px} ghcup-metadata . . . * **Logical components** - ![](terminal.png){#id .class height=32px} cli interface - ![](file.png){#id .class height=32px} file layout / installation destination - ![](tar.png){#id .class height=32px} bindist selection - ![](brick-final-clearbg.png){#id .class height=32px} tui interface - ![](config.svg){#id .class height=32px} configuration ## Basic CLI (context) * **Architectural components** - [ ] ![](sh-file.png){#id .class height=32px} bootstrap scripts - [x] ![](exe-file.png){#id .class height=32px} ghcup binary (compiled) - [ ] ![](config-file.png){#id .class height=32px} ghcup-metadata * **Logical components** - [x] ![](terminal.png){#id .class height=32px} cli interface - [ ] ![](file.png){#id .class height=32px} file layout / installation destination - [ ] ![](tar.png){#id .class height=32px} bindist selection - [ ] ![](brick-final-clearbg.png){#id .class height=32px} tui interface - [ ] ![](config.svg){#id .class height=32px} configuration ## Basic CLI ::: incremental * show all available tools / versions ```sh ghcup list ``` * install a tool ```sh ghcup install ghc latest ``` * make a tool version the default ```sh ghcup set ghc latest ``` * remove a tool version ```sh ghcup rm ghc 9.10.1 ``` ::: ## File layout (context) * **Architectural components** - [ ] ![](sh-file.png){#id .class height=32px} bootstrap scripts - [x] ![](exe-file.png){#id .class height=32px} ghcup binary (compiled) - [ ] ![](config-file.png){#id .class height=32px} ghcup-metadata * **Logical components** - [ ] ![](terminal.png){#id .class height=32px} cli interface - [x] ![](file.png){#id .class height=32px} file layout / installation destination - [ ] ![](tar.png){#id .class height=32px} bindist selection - [ ] ![](brick-final-clearbg.png){#id .class height=32px} tui interface - [ ] ![](config.svg){#id .class height=32px} configuration ## File layout ::: incremental * root dir ```sh $ ls ~/.ghcup 📁bin 📁ghc 📁hls 📁cache 📁db 📁logs 📁tmp 🗑️trash 📄config.yaml 📄env ``` * GHC and HLS are installed into sub-directories ```sh $ ls ~/.ghcup/ghc 📁8.10.7 📁9.0.2 📁9.2.8 ``` * bin directory is mostly symbolic links (compare with [update-alternatives](https://man7.org/linux/man-pages/man1/update-alternatives.1.html)) ```sh $ ls ~/.ghcup/bin 📄ghcup 🔗cabal 📄cabal-3.10.3.0 🔗ghc 🔗ghc-8.10 🔗ghc-8.10.7 🔗ghc-9.0 🔗ghc-9.0.2 🔗ghc-9.2 🔗ghc-9.2.8 ``` ::: ## Bindist selection (context) * **Architectural components** - [ ] ![](sh-file.png){#id .class height=32px} bootstrap scripts - [x] ![](exe-file.png){#id .class height=32px} ghcup binary (compiled) - [x] ![](config-file.png){#id .class height=32px} ghcup-metadata * **Logical components** - [ ] ![](terminal.png){#id .class height=32px} cli interface - [ ] ![](file.png){#id .class height=32px} file layout / installation destination - [x] ![](tar.png){#id .class height=32px} bindist selection - [ ] ![](brick-final-clearbg.png){#id .class height=32px} tui interface - [ ] ![](config.svg){#id .class height=32px} configuration ## Bindist selection (sysinfo) **Gather system information:** ::: incremental * module `GHCup.Platform` * `System.Info.arch` and `System.Info.os` (compile-time strings) * distro detection mostly via `/etc/os-release` - specified by [freedesktop.org](https://www.freedesktop.org/software/systemd/man/latest/os-release.html) - using [os-release](https://hackage.haskell.org/package/os-release) package for parsing ::: ## Bindist selection (mapping) **Map binaries to systems:** ::: incremental - binary compatibility (platform, distro, distro version) - finite set of binaries - statically linked GHC? - static mapping in [ghcup-metadata YAML files](https://github.com/haskell/ghcup-metadata/blob/develop/ghcup-0.0.8.yaml) - Tool -> Version -> Architecture -> Platform/Distro -> binary URL - mapping based on binary compatibility - [https://gitlab.haskell.org/ghc/ghc/-/wikis/platforms/linux](https://gitlab.haskell.org/ghc/ghc/-/wikis/platforms/linux) - alternative dynamic logic (utilizing `ldconfig -p` output) - for stack compatibility - updated on every release - "channels" ::: ## Installation flow ![](install-flow.svg){#id .class height=500px} ## TUI interface (context) * **Architectural components** - [ ] ![](sh-file.png){#id .class height=32px} bootstrap scripts - [x] ![](exe-file.png){#id .class height=32px} ghcup binary (compiled) - [ ] ![](config-file.png){#id .class height=32px} ghcup-metadata * **Logical components** - [ ] ![](terminal.png){#id .class height=32px} cli interface - [ ] ![](file.png){#id .class height=32px} file layout / installation destination - [ ] ![](tar.png){#id .class height=32px} bindist selection - [x] ![](brick-final-clearbg.png){#id .class height=32px} tui interface - [ ] ![](config.svg){#id .class height=32px} configuration ## TUI interface ![](matrix.png){#id .class height=550px} ## Configuration (context) * **Architectural components** - [X] ![](sh-file.png){#id .class height=32px} bootstrap scripts - [x] ![](exe-file.png){#id .class height=32px} ghcup binary (compiled) - [x] ![](config-file.png){#id .class height=32px} ghcup-metadata * **Logical components** - [ ] ![](terminal.png){#id .class height=32px} cli interface - [ ] ![](file.png){#id .class height=32px} file layout / installation destination - [ ] ![](tar.png){#id .class height=32px} bindist selection - [ ] ![](brick-final-clearbg.png){#id .class height=32px} tui interface - [x] ![](config.svg){#id .class height=32px} configuration ## Configuration ::: incremental * Types of configuration: - environment variables - cli switches / TUI options - config file (`~/.ghcup/config.yaml`) * e.g. - env: `GHCUP_INSTALL_BASE_PREFIX` (default `$HOME`) - cli: `ghcup install ghc -u ` - config: `url-source` (selecting and mixing "channels") - file/http/https URI - `GHCupURL` - `StackSetupURL` ::: ## Phew ![](headache.png){#id .class height=550px} # Distribution ## What is distribution? * QA gate between "upstream" and "downstream" * trust relationship (with upstream and downstream) * release selection and defaults * binary distribution ## What happens on e.g. a GHC release - testing and inspection of bindists - manual - automated at [github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata/blob/develop/.github/workflows/bindists.yaml) - fixing bindists - building additional bindists (e.g. FreeBSD) . . . - forks at [github.com/stable-haskell](https://github.com/stable-haskell) - builds cabal, stack and HLS binaries - GHC TBD ## The "recommended" versions (defaults) * community adoption (both industry and "open source") * stack LTS * HLS support * known issues and regressions * GHC HQs opinion and support window * must be consistent across platforms # Contributing ## How to contribute ::: incremental - loose project structure (BDFL) - discuss early - repositories - [github.com/haskell/ghcup-hs](https://github.com/haskell/ghcup-hs): documentation, bootstrap-scripts, ghcup exe - [github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata): bindist mapping (YAML), bindist testing - build with cabal/stack - communication - [#haskell-ghcup](https://kiwiirc.com/nextclient/irc.libera.chat/?nick=Guest%7C?##haskell-ghcup) on libera IRC - [#ghcup:matrix.org](https://matrix.to/#/#ghcup:matrix.org) (bridged with IRC) - [github issues](https://github.com/haskell/ghcup-hs/issues) - [haskell discourse](https://discourse.haskell.org/) (interacting with the community) ::: ## Getting started Head over to [https://github.com/haskell/ghcup-hs/issues/1074](https://github.com/haskell/ghcup-hs/issues/1074) (`zurihac` labeled issue). ## Advanced topics - bootstrap scripts - windows and MSYS2 - security - HLS/GHC interaction - reproducibility - stack integration - bindist work (curation) - isolated installs - mirrors