1{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE TupleSections #-}
5-- | The constraints on package selection for a new build plan.
6module Stackage.BuildConstraints
7 ( BuildConstraints (..)
8 , PackageConstraints (..)
12 , defaultBuildConstraints
14 , BuildConstraintsSource (..)
15 , loadBuildConstraints
18import Control.Monad.Writer.Strict (execWriter, tell)
20import qualified Data.Map as Map
21import Data.Yaml (decodeEither', decodeFileEither)
22import Distribution.Package (Dependency (..))
23import Distribution.System (Arch, OS)
24import qualified Distribution.System
25import Distribution.Version (anyVersion)
26import Filesystem (isFile)
27import Network.HTTP.Client (Manager, httpLbs, responseBody, Request)
28import Stackage.CorePackages
29import Stackage.Prelude
31data TestState = ExpectSuccess
33 | Don'tBuild -- ^ when the test suite will pull in things we don't want
34 deriving (Show, Eq, Ord, Bounded, Enum)
36testStateToText :: TestState -> Text
37testStateToText ExpectSuccess = "expect-success"
38testStateToText ExpectFailure = "expect-failure"
39testStateToText Don'tBuild = "do-not-build"
41instance ToJSON TestState where
42 toJSON = toJSON . testStateToText
43instance FromJSON TestState where
44 parseJSON = withText "TestState" $ \t ->
45 case lookup t states of
46 Nothing -> fail $ "Invalid state: " ++ unpack t
49 states = asHashMap $ mapFromList
50 $ map (\x -> (testStateToText x, x)) [minBound..maxBound]
52data SystemInfo = SystemInfo
53 { siGhcVersion :: Version
56 , siCorePackages :: Map PackageName Version
57 , siCoreExecutables :: Set ExeName
59 deriving (Show, Eq, Ord)
60instance ToJSON SystemInfo where
61 toJSON SystemInfo {..} = object
62 [ "ghc-version" .= display siGhcVersion
63 , "os" .= display siOS
64 , "arch" .= display siArch
65 , "core-packages" .= Map.mapKeysWith const unPackageName (map display siCorePackages)
66 , "core-executables" .= siCoreExecutables
68instance FromJSON SystemInfo where
69 parseJSON = withObject "SystemInfo" $ \o -> do
70 let helper name = (o .: name) >>= either (fail . show) return . simpleParse
71 siGhcVersion <- helper "ghc-version"
73 siArch <- helper "arch"
74 siCorePackages <- (o .: "core-packages") >>= goPackages
75 siCoreExecutables <- o .: "core-executables"
76 return SystemInfo {..}
78 goPackages = either (fail . show) return
80 . Map.mapKeysWith const mkPackageName
82data BuildConstraints = BuildConstraints
83 { bcPackages :: Set PackageName
84 -- ^ This does not include core packages.
85 , bcPackageConstraints :: PackageName -> PackageConstraints
87 , bcSystemInfo :: SystemInfo
89 , bcGithubUsers :: Map Text (Set Text)
90 -- ^ map an account to set of pingees
93data PackageConstraints = PackageConstraints
94 { pcVersionRange :: VersionRange
95 , pcMaintainer :: Maybe Maintainer
96 , pcTests :: TestState
97 , pcHaddocks :: TestState
98 , pcBuildBenchmarks :: Bool
99 , pcFlagOverrides :: Map FlagName Bool
100 , pcEnableLibProfile :: Bool
103instance ToJSON PackageConstraints where
104 toJSON PackageConstraints {..} = object $ addMaintainer
105 [ "version-range" .= display pcVersionRange
107 , "haddocks" .= pcHaddocks
108 , "build-benchmarks" .= pcBuildBenchmarks
109 , "flags" .= Map.mapKeysWith const unFlagName pcFlagOverrides
110 , "library-profiling" .= pcEnableLibProfile
113 addMaintainer = maybe id (\m -> (("maintainer" .= m):)) pcMaintainer
114instance FromJSON PackageConstraints where
115 parseJSON = withObject "PackageConstraints" $ \o -> do
116 pcVersionRange <- (o .: "version-range")
117 >>= either (fail . show) return . simpleParse
118 pcTests <- o .: "tests"
119 pcHaddocks <- o .: "haddocks"
120 pcBuildBenchmarks <- o .: "build-benchmarks"
121 pcFlagOverrides <- Map.mapKeysWith const mkFlagName <$> o .: "flags"
122 pcMaintainer <- o .:? "maintainer"
123 pcEnableLibProfile <- fmap (fromMaybe False) (o .:? "library-profiling")
124 return PackageConstraints {..}
126-- | The proposed plan from the requirements provided by contributors.
128-- Checks the current directory for a build-constraints.yaml file and uses it
129-- if present. If not, downloads from Github.
130defaultBuildConstraints :: Manager -> IO BuildConstraints
131defaultBuildConstraints = loadBuildConstraints BCSDefault
133data BuildConstraintsSource
139loadBuildConstraints :: BuildConstraintsSource -> Manager -> IO BuildConstraints
140loadBuildConstraints bcs man = do
147 BCSFile fp -> loadFile fp
148 BCSWeb req -> loadReq req
150 fp0 = "build-constraints.yaml"
151 req0 = "https://raw.githubusercontent.com/fpco/stackage/master/build-constraints.yaml"
153 loadFile fp = decodeFileEither (fpToString fp) >>= either throwIO toBC
154 loadReq req = httpLbs req man >>=
155 either throwIO toBC . decodeEither' . toStrict . responseBody
158getSystemInfo :: IO SystemInfo
160 siCorePackages <- getCorePackages
161 siCoreExecutables <- getCoreExecutables
162 siGhcVersion <- getGhcVersion
163 return SystemInfo {..}
165 -- FIXME consider not hard-coding the next two values
166 siOS = Distribution.System.Linux
167 siArch = Distribution.System.X86_64
169data ConstraintFile = ConstraintFile
170 { cfPackageFlags :: Map PackageName (Map FlagName Bool)
171 , cfSkippedTests :: Set PackageName
172 , cfExpectedTestFailures :: Set PackageName
173 , cfExpectedHaddockFailures :: Set PackageName
174 , cfSkippedBenchmarks :: Set PackageName
175 , cfPackages :: Map Maintainer (Vector Dependency)
176 , cfGithubUsers :: Map Text (Set Text)
177 , cfSkippedLibProfiling :: Set PackageName
180instance FromJSON ConstraintFile where
181 parseJSON = withObject "ConstraintFile" $ \o -> do
182 cfPackageFlags <- (goPackageMap . fmap goFlagMap) <$> o .: "package-flags"
183 cfSkippedTests <- getPackages o "skipped-tests"
184 cfExpectedTestFailures <- getPackages o "expected-test-failures"
185 cfExpectedHaddockFailures <- getPackages o "expected-haddock-failures"
186 cfSkippedBenchmarks <- getPackages o "skipped-benchmarks"
187 cfSkippedLibProfiling <- getPackages o "skipped-profiling"
188 cfPackages <- o .: "packages"
189 >>= mapM (mapM toDep)
190 . Map.mapKeysWith const Maintainer
191 cfGithubUsers <- o .: "github-users"
192 return ConstraintFile {..}
194 goFlagMap = Map.mapKeysWith const FlagName
195 goPackageMap = Map.mapKeysWith const PackageName
196 getPackages o name = (setFromList . map PackageName) <$> o .: name
198 toDep :: Monad m => Text -> m Dependency
199 toDep = either (fail . show) return . simpleParse
201toBC :: ConstraintFile -> IO BuildConstraints
202toBC ConstraintFile {..} = do
203 bcSystemInfo <- getSystemInfo
204 return BuildConstraints {..}
206 combine (maintainer, range1) (_, range2) =
207 (maintainer, intersectVersionRanges range1 range2)
208 revmap = unionsWith combine $ ($ []) $ execWriter
209 $ forM_ (mapToList cfPackages)
210 $ \(maintainer, deps) -> forM_ deps
211 $ \(Dependency name range) ->
212 tell (singletonMap name (maintainer, range):)
214 bcPackages = Map.keysSet revmap
216 bcPackageConstraints name =
217 PackageConstraints {..}
219 mpair = lookup name revmap
220 pcMaintainer = fmap fst mpair
221 pcVersionRange = maybe anyVersion snd mpair
222 pcEnableLibProfile = not (name `member` cfSkippedLibProfiling)
224 | name `member` cfSkippedTests = Don'tBuild
225 | name `member` cfExpectedTestFailures = ExpectFailure
226 | otherwise = ExpectSuccess
227 pcBuildBenchmarks = name `notMember` cfSkippedBenchmarks
229 | name `member` cfExpectedHaddockFailures = ExpectFailure
231 | otherwise = ExpectSuccess
232 pcFlagOverrides = fromMaybe mempty $ lookup name cfPackageFlags
234 bcGithubUsers = cfGithubUsers
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