[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Debian haskell packaging: We're way behind



On Thu, Jan 15, 2009 at 12:47:39PM -0600, John Goerzen wrote:
> 
> 4) Being able to rebuild binaries on a mass scale when the packages they
> depend upon are rebuilt, or when GHC itself is rebuilt

The attached Build.hs may be a good starting point (I used it for
building extralibs). No warranty etc.


Thanks
Ian

{-
cp ~/unstable64/ghc6/new/*.deb /home/ian/public_html/haskell_deb_archive/
cp ~/unstable64/haskell-utils/new/*.deb /home/ian/public_html/haskell_deb_archive/

find /home/ian/public_html/haskell_deb_archive/ -type f \( \! -name "ghc6*" -a \! -name "haskell-utils*" \) -exec rm {} \;

Either:
for i in packages/*; do cd $i; dpkg-source -x *.dsc; cd ../..; done
Or:
for i in packages/*/*; do ( cd $i && fakeroot debian/rules clean ) ; done

./Build packages/*/*/ 2>&1 | tee log
runghc Build.hs packages/*/*/ 2>&1 | tee log
runghc -f/home/ian/ghc/6.8-branch/ghc/compiler/stage2/ghc-inplace Build.hs packages/*/*/ 2>&1 | tee log
-}

-- We make no attempt at efficiency here. In particular, after building
-- any package we re-sort the whole list of packages to be built.

-- We assume that each package appears only in the arguments, and that
-- there are no overlaps with packages that come with GHC.

module Main (main) where

import Control.Monad
import Distribution.Package
import Distribution.PackageDescription
import Distribution.Simple.Utils
import Distribution.Verbosity
import Distribution.Version
import Data.List
import Data.Ord
import System.Cmd
import System.Environment
import System.Exit
import System.IO

type Command = String -- XXX security!
type PackageName = String
type Directory = FilePath
data Package = Package GenericPackageDescription Directory deriving Show
data PackageDeps = PackageDeps Package [PackageName] deriving Show

-- XXX Can we get this from somewhere sensible?
packagesThatComeWithGHC :: [PackageName]
packagesThatComeWithGHC = [
    "array", "base", "bytestring",
    "Cabal", "containers", "directory", "filepath",
    "haskell98", "hpc", "old-locale", "old-time", "packedstring",
    "pretty", "process", "random", "readline",
    "template-haskell", "unix", "Win32"
    ]

extraPackagesDone :: [PackageName]
extraPackagesDone = []

archiveDir :: Directory
archiveDir = "/home/ian/public_html/haskell_deb_archive/"

main :: IO ()
main = do hSetBuffering stdout LineBuffering
          dirs <- getArgs
          buildAll (packagesThatComeWithGHC ++ extraPackagesDone) dirs

buildAll :: [PackageName] -> [Directory] -> IO ()
buildAll prebuilt dirs
 = do let verbosity = flagToVerbosity Nothing
      packages <- mapM (mkPackage verbosity) dirs
      let packageDepss = map mkPackagesDeps packages
          packageDepss' = foldr markBuilts packageDepss prebuilt
      doBuilds [] [] packageDepss'

mkPackage :: Verbosity -> Directory -> IO Package
mkPackage verbosity dir
    = do fp <- findPackageDesc verbosity dir
         pd <- readPackageDescription verbosity (dir ++ "/" ++ fp)
         return (Package pd dir)

mkPackagesDeps :: Package -> PackageDeps
mkPackagesDeps p@(Package gpd _) = PackageDeps p ps
    where pd = flattenPackageDescription gpd
          ps = nub $ map getDepPackageName $ buildDepends pd

getDepPackageName :: Dependency -> PackageName
getDepPackageName (Dependency pn _) = pn

markBuilts :: PackageName -> [PackageDeps] -> [PackageDeps]
markBuilts pn = sortPackageDeps . map (markBuilt pn)

