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
HaXml-1.13.2
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.
Bryan,
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
#
# 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
%pre
# 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 || :
%post
%{pkgscripts}/register.sh >& /dev/null
%preun
# always unregister the library before we remove its files
# (see %postun, however)
%{pkgscripts}/unregister.sh >& /dev/null || :
%postun
# 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.