[Haskell-cafe] Install a script with Cabal?
Rogan Creswick
creswick at gmail.com
Tue Jun 5 19:07:42 CEST 2012
On Tue, Jun 5, 2012 at 4:28 AM, Ketil Malde <ketil at malde.org> wrote:
> 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).
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
More information about the Haskell-Cafe
mailing list