markBuilt :: PackageName -> PackageDeps -> PackageDeps
markBuilt pn (PackageDeps pd pns) = PackageDeps pd (delete pn pns)

sortPackageDeps :: [PackageDeps] -> [PackageDeps]
sortPackageDeps = sortBy (comparing numDeps)
    where numDeps (PackageDeps _ deps) = length deps

doBuilds :: [PackageName] -- Done (in reverse order)
         -> [PackageName] -- Failed (in reverse order)
         -> [PackageDeps] -- To build
         -> IO ()
doBuilds done fails pds
 = do printStatus fails pds
      case pds of
          (PackageDeps (Package gpd dir) []):pds' ->
              do built <- doBuild dir
                 let pn = packageName $ packageDescription gpd
                 if built
                   then doBuilds (pn:done) fails $ markBuilts pn pds'
                   else doBuilds done (pn:fails) pds'
          _ -> putStrLn ("Done: " ++ unwords (reverse done))

-- XXX We trust dir to not be nasty
doBuild :: Directory -> IO Bool
doBuild dir = runCommands
    [
     -- so that we know when to make changelog entries.
     -- Get the package ready to be built
     inChroot $ inDir dir $ withTools "debian/rules update-generated-files",
     "find " ++ dir ++ "/../ -name \"*.dsc\" -exec rm {} \\;",
     -- Running setup-ghc clean outside the chroot might fail due to
     -- (presumably) libc differences, so we make the source package in
     -- the chroot
     inChroot $ inDir dir "dpkg-buildpackage -S -rfakeroot -us -uc",
     -- Make a clean build result directory
     "rm -rf buildres",
     "mkdir buildres",
     -- Get the archive ready
     inDir archiveDir "dpkg-scanpackages . . > Packages",
     inDir archiveDir "gzip -f Packages",
     -- Make sure pbuilder knows about the archive
     asRoot "pbuilder update",
     -- Go for it!
     asRoot ("pbuilder build --buildresult buildres " ++ dir ++ "/../*.dsc"),
     -- And put the result in the archive
     "mv buildres/* " ++ archiveDir
    ]

inChrootLocation :: FilePath
inChrootLocation = "/home/ian/debian_packages/extralibs"

inChroot :: Command -> Command
inChroot c = "dchroot -c unstable64 'cd " ++ inChrootLocation
        ++ " && " ++ c ++ "'"

withTools :: Command -> Command
withTools c = "PATH=/home/ian/debian_packages/extralibs/tools:$PATH " ++ c

inDir :: Directory -> Command -> Command
inDir dir c = "cd " ++ dir ++ " && " ++ c

asRoot :: Command -> Command
asRoot cmd = "sudo " ++ cmd

runCommands :: [Command] -> IO Bool
runCommands [] = return True
{-
runCommands (c:cs) = do putStrLn c
                        runCommands cs
-}
runCommands (c:cs) = do putStrLn ("Executing: " ++ c)
                        res <- system c
                        case res of
                            ExitSuccess -> do putStrLn "Success"
                                              runCommands cs
                            _ -> do putStrLn "Failed"
                                    return False

printStatus :: [PackageName] -> [PackageDeps] -> IO ()
printStatus fails pds
 = do let nullFails = null fails
          nullPds = null pds
      unless nullFails $ putStrLn ("Failed: " ++ unwords fails)
      unless nullPds $ do putStrLn "Still to do:"
                          mapM_ (putStrLn . f) pds
      unless (nullFails && nullPds) $ putStrLn "---"
    where f (PackageDeps (Package gpd _) deps)
              = let pn = packageName $ packageDescription gpd
                in case length deps of
                   0 -> "Ready: " ++ pn
                   1 ->          "1 dep for "  ++ pn ++ ": " ++ unwords deps
                   n -> show n ++ " deps for " ++ pn ++ ": " ++ unwords deps

packageName :: PackageDescription -> PackageName
packageName pd = pkgName $ package pd


Reply to: