RFC: Move GHC environment files into `dist-newstyle` and add `cabal shell`
- Dominant language
- Haskell
- Stars
- 1.7k
- Forks
- 750
- Avg merge
- 4d 3h
- Merged PRs (30d)
- 28
Description
# Background
GHC environment files (from here on: GEFs) describe where GHC package databases reside and what packages from those databases should be exposed in a particular GHC session. As an example, here's what a GEF looks like after compiling `aeson-pretty`:
```
clear-package-db
global-package-db
package-db /home/martijn/cabal/store/ghc-8.10.3/package.db
package-db dist-newstyle/packagedb/ghc-8.10.3
package-id aeson-pretty-0.8.8-inplace
package-id aeson-2.0.1.0-87763de9410f519d0c483501155e5ff011c56e8c7ad8eac58f4d552d4171a333
...
```
Unless explicitly surpressed, any GHC session executed in either the directory containing the GEF or any directory under that will use this file to find and expose packages. Cabal used to generate these files automatically, however, this has been disabled as many users found it confusing. Users can now opt-in by passing `--write-ghc-environment-files=always` or by adding a similar section to their `cabal.project`.
I purposely won't go into the perceived downsides of GEFs, as I think this has been discussed many times over on various fora.
# So what's wrong?
While seemingly opt-in, GEFs are currently the only way Cabal communicates what package databases it used and what packages are exposed to subcommands. This leads to some projects (`doctest-parallel`, `clash-ghc` + projects build with Clash) requiring their presence. In general, any downstream consumer of the GHC API is likely to want access to these files. There's currently a couple of ways these projects solve this, but without delving into them: this gets us right back to square one. That is, for these projects, all the downsides of GEFs are exposed once more.
Brushing aside projects _needing_ GEFs, even users who liked their behavior are now in a rough spot: they need to remember to either enable them globally, per project, or even on a per command basis.
# Consumers
As far as I can see, there are two groups of GEF users:
1. Those that _need_ GEFs for their application to work properly. They're only interested in executables that access the GHC API in some way. These executables typically run as a Cabal subcommand (e.g., through `cabal run`).
2. Those that _want_ GEFs for perceived benefits. I'm not a member of this group, but I believe people like compiling their project once, and then having access to modules in any GHCi / GHC session run within their project.
Then, there's a (I believe, large) group that doesn't care at all but is prone to being bitten by unexpected behavior imposed by GEFs. This group overlaps with group (1).
# Tackling this
I strongly believe a few things should happen:
1. Cabal should _never_ generate a GEF at the root of a project. Instead, it should generate it inside `dist-newstyle`.
2. Cabal should set `GHC_ENVIRONMENT` and set it to the GEF mentioned in (1) when running subcommands. E.g., for `run` and `test`.
3. Cabal should offer a subcommand `shell` that drops you into a shell with `GHC_ENVIRONMENT` set. Like `build`, one can set a target, or none at all. This command should be able to be run anywhere in your project. It should optionally take a command to run, e.g. `cabal shell my-exe -- ghci`.
# Notes
In no particular order:
* There's already `cabal exec` that generates a temporary GEF and sets `GHC_ENVIRONMENT`. However, `exec` doesn't actually build anything.
* It seems weird to me that Cabal writes GEFs to the root of a project, while `--distdir` also exist.
* Stack already uses a combination of `GHC_PACKAGE_PATH` and `GHC_ENVIRONMENT` for subcommands. This works really well for the projects I work on.
* I've lost a lot of time to GEFs either due to confusion on my own side, interns, or colleagues.
* As noted, I work on [Clash](https://github.com/clash-lang/clash-compiler) and [doctest-parallel](https://github.com/martijnbastiaan/doctest-parallel), both of which heavily rely on using the GHC API. I understand this is not typical.
* I believe the proposal could satisfy all stakeholders in https://github.com/haskell/cabal/issues/4542. I'd love to hear different opinions.
* If people agree with this RFC, I'm willing to write a more detailed proposal if necessary. I'm also willing to do implementation work, as long as I get some pointer on where to look.
* GHC should _probably_ be patched to not automatically find `.ghc.*` files - this is IMO where most of the frustration stems from.
* I'm assuming `v2-*` commands in this RFC.
Contributor guide
Research direction
This RFC names no implementation files or tests. Start by locating the v2 command handling, existing `cabal exec` environment setup, and `dist-newstyle` GHC environment file generation. A completed design would cover project-local GEFs, `GHC_ENVIRONMENT` for subcommands, and a target-aware `cabal shell` command, with tests for these behaviors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- build-system, cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100