Why cabal sandbox init does not change PATH like virtualenv does? - haskell

Haskell newbie and Python guy here.
I think I may be missing something here but if you look at Yesod's quickstart, the autor install some packages before cabal sandbox init. I have seen the same pattern elsewhere. Questions:
Am I missing something? Is this the real way to use cabal sandbox?
Why can't (or shouldn't) I install yesod-bin inside a sandbox?
What if I use different versions of yesod-bin throughout some projects?
If there is some libraries that install binaries inside .cabal-sandbox/bin, why cabal sandbox init don't change PATH in order to match the sandboxed version?
Thank you very much in advance!

Yes, this how to use a sandbox.
cabal sandbox init will create some files / directories for you that will keep track of the packages you have installed.
cabal install some_package will install that package into the sandbox.
You are more than welcome to install yesod-bin into a sandbox.
Read point 2
cabal sandbox init doesn't change your path, because it doesn't really need to. Just add PATH=.cabal-sandbox/bin:$PATH in your .bash_profile.
Unlike virtual-env, you never need to 'enable' or 'disable' a sandbox. You just cd into a directory, and it is automatically enabled.
The only real downside I have found to cabal sandboxes, is that you need to be in the root directory in order to act upon a sandbox. Meaning, if you are in a sub-directory, running cabal install some_package will not install it into the sandbox that is up a level, instead it will install it into either the global or user database, depending on how you have cabal configured.

cabal exec lets you execute a program in the context of a sandbox. It changes the path to include the bin folder of the sandbox. You can see it by executing cabal exec printenv inside the sandbox.
Also, the latest versions of cabal let you create sandboxes in folders without .cabal files. Once you run cabal sandbox init, you can just cabal install the dependencies you need.
So, to use different versions of yesod-bin, install them in different sandboxes, and then invoke cabal exec yesod-bin on each one.
(Extra tip: cabal exec gchi is a useful command that makes ghci aware of the contents of the sandbox.)

Related

Cabal: only work with checked in libraries in project directory

I would like to download all the dependencies of my cabal project to my project directory/repository and force cabal to never use ~/.cabal/ and never download any new dependency from the internet. The downloaded libraries should be system independent (not include local paths etc.). Is this possible?
The idea behind this is, to copy the project directory to another (offline) system where the same ghc is installed and it should work out of the box.
According to the cabal docs, you can set active-repositories: none in your cabal.project or cabal file to put cabal-install in offline mode.
Then, you could create a cabal project and try to make all the dependencies that don't come bundled with GHC itself local packages. Clone them in your project folder and add them to the packages: section of cabal.project.
The problem with the above is that running cabal clean would require re-compiling all packages afterwards.
As an alternative, create a local no-index package repository in your offline machine, make it the only available package repository using active-repositories:, and put the sdist tarballs of all your dependencies there.
That way the dependencies will only be compiled once.

How to version control patched dependencies?

Using cabal sandboxes, how do I specify that my project depends on a patched version of a library, then check that dependency into version control?
I know I can use cabal sandbox add-source to add a dependency on a patched version, but that just changes something locally right? Any other programmer would have to remember to run that command in order to build the project.
There are a couple of ways to constrain the version for installation.
Add lower and upper bounds to package versions in the cabal file, example of such a file here
Additionally, you can override the settings in the .cabal file with the flag constraint like so: cabal install --constraint="bar-2.1"
To remove a specific version of a package:
In a sandbox you can unregister a version with cabal sandbox hc-pkg unregister bar-2.1
Global unregistering can be done with this command outside of sandbox ghc-pkg unregister bar-2.1

Link cabal to local library

