
On Tue, Jun 5, 2012 at 4:28 AM, Ketil Malde
Rogan Creswick
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).
I'm not sure if/when copy hooks are used during the typical cabal configure/build/install process; I suspect they are not used. There is, however, a `cabal copy` command, which I believe does. Not that that helps you right now, but that may explain the behavior you saw.
LocalBuildInfo /probably/ has the details you need (eg: installDirTemplates).
This was harder than I thought. Here's an example (I hacked an example on to the cabal-dev Setup.hs, so there are extra imports you won't necessarily need -- full Setup.hs is in the linked gist, the email only contains the relevant exceprts): Gist: https://gist.github.com/2876277 main = defaultMainWithHooks $ simpleUserHooks { hookedPrograms = [cabalInstallProgram] , postInst = postInstCp (postInst simpleUserHooks) } type PostInstHook = Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO () postInstCp :: PostInstHook -> Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO () postInstCp oldHook args iflags pDesc lbi = do let -- The filename to copy from: inFile :: FilePath inFile = "srcFileName.sh" -- The filename to copy to: outFile :: FilePath outFile = "destFileName.sh" prefix = fromFlag $ installDistPref iflags -- Make a concrete binDir from the LocalBuildInfo & PackageDescription: instBinDir :: FilePath instBinDir = bindir $ absoluteInstallDirs pDesc lbi (fromFlag $ copyDest defaultCopyFlags) -- layer of indirection, in case we wanted to get a specific -- src directory from the cabal file: src :: FilePath src = inFile -- qualify the destination. dest :: FilePath dest = instBinDir > outFile -- Do the copy, creating outFile in the bin dir: copyFile src dest -- now invoke the old hook: oldHook args iflags pDesc lbi --Rogan
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.
-k
¹ 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