Topological sorting of build tools

This commit is contained in:
Michael Snoyman 2013-01-28 14:33:33 +02:00
parent 1bc90402b2
commit e1d3714e8c

View File

@ -5,6 +5,7 @@ module Stackage.Select
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (mapMaybe) import Data.Maybe (mapMaybe)
import Data.Either (partitionEithers)
import Data.Set (empty) import Data.Set (empty)
import qualified Data.Set as Set import qualified Data.Set as Set
import Prelude hiding (pi) import Prelude hiding (pi)
@ -29,19 +30,24 @@ select :: SelectSettings -> IO BuildPlan
select settings' = do select settings' = do
ii <- getInstallInfo settings' ii <- getInstallInfo settings'
bt <-
case iiBuildTools ii of
Left s -> error $ "Could not topologically sort build tools: " ++ s
Right x -> return x
return BuildPlan return BuildPlan
{ bpTools = iiBuildTools ii { bpTools = bt
, bpPackages = iiPackages ii , bpPackages = iiPackages ii
, bpOptionalCore = iiOptionalCore ii , bpOptionalCore = iiOptionalCore ii
, bpCore = iiCore ii , bpCore = iiCore ii
} }
-- | Get all of the build tools required. -- | Get all of the build tools required.
iiBuildTools :: InstallInfo -> [String] iiBuildTools :: InstallInfo -> Either String [String]
iiBuildTools InstallInfo { iiPackageDB = PackageDB m, iiPackages = packages } = iiBuildTools InstallInfo { iiPackageDB = PackageDB m, iiPackages = packages } =
-- FIXME possible improvement: track the dependencies between the build fmap (map packageVersionString)
-- tools themselves, and install them in the correct order. $ topSort
map packageVersionString $ map addDependencies
$ filter (flip Set.notMember coreTools . fst) $ filter (flip Set.notMember coreTools . fst)
$ mapMaybe (flip Map.lookup buildToolMap) $ mapMaybe (flip Map.lookup buildToolMap)
$ Set.toList $ Set.toList
@ -58,9 +64,43 @@ iiBuildTools InstallInfo { iiPackageDB = PackageDB m, iiPackages = packages } =
coreTools = Set.fromList $ map PackageName $ words "hsc2hs" coreTools = Set.fromList $ map PackageName $ words "hsc2hs"
-- The map from build tool name to the package it comes from. -- The map from build tool name to the package it comes from.
buildToolMap :: Map Executable (PackageName, Version)
buildToolMap = Map.unions $ map toBuildToolMap $ Map.toList m buildToolMap = Map.unions $ map toBuildToolMap $ Map.toList m
toBuildToolMap :: (PackageName, PackageInfo) -> Map Executable (PackageName, Version) toBuildToolMap :: (PackageName, PackageInfo) -> Map Executable (PackageName, Version)
toBuildToolMap (pn, pi) = Map.unions toBuildToolMap (pn, pi) = Map.unions
$ map (flip Map.singleton (pn, piVersion pi)) $ map (flip Map.singleton (pn, piVersion pi))
$ Set.toList $ Set.toList
$ piExecs pi $ piExecs pi
addDependencies :: (PackageName, Version) -> ((PackageName, Version), Set (PackageName, Version))
addDependencies (pn, pv) =
((pn, pv), deps)
where
deps =
case Map.lookup pn m of
Nothing -> Set.empty
Just pi -> Set.fromList
$ mapMaybe (flip Map.lookup buildToolMap)
$ Set.toList
$ piBuildTools pi
topSort :: (Show a, Ord a) => [(a, Set a)] -> Either String [a]
topSort orig =
uncurry go . partitionEithers . map (splitter . limitDeps) $ orig
where
splitter (x, y)
| Set.null y = Left x
| otherwise = Right (x, y)
go x [] = Right x
go [] y = Left $ "The following form a cycle: " ++ show (map fst y)
go (x:xs) ys = do
let (xs', ys') = partitionEithers $ map (splitter . dropDep x) ys
rest <- go (xs ++ xs') ys'
return $ x : rest
dropDep x (y, z) = (y, Set.delete x z)
allVertices = Set.fromList $ map fst orig
limitDeps (x, y) = (x, Set.intersection allVertices y)