8

tl;dr

About a week ago I released the 0.1.0.0 package for my first non-trivial Haskell project. I want the executable to be easy to install and upgrade, even for non-Haskellers. In the README, I suggested installing using cabal install. Is this a mistake?

Context

I had heard of "Cabal hell", but didn't realize how darn hard it would be for users to upgrade a globally installed copy of the package, even when I conservatively did not actually change any version dependencies in the .cabal file. In the end, I went down a deep rabbit hole trying to update from 0.1.0.0 to 0.2.0.0. It warned me about breaking dependencies, I tried various incantations to force the upgrade or reset my local state, and wound up borking the system so hard that I had to reinstall the ghc and cabal-install Brew packages (this is on macOS) in order to get everything back to a state in which I could install and run again.

Alternatives:

  • stack install: I was already using Stack to manage local development environment, but it seems to work pretty well for independent installs as well, as long as you have Stack installed first. (Just need to set up your $PATH appropriately.)
  • Distributing a prebuilt binary: Would be nice and easy for end users, but at least on OS X, I'd need to worry about code signing and I don't even have an identity set up for that any more.

So, in my README right now I am mentioning both stack install and cabal install. But what is the 2016 best practice?

Greg Hurrell
  • 5,177
  • 23
  • 27
  • 1
    If users run into "cabal-hell" attempting to install your package it's not really your problem if you have specified your dependencies correctly. For installing an application cabal users can always use a sandbox if they run into package version issues. Both `stack` and `cabal` have their place in the Haskell ecosystem. I think the best practice is to make sure your application can be installed using either. – ErikR Jun 17 '16 at 07:27

1 Answers1

4

Having looked at your .cabal file I see that you don't have any bounds on your dependencies. You really should have at least lower bounds and preferably both lower and upper bounds.

As @Emanuel Borsboom mentioned, you can have stack fill in the version constraints for you when you upload a package to Hackage with:

stack upload --pvp-bounds=both

In fact, for applications I suggest including the cabal.config file generated by cabal freeze in the package:

cabal freeze
mv cabal.config cabal.config-sample

When running into trouble building legacy applications from Hackage I've often wished the authors had included this information. You can get the cabal.config file for a particular snapshot at:

https://www.stackage.org/{RESOLVER}/cabal.config

And in your stack.yaml file I would use a standard LTS version rather than a nightly-. Supposedly they will never get deleted. On the other hand you'll be helping out your users by reducing the number of snapshot directories they have to maintain.

ErikR
  • 51,541
  • 9
  • 73
  • 124
  • Funnily, I had a `cabal.config` but [I removed it](https://github.com/wincent/docvim/commit/5ad9b0f4cdc) because I figured it wasn't really needed under Stack, and under Cabal I thought it might be contributing to Travis build failures like [this one](https://travis-ci.org/wincent/docvim/jobs/137999458). I was also [using the lts resolver](https://github.com/wincent/docvim/commit/06abd8de13238dbcd5f9ec5f26b543640be6dd4f) until a while back. As you can see, blindly thrashing around banging pots together in the kitchen without really knowing how to cook. – Greg Hurrell Jun 17 '16 at 08:19
  • 2
    The nightly snapshots will not be deleted, although using LTS if you don't need anything in nightly is a good practise. – Emanuel Borsboom Jun 17 '16 at 12:38
  • 1
    You can also grab a `cabal.config` for your stack.yaml's resolver from stackage.org. For your current snapshot, you'd use 'https://www.stackage.org/nightly-2016-06-15/cabal.config'. This means `cabal install` will use the exact same package versions as `stack install`. – Emanuel Borsboom Jun 17 '16 at 12:42
  • 3
    Finally, you can leave out the constraints in your source `.cabal` file, but upload your package to Hackage using `stack upload --pvp-bounds=both`. Stack will then automatically add upper and lower bounds based on the snapshot in the `.cabal` file uploaded to Hackage. – Emanuel Borsboom Jun 17 '16 at 12:43
  • I've marked this answer as accepted, Erik, but it would be nice to incorporate the points made by @emanuel-borsboom too. – Greg Hurrell Jun 17 '16 at 15:13
  • One gotcha: my package actually [got disabled in Stackage](https://github.com/fpco/stackage/pull/1618#issuecomment-226976704) because I applied both the advice to switch from a `nightly-`* to an `lts-*` resolver *and* the advice to use `--pvp-bounds both`. This made my package incompatible with the latest versions of the `lens` and `process` libraries, as specified in the nightlies. Fix I've gone with for now is to use an `lts-*` resolver, but `--pvp-bounds lower`. [This issue](https://github.com/commercialhaskell/stack/issues/2262) might allow me to to use `--pvp-bounds both` in the future. – Greg Hurrell Jun 19 '16 at 05:47
  • cabal.config-sample seems like an odd suggestion to me. The exact versions of which packages will be used in the build plan is entirely specified by the stack.yaml file. You can use `stack exec ghc-pkg list` to see what you've got already installed, and `stack build --dry-run` to see what still needs to be installed. – Dan Burton Jun 19 '16 at 19:34