building a helper binary with cabal

[resent after subscribing to the list] I'm working on a package that requires building a helper binary as part of its install process. This binary is written in C (with good reason), and on a Debian system would be installed into a path like /usr/lib/myproject/mybinary, because it's only used internally by the main program binary. My setup is up to this point:
gccProgram :: Program gccProgram = simpleProgram "gcc"
type BuildHook = PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO () creplChildBuild :: BuildHook -> BuildHook creplChildBuild oldhook desc buildinfo hooks flags = do oldhook desc buildinfo hooks flags rawSystemProgramConf (buildVerbose flags) gccProgram (buildPrograms flags) ["child.c", "-o", buildDir buildinfo > "child", "-ldl"]
buildHooks = simpleUserHooks { hookedPrograms = [gccProgram], buildHook = creplChildBuild (buildHook simpleUserHooks) }
main = defaultMainWithHooks buildHooks
However, it's unclear to me how to set up the install rule that would copy that binary into the appropriate place. Any tips? It seems I want to grab the "install" hook, but from there how do I specify where to install the file? There seem to be no helper functions to facilitate installing. (I appreciate that all of this is pretty new. So if it's not possible for me to do what I want, take it as an example of the sort of thing I'd like for Cabal to support...)

On Sat, 2008-03-01 at 00:02 -0800, Evan Martin wrote:
[resent after subscribing to the list]
I'm working on a package that requires building a helper binary as part of its install process.
I presume you mean a private binary that's needed at runtime, rather than a program like a code-gen needed at build time but not installed. Cabal does not have any direct support for private binaries, so you'll have to use a custom Setup.hs. Perhaps it should, if you can describe what you think is needed in general, file a feature request here: http://hackage.haskell.org/trac/hackage/
This binary is written in C (with good reason), and on a Debian system would be installed into a path like /usr/lib/myproject/mybinary, because it's only used internally by the main program binary.
Cabal knows about the 'libexec' path which sounds like it might be what you want, though that doesn't include any package subdir (though perhaps it should - it is currently unused). Alternatively just install into the 'libdir' which does include the package subdir. let dirs = absoluteInstallDirs pkg_descr lbi copydest copyFileVerbose verbosity file (libdir dirs > file) At runtime you can get the libexec and libdir that the user chose at configure time using the Paths_pkgname module which exports getLibDir and getLibexecDir amongst others. Duncan

On Sat, Mar 1, 2008 at 3:21 AM, Duncan Coutts
I presume you mean a private binary that's needed at runtime, rather than a program like a code-gen needed at build time but not installed.
That's right.
Cabal does not have any direct support for private binaries, so you'll have to use a custom Setup.hs.
Perhaps it should, if you can describe what you think is needed in general, file a feature request here: http://hackage.haskell.org/trac/hackage/
I can't think of many scenarios where they'd really be needed. The two instances I've seen are: 1) to write a simplified (commands via stdin, output via stdout) interface to an API that I don't want to link in (or don't want to write bindings for) 2) this particular program, which would be written in Haskell except that it does some C libdl tricks
Cabal knows about the 'libexec' path which sounds like it might be what you want, though that doesn't include any package subdir (though perhaps it should - it is currently unused). Alternatively just install into the 'libdir' which does include the package subdir.
let dirs = absoluteInstallDirs pkg_descr lbi copydest copyFileVerbose verbosity file (libdir dirs > file)
From the docs, it may be the case that copyDest is only available in
Thanks, this is very helpful! absoluteInstallDirs takes a few more args, so here's a longer example for the mailing list archives. I'm 98% of the way there, just need to figure out copyDest. the "copy" step? If that's right, does that mean the user needs to run the copy step manually?
type InstHook = PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()) creplChildInst :: InstHook -> InstHook creplChildInst oldhook desc buildinfo hooks flags = do oldhook desc buildinfo hooks flags let compilerpkg = compilerId (compiler buildinfo) let copydest = -- XXX still haven't figured this one out let dirtemplates = installDirTemplates buildinfo let dirs = absoluteInstallDirs desc compilerpkg copydest dirtemplates copyFileVerbose (installVerbose flags) (creplChildPath buildinfo) (libdir dirs > "child")
At runtime you can get the libexec and libdir that the user chose at configure time using the Paths_pkgname module which exports getLibDir and getLibexecDir amongst others.
Cool, thanks.

On Sat, Mar 1, 2008 at 9:17 AM, Evan Martin
let dirs = absoluteInstallDirs pkg_descr lbi copydest copyFileVerbose verbosity file (libdir dirs > file)
Thanks, this is very helpful! absoluteInstallDirs takes a few more args, so here's a longer example for the mailing list archives. I'm 98% of the way there, just need to figure out copyDest.
I see now that there are two different absoluteInstallDirs and you were referring to the other one. It's much simpler now. Though my confusion about CopyDest remains... (Is there a way to search Hoogle for "functions returning CopyDest"?)

