Thursday, August 26, 2010

Speeding up your cabal builds

Every waited too long for your cabal builds to finish? If that’s because you have multiple executable sections in your .cabal file, then there might be a solution.

By default, cabal rebuilds all relevant object files for each executable in separation. In other words, object files are not shared between executables. So if you have n executables and m source files, then cabal needs n * m compilation steps plus n link steps to rebuild the executables, no matter whethe any source file contributes to multiple executables.

Starting with cabal 1.8, there is a better solution, provided your executables have some source files in common. In this case, you might build a library from these common source files and then link the executables against the library. In the example above, if all n executables use the same set of m source files, then you end up with m compilation steps plus n + 1 link steps. Sounds good, doesn’t it?!

Here is a simple .cabal file that demonstrates how linking against an internal library works:

Name:                test
Version:             0.1
Synopsis:            test package for linking against internal libraries
Author:              Stefan Wehr
Build-type:          Simple
Cabal-version:       >=1.8 -- IMPORTANT

Library
  Hs-source-dirs: lib -- IMPORTANT
  Exposed-modules: A
  Build-Depends: base >= 4

Executable test-exe
  Build-depends: base >= 4, test, -- link against the internal library
  Main-is: Main.hs -- imports A
  Hs-source-dirs: prog  -- IMPORTANT

There are some things to consider:

  • The Cabal-Version must be greater or equal 1.8.
  • The library and the executable must not use common source directories, otherwise the compiler does not pick the library but recompiles the source files.
  • The library must be mentioned in the Build-depends of the executable

Running cabal build now gives the following output:

Building test-0.1...
[1 of 1] Compiling A                ( lib/A.hs, dist/build/A.o )
Registering test-0.1...
[1 of 1] Compiling Main             ( prog/Main.hs, dist/build/test-exe/test-exe-tmp/Main.o )
Linking dist/build/test-exe/test-exe ...

No rebuilding of A when compiling Main!!!

This feature of cabal isn’t mentioned in the manual, at least I didn’t find it. Further, there seems to be no changelog for cabal. I found out about this feature by browsing the bug tracker for cabal. Is there a better way to get informed of new features of cabal?

Note: I successfully tested this with cabal-install version 0.8.2 (cabal library 1.8.0.4). I couldn’t get it to work with cabal-install version 0.8.0.

Author: Stefan Wehr

4 comments:

Edward Z. Yang said...

It may still be advisable to skip out on this feature, for two reasons:

1. When you're just doing source builds for typechecking, the link step gets old really fast.

2. It causes Haddock builds to break. See http://hackage.haskell.org/trac/hackage/ticket/656

David Leuschner said...

Thanks for pointing that out, Eward! Linking indeed takes very long. It would be nice if cabal wouldn't relink if there were no changes and if cabal would first compile all executables and then link them. Until then ghci proably is the best alternative for fast typechecking.

Erik said...

You can use the -c option to GHC to omit linking, if you just want to typecheck.

Duncan Coutts said...

The changelog file is available from the download page on the Cabal website:

http://haskell.org/cabal/download.html

http://haskell.org/cabal/release/cabal-latest/changelog

Yes, we're still missing documentation in the user guide. The user guide is now much easier to contribute to, it's just markdown. It's in the darcs repo. It'd be great if you could turn your blog post into a darcs patch for the user guide!

Post a Comment