Updating GHC on Arch
Arch is somewhat of a hybrid distribution in the sense that if you have any
sort of ‘peculiar’ needs then you are likely to have to build packages from
source. As expected developing in Haskell is a “peculiar need”
After every upgrade of GHC I find myself in the situation where the system
(pacman) and GHC have different views of what packages are available. What is
needed then is somehow finding out the difference, and this is how I found
that difference after the recent 6.10.3 -> 6.10.4 upgrade of GHC. Once I know
what packages are missing from GHC’s view of the world I can use pacman to
first remove and then yaourt to rebuild the packages.
First I noted that the old package.conf file wasn’t deleted during the
upgrade, apparently pacman noted the changes that installing packages
resulted in and saved the file as package.conf.pacsave. Finding the name of
all the ‘missing’ packages was then as simple as loading both
/usr/lib/ghc-6.10.3/package.conf.pacsave and
/usr/lib/ghc-6.10.4/package.conf, filter out the package names and take the
difference:
printMissingPackages = let pkgNameStr = PackageName . display . packageName in do oldPackConf <- readFile "/usr/lib/ghc-6.10.3/package.conf.pacsave" curPackConf <- readFile "/usr/lib/ghc-6.10.4/package.conf" let oldPacks = (read oldPackConf) :: [InstalledPackageInfo_ String] let curPacks = (read curPackConf) :: [InstalledPackageInfo_ String] let gonePacks = (map pkgNameStr oldPacks) \\ (map pkgNameStr curPacks) putStrLn "Missing packages:" mapM_ (putStrLn . display) gonePacks |
That isn’t the most useful output however, so I decided to modify it to print
out the name of the Arch package that needed re-compilation. The following
functions generates the name of the .hi of the first module in the Haskell
package, it then uses pacman to look up the owner of the file:
ghcPkg2ArchPkg pkg = let hsFileLoc = head $ libraryDirs pkg hsFile = map (\ c -> if c == '.' then '/' else c) $ head $ exposedModules pkg hsFullFile = hsFileLoc </> hsFile <.> "hi" in do exists <- doesDirectoryExist hsFullFile if exists then liftM Just $ archOwnerOfFile hsFullFile else return Nothing archOwnerOfFile fn = let pkgFromPacmanOutput = head . tail . reverse . words in do res <- rawSystemStdout silent "/usr/bin/pacman" ["-Qo", fn] return $ pkgFromPacmanOutput res |
Now I can find the list of Arch packages that aren’t known to the new version
of GHC by mapping ghcPkg2ArchPkg over gonePkgs. In other words that is
the list of packages that need to be removed, but that can be different from
the list of packages that needs to be rebuilt with yaourt (basically I only
want to tell it to build and install the ‘top-level’ packages, i.e. packages
that aren’t dependencies of any other packages. It’s of course possible to
build that second list from the first one, with the help of pacman.
archGetRequiredBy pkg = let extractPkgs pkgDesc = let deps = (drop 3 . words . head . filter (isPrefixOf "Required By") . lines) pkgDesc in if deps == ["None"] then [] else deps in do res <- rawSystemStdout silent "/usr/bin/pacman" ["-Qi", pkg] return $ extractPkgs res |
Now I can modify printMissingPackages to print some more useful information.
This is the full function:
printMissingPackages = let pkgNameStr = PackageName . display . packageName in do oldPackConf <- readFile "/usr/lib/ghc-6.10.3/package.conf.pacsave" curPackConf <- readFile "/usr/lib/ghc-6.10.4/package.conf" let oldPacks = (read oldPackConf) :: [InstalledPackageInfo_ String] let curPacks = (read curPackConf) :: [InstalledPackageInfo_ String] let gonePacks = (map pkgNameStr oldPacks) \\ (map pkgNameStr curPacks) putStrLn "Missing packages:" mapM_ (putStrLn . display) gonePackprints let gonePkgs = filter (\ p -> pkgNameStr p `elem` gonePacks) oldPacks archPkgs <- liftM catMaybes $ mapM ghcPkg2ArchPkg gonePkgs putStrLn "Packages to remove:" mapM_ putStrLn archPkgs archTopPkgs <- filterM (liftM ([] ==) . archGetRequiredBy) archPkgs putStrLn "\nPackages to install:" mapM_ putStrLn archTopPkgs |
On my system it produced the following output:
Missing packages:
terminfo
vty
wl-pprint
Packages to remove:
haskell-terminfo
haskell-vty
haskell-wl-pprint
Packages to install:
haskell-vty
haskell-wl-pprint
And after a quick pacman -Rn ... and a not so quick yaourt -S ... I reran
it and the output was
Packages to remove:
Packages to install:
Exactly as expected.
Gentoo Linux users interested in doing the equivalent can use the haskell-updater tools, which replaces our previous ghc-updater tool. It’s available both through the overlay and from the portage tree. See the announce here: http://gentoohaskell.wordpress.com/2009/07/22/announcing-haskell-updater/
@Lennart Kolmodin, yes, I’ve looked at that. The long-term goal is to rework my code into a similar tool for Arch. One thing I need to look at a bit more carefully is how to handle updated libraries.
[...] A few days ago I noticed that there were a few Haskell packages on AUR that had received updates. This was the excuse I had been waiting for to address the second part of keeping my Haskell packages up-to-date (I’ve written about the first part before, dealing with an update to GHC). [...]