On Sat, 2008-03-01 at 09:40 -0800, Evan Martin wrote:
On Sat, Mar 1, 2008 at 9:17 AM, Evan Martin
wrote: let dirs = absoluteInstallDirs pkg_descr lbi copydest copyFileVerbose verbosity file (libdir dirs > file)
Thanks, this is very helpful! absoluteInstallDirs takes a few more args, so here's a longer example for the mailing list archives. I'm 98% of the way there, just need to figure out copyDest.
I see now that there are two different absoluteInstallDirs and you were referring to the other one. It's much simpler now. Though my confusion about CopyDest remains...
The install phase is really two phases, copy and register. The copy phase has the CopyDest param. The default install hook just runs the copy and register phases. So you probably want to override the copy hook and not the install one. The hooks stuff is all really very confusing and unsatisfactory.
(Is there a way to search Hoogle for "functions returning CopyDest"?)
No idea, ask Neil. In this case it would not have helped since it's not the result of any function directly. It's an arg that your function will be passed. It does appear in a covariant position at least. Duncan

On Sun, Mar 2, 2008 at 10:11 AM, Duncan Coutts
The install phase is really two phases, copy and register. The copy phase has the CopyDest param. The default install hook just runs the copy and register phases. So you probably want to override the copy hook and not the install one.
It seems the default copy hook just runs the install hook, and that the install hook doesn't run the copy one... ? http://haskell.org/ghc/docs/latest/html/libraries/Cabal/src/Distribution-Sim... copyHook = \desc lbi _ f -> install desc lbi f, -- has correct 'copy' behavior with params I'm sure I'm just missing something here, but my "postCopy" hook doesn't seem to be running with "install -v3".
The hooks stuff is all really very confusing and unsatisfactory.
I agree, but I can also appreciate how difficult it must be to design, and can acknowledge that it may be the case that it really just needs to be this complicated. Having used autoconf and friends before, one thing I really prefer about this system is that there are bazillion different types which helps prevent you from accidentally doing something like putting a intermediate object in the source dir or installing while ignoring the user's prefix.

Sorry, this dropped of my to-reply-to list, did you get this figured out? Duncan On Sun, 2008-03-02 at 14:34 -0800, Evan Martin wrote:
On Sun, Mar 2, 2008 at 10:11 AM, Duncan Coutts
wrote: The install phase is really two phases, copy and register. The copy phase has the CopyDest param. The default install hook just runs the copy and register phases. So you probably want to override the copy hook and not the install one.
It seems the default copy hook just runs the install hook, and that the install hook doesn't run the copy one... ?
http://haskell.org/ghc/docs/latest/html/libraries/Cabal/src/Distribution-Sim... copyHook = \desc lbi _ f -> install desc lbi f, -- has correct 'copy' behavior with params
I'm sure I'm just missing something here, but my "postCopy" hook doesn't seem to be running with "install -v3".
The hooks stuff is all really very confusing and unsatisfactory.
I agree, but I can also appreciate how difficult it must be to design, and can acknowledge that it may be the case that it really just needs to be this complicated. Having used autoconf and friends before, one thing I really prefer about this system is that there are bazillion different types which helps prevent you from accidentally doing something like putting a intermediate object in the source dir or installing while ignoring the user's prefix.

