Updating GHC on Arch
- Magnus Therning
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:
= let
printMissingPackages = PackageName . display . packageName
pkgNameStr in do
<- readFile "/usr/lib/ghc-6.10.3/package.conf.pacsave"
oldPackConf <- readFile "/usr/lib/ghc-6.10.4/package.conf"
curPackConf 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:
= let
ghcPkg2ArchPkg pkg = head $ libraryDirs pkg
hsFileLoc = map (\ c -> if c == '.' then '/' else c) $ head $ exposedModules pkg
hsFile = hsFileLoc </> hsFile <.> "hi"
hsFullFile in do
<- doesDirectoryExist hsFullFile
exists if exists
then liftM Just $ archOwnerOfFile hsFullFile
else return Nothing
= let
archOwnerOfFile fn = head . tail . reverse . words
pkgFromPacmanOutput in do
<- rawSystemStdout silent "/usr/bin/pacman" ["-Qo", fn]
res 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
.
= let
archGetRequiredBy pkg = let
extractPkgs pkgDesc = (drop 3 . words . head . filter (isPrefixOf "Required By") . lines) pkgDesc
deps in
if deps == ["None"]
then []
else deps
in do
<- rawSystemStdout silent "/usr/bin/pacman" ["-Qi", pkg]
res return $ extractPkgs res
Now I can modify printMissingPackages
to print some more useful information. This is the full function:
= let
printMissingPackages = PackageName . display . packageName
pkgNameStr in do
<- readFile "/usr/lib/ghc-6.10.3/package.conf.pacsave"
oldPackConf <- readFile "/usr/lib/ghc-6.10.4/package.conf"
curPackConf 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
<- liftM catMaybes $ mapM ghcPkg2ArchPkg gonePkgs
archPkgs putStrLn "Packages to remove:"
mapM_ putStrLn archPkgs
<- filterM (liftM ([] ==) . archGetRequiredBy) archPkgs
archTopPkgs 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.