Tuesday, September 28, 2010
Speeding up your cabal builds - Part II
Author:
Last time, I blogged about how linking your binaries against an internal library might speed up your cabal builds. This time, I show how you can avoid building certain binaries at all.
In our company, we work at a rather large Haskell project. The cabal file specifies more then ten binaries, so it takes rather long to build all of them. But often, you only need one or two of these binaries, so building them all is a waste of time.
Unfortunately, cabal does not allow you to build only a subset of your binaries. One workaround is the set the buildable flag in your .cabal file to false for the binaries you don’t want to build. However, this approach is rather unflexible because you need to edit the .cabal file and do a cabal configure after every change.
The solution I present in this article allows you to specify the binaries to build as arguments to the cabal build command. For example, if you want to build only binary B, you invoke cabal as cabal build B
and cabal only builds binary B.
To get this working, all you need to do is writing a custom Setup.hs file:
import Data.List import System.Exit import Control.Exception import Distribution.Simple import Distribution.Simple.Setup import Distribution.PackageDescription hiding (Flag) import Distribution.PackageDescription.Parse import Distribution.Verbosity (normal) _CABAL_FILE_ = "DociGateway.cabal" -- enable only certain binaries (specified on the commandline) myPreBuildHook :: Args -> BuildFlags -> IO HookedBuildInfo myPreBuildHook [] flags = return emptyHookedBuildInfo myPreBuildHook args flags = do let verbosity = case buildVerbosity flags of Flag v -> v NoFlag -> normal descr <- readPackageDescription verbosity _CABAL_FILE_ let execs = map fst (condExecutables descr) unbuildableExecs = execs \\ args mapM_ (checkExistingExec execs) args putStrLn ("Building only " ++ intercalate ", " args) return (Nothing, map (\e -> (e, unbuildable)) unbuildableExecs) where unbuildable = emptyBuildInfo { buildable = False } checkExistingExec all x = if not (x `elem` all) then do putStrLn ("Unknown executable: " ++ x) throw (ExitFailure 1) else return () main = defaultMainWithHooks $ simpleUserHooks { preBuild = myPreBuildHook }
That’s all! Don’t forget the set the Build-Type in your .cabal file to Custom. I’ve tested this approach with cabal-install version 0.8.2, using version 1.8.6 of the Cabal library.
Happy hacking and have fun!
Author: Stefan Wehr