1{-# LANGUAGE TupleSections #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE NoImplicitPrelude #-}
5{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE RecordWildCards #-}
7{-# LANGUAGE ViewPatterns #-}
8-- | Confirm that a build plan has a consistent set of dependencies.
9module Stackage.CheckBuildPlan
14import Control.Monad.Writer.Strict (Writer, execWriter, tell)
15import qualified Data.Map.Strict as M
16import qualified Data.Text as T
17import Stackage.BuildConstraints
18import Stackage.BuildPlan
19import Stackage.PackageDescription
20import Stackage.Prelude
22-- | Check the build plan for missing deps, wrong versions, etc.
23checkBuildPlan :: (MonadThrow m) => BuildPlan -> m ()
24checkBuildPlan BuildPlan {..}
25 | null errs' = return ()
26 | otherwise = throwM errs
28 allPackages = map (,mempty) (siCorePackages bpSystemInfo) ++
29 map (ppVersion &&& M.keys . M.filter libAndExe . sdPackages . ppDesc) bpPackages
30 errs@(BadBuildPlan errs') =
31 execWriter $ mapM_ (checkDeps allPackages) $ mapToList bpPackages
32 -- Only looking at libraries and executables, benchmarks and tests
33 -- are allowed to create cycles (e.g. test-framework depends on
34 -- text, which uses test-framework in its test-suite).
35 libAndExe (DepInfo cs _) = any (flip elem [CompLibrary,CompExecutable]) cs
37-- | For a given package name and plan, check that its dependencies are:
39-- 1. Existent (existing in the provided package map)
40-- 2. Within version range
41-- 3. Check for dependency cycles.
42checkDeps :: Map PackageName (Version,[PackageName])
43 -> (PackageName, PackagePlan)
44 -> Writer BadBuildPlan ()
45checkDeps allPackages (user, pb) =
46 mapM_ go $ mapToList $ sdPackages $ ppDesc pb
48 go (dep, diRange -> range) =
49 case lookup dep allPackages of
50 Nothing -> tell $ BadBuildPlan $ singletonMap (dep, Nothing) errMap
52 | version `withinRange` range ->
53 occursCheck allPackages
55 tell $ BadBuildPlan $ singletonMap
61 | otherwise -> tell $ BadBuildPlan $ singletonMap
65 errMap = singletonMap pu range
68 , puVersion = ppVersion pb
69 , puMaintainer = pcMaintainer $ ppConstraints pb
70 , puGithubPings = ppGithubPings pb
73-- | Check whether the package(s) occurs within its own dependency
77 => Map PackageName (Version,[PackageName])
79 -> (PackageName -> Maybe Version -> m ())
80 -- ^ Report an erroneous package.
82 -- ^ Starting package to check for cycles in.
84 -- ^ Dependencies of the package.
86 -- ^ Previously seen packages up the dependency tree.
88occursCheck allPackages reportError =
92 case find (flip elem seen) deps of
95 fmap fst (lookup cyclic allPackages)
99 case lookup pkg' allPackages of
101 | pkg' /= pkg -> go pkg' deps' seen'
103 where seen' = pkg : seen
105data PkgUser = PkgUser
106 { puName :: PackageName
107 , puVersion :: Version
108 , puMaintainer :: Maybe Maintainer
109 , puGithubPings :: Set Text
113pkgUserShow1 :: PkgUser -> Text
114pkgUserShow1 PkgUser {..} = concat
120pkgUserShow2 :: PkgUser -> Text
121pkgUserShow2 PkgUser {..} = unwords
122 $ (maybe "No maintainer" unMaintainer puMaintainer ++ ".")
123 : map (cons '@') (setToList puGithubPings)
125newtype BadBuildPlan =
126 BadBuildPlan (Map (PackageName, Maybe Version) (Map PkgUser VersionRange))
128instance Exception BadBuildPlan
129instance Show BadBuildPlan where
130 show (BadBuildPlan errs) =
131 unpack $ concatMap go $ mapToList errs
133 go ((dep, mdepVer), users) = unlines
135 : showDepVer dep mdepVer
136 : map showUser (mapToList users)
138 showDepVer :: PackageName -> Maybe Version -> Text
139 showDepVer dep Nothing = display dep ++ " (not present) depended on by:"
140 showDepVer dep (Just version) = concat
147 showUser :: (PkgUser, VersionRange) -> Text
148 showUser (pu, range) = concat
152 -- add a space after < to avoid confusing Markdown processors (like
153 -- Github's issue tracker)
154 , T.replace "<" "< " $ display range
159instance Monoid BadBuildPlan where
160 mempty = BadBuildPlan mempty
161 mappend (BadBuildPlan x) (BadBuildPlan y) =
162 BadBuildPlan $ unionWith (unionWith intersectVersionRanges) x y
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