haskell / cabal

Official upstream development repository for Cabal and cabal-install
https://haskell.org/cabal
Other
1.62k stars 695 forks source link

[RFC] Cabal plugin infrastructure #7825

Open Kleidukos opened 2 years ago

Kleidukos commented 2 years ago

This is a WIP. Fendor & I will edit this description to refine the features & requirements.

Inspirations

Reasoning

To avoid bloat in cabal-install CLI and offer a more ergonomic way to use plugins, we must offer first-class support for cabal CLI plugins.

More importantly, it allows us to provide features in "beta" or "experimental" status to our users before integrating them into the CLI codebase when they are more mature.
This is a low-tech solution but tech is not the only aspect being considered here.

What we need

cc @fendor

jneira commented 2 years ago

Having that would be really great, thanks for the proposal

Related: #2349, #2395

gbaz commented 2 years ago

I have a fair amount of trepidation about this, and my main comment would be that we should follow the model of git and cargo, which seems to be proposal #2349 and keep this as simple as possible. I propose that we do not need to have a --plugin flag for cabal install. It just installs plugins like any other binaries. It just looks up executables by name to execute subcommands, and further, perhaps, lists them under cabal --help. I also suggest we do nothing directly to enforce version compat, and we do nothing to worry about cli or env argument passing. I.e. this is just sugar for invoking a subcommand.

Anything else, while feeling "clever" at first, will in my estimation lead to more confusing and undocumented behavior in the long run.

Kleidukos commented 2 years ago

@gbaz thanks for restricting the scope :)

Ericson2314 commented 2 years ago

I don't think cabal-foo vs cabal foo is a very high priority thing to worry about. I have used Cargo plugins and git plugins and don't really see the point of that feature over there either.

However, trying to break out some libraries from Cabal and cabal-install with just parsers and ASTs is always a good idea, and one I am perennially excited about :). (We should do the same thing with GHC too, so ghc depends on ghc-lib-parser or so!)

Kleidukos commented 2 years ago

I don't think cabal-foo vs cabal foo is a very high priority thing to worry about. I have used Cargo plugins and git plugins and don't really see the point of that feature over there either.

@Ericson2314 They give better ergonomy and first-class support that we can improve later on. We can also organise a "beta" workflow of new subcommands if they can live in separate binaries during their "experimental" life cycle, before being integrated in the codebase of the CLI.

Thank you for your support regarding cabal-metadata. :)

jneira commented 2 years ago

They give better ergonomy and first-class support that we can improve later on. We can also organise a "beta" workflow of new subcommands if they can live in separate binaries during their "experimental" life cycle, before being integrated in the codebase of the CLI.

That is a key argument, we could, for example, bring here cabal-plan and have cabal plan almost for free

Kleidukos commented 2 years ago

@jneira The idea was to implement the featureset described here as a plugin: https://github.com/haskell/cabal/pull/7500#issuecomment-972684947

yvan-sraka commented 1 year ago

After reading through the discussions here (and discussing it while ZuriHac with @Kleidukos), I propose to stick to a simple, straightforward approach for a cabal-install CLI plugin system, inspired by how git and cargo handle commands:

  1. Treat each plugin as a standalone executable named cabal-$COMMAND in the PATH. It should be callable as cabal $COMMAND ;
  2. Pass any additional CLI arguments directly to this command. This allows plugins to range from a simple shell script to more complex applications ;
  3. Rather than, e.g., passing data in specific formats like JSON or constraining the user to link their program against the Cabal library, we provide necessary information (like GHC version or project root directory) as ENV variables. This allows plugins to rely on cabal-install CLI or Cabal library as they wish, or not at all. cargo follows a similar approach: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-3rd-party-subcommands

I've taken a brief look at the Cabal codebase, and it seems feasible to implement this approach just by slightly modifying this function: https://github.com/haskell/cabal/blob/1cb92a38c016fe0b0af2b5c0aa1d448e675f0ab1/Cabal/src/Distribution/Simple/Command.hs#L622-L687

This method keeps the system simple and functional, gives users more control over plugin creation and installation, and allows for a “beta” workflow for new commands before they're integrated into the CLI codebase. WDYT @fendor?

fendor commented 1 year ago

Hi!

After discussing the proposed solution with @Kleidukos, this looks like a good way forward! I don't have much to add for now, looking forward to the PR!

Kleidukos commented 1 year ago

I have a question: should we encourage the usage of extra field (starting with x-) in cabal.project? I'm writing a cabal plugin that would read from the codebase, and obviously the maintainers of a project may want to exclude some paths, and specifying file patterns.

@gbaz This question is for you: Was there ever a plan to support arbitrary stanzas that operate the same as source-repository-package (that is, with subfields)? If they started with x- (like in the .cabal format), cabal-install could treat them as opaque and the tools would bring their own parser for the file.

I'm especially looking at https://github.com/haskell/cabal/blob/1264967b45dbcfd7d80b4ed63d4fead8d96f6f3c/cabal-install/src/Distribution/Client/Types/SourceRepo.hs#L101-L118 and I wonder if the plugins couldn't define their own (since most of the stuff is defined in Cabal-syntax).

However the parser itself is defined in cabal-install, so that would mean taking it out? I'm not super confident that it is a painless process.