haskell / haskell/cabal

RFC: design for better GHC plugin support in cabal

Open
#7,901 21 comments 8 reactions 0 assignees View on GitHub
re: ghc-plugins type: discussion type: RFC
Dominant language
Haskell
Stars
1.7k
Forks
750
Avg merge
4d 3h
Merged PRs (30d)
28

Description

This came out of some discussion on IRC. I started writing an issue, but on reflection I think there's a relatively plausible design sitting there, so I wrote it down.

# Motivation

GHC compiler plugins are currently not terribly easy to use with `cabal`, and require some amount of manual fiddling. In addition, they are not specified as declaratively as they could be (i.e. cabal has no concept of a "plugin"), which prevents cabal from doing some things as well as it could do.

I think there are two broad kinds of plugin that we should be thinking about:

1. Package plugins

Package plugins are those which are required by the package itself. They usually have a load-bearing role, in that they are actually necessary to compile the package. An example is the `record-dot-preprocessor` plugin.

Today, these can be used by:
- Adding the plugin package to the component's `build-depends` stanza
- Passing the appropriate `-fplugin` GHC option

Package plugins as implemented today have a few weaknesses:
- They don't use `-fplugin-package`, since they are just specified as normal `build-depends`. This means they get unnecessarily linked into the final build product (https://github.com/haskell/cabal/issues/6169).
- They are solved for as part of the main build plan, even though (see above) they don't need to be linked into the final build product.
- If/when the upstream GHC support for a native package db for plugins is completed, cabal will not be able to use it (because it doesn't know they're plugins).
- Users have to specify the `-fplugin` options themselves, as well as any `-fplugin-opt` flags.

2. Global plugins

Global plugins are ones which users want to run on all packages that get built, regardless of whether the package itself says it requires any plugins. An example is the `ghc-tags-plugin` plugin. Often these are used to e.g. get additional compile-time information out of dependencies.

Today these are very difficult to use (https://github.com/haskell/cabal/issues/7685, https://github.com/haskell/cabal/issues/6307, probably more). Partly the issue is that doing this nicely requires both setting a GHC option for upstream packages (doable today in `cabal.project`), but also building the plugin package and ensuring it is present in the appropriate package db when building the upstream package (hard to do without hacks like specifying your own manually created plugin db).

Since these sorts of plugins are also frequently used just in development, it would be particularly nice if they could easily be specified via `cabal.project.local` (https://github.com/haskell/cabal/issues/6169#issuecomment-593062344).

## Proposal

1. `plugin-depends` field for `.cabal` files

(Prior art: https://github.com/haskell/cabal/issues/2965, but I'm putting it a little differently, so I'll repeat.)

The goal here is to make the situation nicer for package plugins. To that end, we teach cabal a `plugin-depends` field for component stanzas for `.cabal` files. `plugin-depends` contains a list of package/module pairs.

Initially, it could start out as a simple wrapper for `build-depends`, and then we could progressively augment it to have the following features:
- Also imply the appropriate `-fplugin` flags. (This is why we need the module component! `-fplugin` needs the actual plugin module.)
- Be passed via `-fplugin-package` (in particular, I don't think this needs to be a prerequisite, contra https://github.com/haskell/cabal/issues/6307#issuecomment-607146612)
- Be included in an appropriate native package db when/if that exists
- Be solved for as part of an independent build plan for native dependencies (relies on the previous item)

This deliberately doesn't cover `-fplugin-opt` flags. It seems reasonable for users to add those themselves.

(I'm also disagreeing with [my past self](https://github.com/haskell/cabal/issues/2965#issuecomment-607271932) here, who thought that we should just have `native-build-depends` and have that include plugins. However, the idea of including the extra information about modules that's needed to automatically pass `-fplugin` flags seems appealing to me, and requires a plugin-specific field. You *could* do this with a combination of `native-build-depends` and `active-plugins` (the latter taking a list of modules); maybe that would be nicer.)

2. `plugin-depends` package field for `cabal.project` files

The goal here is to make the situation nicer for global plugins. To that end, we teach cabal a `plugin-depends` field for package stanzas in `cabal.project` files.

This field simply augments the `plugin-depends` field for the corresponding package. Plugins that truly want to be globally applied can then use `package *` as usual.

Contributor guide

Open the contributing guide

Research direction

Start by reading the proposal and the linked prior issues on plugin dependencies, then compare existing handling of build-depends in .cabal files with package stanzas in cabal.project and cabal.project.local. Done means agreeing on the plugin-depends design and its package/module semantics before implementing support across those configuration files.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell
Domain
build-system
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.