From 842090196fbb23dd65299446dad5f9493c835831 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 10:53:06 -0400 Subject: [PATCH 1/9] Add debugger project --- cabal.hdb.project | 15 +++++++++++++++ hie-hdb.yaml | 16 ++++++++++++++++ project-cabal/README.md | 8 ++++++++ 3 files changed, 39 insertions(+) create mode 100644 cabal.hdb.project create mode 100644 hie-hdb.yaml diff --git a/cabal.hdb.project b/cabal.hdb.project new file mode 100644 index 00000000000..f97462cfdc0 --- /dev/null +++ b/cabal.hdb.project @@ -0,0 +1,15 @@ +-- A project for debugging cabal with the Haskell debugger (hdb), used by the +-- hie-hdb.yaml cradle. See the "Using the Haskell debugger" section of +-- CONTRIBUTING.md. +-- +-- This leaves out cabal-testsuite because `cabal repl --with-repl`, that the +-- debugger relies on, needs Cabal >= 3.16 for the setup of every package in the +-- project and the custom setup of cabal-testsuite depends on an older Cabal. +import: project-cabal/ghc-options.config +import: project-cabal/ghc-latest.config +import: project-cabal/pkgs/cabal.config +import: project-cabal/pkgs/install.config +import: project-cabal/pkgs/tests.config +import: project-cabal/constraints.config + +tests: True diff --git a/hie-hdb.yaml b/hie-hdb.yaml new file mode 100644 index 00000000000..7d6e63db939 --- /dev/null +++ b/hie-hdb.yaml @@ -0,0 +1,16 @@ +# A hie-bios cradle for debugging cabal with the Haskell debugger (hdb), see +# the "Using the Haskell debugger" section of CONTRIBUTING.md. +# +# This file is deliberately not named hie.yaml so that HLS does not pick it up. +# Pass it to hdb explicitly, with --cradle-file or "cradleFile". +cradle: + cabal: + cabalProject: cabal.hdb.project + # Breakpoints can only be set in the components loaded here. To debug a + # test suite, add its component, e.g. cabal-install:test:unit-tests. + componentsToLoad: + - cabal-install:exe:cabal + - cabal-install:lib:cabal-install + - cabal-install-solver:lib:cabal-install-solver + - Cabal:lib:Cabal + - Cabal-syntax:lib:Cabal-syntax diff --git a/project-cabal/README.md b/project-cabal/README.md index d12a316e3e4..755747a8b33 100644 --- a/project-cabal/README.md +++ b/project-cabal/README.md @@ -6,6 +6,7 @@ We have these projects, all in the root: $ tree -P '*.project' --prune -L 1 . ├── cabal.bootstrap.project +├── cabal.hdb.project ├── cabal.meta.project ├── cabal.project ├── cabal.release.project @@ -77,11 +78,17 @@ package group. | Project | pkgs | cabal | tests | install | |------------------|:---: |:---: |:---: |:---: | | default | ✓ | | | | +| hdb | | ✓ | ✓ | ✓ | | libonly | | ✓ | ✓ | | | release | | ✓ | ✓ | ✓ | | validate | ✓ | | | | | validate.libonly | | ✓ | ✓ | | +The `hdb` project is for use with the Haskell debugger, see +[CONTRIBUTING.md](../CONTRIBUTING.md#using-the-haskell-debugger). It imports the +same package groups as the `release` project, leaving out `cabal-testsuite` and +the benchmarks. + The `meta` project is a one-liner: ``` @@ -96,6 +103,7 @@ Additional configuration is imported: | Project | ghc-options | ghc-latest | constraints | |------------------|:---: |:---: |:---: | | default | ✓ | ✓ | ✓ | +| hdb | ✓ | ✓ | ✓ | | libonly | ✓ | | | | release | | | | | validate | ✓ | ✓ | ✓ | From ecfe5936dc9e33449adab58c6783df7510b4c483 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 10:53:39 -0400 Subject: [PATCH 2/9] Add a section on the debugger for contributors --- CONTRIBUTING.md | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc34539b437..d952fe7d907 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -167,6 +167,142 @@ For these test executables, `-p` which applies a regex filter to the test names. When running `cabal-install` test suites, one need only use `cabal test` or `cabal run ` in order to test locally. +## Using the Haskell Debugger + +We can debug `cabal` with the [Haskell debugger][hdb] to set breakpoints, step +through, and watch variables. This can be done from the command line or within +any editor that supports the [Debug Adapter Protocol][debug-protocol], such as +VS Code. + +To get set up: + +1. Install `hdb`. It needs GHC 9.14 or later, and the `ghc` on your `PATH` when + you run `hdb` has to be the version `hdb` was built with. + +2. Make sure the `cabal` on your `PATH` supports `cabal repl --with-repl` + (cabal-install 3.16 or later). `hdb` uses [hie-bios][hie-bios] to find out + how to load the project and hie-bios in turn runs this command. + +[hdb]: https://well-typed.github.io/haskell-debugger/ +[debug-protocol]: https://microsoft.github.io/debug-adapter-protocol/ +[hie-bios]: https://github.com/haskell/hie-bios + +### Debugging Project and Cradle + +We have a project and cradle for debugging: + +* [`cabal.hdb.project`](./cabal.hdb.project) with a subset of packages, leaving + out `cabal-testsuite`. + +* [`hie-hdb.yaml`](./hie-hdb.yaml) uses this project and lists which components + to load. Breakpoints can only be set in those components. Everything else is + a compiled dependency. The cradle is not named `hie.yaml` so that the Haskell + Language Server won't pick it up. This means it has to be given explicitly to + `hdb`. + +> [!WARNING] +> Do not use `cabal.project`, the default project, for debugging. `hdb` tries to +> load every component of every package but will fail with: +> +> ``` +> Failed to get compiler options using hie-bios cradle +> ``` +> +> This is because `cabal-testsuite` and its custom setup uses an older and +> incompatible version of `Cabal`. + +### Debugging from the Command Line + +Run `hdb` from the root of the repository, giving it the cradle, the file with +`main` in it and, after `--`, the arguments for `cabal`: + +``` +$ hdb --cradle-file hie-hdb.yaml cabal-install/main/Main.hs -- --version +``` + +Each time it starts, `hdb` loads all of the components as bytecode, more than +500 modules. Expect this to take a while and a decent chunk of memory. The first +time, `hie-bios` also builds the dependencies into its own build directory, +under `~/.cache/hie-bios`, leaving `dist-newstyle` alone. + +Once loaded, set breakpoints by file and line, with the path relative to the +root of the repository, and then `run`: + +``` +(hdb) break cabal-install/src/Distribution/Client/Main.hs 323 +(hdb) run +Stopped at breakpoint +(hdb) variables +_result : IO () = :: IO () +args : [String] = [...] + 0 : [Char] = "--version" +(hdb) next +(hdb) continue +cabal-install version 3.19.0.0 +compiled using version 3.19.0.0 of the Cabal library +(hdb) exit +``` + +The commands are `break`, `delete`, `run`, `continue`, `next` (step over), +`step` (step in), `finish` (step out), `variables`, `print`, `backtrace`, +`threads` and `exit`. + +The `cabal` being debugged runs in the root of the repository. To have it work +on another project, use `--project-dir`: + +``` +$ hdb --cradle-file hie-hdb.yaml cabal-install/main/Main.hs -- \ + build all --dry-run --project-dir=/path/to/project +``` + +> [!TIP] +> If a breakpoint is not hit, check that `cabal` has not skipped that work. +> For instance, the solver does not run when the project already has an +> up-to-date plan. If `hdb` fails to start, add `-v 3` to see what hie-bios +> and `cabal repl` are doing. + +### Debugging from VS Code + +Install the [Haskell Debugger +extension](https://marketplace.visualstudio.com/items?itemName=Well-Typed.haskell-debugger-extension) +and start VS Code from a shell that has the right `ghc` and `hdb` on its +`PATH`. Then add a configuration to `.vscode/launch.json`, changing `entryArgs` +to the arguments for `cabal`: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "haskell-debugger", + "request": "launch", + "name": "cabal --version", + "projectRoot": "${workspaceFolder}", + "entryFile": "cabal-install/main/Main.hs", + "entryPoint": "main", + "entryArgs": ["--version"], + "extraGhcArgs": [], + "cradleFile": "hie-hdb.yaml" + } + ] +} +``` + +The `cradleFile` is relative to the `projectRoot`. + +### Debugging a Test Suite + +To debug a test suite, add its component to `componentsToLoad` in +`hie-hdb.yaml`, for example `cabal-install:test:unit-tests`, and give `hdb` the +file with the `main` of the test suite and the arguments for the test suite: + +``` +$ hdb --cradle-file hie-hdb.yaml cabal-install/tests/UnitTests.hs -- -p "parse examples" +``` + +Breakpoints can then be set in the tests as well. Keep in mind that the test +suite runs in the root of the repository, not in the directory of its package. + ## Running other checks locally Various other checks done by CI can be run locally to make sure your code doesn't From 557b61638dd8e13540fe686f7925401b6124992a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 16:21:11 -0400 Subject: [PATCH 3/9] Add instructions for setting directory --- CONTRIBUTING.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d952fe7d907..bd9a7b84686 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -303,6 +303,26 @@ $ hdb --cradle-file hie-hdb.yaml cabal-install/tests/UnitTests.hs -- -p "parse e Breakpoints can then be set in the tests as well. Keep in mind that the test suite runs in the root of the repository, not in the directory of its package. +Some tests read or write files relative to the directory of their package. To +run a test suite from there with the command line debugger, start `hdb` in the +package directory, giving it the absolute path of the cradle and paths relative +to the package for the file with `main` and for breakpoints: + +``` +$ cd cabal-install +$ hdb --cradle-file "$(realpath ../hie-hdb.yaml)" tests/UnitTests.hs -- -p "parse examples" +(hdb) break tests/UnitTests.hs 32 +``` + +`hdb` has no setting for the working directory of the program being debugged. +When it is started from an editor, the way to change directory is to stop at a +breakpoint on the first line of `main` and evaluate this, in the debug console +for VS Code, before continuing: + +``` +System.Directory.setCurrentDirectory "cabal-install" +``` + ## Running other checks locally Various other checks done by CI can be run locally to make sure your code doesn't From 97ea84a91f491aff4bd558911c16dea28b5b3b1a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 16:29:47 -0400 Subject: [PATCH 4/9] Avoid getting flooded with warnings --- CONTRIBUTING.md | 11 +++++++++-- cabal.hdb.project | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd9a7b84686..06b0ba72305 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -261,6 +261,11 @@ $ hdb --cradle-file hie-hdb.yaml cabal-install/main/Main.hs -- \ > up-to-date plan. If `hdb` fails to start, add `-v 3` to see what hie-bios > and `cabal repl` are doing. +> [!TIP] +> Each time it stops or evaluates an expression, `hdb` repeats some warnings +> from GHC, such as `:1:1: warning: [GHC-15328] [-Wdeprecations]`. +> Add `--extra-ghc-args=-w` before the file with `main` in it to silence these. + ### Debugging from VS Code Install the [Haskell Debugger @@ -281,14 +286,16 @@ to the arguments for `cabal`: "entryFile": "cabal-install/main/Main.hs", "entryPoint": "main", "entryArgs": ["--version"], - "extraGhcArgs": [], + "extraGhcArgs": ["-w"], "cradleFile": "hie-hdb.yaml" } ] } ``` -The `cradleFile` is relative to the `projectRoot`. +The `cradleFile` is relative to the `projectRoot`. The `-w` in `extraGhcArgs` +keeps the debug console free of the warnings GHC would otherwise repeat each +time the debugger stops or evaluates an expression. ### Debugging a Test Suite diff --git a/cabal.hdb.project b/cabal.hdb.project index f97462cfdc0..c956c500b94 100644 --- a/cabal.hdb.project +++ b/cabal.hdb.project @@ -13,3 +13,11 @@ import: project-cabal/pkgs/tests.config import: project-cabal/constraints.config tests: True + +-- The debugger loads packages as bytecode, ignoring optimization flags and +-- warning that it does so. +optimization: False + +-- Keep compiler warnings out of the debugger's console. +program-options + ghc-options: -w From dfc7d767a4d3f93dc05d53ecff0b2850decbcd03 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 16:39:14 -0400 Subject: [PATCH 5/9] Use simpleTest1 as the pattern --- CONTRIBUTING.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06b0ba72305..8e5f50bac74 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -301,10 +301,17 @@ time the debugger stops or evaluates an expression. To debug a test suite, add its component to `componentsToLoad` in `hie-hdb.yaml`, for example `cabal-install:test:unit-tests`, and give `hdb` the -file with the `main` of the test suite and the arguments for the test suite: +file with the `main` of the test suite and the arguments for the test suite. +Where we would run a test with: ``` -$ hdb --cradle-file hie-hdb.yaml cabal-install/tests/UnitTests.hs -- -p "parse examples" +$ cabal run cabal-install:test:unit-tests -- --pattern simpleTest1 +``` + +The equivalent for debugging that test is: + +``` +$ hdb --cradle-file hie-hdb.yaml cabal-install/tests/UnitTests.hs -- --pattern simpleTest1 ``` Breakpoints can then be set in the tests as well. Keep in mind that the test @@ -317,7 +324,7 @@ to the package for the file with `main` and for breakpoints: ``` $ cd cabal-install -$ hdb --cradle-file "$(realpath ../hie-hdb.yaml)" tests/UnitTests.hs -- -p "parse examples" +$ hdb --cradle-file "$(realpath ../hie-hdb.yaml)" tests/UnitTests.hs -- --pattern simpleTest1 (hdb) break tests/UnitTests.hs 32 ``` From 9cd6ff34c6b0868e143291269a7dba0f611afe9d Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 16:42:39 -0400 Subject: [PATCH 6/9] Expect exception success --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e5f50bac74..11857965344 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -317,6 +317,16 @@ $ hdb --cradle-file hie-hdb.yaml cabal-install/tests/UnitTests.hs -- --pattern s Breakpoints can then be set in the tests as well. Keep in mind that the test suite runs in the root of the repository, not in the directory of its package. +> [!NOTE] +> A test suite ends by exiting with an exit code, which is an exception that +> the debugger reports as uncaught. So when all tests pass, expect to see +> `ExitSuccess`, and from VS Code: +> +> ``` +> Uncaught exception of type SomeException was thrown! +> ExitSuccess +> ``` + Some tests read or write files relative to the directory of their package. To run a test suite from there with the command line debugger, start `hdb` in the package directory, giving it the absolute path of the cradle and paths relative From 45f9484c1c9f8f5884c62ea0790e0b0879a933cd Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 6 Oct 2026 16:43:35 -0400 Subject: [PATCH 7/9] Outputs go to the terminal window --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11857965344..5b870884f1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -326,6 +326,9 @@ suite runs in the root of the repository, not in the directory of its package. > Uncaught exception of type SomeException was thrown! > ExitSuccess > ``` +> +> This goes to the debug console. The output of the test suite itself, with the +> results of the tests, can be seen in the terminal window. Some tests read or write files relative to the directory of their package. To run a test suite from there with the command line debugger, start `hdb` in the From 797f4143890ca6267b86310567832738d7195777 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 7 Oct 2026 13:55:54 -0400 Subject: [PATCH 8/9] Delete hbd commands --- CONTRIBUTING.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b870884f1c..e6b0494d8bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -243,10 +243,6 @@ compiled using version 3.19.0.0 of the Cabal library (hdb) exit ``` -The commands are `break`, `delete`, `run`, `continue`, `next` (step over), -`step` (step in), `finish` (step out), `variables`, `print`, `backtrace`, -`threads` and `exit`. - The `cabal` being debugged runs in the root of the repository. To have it work on another project, use `--project-dir`: From ce68f962dc48a6a101636125088c6b3648075d8a Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 7 Oct 2026 16:19:18 -0400 Subject: [PATCH 9/9] Put the directory stuff together --- CONTRIBUTING.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6b0494d8bf..f8eafe4cbba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -310,9 +310,6 @@ The equivalent for debugging that test is: $ hdb --cradle-file hie-hdb.yaml cabal-install/tests/UnitTests.hs -- --pattern simpleTest1 ``` -Breakpoints can then be set in the tests as well. Keep in mind that the test -suite runs in the root of the repository, not in the directory of its package. - > [!NOTE] > A test suite ends by exiting with an exit code, which is an exception that > the debugger reports as uncaught. So when all tests pass, expect to see @@ -326,10 +323,11 @@ suite runs in the root of the repository, not in the directory of its package. > This goes to the debug console. The output of the test suite itself, with the > results of the tests, can be seen in the terminal window. -Some tests read or write files relative to the directory of their package. To -run a test suite from there with the command line debugger, start `hdb` in the -package directory, giving it the absolute path of the cradle and paths relative -to the package for the file with `main` and for breakpoints: +A test suite runs in the root of the repository, not in the directory of its +package. Some tests read or write files relative to the directory of their +package. To run a test suite from there with the command line debugger, start +`hdb` in the package directory, giving it the absolute path of the cradle and +paths relative to the package for the file with `main` and for breakpoints: ``` $ cd cabal-install