Linking executables and the library of a Cabal package description

I'm sure this will be an easy question for those on this list. What do you need to add to a Cabal package description so as to allow an executable in the description to be built from the library it also describes? I searched the Cabal documentation many times, but came up empty handed. John $ make runhaskell Setup.hs build Preprocessing library a-1.0... Preprocessing executables for a-1.0... Building a-1.0... [1 of 1] Compiling A ( A.hs, dist/build/A.o ) /usr/bin/ar: creating dist/build/libHSa-1.0.a exec/Main.hs:3:7: Could not find module `A': Use -v to see a list of the files searched for. make: *** [all] Error 1 $ : after cleaning... $ more `find . -type f ` > ../a.txt $ cat ../a.txt :::::::::::::: ./a.cabal :::::::::::::: Name: a Version: 1.0 Build-Depends: base Exposed-Modules: A Executable: b Main-Is: Main.hs Other-Modules: A Hs-Source-Dirs: exec :::::::::::::: ./Setup.hs :::::::::::::: import Distribution.Simple main = defaultMain :::::::::::::: ./Makefile :::::::::::::: SETUP = runhaskell Setup.hs all: if test ! -f .setup-config; then $(SETUP) configure; fi $(SETUP) build Makefile: @echo make $@ %: force $(SETUP) $@ .PHONY: all force :::::::::::::: ./A.hs :::::::::::::: module A where a :: Int a = 3 :::::::::::::: ./exec/Main.hs :::::::::::::: module Main (main) where import A main :: IO () main = print a $ ghc -h ghc-6.6.1: unrecognised flags: -h Usage: For basic information, try the `--help' option. $ uname -a Linux goo 2.6.22.9-91.fc7 #1 SMP Thu Sep 27 23:10:59 EDT 2007 i686 i686 i386 GNU/Linux $

John D. Ramsdell wrote:
I'm sure this will be an easy question for those on this list. What do you need to add to a Cabal package description so as to allow an executable in the description to be built from the library it also describes? I searched the Cabal documentation many times, but came up empty handed.
John
$ make runhaskell Setup.hs build Preprocessing library a-1.0... Preprocessing executables for a-1.0... Building a-1.0... [1 of 1] Compiling A ( A.hs, dist/build/A.o ) /usr/bin/ar: creating dist/build/libHSa-1.0.a
exec/Main.hs:3:7: Could not find module `A': Use -v to see a list of the files searched for.
I'm not sure exactly what problem you're running into here, but my advice would be to split the library off into a separate package. Libraries and executables in the same package are not well supported by Cabal. Really we should fix or remove that feature, my vote would be to remove it. Cheers, Simon

Simon Marlow
Really we should fix or remove that feature, my vote would be to remove it.
Simon, Thank you for your quick reply. Whatever is done about this feature, I urge a change be made in the documentation as soon as possible. Section two of the Cabal documentation includes Example 3, an example even more complex than the one I sent to the list. It's prominent position in the document suggested to me that this feature is well tested, and that any problem I was having using it was, as they say, user error. I would not have spent so much time rereading the document had I know what I do now. For my application, it is natural to package it as a library and several binaries built on top of the library, so I would like to see this feature supported, but the description of it removed from the documentation until it works. John

Simon Marlow
Libraries and executables in the same package are not well supported by Cabal.
I'm starting to believe the situation is not as bad as you suggest. I recently realized a post configuration hook can be used to generate the needed package configuration information, so that it is easy to write packages in which executables are linked to the library in the package. I have enclosed the code that does so. There is one remaining problem, you have to figure out how to force executables to be rebuilt when only the library changes. I use a makefile to delete all the binaries before I run Setup.hs build. John $ make find dist -type f -perm /u+x -delete runghc Setup.hs build Preprocessing library a-1.0... Preprocessing executables for a-1.0... Building a-1.0... [1 of 1] Compiling A ( A.hs, dist/build/A.o ) /usr/bin/ar: creating dist/build/libHSa-1.0.a [1 of 1] Compiling Main ( exec/Main.hs, dist/build/b/b-tmp/Main.o ) Linking dist/build/b/b ... $ make clean runghc Setup.hs clean cleaning... $ rm package.conf $ more `find . -type f ` > ../a.txt $ cat ../a.txt :::::::::::::: ./a.cabal :::::::::::::: Name: a Version: 1.0 Build-Depends: base Exposed-Modules: A Executable: b Main-Is: Main.hs Hs-Source-Dirs: exec ghc-options: -package-conf package.conf -package a :::::::::::::: ./Setup.hs :::::::::::::: -- Provide support for executables that link with the library defined -- within a package. The trick is to generate a package description -- that supports the linking phase. import Distribution.Simple import qualified Distribution.InstalledPackageInfo as I import Distribution.PackageDescription import Distribution.Setup import Distribution.Simple.LocalBuildInfo import System.IO import System.Exit -- The package description is generated using a post configuration hook. main = defaultMainWithHooks userHooks userHooks = defaultUserHooks { postConf = conf } -- The hook always runs the old actions last. old = postConf defaultUserHooks -- If the package defines no library, do nothing. conf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ExitCode conf args flags pkg bld = case library pkg of Nothing -> old args flags pkg bld Just lib -> confLib lib args flags pkg bld -- Create package descriptions and then print it. confLib :: Library -> Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ExitCode confLib lib args flags pkg bld = do let hsLib = "HS" ++ showPackageId (package pkg) let info0 = I.emptyInstalledPackageInfo let info1 = info0 { I.package = package pkg, I.exposed = True, I.exposedModules = exposedModules lib, I.importDirs = [buildDir bld], I.libraryDirs = [buildDir bld], I.hsLibraries = [hsLib] } writeFile "package.conf" (show [info1]) old args flags pkg bld -- A clean hook should be written that deletes package.conf. :::::::::::::: ./Makefile :::::::::::::: SETUP = runghc Setup.hs CONFIGURE = $(SETUP) configure --ghc all: @if test ! -f .setup-config; \ then echo $(CONFIGURE); $(CONFIGURE); fi find dist -type f -perm /u+x -delete $(SETUP) build Makefile: @echo make $@ %: force $(SETUP) $@ .PHONY: all force :::::::::::::: ./A.hs :::::::::::::: module A where a :: Int a = 3 :::::::::::::: ./exec/Main.hs :::::::::::::: module Main (main) where import A main :: IO () main = print a

