[Haskell-cafe] Install a script with Cabal?

Ketil Malde ketil at malde.org
Tue Jun 5 13:28:46 CEST 2012

Rogan Creswick <creswick at gmail.com> writes:

>> I have a small project that installs a couple of Haskell tools and a
>> script that uses these. Cabal will of course build and install the
>> Haskell programs, but how can I get Cabal to install the script as
>> well? There's a host of UserHooks available¹, but it'd probably be
>> better to have an example than to try to experiment with the right
>> configuration.

> I don't have an example handy, but I think you'll want preInts,
> instHook, or postInst.  I'd probably go with postInst unless you need
> data provided by instHook's type that isn't passed to preInst or
> postInst.

I found an example¹ using copyHook. However, this seems to be the wrong
thing, at least I am unable to get cabal to ever call this hook (or
preCopy, etc).

> LocalBuildInfo /probably/ has the details you need (eg: installDirTemplates).

I just printed out the contents of LocalBuildInfo, but that's 33 pages
(I counted) of output.  Redirected to a file, it makes it easier to
search to find the relevant needles in this haystack.

Okay, looking at installDirTemplates, I see `bindir` can extract this
data member.  It's of course an InstallDirTemplate, not a FilePath, but
I found 'fromPathTemplate', which has the right type.  Except it only
dumps the template, with $prefix and all.

Hoogle doesn't seem to know about any of the Cabal stuff, Hayoo has a *very*
annoying behavior where it cleans out everything you did if you use the
"back" button, and the "source" links it advertises seem to point into
the wide blue 404.  But using the latter, I managed to find a link to
'substPathTemplate', worked out that the PackageIdentifier it needs is a
member of PackageDescription, and tried to use that.  Except it gives
the same result, and doesn't appear to substitute anything.  I guess the
fact that it is undocumented is a hint that it's the wrong thing to use.

Reading some more, maybe 'absoluteInstallDirs' is what I'm looking for?
In addition to the usual heap of huge config structs, it needs a
"CopyDest", though.  Since I have no idea what to use, I just put
NoCopyDest there.  Does that make sense?  Okay, it works now, in the
sense that I ran it once on my laptop, and it copied the file to
~/.cabal/bin.  Here is the code, comments welcome:

  #!/usr/bin/env runhaskell

  import Distribution.Simple (defaultMainWithHooks, simpleUserHooks,UserHooks(..))
  import Distribution.Simple.Setup (InstallFlags,CopyDest(..))
  import Distribution.Simple.Utils (rawSystemExit)
  import Distribution.PackageDescription (PackageDescription())
  import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..), InstallDirs(..), absoluteInstallDirs)
  import Distribution.Verbosity (normal)

  main :: IO ()
  main = defaultMainWithHooks simpleUserHooks
      { instHook = \pd lbi uh ifs -> myinst pd lbi uh ifs >> instHook simpleUserHooks pd lbi uh ifs }

  myinst :: PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()
  myinst pd lbi _uh _fs = do
      let bin = bindir $ absoluteInstallDirs pd lbi NoCopyDest
      rawSystemExit normal "cp" ["asmeval", bin]

PS: This was a rather frustrating excercise, and after wallowing through
a wilderness of modules, types, records and functions, I can't help but
feel that Cabal is a bit overengineered. Surely including a script or
similar in a package isn't all that outlandish? Could there conceivably
have been a simpler way?  Or at least, better documented?  I suspect the
fact that - with the sole exception of the link below -- I could find *no*
examples to help me out indicates that it is a bit too complicated.


¹ http://blog.ezyang.com/2010/06/setting-up-cabal-the-ffi-and-c2hs/
If I haven't seen further, it is by standing in the footprints of giants

More information about the Haskell-Cafe mailing list