@@ -1,3 +1,4 @@
1+{-# LANGUAGE TupleSections #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE NoImplicitPrelude #-}
@@ -11,30 +12,34 @@ module Stackage.CheckBuildPlan
13import Control.Monad.Writer.Strict (Writer, execWriter, tell)
14import Control.Monad.Writer.Strict (Writer, execWriter, tell)
14−import qualified Data.Text as T
15+import qualified Data.Map.Strict as M
16+import qualified Data.Text as T
15import Stackage.BuildConstraints
17import Stackage.BuildConstraints
16import Stackage.BuildPlan
18import Stackage.BuildPlan
17import Stackage.PackageDescription
19import Stackage.PackageDescription
18import Stackage.Prelude
20import Stackage.Prelude
20−-- FIXME check cycles in dependencies, only looking at libraries and
23-- | Check the build plan for missing deps, wrong versions, etc.
22-- | Check the build plan for missing deps, wrong versions, etc.
24−checkBuildPlan :: MonadThrow m => BuildPlan -> m ()
23+checkBuildPlan :: (MonadThrow m) => BuildPlan -> m ()
25checkBuildPlan BuildPlan {..}
24checkBuildPlan BuildPlan {..}
26 | null errs' = return ()
25 | null errs' = return ()
27 | otherwise = throwM errs
26 | otherwise = throwM errs
29− allPackages = siCorePackages bpSystemInfo ++ map ppVersion bpPackages
28+ allPackages = map (,mempty) (siCorePackages bpSystemInfo) ++
29+ map (ppVersion &&& M.keys . M.filter libAndExe . sdPackages . ppDesc) bpPackages
30 errs@(BadBuildPlan errs') =
30 errs@(BadBuildPlan errs') =
31 execWriter $ mapM_ (checkDeps allPackages) $ mapToList bpPackages
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
33-- | For a given package name and plan, check that its dependencies are:
37-- | For a given package name and plan, check that its dependencies are:
35-- 1. Existent (existing in the provided package map)
39-- 1. Existent (existing in the provided package map)
36-- 2. Within version range
40-- 2. Within version range
41+-- 3. Check for dependency cycles.
37−checkDeps :: Map PackageName Version
42+checkDeps :: Map PackageName (Version,[PackageName])
38 -> (PackageName, PackagePlan)
43 -> (PackageName, PackagePlan)
39 -> Writer BadBuildPlan ()
44 -> Writer BadBuildPlan ()
40checkDeps allPackages (user, pb) =
45checkDeps allPackages (user, pb) =
@@ -43,8 +48,16 @@ checkDeps allPackages (user, pb) =
43 go (dep, diRange -> range) =
48 go (dep, diRange -> range) =
44 case lookup dep allPackages of
49 case lookup dep allPackages of
45 Nothing -> tell $ BadBuildPlan $ singletonMap (dep, Nothing) errMap
50 Nothing -> tell $ BadBuildPlan $ singletonMap (dep, Nothing) errMap
46− Just version
51+ Just (version,deps)
47− | version `withinRange` range -> return ()
52+ | version `withinRange` range ->
53+ occursCheck allPackages
55+ tell $ BadBuildPlan $ singletonMap
48 | otherwise -> tell $ BadBuildPlan $ singletonMap
61 | otherwise -> tell $ BadBuildPlan $ singletonMap
49 (dep, Just version)
62 (dep, Just version)
@@ -57,6 +70,38 @@ checkDeps allPackages (user, pb) =
57 , puGithubPings = ppGithubPings 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.
88+occursCheck 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
60data PkgUser = PkgUser
105data PkgUser = PkgUser
61 { puName :: PackageName
106 { puName :: PackageName
62 , puVersion :: Version
107 , puVersion :: Version