Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,183 @@ 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 <test-target>` in order to test locally.

## Using the Haskell Debugger

We can debug `cabal` with the [Haskell debugger][hdb] to set breakpoints, step

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be condensed? Or portions moved somewhere where only the debugger users look? Or maybe the link to the blog post already covers most of this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can assume anyone using the debugger would know what debugging is but I thought I should say something about it as an intro. What do you suggest? Should I dive right into the setup?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe the link to the blog post already covers most of this?

The link is to a flashy new web site for the Haskell Debugger!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I deleted the list of commands.

A lot of the other stuff is cabal-specific. The warning about cabal-testsuite and its setup-depends can go when we merge #12369. I'd like the tips to stay. It is important to mention the way to change directory before debugging a testsuite with relative fixtures. Perhaps the "Debugging from VS Code" section could go but having it means we cover the most common editor case in a paste and go fashion.

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 () = <fn> :: 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 `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.

> [!TIP]
> Each time it stops or evaluates an expression, `hdb` repeats some warnings
> from GHC, such as `<interactive>: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
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": ["-w"],
"cradleFile": "hie-hdb.yaml"
}
]
}
```

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

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.
Where we would run a test with:

```
$ 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
```

> [!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
> ```
>
> 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.

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
$ hdb --cradle-file "$(realpath ../hie-hdb.yaml)" tests/UnitTests.hs -- --pattern simpleTest1
(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"
```
Comment on lines +326 to +345

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alt-romes is this a good way to get a test suite to run next to its fixtures when being debugged?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That snippet has two examples, one for the command line and one for use with an editor.


## Running other checks locally

Various other checks done by CI can be run locally to make sure your code doesn't
Expand Down
23 changes: 23 additions & 0 deletions cabal.hdb.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- 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

-- 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
16 changes: 16 additions & 0 deletions hie-hdb.yaml
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions project-cabal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:

```
Expand All @@ -96,6 +103,7 @@ Additional configuration is imported:
| Project | ghc-options | ghc-latest | constraints |
|------------------|:---: |:---: |:---: |
| default | ✓ | ✓ | ✓ |
| hdb | ✓ | ✓ | ✓ |
| libonly | ✓ | | |
| release | | | |
| validate | ✓ | ✓ | ✓ |
Expand Down
Loading