On Mon, 2007-10-15 at 14:32 -0400, John D. Ramsdell wrote:
I'm sure this will be an easy question for those on this list. What do you need to add to a Cabal package description so as to allow an executable in the description to be built from the library it also describes? I searched the Cabal documentation many times, but came up empty handed.
Name: a Version: 1.0 Build-Depends: base Exposed-Modules: A
Executable: b Main-Is: Main.hs Other-Modules: A Hs-Source-Dirs: exec
I think the problem here is search paths. Try adding . to the Hs-Source-Dirs, or alternatively moving everything into the root dir rather than under exec. This smells like a bug in cabal to me. Duncan

Duncan Coutts
I think the problem here is search paths. Try adding . to the Hs-Source-Dirs, or alternatively moving everything into the root dir rather than under exec.
If you add . to the Hs-Source-Dirs, you build module A twice, once for the library, and once for the building of b. Notice b is not linked with the library. $ runhaskell Setup.hs build Preprocessing library a-1.0... Preprocessing executables for a-1.0... Building a-1.0... [1 of 1] Compiling A ( A.hs, dist/build/A.o ) /usr/bin/ar: creating dist/build/libHSa-1.0.a [1 of 2] Compiling A ( A.hs, dist/build/b/b-tmp/A.o ) [2 of 2] Compiling Main ( exec/Main.hs, dist/build/b/b-tmp/Main.o ) Linking dist/build/b/b ... $
This smells like a bug in cabal to me.
Time for me to submit a bug report? John

On Tue, 2007-10-16 at 20:09 -0400, John D. Ramsdell wrote:
Duncan Coutts
writes: I think the problem here is search paths. Try adding . to the Hs-Source-Dirs, or alternatively moving everything into the root dir rather than under exec.
If you add . to the Hs-Source-Dirs, you build module A twice, once for the library, and once for the building of b. Notice b is not linked with the library.
Correct. There is a design choice here. If you link with the library you cannot use private modules. If you go direct to the source you can use anything but have to build twice. To link with the lib, use a separate .cabal file that has an executable.
$ runhaskell Setup.hs build Preprocessing library a-1.0... Preprocessing executables for a-1.0... Building a-1.0... [1 of 1] Compiling A ( A.hs, dist/build/A.o ) /usr/bin/ar: creating dist/build/libHSa-1.0.a [1 of 2] Compiling A ( A.hs, dist/build/b/b-tmp/A.o ) [2 of 2] Compiling Main ( exec/Main.hs, dist/build/b/b-tmp/Main.o ) Linking dist/build/b/b ... $
This smells like a bug in cabal to me.
Time for me to submit a bug report?
For the search path problem, yes. So looking at the code, it seems we add '.' as a default, but if you set any hs-src-dirs explicitly then you don't get the '.' default, you have to add that explicitly. Perhaps this is the right behaviour. It's a bit confusing though. Duncan

Duncan Coutts
If you add . to the Hs-Source-Dirs, you build module A twice, once for the library, and once for the building of b. Notice b is not linked with the library.
Correct.
There is a design choice here. If you link with the library you cannot use private modules.
That's good. I would expect you cannot use the private modules. Otherwise, they wouldn't be very private! One of the points of linking against the library is to ensure clients have access to only the exposed modules.
If you go direct to the source you can use anything but have to build twice.
This is obviously not what anyone would want.
To link with the lib, use a separate .cabal file that has an executable.
This is obviously not what one wants. The executables and the library are conceptually part of the same unit of code. At the very least, the Cabal documentation should up front about this design choice. The text surrounding Example 3 in Section 2 should explain that the generated library is not used to build the executables in the package. Having said all of that, are you sure there is not something I can add to get the behavior I'm looking for? In the real application, the library sources are in src/lib, and one of the executables is in src/basic. Cabal builds the library in dist/build, and builds the executable in dist/build/basic. It seems that everything needed for linking with every executable in my package is in its parent directory. Perhaps the stanza describing an executable has to contain a property for extra-lib-dirs, extra-libraries, or something about includes. Shouldn't I be able to tell the linker for an executable to look in parent of its build directory? John