I got caught up in other things, so I'm also late to reply. Here's the whole story now, so you don't have to reskim the archive. My program has a helper executable that's built with gcc. I want to install it alongside my Haskell binary. I can write a post-copy hook like this:
creplChildCopy :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO () creplChildCopy args flags desc buildinfo = do print "copy hook" let dirs = absoluteInstallDirs desc buildinfo (copyDest flags) print ("copying child to ", libexecdir dirs > creplChildName) copyFileVerbose (copyVerbose flags) (creplChildPath buildinfo) (libexecdir dirs > creplChildName)
And then run it like this:
$ ./Setup.lhs copy -v3
directory dist/doc/html/c-repl does exist: False
Creating /home/martine/.local/share/doc/c-repl-0.1 (and its parents)
copy LICENSE to /home/martine/.local/share/doc/c-repl-0.1/LICENSE
Installing: /home/martine/.local/bin
Creating /home/martine/.local/bin (and its parents)
copy dist/build/c-repl/c-repl to /home/martine/.local/bin/c-repl
"copy hook"
("copying child to ","/home/martine/.local/libexec/c-repl-child")
copy dist/build/c-repl-child to /home/martine/.local/libexec/c-repl-child
Setup.lhs: /home/martine/.local/libexec: copyFile: does not exist (No
such file or directory)
That fails because this "libexec" dir doesn't exist.
My questions are:
1) Is it my responsibility to create the libexec dir if it doesn't exist?
2) How can I get the "copy" phase to run as part of "install"?
3) Do you have advice on installed vs. uninstalled paths? Currently
to facilitate development when getting the path to that executable I
do something like this (where "Paths" is the module generated by
cabal):
findChildBinary :: IO (Either String FilePath)
findChildBinary = do
let path = "dist/build/c-repl-child"
ok1 <- isReadable path
if ok1
then return (Right path)
else do
libexecdir <- Paths_c_repl.getLibexecDir
let path = libexecdir ++ "/c-repl-child"
ok2 <- isReadable path
if ok2
then return (Right path)
else return (throwError "can't find child executable")
where
isReadable path =
do
perms <- getPermissions path
return $ readable perms
`catch` \e -> return False
On Thu, Mar 20, 2008 at 7:29 AM, Duncan Coutts
Sorry, this dropped of my to-reply-to list, did you get this figured out?
Duncan
On Sun, 2008-03-02 at 14:34 -0800, Evan Martin wrote:
On Sun, Mar 2, 2008 at 10:11 AM, Duncan Coutts
wrote: The install phase is really two phases, copy and register. The copy phase has the CopyDest param. The default install hook just runs the copy and register phases. So you probably want to override the copy hook and not the install one.
It seems the default copy hook just runs the install hook, and that the install hook doesn't run the copy one... ?
http://haskell.org/ghc/docs/latest/html/libraries/Cabal/src/Distribution-Sim... copyHook = \desc lbi _ f -> install desc lbi f, -- has correct 'copy' behavior with params
I'm sure I'm just missing something here, but my "postCopy" hook doesn't seem to be running with "install -v3".
The hooks stuff is all really very confusing and unsatisfactory.
I agree, but I can also appreciate how difficult it must be to design, and can acknowledge that it may be the case that it really just needs to be this complicated. Having used autoconf and friends before, one thing I really prefer about this system is that there are bazillion different types which helps prevent you from accidentally doing something like putting a intermediate object in the source dir or installing while ignoring the user's prefix.

On Wed, 2008-04-02 at 21:07 -0700, Evan Martin wrote:
I got caught up in other things, so I'm also late to reply.
Here's the whole story now, so you don't have to reskim the archive. My program has a helper executable that's built with gcc. I want to install it alongside my Haskell binary. I can write a post-copy hook like this:
creplChildCopy :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO () creplChildCopy args flags desc buildinfo = do print "copy hook" let dirs = absoluteInstallDirs desc buildinfo (copyDest flags) print ("copying child to ", libexecdir dirs > creplChildName) copyFileVerbose (copyVerbose flags) (creplChildPath buildinfo) (libexecdir dirs > creplChildName)
And then run it like this:
$ ./Setup.lhs copy -v3 directory dist/doc/html/c-repl does exist: False Creating /home/martine/.local/share/doc/c-repl-0.1 (and its parents) copy LICENSE to /home/martine/.local/share/doc/c-repl-0.1/LICENSE Installing: /home/martine/.local/bin Creating /home/martine/.local/bin (and its parents) copy dist/build/c-repl/c-repl to /home/martine/.local/bin/c-repl "copy hook" ("copying child to ","/home/martine/.local/libexec/c-repl-child") copy dist/build/c-repl-child to /home/martine/.local/libexec/c-repl-child Setup.lhs: /home/martine/.local/libexec: copyFile: does not exist (No such file or directory)
That fails because this "libexec" dir doesn't exist.
My questions are: 1) Is it my responsibility to create the libexec dir if it doesn't exist?
At the moment, yes. Though Cabal knows of the libexec dir it doesn't actually use it at all for anything yet. We have no particular support for private binaries that you'd want to install into the libexec dir.
2) How can I get the "copy" phase to run as part of "install"?
From my reading of the code, the copyHook is only run by the copy action and not the install action as I previously suggested. So you'd have to override the instHook rather than the copyHook.
3) Do you have advice on installed vs. uninstalled paths? Currently to facilitate development when getting the path to that executable I do something like this (where "Paths" is the module generated by cabal):
We recently added support for finding data files when the package is in the build tree rather than in its final installed location. We do that by having the Paths_foo module check an env var which if set provides the datadir. There's no reason we could not generalise that and support it for each of bindir, libdir, libexec dir etc. If that would help you perhaps you could file a ticket. Duncan
participants (2)
-
Duncan Coutts
-
Evan Martin