Haskell Cabal, now with extra crunchy RPM goodness

The Haskell community has a very nice implementation-independent mechanism for building libraries and applications, called Cabal.

I spent a few hours over the past couple of days hacking on Cabal to add the ability to build RPM packages. You can fetch my darcs repository from here:

darcs get --partial http://darcs.serpentine.com/cabal-rpm

This new capability is easy to use. It adds a single new Cabal command, called rpm.

runhaskell Setup.*hs rpm

This generates a spec file, and builds source and binary RPMs.

Here’s a quick example, of trying to build the Haskell XML-RPC library:

~/src/darcs/haxr $ runghc Setup.*hs rpm
Source tarball created: dist/SOURCES/haxr-3000.0.0.tar.gz
error: Failed build dependencies:
    HaXml-ghc66 >= 1.13 is needed by haxr-3000.0.0-1.i386
    HaXml-ghc66 < 1.14 is needed by haxr-3000.0.0-1.i386
Setup.lhs: rpmbuild failed with status 1

The rpm command has converted the dependencies in the haxr.cabal file into build-time and runtime dependencies in the haxr.spec file that it generated, but rpmbuild can’t find the HaXml package.

Having earlier built a HaXml package using the rpm command, I can install it with the system’s rpm command.

# rpm -i haxml-ghc66-1.13.2-1.i386.rpm 
Reading package info from stdin ... done.
Saving old package config file... done.
Writing new package config file... done.

The RPM’s post-install scriptlet informs GHC’s package manager about the package’s availability:

# ghc-pkg list --simple | tr ' ' '\n' | grep -i haxml

Now if I try to build haxr again, it will succeed.

By default, the rpm command builds both normal and profiling-enabled libraries. It also uses Haddock to generate library documentation. It’s possible to control these behaviours from the command line.

The command also provides a --gen-spec option, which only generates a spec file. You can use this >spec file as a basis for crafting one of your own.

I’ve used the rpm command to build about a third of the packages listed in the Hackage package database, with no problems.

Posted in haskell
One comment on “Haskell Cabal, now with extra crunchy RPM goodness
  1. Tom Moertel says:


    As a fellow Fedora Core user, I am delighted to learn of your RPM additions to Cabal. I’ve written more than a few spec files for Haskell packages in the past, and I look forward to the day when I’m free of that task.

    Let me point out a wrinkle I encountered when registering RPM-installed packages with GHC. When upgrading an RPM file that contains the same Cabal name+version of a package as the previously installed version (say with only an RPM release being different), the %pre, %post, %preun, and %postun scripts will all be asking GHC to register/unregister what it thinks are the same library. As a result, when the %preun script is called, which occurs *after* the new package is installed but before the old package is removed, its unregister script will actually unregister the newly installed library, leaving the new library unregistered with GHC.

    To test for the problem, take the existing spec file for an already-installed package, bump its RPM release, rebuild the RPMs, and then try to upgrade to the new version. After the upgrade, GHC will no longer know about the package.

    To avoid this problem, I’ve worked out the following spec-script dance:

    # Scripts for registering/unregistering w/ GHC. Note that
    # the scripts are called in the following order during an upgrade:
    # %pre for new version of package being installed
    # ... (all new files are installed)
    # %post for new version of package being installed
    # %preun for old version of package being removed
    # ... (all old files are removed)
    # %postun for old version of package being removed

    # in case we're upgrading to a library having the same Cabal
    # name+version as the currently installed library, we need to
    # unregister the old library first, so that the register script in
    # %post may succeed. (note that this command runs *before* the new
    # package's files are installed, and thus will execute the *previous*
    # version's unregister script, if the script exists in the same location
    # as the about-to-be-installed library's script)
    [ "$1" = 2 ] && %{pkgscripts}/unregister.sh >& /dev/null || :

    %{pkgscripts}/register.sh >& /dev/null

    # always unregister the library before we remove its files
    # (see %postun, however)
    %{pkgscripts}/unregister.sh >& /dev/null || :

    # if we're upgrading, the %preun step may have unregistered
    # the *new* version of the library (if it had an identical
    # Cabal name+version, even though the RPM release differs);
    # therefore, we must attempt to re-register it
    [ "$1" = 1 ] && %{pkgscripts}/register.sh >& /dev/null || :

    I hope all that makes sense. Anyway, feel free to borrow this logic for your additions to Cabal.

    If I can help in any other way, please let me know.

    Cheers, Tom.

Leave a Reply

Your email address will not be published. Required fields are marked *