1{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE TupleSections #-}
4{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE DeriveFunctor #-}
7{-# LANGUAGE DeriveFoldable #-}
8{-# LANGUAGE DeriveTraversable #-}
9{-# LANGUAGE TypeFamilies #-}
10-- | Representation of a concrete build plan, and how to generate a new one
11-- based on constraints.
12module Stackage2.BuildPlan
18import Distribution.Package (Dependency (..))
19import Distribution.PackageDescription
20import Distribution.Version (withinRange, intersectVersionRanges)
21import Stackage2.PackageConstraints
22import Stackage2.PackageIndex
23import Stackage2.Prelude
24import Stackage2.GithubPings
25import Control.Monad.State.Strict (execState, get, put)
26import qualified Data.Map as Map
27import qualified Data.Set as Set
29import Stackage2.PackageDescription
30import qualified Distribution.System
31import qualified Distribution.Compiler
33data BuildPlan desc = BuildPlan
34 { bpCore :: Map PackageName Version
35 , bpTools :: Vector (PackageName, Version)
36 , bpExtra :: Map PackageName (PackageBuild desc)
38 deriving (Functor, Foldable, Traversable, Show, Eq)
39type instance Element (BuildPlan desc) = desc
40instance MonoFunctor (BuildPlan desc)
41instance MonoFoldable (BuildPlan desc)
42instance MonoTraversable (BuildPlan desc)
44instance ToJSON (BuildPlan desc) where
45 toJSON BuildPlan {..} = object
46 [ "core" .= asMap (mapFromList $ map toCore $ mapToList bpCore)
47 , "tools" .= map goTool bpTools
48 , "extra" .= Map.mapKeysWith const (unPackageName) bpExtra
51 toCore (x, y) = (asText $ display x, asText $ display y)
52 goTool (name, version) = object
53 [ "name" .= asText (display name)
54 , "version" .= asText (display version)
56instance desc ~ () => FromJSON (BuildPlan desc) where
57 parseJSON = withObject "BuildPlan" $ \o -> BuildPlan
58 <$> ((o .: "core") >>= goCore)
59 <*> ((o .: "tools") >>= mapM goTool)
60 <*> (goExtra <$> (o .: "extra"))
63 fmap mapFromList . mapM goCore' . mapToList . asHashMap
66 k' <- either (fail . show) return $ simpleParse $ asText k
67 v' <- either (fail . show) return $ simpleParse $ asText v
70 goTool = withObject "Tool" $ \o -> (,)
71 <$> ((o .: "name") >>=
72 either (fail . show) return . simpleParse . asText)
73 <*> ((o .: "version") >>=
74 either (fail . show) return . simpleParse . asText)
76 goExtra = Map.mapKeysWith const PackageName
78data PackageBuild desc = PackageBuild
79 { pbVersion :: Version
80 , pbMaintainer :: Maybe Maintainer
81 , pbGithubPings :: Set Text
82 , pbUsers :: Set PackageName
83 , pbFlags :: Map FlagName Bool
84 , pbTestState :: TestState
85 , pbHaddockState :: TestState
86 , pbTryBuildBenchmark :: Bool
89 deriving (Functor, Foldable, Traversable, Show, Eq)
90type instance Element (PackageBuild desc) = desc
91instance MonoFunctor (PackageBuild desc)
92instance MonoFoldable (PackageBuild desc)
93instance MonoTraversable (PackageBuild desc)
95instance ToJSON (PackageBuild desc) where
96 toJSON PackageBuild {..} = object $ concat
97 [ maybe [] (\m -> ["maintainer" .= m]) pbMaintainer
99 [ "version" .= asText (display pbVersion)
100 , "github-pings" .= pbGithubPings
101 , "users" .= map unPackageName (unpack pbUsers)
102 , "flags" .= Map.mapKeysWith const (\(FlagName f) -> asText $ pack f) pbFlags
103 , "test-state" .= pbTestState
104 , "haddock-state" .= pbHaddockState
105 , "build-benchmark" .= pbTryBuildBenchmark
108instance desc ~ () => FromJSON (PackageBuild desc) where
109 parseJSON = withObject "PackageBuild" $ \o -> PackageBuild
110 <$> (o .: "version" >>= efail . simpleParse . asText)
111 <*> o .:? "maintainer"
112 <*> o .:? "github-pings" .!= mempty
113 <*> (Set.map PackageName <$> (o .:? "users" .!= mempty))
114 <*> (toFlags <$> (o .:? "flags" .!= mempty))
115 <*> o .: "test-state"
116 <*> o .: "haddock-state"
117 <*> o .: "build-benchmark"
120 toFlags = Map.mapKeysWith const (FlagName . unpack . asText)
122 efail = either (fail . show) return
124newBuildPlan :: MonadIO m => PackageConstraints -> m (BuildPlan FlatComponent)
125newBuildPlan pc = liftIO $ do
126 extraOrig <- getLatestDescriptions (isAllowed pc) (mkPackageBuild pc)
127 let toolMap = makeToolMap extraOrig
128 extra = populateUsers $ removeUnincluded pc toolMap extraOrig
129 toolNames :: [ExeName]
130 toolNames = concatMap (Map.keys . seTools . fcExtra . pbDesc) extra
131 tools <- topologicalSortTools toolMap $ mapFromList $ do
133 guard $ exeName `notMember` pcCoreExecutables pc
134 packageName <- maybe mempty setToList $ lookup exeName toolMap
135 packageBuild <- maybeToList $ lookup packageName extraOrig
136 return (packageName, packageBuild)
137 -- FIXME topologically sort packages? maybe just leave that to the build phase
139 { bpCore = pcCorePackages pc
140 -- bpCoreExes = pcCoreExecutables pc
145makeToolMap :: Map PackageName (PackageBuild FlatComponent)
146 -> Map ExeName (Set PackageName)
148 unionsWith (++) . map go . mapToList
150 go (packageName, pb) =
151 foldMap go' $ seProvidedExes $ fcExtra $ pbDesc pb
153 go' exeName = singletonMap exeName (singletonSet packageName)
155topologicalSortTools :: MonadThrow m
156 => Map ExeName (Set PackageName)
157 -> Map PackageName (PackageBuild FlatComponent)
158 -> m (Vector (PackageName, Version))
159topologicalSortTools toolMap = topologicalSort
161 (concatMap (fromMaybe mempty . flip lookup toolMap) . Map.keys . seTools . fcExtra . pbDesc)
163topologicalSort :: (Ord key, Show key, MonadThrow m, Typeable key)
164 => (value -> finalValue)
165 -> (value -> Set key) -- ^ deps
167 -> m (Vector (key, finalValue))
168topologicalSort toFinal toDeps =
169 loop id . mapWithKey removeSelfDeps . fmap (toDeps &&& toFinal)
171 removeSelfDeps k (deps, final) = (deleteSet k deps, final)
172 loop front toProcess | null toProcess = return $ pack $ front []
174 | null noDeps = throwM $ NoEmptyDeps (map fst toProcess')
175 | otherwise = loop (front . noDeps') (mapFromList hasDeps)
177 toProcess' = fmap (first removeUnavailable) toProcess
178 allKeys = Map.keysSet toProcess
179 removeUnavailable = asSet . setFromList . filter (`member` allKeys) . setToList
180 (noDeps, hasDeps) = partition (null . fst . snd) $ mapToList toProcess'
181 noDeps' = (map (second snd) noDeps ++)
183data TopologicalSortException key = NoEmptyDeps (Map key (Set key))
184 deriving (Show, Typeable)
185instance (Show key, Typeable key) => Exception (TopologicalSortException key)
187removeUnincluded :: PackageConstraints
188 -> Map ExeName (Set PackageName)
189 -> Map PackageName (PackageBuild FlatComponent)
190 -> Map PackageName (PackageBuild FlatComponent)
191removeUnincluded pc toolMap orig =
192 mapFromList $ filter (\(x, _) -> x `member` included) $ mapToList orig
194 coreExes = pcCoreExecutables pc
196 included :: Set PackageName
197 included = flip execState mempty $
198 mapM_ (add . fst) $ mapToList $ pcPackages pc
202 when (name `notMember` inc) $ do
203 put $ insertSet name inc
204 case lookup name orig of
207 mapM_ (add . fst) $ mapToList $ fcDeps $ pbDesc pb
208 forM_ (map fst $ mapToList $ seTools $ fcExtra $ pbDesc pb) $
209 \exeName -> when (exeName `notMember` coreExes)
210 $ mapM_ add $ fromMaybe mempty $ lookup exeName toolMap
212populateUsers :: Map PackageName (PackageBuild FlatComponent)
213 -> Map PackageName (PackageBuild FlatComponent)
217 go name pb = pb { pbUsers = foldMap (go2 name) (mapToList orig) }
220 | dep `member` fcDeps (pbDesc pb) = singletonSet user
223isAllowed :: PackageConstraints
224 -> PackageName -> Version -> Bool
225isAllowed pc = \name version ->
226 case lookup name $ pcCorePackages pc of
227 Just _ -> False -- never reinstall a core package
229 case lookup name $ pcPackages pc of
230 Nothing -> True -- no constraints
231 Just (range, _) -> withinRange version range
233mkPackageBuild :: MonadThrow m
234 => PackageConstraints
235 -> GenericPackageDescription
236 -> m (PackageBuild FlatComponent)
237mkPackageBuild pc gpd = do
238 let overrides = pcFlagOverrides pc name
239 getFlag MkFlag {..} =
240 (flagName, fromMaybe flagDefault $ lookup flagName overrides)
241 flags = mapFromList $ map getFlag $ genPackageFlags gpd
242 desc <- getFlattenedComponent
244 { ccPackageName = name
247 , ccCompilerFlavor = Distribution.Compiler.GHC
248 , ccCompilerVersion = pcGhcVersion pc
251 (pcTests pc name /= Don'tBuild)
252 (pcBuildBenchmark pc name)
255 { pbVersion = version
256 , pbMaintainer = fmap snd $ lookup name $ pcPackages pc
257 , pbGithubPings = getGithubPings gpd
258 , pbUsers = mempty -- must be filled in later
260 , pbTestState = pcTests pc name
261 , pbHaddockState = pcHaddocks pc name
262 , pbTryBuildBenchmark = pcBuildBenchmark pc name
266 PackageIdentifier name version = package $ packageDescription gpd
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list