This post outlines changes I think we should make to the Cabal build infrastructure, in order for it to stay relevant to Haskell developers. This post is intentionally high level and uses strong statements; I'm trying to establish a direction to go in, not discuss implementation details. I have a huge amount of respect and appreciation for the Cabal developers; this post is in no way intended as a criticism of them or their work.
Here's what I expect of a build system:
Hermetic builds
Builds should be completely independent. The build system must behave as if all artifacts were rebuilt every time. If any artifacts are shared, they must be both immutable and uniquely determined by the inputs used to create them (e.g. compiler flags, source files, dependencies used, etc.)
If artifacts can't be safely shared, you're better of rebuilding every time than trying to share them anyway.
Parallel builds
Real world programs are too large to be built sequentially. Parallel builds are a necessary feature; soon distributed builds will be as well.
Changes needed to Cabal
cabal build means "build my package"
If I'm in a source directory and type cabal build, I want Cabal to build my package, not tell me about steps I could take to build my package. cabal build should
- configure the package, even if I haven't configured the package before,
- build any dependencies and store them locally in the source tree (optionally sharing them with other builds if that can be done safely), and
- build the package.
Aside: configure very much feels like an unnecessary step. It would be better if build took all the flags configure takes today, so we never have to run configure again. This is after all what cabal install does today.
cabal test means "test my package"
If I'm in a source directory and type cabal test, I want Cabal to run my test suites. It is understood that to run the test suites they first need to be built, so go build and run them already!
I don't know what cabal install means
At the end of the day we build code to produce executables. When I type cabal install <library> I'm doing busy work that the build system should do for me, manually telling it about dependencies it might need to install later. cabal install should, except perhaps in the case of executables, be replaced by cabal build as described above.
The only reason I can see for running cabal install <library> manually is to save some compilation time in the future or to make sure the packages exists locally e.g. before getting on a plane.
No package is an island
We frequently have to work on multiple dependent packages at once and thus we also need a way to build them together, without registering the by-products in some shared package database. For example, given this directory structure
src
|-- foo
| | -- foo.cabal
|-- bar
| -- bar.cabal
where foo is a package that depends on bar, I should be able to do
cd src
cabal build foo
and that better traverse the directory structure, find, and build dependencies and use them when compiling foo.
Aside: This could be implemented in a few different, more or less explicit, ways e.g. by specifying the directories used to search for dependencies explicitly, as in
cd src/foo
cabal build --root-dir=~/src
Parallel builds
We're almost there. Parallel builds were developed by a GSoC student last year. We need to get the patches merged into Cabal.
Building specific components within a package
Not as important as the above changes, but having cabal build build all components in a single package starts getting annoying when you have several of them. For example, if you want to run a single test suite, but the package defines several, you end up unnecessarily linking binaries you won't run.
Proposal, take the component to build as an extra, optional argument to build. For example,
cd src/foo
cabal build some-executable
would only build some-executable. Combine that with multi-package builds and we can use
cd src
cabal build foo:some-component
to build a specific component in a specific package in a source tree of multiple packages.