ramsdell@mitre.org (John D. Ramsdell) writes:
To link with the lib, use a separate .cabal file that has an executable.
This is obviously not what one wants. The executables and the library are conceptually part of the same unit of code.
But with different requirements (i.e. the executable depends on the library, but clearly the library does not depend on itself). I think two cabal files is a good solution, how about using the name as a parameter to Setup.hs? E.g., "./Setup.hs configure foo" would prepare to build using "foo.cabal"? -k -- If I haven't seen further, it is by standing in the footprints of giants

I have a solution to the linking problem, at least for ghc users. I added the -v option to my "runhaskell Setup.hs build" command, and tried adding various properties. The break came when I realized ghc was being given the --make option, and with this option, one cannot satisfy it only with an interface file. Ghc either gets the information it needs from a source file, or from a package. So the trick is to include a package.conf file in your distribution that specifies only relative paths (dist/build). You then add ghc specific options to your Cabal package description. a.cabal --------------------- Name: a Version: 1.0 Build-Depends: base Exposed-Modules: A Executable: b Main-Is: Main.hs Hs-Source-Dirs: exec ghc-options: -package-conf package.conf -package a --------------------- package.conf --------------------- [InstalledPackageInfo {package = PackageIdentifier {pkgName = "a", pkgVersion = Version {versionBranch = [1,0], versionTags = []}}, license = AllRightsReserved, copyright = "", maintainer = "", author = "", stability = "", homepage = "", pkgUrl = "", description = "", category = "", exposed = True, exposedModules = ["A"], hiddenModules = [], importDirs = ["dist/build"], libraryDirs = ["dist/build"], hsLibraries = ["HSa-1.0"], extraLibraries = [], extraGHCiLibraries = [], includeDirs = ["dist/build/include"], includes = [], depends = [PackageIdentifier {pkgName = "base", pkgVersion = Version {versionBranch = [2,1,1], versionTags = []}}], hugsOptions = [], ccOptions = [], ldOptions = [], frameworkDirs = [], frameworks = [], haddockInterfaces = ["/home/ramsdell/proj/b/goo/share/a-1.0/doc/html/a.haddock"], haddockHTMLs = ["/home/ramsdell/proj/b/goo/share/a-1.0/doc/html"]}] --------------------- So it's not a Cabal bug after all! In my package.conf file, can I get away with just specifying the pkgName, exposed, exposedModules, importDirs, libraryDirs, and hsLibraries fields? John

On Wed, 2007-10-17 at 09:07 -0400, John D. Ramsdell wrote:
Duncan Coutts
writes: If you add . to the Hs-Source-Dirs, you build module A twice, once for the library, and once for the building of b. Notice b is not linked with the library.
Correct.
There is a design choice here. If you link with the library you cannot use private modules.
That's good. I would expect you cannot use the private modules. Otherwise, they wouldn't be very private! One of the points of linking against the library is to ensure clients have access to only the exposed modules.
If you go direct to the source you can use anything but have to build twice.
This is obviously not what anyone would want.
It is exactly what some people want for test programs. They want to be able to access the private modules to test functions that are not exposed in the library public api.
To link with the lib, use a separate .cabal file that has an executable.
This is obviously not what one wants. The executables and the library are conceptually part of the same unit of code.
At the very least, the Cabal documentation should up front about this design choice. The text surrounding Example 3 in Section 2 should explain that the generated library is not used to build the executables in the package.
Having said all of that, are you sure there is not something I can add to get the behavior I'm looking for? In the real application, the library sources are in src/lib, and one of the executables is in src/basic. Cabal builds the library in dist/build, and builds the executable in dist/build/basic. It seems that everything needed for linking with every executable in my package is in its parent directory. Perhaps the stanza describing an executable has to contain a property for extra-lib-dirs, extra-libraries, or something about includes. Shouldn't I be able to tell the linker for an executable to look in parent of its build directory?
They can't share a build directory since there's nothing that says that they build the same modules using the same build flags. you could try and hack it but I wouldn't recommend it. The right way to solve this is to only allow a single library or executable in a package and to make it possible to have multiple .cabal files in a directory. Then they can describe components that may depend on each other and we should be able to build the dependent components automatically. Then it's possible to either make an executable that depend on the library, or to make one that uses the modules directly. Duncan

Duncan Coutts
The right way to solve this ...
I think the right way to solve this is to have Cabal generate a package.conf file in dist/build for development whenever it successfully builds a library. If people want to link executables in package with the library in the package, they need only to add -package-conf dist/build/package.conf -package PACKAGE to their ghc-options property. If they prefer the current behavior, they leave the ghc-options property as it is. With this scheme, everybody gets what they want. John
participants (4)
-
Duncan Coutts
-
Ketil Malde
-
ramsdell@mitre.org
-
Simon Marlow