Cleaning up hooks

Simon Marlow simonmarhaskell at
Tue Apr 24 04:45:54 EDT 2007

I think the API for hooks is in need of an overhaul.  It has a few problems:

1. it isn't extensible.  When a new command is added, typically
    all the Setup.hs scripts break because they haven't hooked then
    new command.

2. it isn't composable.  It ought to be easy to augment
    PackageDescription in Setup.hs, but it isn't: to do it right,
    you have to modify every hook.

3. It's too complicated.  I'm sure we don't need all those pre- and
    post- hooks.

4. tools that build on Cabal (e.g. cabal-rpm) don't work with hooks.
    If your Setup.hs uses hooks to modify the PackageDescrption, then
    cabal-rpm won't see the modifications.

I can fix 1-3, but 4 is more of a design problem.  I'll outline a possible 
partial solution to 4 later, however.

Anyway, here's the basic idea.  A common thing to want to do in Setup.lhs is to 
modify the PackageDescription, so we provide a way to do that:

         :: (PackageDescription -> IO PackageDescription)
         -> UserHooks -> UserHooks

         :: (PackageDescription -> LocalBuildInfo -> IO PackageDescription)
         -> UserHooks -> UserHooks

modifyPackageDescription supplies a function that is run before every command, 
to modify the existing PackageDescription, and it can do IO. 
modifyPackageDescriptionPostConf is similar, except that it does not apply to 
the configure command, and it gets to see the LocalBUildInfo.

These mostly subsume all the preHooks.  For example, 
modifyPackageDescriptionPostConf can be used to read the results of the autoconf 
  script.  (We'll also need to provide a way to merge a new BuildInfo with the 
existing PackageDescription).

The good thing about this is it's extensible and composable.  Adding more 
commands to Cabal won't break Setup.lhs scripts that use these combinators. 
This could also help us solve problem (4) above: Cabal can provide a way to 
output the modified PackageDescription to a file, that can be read in by 
cabal-rpm.  For example, "cabal-setup descr" could output the real 

We might still want to hook the real commands, however.  So let's look at 
simplifying the UserHooks type:

  type ConfHook = Args -> ConfigFlags -> PackageDescription -> IO LocalBuildInfo
  type Hook a   = Args -> a -> PackageDescription -> LocalBuildInfo -> IO ()

  data Hooks = Hooks {
   confHook       :: ConfHook,
   postConfHook   :: Hook ConfigFlags
   buildHook      :: Hook BuildFlags,
   makefileHook   :: Hook MakefileFlags,
   cleanHook      :: Hook CleanFlags,
   copyHook       :: Hook CopyFlags,
   installHook    :: Hook InstallFlags,
   sdistHook      :: Hook SDistFlags,
   registerHook   :: Hook RegisterFlags,
   unregisterHook :: Hook UnregisterFlags,
   haddockHook    :: Hook HaddockFlags,
   pfeHook        :: Hook PfeFlags

I've missed out runTests and a few others, but the main idea is that I got rid 
of most the pre- and post-hooks.  The configure command is special, because it 
doesn't see the LocalBuildInfo, so we also need a postConfHook for doing further 
actions after configure (e.g. running the autoconf script).

Suppose we supply thenHook:

  thenHook :: Hook a -> Hook a -> Hook a
  thenHook hook1 hook2 args flags pd lbi = do
    hook1 args flags pd lbi
    hook2 args flags pd lbi

Then to add a post-hook to the build command you could do this:

  addPostBuild :: Hook BuildFlags -> Hooks -> Hooks
  addPostBuild hook hooks
    = hooks { buildHook = buildHook hooks `thenHook` hook }

Alternatively, we could supply addPostBuild, addPreBuild, etc. and hide the 
representation of the Hooks type completely.



More information about the cabal-devel mailing list