Lets MyLib be my local Haskell library. I can build it with cabal build and install it with cabal install, but can I use it in other projects without the installation step?
I'm developing several libraries and installing them after every change is not a good solution.
Let's say you have two entirely separate projects, one called my-library and another called my-project. And my-project depends on my-library.
The way to build my-library and make it available to other projects is cabal install my-library. Once that's done, any other project can use the library.
Now you're ready to build my-project using the command cabal install my-project. It will not rebuild or reinstall my-library, but it will link your project with the library.
Now, if you make modifications to my-library, be sure to update the version number before running cabal install my-library. If you forget to bump the version number, you will be warned that my-project will be made obsolete. Now the old version and the new version of your library are available to other projects.
You can continue to run your projects. They will happily continue to use the previous version of my-library until you do another cabal install my-project. So there is no need to re-install everything after every change.
If you do want to rebuild your projects, but continue to work with an older version of your library, you can specify that in the build-depends section of your cabal file. For example, if you have versions 1.0 and 2.0 of my-library installed, you can build your project against the older version like this:
build-depends: my-library==1.0, ...
There isn't a great solution to your problem, but you can use sandboxes to keep your development environment a bit cleaner.
With cabal-1.18 or newer, you can create a sandbox with cabal sandbox init and then you can either install to that sandbox or add-source (cabal sandbox add-source <path to library>).
This helps to keep unstable libraries (and their potentially unstable dependencies) out of your user package database, and that can help prevent 'cabal hell' (unsolvable conflicts between dependencies). However, that doesn't directly help reduce the number of commands you need to issue each time you want to do a top-level build.
What you can do though, is set up a simple script that performs the add-source commands and builds your top-level package. eg:
#!/bin/bash
cabal sandbox init # a no-op if the sandbox exists.
cabal sandbox add-source ../MyLib
cabal install --dependencies-only
cabal build
Granted, you could do that before, but this time you can also easily clean up (removing all the installed artifacts) by cleaning the sandbox:
cabal sandbox delete

Is it possible to install Cabal package locally à la npm?

In npm, dependencies are installed in a directory node_modules relative to the directory of the dependent package. Each package stores its dependencies inside itself.
With Cabal, however, installing a package always installs it globally (i.e. in ~/.cabal), which is a perfect recipe for nightmares and tears because different versions of packages get to conflict with eachother and everything will fail and go wrong.
I would like to install Cabal packages locally, i.e. in a subdirectory of my own package, rather than globally. All the dependencies of these packages would do the same. An example of the directory tree of my package could look like this:
my_package/
dependencies/
json/
dependencies/
foo/
etc...
bar/
etc...
mtl/
etc...
my_package.cabal
src/
Main.hs
Is this possible to do, and if so how?
EDIT: With newer versions of cabal, you should use cabal sandboxes, which are now built-in, rather than cabal-dev.
Take a look at the cabal-dev tool. It's similar to virtualenv for Python.
Basically, where you would use the command cabal, use cabal-dev. So to install the package you're working on, go that directory and do cabal-dev install. You can also run ghc-pkg through cabal-dev, so you can do something like cabal-dev ghc-pkg unregister foo-bar. Also, you can start GHCi with it too: cabal-dev ghci.
By default, cabal-dev installs packages into a cabal-dev directory inside your project--this is what you call dependencies in your example.

How do I disable dependency checking with a local cabal install?

So I'm trying to install a package with a big messy dependency set (gitit, in this case). A direct cabal install from hackage forces rebuilds of plenty of libraries I don't want to rebuild (having to do with constraints on text, constraints on network, constraints on parsec, etc.) I did the right thing, ran cabal unpack gitit, manually edited the .cabal file, and successfully put it through a cabal configure, cabal build cycle. So far, so good.
Now, I want to run a cabal install. In the good old days (last year), this would just install the already built binaries and files where they belong. However, now, running cabal install runs the dependency checker, which decides that all the packages that I'm building with don't use the same parsec, etc., and tries to reinstall them anyway! Even though I just ran a perfectly fine cabal build. What's the magic flag to turn this off and get the old, not-clever, and perfectly acceptable behavior?
Looking at the flags, there doesn't seem to be any indication of cabal install doing this. In times of yore, before cabal install and when you had to manually get your own packages, the incantation at the install stage was runghc Setup install --user after you had run runghc Setup configure --prefix=$FOO --user - perhaps this will work? Setup.hs will not automatically invoke 'build' when you tell it to 'install', if my memory serves correctly.
Now, for the future, if you want to avoid this entire nasty dependency hell, I would highly suggest you use cabal-dev which will sandbox your package installations and never touch your actual user/global package database, in this case you'd just do:
$ cabal unpack gitit
$ cd gitit-0.8.0.1 # latest hackage version
$ cabal-dev install
It'll properly download and install all the needed dependencies like cabal install, but it'll sandbox them by creating a ./cabal-dev directory containing a self-contained package database. It never touches your global or user package db in ~/.ghc/. cabal-dev effectively makes editing cabal files and dealing with the diamond dependency problems Cabal faces a thing of the past, the way cabal-install made manually downloading packages a thing of the past.
It also turns out that there is an --only flag that lets one build and install only that package, just as the ./Setup route does.

Resources