1 {-# LANGUAGE BangPatterns #-}
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DeriveDataTypeable #-}
4 {-# LANGUAGE DeriveGeneric #-}
5 {-# LANGUAGE FlexibleContexts #-}
6 {-# LANGUAGE ScopedTypeVariables #-}
7 {-# LANGUAGE TypeFamilies #-}
9 -----------------------------------------------------------------------------
11 -----------------------------------------------------------------------------
14 -- Module : Distribution.Client.InstallPlan
15 -- Copyright : (c) Duncan Coutts 2008
18 -- Maintainer : duncan@community.haskell.org
19 -- Stability : provisional
20 -- Portability : portable
22 -- Package installation plan
23 module Distribution
.Client
.InstallPlan
27 , GenericPlanPackage
(..)
31 -- * Operations on 'InstallPlan's
40 , fromSolverInstallPlan
41 , fromSolverInstallPlanWithProgress
42 , configureInstallPlan
55 -- ** Traversal helpers
69 -- * Graph-like operations
71 , reverseTopologicalOrder
72 , reverseDependencyClosure
75 import Distribution
.Client
.Compat
.Prelude
hiding (lookup, toList
)
76 import Distribution
.Compat
.Stack
(WithCallStack
)
79 import Distribution
.Client
.Types
hiding (BuildOutcomes
)
80 import qualified Distribution
.PackageDescription
as PD
81 import qualified Distribution
.Simple
.Configure
as Configure
82 import qualified Distribution
.Simple
.Setup
as Cabal
84 import Distribution
.Client
.JobControl
85 import Distribution
.Client
.SolverInstallPlan
(SolverInstallPlan
)
86 import qualified Distribution
.Client
.SolverInstallPlan
as SolverInstallPlan
87 import Distribution
.InstalledPackageInfo
88 ( InstalledPackageInfo
90 import Distribution
.Package
91 ( HasMungedPackageId
(..)
96 import Distribution
.Pretty
(defaultStyle
)
97 import Distribution
.Solver
.Types
.SolverPackage
98 import Text
.PrettyPrint
100 import qualified Distribution
.Solver
.Types
.ComponentDeps
as CD
101 import Distribution
.Solver
.Types
.InstSolverPackage
102 import Distribution
.Solver
.Types
.Settings
103 import Distribution
.Solver
.Types
.SolverId
105 import Distribution
.Utils
.LogProgress
106 import Distribution
.Utils
.Structured
(Structure
(Nominal
), Structured
(..))
108 -- TODO: Need this when we compute final UnitIds
109 -- import qualified Distribution.Simple.Configure as Configure
111 import Control
.Exception
114 import qualified Data
.Foldable
as Foldable
(all, toList
)
115 import qualified Data
.Map
as Map
116 import qualified Data
.Set
as Set
117 import Distribution
.Compat
.Graph
(Graph
, IsNode
(..))
118 import qualified Distribution
.Compat
.Graph
as Graph
120 -- When cabal tries to install a number of packages, including all their
121 -- dependencies it has a non-trivial problem to solve.
125 -- In general we start with a set of installed packages and a set of source
128 -- Installed packages have fixed dependencies. They have already been built and
129 -- we know exactly what packages they were built against, including their exact
132 -- Source package have somewhat flexible dependencies. They are specified as
133 -- version ranges, though really they're predicates. To make matters worse they
134 -- have conditional flexible dependencies. Configuration flags can affect which
135 -- packages are required and can place additional constraints on their
138 -- These two sets of package can and usually do overlap. There can be installed
139 -- packages that are also available as source packages which means they could
140 -- be re-installed if required, though there will also be packages which are
141 -- not available as source and cannot be re-installed. Very often there will be
142 -- extra versions available than are installed. Sometimes we may like to prefer
143 -- installed packages over source ones or perhaps always prefer the latest
144 -- available version whether installed or not.
146 -- The goal is to calculate an installation plan that is closed, acyclic and
147 -- consistent and where every configured package is valid.
149 -- An installation plan is a set of packages that are going to be used
150 -- together. It will consist of a mixture of installed packages and source
151 -- packages along with their exact version dependencies. An installation plan
152 -- is closed if for every package in the set, all of its dependencies are
153 -- also in the set. It is consistent if for every package in the set, all
154 -- dependencies which target that package have the same version.
156 -- Note that plans do not necessarily compose. You might have a valid plan for
157 -- package A and a valid plan for package B. That does not mean the composition
158 -- is simultaneously valid for A and B. In particular you're most likely to
159 -- have problems with inconsistent dependencies.
160 -- On the other hand it is true that every closed sub plan is valid.
162 -- | Packages in an install plan
164 -- NOTE: 'ConfiguredPackage', 'GenericReadyPackage' and 'GenericPlanPackage'
165 -- intentionally have no 'PackageInstalled' instance. `This is important:
166 -- PackageInstalled returns only library dependencies, but for package that
167 -- aren't yet installed we know many more kinds of dependencies (setup
168 -- dependencies, exe, test-suite, benchmark, ..). Any functions that operate on
169 -- dependencies in cabal-install should consider what to do with these
170 -- dependencies; if we give a 'PackageInstalled' instance it would be too easy
171 -- to get this wrong (and, for instance, call graph traversal functions from
172 -- Cabal rather than from cabal-install). Instead, see 'PackageInstalled'.
173 data GenericPlanPackage ipkg srcpkg
177 deriving (Eq
, Show, Generic
)
179 displayGenericPlanPackage
:: (IsUnit ipkg
, IsUnit srcpkg
) => GenericPlanPackage ipkg srcpkg
-> String
180 displayGenericPlanPackage
(PreExisting pkg
) = "PreExisting " ++ prettyShow
(nodeKey pkg
)
181 displayGenericPlanPackage
(Configured pkg
) = "Configured " ++ prettyShow
(nodeKey pkg
)
182 displayGenericPlanPackage
(Installed pkg
) = "Installed " ++ prettyShow
(nodeKey pkg
)
184 -- | Convenience combinator for destructing 'GenericPlanPackage'.
185 -- This is handy because if you case manually, you have to handle
186 -- 'Configured' and 'Installed' separately (where often you want
187 -- them to be the same.)
191 -> GenericPlanPackage ipkg srcpkg
193 foldPlanPackage f _
(PreExisting ipkg
) = f ipkg
194 foldPlanPackage _ g
(Configured srcpkg
) = g srcpkg
195 foldPlanPackage _ g
(Installed srcpkg
) = g srcpkg
197 type IsUnit a
= (IsNode a
, Key a ~ UnitId
)
199 depends
:: IsUnit a
=> a
-> [UnitId
]
200 depends
= nodeNeighbors
202 -- NB: Expanded constraint synonym here to avoid undecidable
203 -- instance errors in GHC 7.8 and earlier.
205 (IsNode ipkg
, IsNode srcpkg
, Key ipkg ~ UnitId
, Key srcpkg ~ UnitId
)
206 => IsNode
(GenericPlanPackage ipkg srcpkg
)
208 type Key
(GenericPlanPackage ipkg srcpkg
) = UnitId
209 nodeKey
(PreExisting ipkg
) = nodeKey ipkg
210 nodeKey
(Configured spkg
) = nodeKey spkg
211 nodeKey
(Installed spkg
) = nodeKey spkg
212 nodeNeighbors
(PreExisting ipkg
) = nodeNeighbors ipkg
213 nodeNeighbors
(Configured spkg
) = nodeNeighbors spkg
214 nodeNeighbors
(Installed spkg
) = nodeNeighbors spkg
216 instance (Binary ipkg
, Binary srcpkg
) => Binary
(GenericPlanPackage ipkg srcpkg
)
217 instance (Structured ipkg
, Structured srcpkg
) => Structured
(GenericPlanPackage ipkg srcpkg
)
222 (ConfiguredPackage UnresolvedPkgLoc
)
225 (Package ipkg
, Package srcpkg
)
226 => Package
(GenericPlanPackage ipkg srcpkg
)
228 packageId
(PreExisting ipkg
) = packageId ipkg
229 packageId
(Configured spkg
) = packageId spkg
230 packageId
(Installed spkg
) = packageId spkg
233 (HasMungedPackageId ipkg
, HasMungedPackageId srcpkg
)
234 => HasMungedPackageId
(GenericPlanPackage ipkg srcpkg
)
236 mungedId
(PreExisting ipkg
) = mungedId ipkg
237 mungedId
(Configured spkg
) = mungedId spkg
238 mungedId
(Installed spkg
) = mungedId spkg
241 (HasUnitId ipkg
, HasUnitId srcpkg
)
243 (GenericPlanPackage ipkg srcpkg
)
245 installedUnitId
(PreExisting ipkg
) = installedUnitId ipkg
246 installedUnitId
(Configured spkg
) = installedUnitId spkg
247 installedUnitId
(Installed spkg
) = installedUnitId spkg
250 (HasConfiguredId ipkg
, HasConfiguredId srcpkg
)
251 => HasConfiguredId
(GenericPlanPackage ipkg srcpkg
)
253 configuredId
(PreExisting ipkg
) = configuredId ipkg
254 configuredId
(Configured spkg
) = configuredId spkg
255 configuredId
(Installed spkg
) = configuredId spkg
257 data GenericInstallPlan ipkg srcpkg
= GenericInstallPlan
258 { planGraph
:: !(Graph
(GenericPlanPackage ipkg srcpkg
))
259 , planIndepGoals
:: !IndependentGoals
263 -- | 'GenericInstallPlan' specialised to most commonly used types.
267 (ConfiguredPackage UnresolvedPkgLoc
)
269 -- | Smart constructor that deals with caching the 'Graph' representation.
271 :: (IsUnit ipkg
, IsUnit srcpkg
)
273 -> Graph
(GenericPlanPackage ipkg srcpkg
)
275 -> GenericInstallPlan ipkg srcpkg
276 mkInstallPlan loc graph indepGoals
=
281 , planIndepGoals
= indepGoals
284 internalError
:: WithCallStack
(String -> String -> a
)
285 internalError loc msg
=
287 "internal error in InstallPlan."
289 ++ if null msg
then "" else ": " ++ msg
291 instance (Structured ipkg
, Structured srcpkg
) => Structured
(GenericInstallPlan ipkg srcpkg
) where
297 [ structure
(Proxy
:: Proxy ipkg
)
298 , structure
(Proxy
:: Proxy srcpkg
)
305 , Key srcpkg ~ UnitId
309 => Binary
(GenericInstallPlan ipkg srcpkg
)
314 , planIndepGoals
= indepGoals
315 } = put graph
>> put indepGoals
320 return $! mkInstallPlan
"(instance Binary)" graph indepGoals
322 data ShowPlanNode
= ShowPlanNode
323 { showPlanHerald
:: Doc
324 , showPlanNeighbours
:: [Doc
]
327 showPlanGraph
:: [ShowPlanNode
] -> String
328 showPlanGraph graph
=
329 renderStyle defaultStyle
$
330 vcat
(map dispPlanPackage graph
)
332 dispPlanPackage
(ShowPlanNode herald neighbours
) =
333 hang herald
2 (vcat neighbours
)
335 -- | Generic way to show a 'GenericInstallPlan' which elicits quite a lot of information
337 :: forall ipkg srcpkg
338 . (GenericPlanPackage ipkg srcpkg
-> ShowPlanNode
)
339 -> GenericInstallPlan ipkg srcpkg
341 showInstallPlan_gen toShow
= showPlanGraph
. fmap toShow
. Foldable
.toList
. planGraph
344 :: forall ipkg srcpkg
345 . (Package ipkg
, Package srcpkg
, IsUnit ipkg
, IsUnit srcpkg
)
346 => GenericInstallPlan ipkg srcpkg
348 showInstallPlan
= showInstallPlan_gen toShow
350 toShow
:: GenericPlanPackage ipkg srcpkg
-> ShowPlanNode
354 [ text
(showPlanPackageTag p
)
355 , pretty
(packageId p
)
356 , parens
(pretty
(nodeKey p
))
359 (map pretty
(nodeNeighbors p
))
361 showPlanPackageTag
:: GenericPlanPackage ipkg srcpkg
-> String
362 showPlanPackageTag
(PreExisting _
) = "PreExisting"
363 showPlanPackageTag
(Configured _
) = "Configured"
364 showPlanPackageTag
(Installed _
) = "Installed"
366 -- | Build an installation plan from a valid set of resolved packages.
368 :: (IsUnit ipkg
, IsUnit srcpkg
)
370 -> Graph
(GenericPlanPackage ipkg srcpkg
)
371 -> GenericInstallPlan ipkg srcpkg
372 new indepGoals graph
= mkInstallPlan
"new" graph indepGoals
375 :: GenericInstallPlan ipkg srcpkg
376 -> Graph
(GenericPlanPackage ipkg srcpkg
)
380 :: GenericInstallPlan ipkg srcpkg
381 -> [GenericPlanPackage ipkg srcpkg
]
382 toList
= Foldable
.toList
. planGraph
385 :: GenericInstallPlan ipkg srcpkg
386 -> Map UnitId
(GenericPlanPackage ipkg srcpkg
)
387 toMap
= Graph
.toMap
. planGraph
389 keys
:: GenericInstallPlan ipkg srcpkg
-> [UnitId
]
390 keys
= Graph
.keys
. planGraph
392 keysSet
:: GenericInstallPlan ipkg srcpkg
-> Set UnitId
393 keysSet
= Graph
.keysSet
. planGraph
395 -- | Remove packages from the install plan. This will result in an
396 -- error if there are remaining packages that depend on any matching
397 -- package. This is primarily useful for obtaining an install plan for
398 -- the dependencies of a package or set of packages without actually
399 -- installing the package itself, as when doing development.
401 :: (IsUnit ipkg
, IsUnit srcpkg
)
402 => (GenericPlanPackage ipkg srcpkg
-> Bool)
403 -> GenericInstallPlan ipkg srcpkg
404 -> GenericInstallPlan ipkg srcpkg
405 remove shouldRemove plan
=
406 mkInstallPlan
"remove" newGraph
(planIndepGoals plan
)
409 Graph
.fromDistinctList
$
410 filter (not . shouldRemove
) (toList plan
)
412 -- | Change a number of packages in the 'Configured' state to the 'Installed'
415 -- To preserve invariants, the package must have all of its dependencies
416 -- already installed too (that is 'PreExisting' or 'Installed').
418 :: (IsUnit ipkg
, IsUnit srcpkg
)
420 -> GenericInstallPlan ipkg srcpkg
421 -> GenericInstallPlan ipkg srcpkg
422 installed shouldBeInstalled installPlan
=
427 | Configured pkg
<- reverseTopologicalOrder installPlan
428 , shouldBeInstalled pkg
431 markInstalled plan pkg
=
432 assert
(all isInstalled
(directDeps plan
(nodeKey pkg
))) $
434 { planGraph
= Graph
.insert (Installed pkg
) (planGraph plan
)
437 -- | Lookup a package in the plan.
439 :: (IsUnit ipkg
, IsUnit srcpkg
)
440 => GenericInstallPlan ipkg srcpkg
442 -> Maybe (GenericPlanPackage ipkg srcpkg
)
443 lookup plan pkgid
= Graph
.lookup pkgid
(planGraph plan
)
445 -- | Find all the direct dependencies of the given package.
447 -- Note that the package must exist in the plan or it is an error.
449 :: GenericInstallPlan ipkg srcpkg
451 -> [GenericPlanPackage ipkg srcpkg
]
452 directDeps plan pkgid
=
453 case Graph
.neighbors
(planGraph plan
) pkgid
of
455 Nothing
-> internalError
"directDeps" "package not in graph"
457 -- | Find all the direct reverse dependencies of the given package.
459 -- Note that the package must exist in the plan or it is an error.
461 :: GenericInstallPlan ipkg srcpkg
463 -> [GenericPlanPackage ipkg srcpkg
]
464 revDirectDeps plan pkgid
=
465 case Graph
.revNeighbors
(planGraph plan
) pkgid
of
467 Nothing
-> internalError
"revDirectDeps" "package not in graph"
469 -- | Return all the packages in the 'InstallPlan' in reverse topological order.
470 -- That is, for each package, all dependencies of the package appear first.
472 -- Compared to 'executionOrder', this function returns all the installed and
473 -- source packages rather than just the source ones. Also, while both this
474 -- and 'executionOrder' produce reverse topological orderings of the package
475 -- dependency graph, it is not necessarily exactly the same order.
476 reverseTopologicalOrder
477 :: GenericInstallPlan ipkg srcpkg
478 -> [GenericPlanPackage ipkg srcpkg
]
479 reverseTopologicalOrder plan
= Graph
.revTopSort
(planGraph plan
)
481 -- | Return the packages in the plan that are direct or indirect dependencies of
482 -- the given packages.
484 :: GenericInstallPlan ipkg srcpkg
486 -> [GenericPlanPackage ipkg srcpkg
]
487 dependencyClosure plan
=
489 . Graph
.closure
(planGraph plan
)
491 -- | Return the packages in the plan that depend directly or indirectly on the
493 reverseDependencyClosure
494 :: GenericInstallPlan ipkg srcpkg
496 -> [GenericPlanPackage ipkg srcpkg
]
497 reverseDependencyClosure plan
=
499 . Graph
.revClosure
(planGraph plan
)
501 -- Alert alert! Why does SolverId map to a LIST of plan packages?
502 -- The sordid story has to do with 'build-depends' on a package
503 -- with libraries and executables. In an ideal world, we would
504 -- ONLY depend on the library in this situation. But c.f. #3661
505 -- some people rely on the build-depends to ALSO implicitly
506 -- depend on an executable.
508 -- I don't want to commit to a strategy yet, so the only possible
509 -- thing you can do in this case is return EVERYTHING and let
510 -- the client filter out what they want (executables? libraries?
511 -- etc). This similarly implies we can't return a 'ConfiguredId'
512 -- because that's not enough information.
514 fromSolverInstallPlan
515 :: (IsUnit ipkg
, IsUnit srcpkg
)
516 => ( (SolverId
-> [GenericPlanPackage ipkg srcpkg
])
517 -> SolverInstallPlan
.SolverPlanPackage
518 -> [GenericPlanPackage ipkg srcpkg
]
521 -> GenericInstallPlan ipkg srcpkg
522 fromSolverInstallPlan f plan
=
524 "fromSolverInstallPlan"
525 (Graph
.fromDistinctList pkgs
'')
526 (SolverInstallPlan
.planIndepGoals plan
)
531 (Map
.empty, Map
.empty, [])
532 (SolverInstallPlan
.reverseTopologicalOrder plan
)
534 f
' (pidMap
, ipiMap
, pkgs
) pkg
= (pidMap
', ipiMap
', pkgs
' ++ pkgs
)
536 pkgs
' = f
(mapDep pidMap ipiMap
) pkg
540 PreExistingId _ uid
-> (pidMap
, Map
.insert uid pkgs
' ipiMap
)
541 PlannedId pid
-> (Map
.insert pid pkgs
' pidMap
, ipiMap
)
543 mapDep _ ipiMap
(PreExistingId _pid uid
)
544 | Just pkgs
<- Map
.lookup uid ipiMap
= pkgs
545 |
otherwise = error ("fromSolverInstallPlan: PreExistingId " ++ prettyShow uid
)
546 mapDep pidMap _
(PlannedId pid
)
547 | Just pkgs
<- Map
.lookup pid pidMap
= pkgs
548 |
otherwise = error ("fromSolverInstallPlan: PlannedId " ++ prettyShow pid
)
550 -- This shouldn't happen, since mapDep should only be called
551 -- on neighbor SolverId, which must have all been done already
552 -- by the reverse top-sort (we assume the graph is not broken).
554 fromSolverInstallPlanWithProgress
555 :: (IsUnit ipkg
, IsUnit srcpkg
)
556 => ( (SolverId
-> [GenericPlanPackage ipkg srcpkg
])
557 -> SolverInstallPlan
.SolverPlanPackage
558 -> LogProgress
[GenericPlanPackage ipkg srcpkg
]
561 -> LogProgress
(GenericInstallPlan ipkg srcpkg
)
562 fromSolverInstallPlanWithProgress f plan
= do
566 (Map
.empty, Map
.empty, [])
567 (SolverInstallPlan
.reverseTopologicalOrder plan
)
570 "fromSolverInstallPlanWithProgress"
571 (Graph
.fromDistinctList pkgs
'')
572 (SolverInstallPlan
.planIndepGoals plan
)
574 f
' (pidMap
, ipiMap
, pkgs
) pkg
= do
575 pkgs
' <- f
(mapDep pidMap ipiMap
) pkg
576 let (pidMap
', ipiMap
') =
578 PreExistingId _ uid
-> (pidMap
, Map
.insert uid pkgs
' ipiMap
)
579 PlannedId pid
-> (Map
.insert pid pkgs
' pidMap
, ipiMap
)
580 return (pidMap
', ipiMap
', pkgs
' ++ pkgs
)
582 mapDep _ ipiMap
(PreExistingId _pid uid
)
583 | Just pkgs
<- Map
.lookup uid ipiMap
= pkgs
584 |
otherwise = error ("fromSolverInstallPlan: PreExistingId " ++ prettyShow uid
)
585 mapDep pidMap _
(PlannedId pid
)
586 | Just pkgs
<- Map
.lookup pid pidMap
= pkgs
587 |
otherwise = error ("fromSolverInstallPlan: PlannedId " ++ prettyShow pid
)
589 -- This shouldn't happen, since mapDep should only be called
590 -- on neighbor SolverId, which must have all been done already
591 -- by the reverse top-sort (we assume the graph is not broken).
593 -- | Conversion of 'SolverInstallPlan' to 'InstallPlan'.
594 -- Similar to 'elaboratedInstallPlan'
595 configureInstallPlan
:: Cabal
.ConfigFlags
-> SolverInstallPlan
-> InstallPlan
596 configureInstallPlan configFlags solverPlan
=
597 flip fromSolverInstallPlan solverPlan
$ \mapDep planpkg
->
599 SolverInstallPlan
.PreExisting pkg
->
600 PreExisting
(instSolverPkgIPI pkg
)
601 SolverInstallPlan
.Configured pkg
->
602 Configured
(configureSolverPackage mapDep pkg
)
605 configureSolverPackage
606 :: (SolverId
-> [PlanPackage
])
607 -> SolverPackage UnresolvedPkgLoc
608 -> ConfiguredPackage UnresolvedPkgLoc
609 configureSolverPackage mapDep spkg
=
612 Configure
.computeComponentId
613 ( Cabal
.fromFlagOrDefault
615 (Cabal
.configDeterministic configFlags
)
620 (PD
.CLibName PD
.LMainLibName
)
622 ( map confInstId
(CD
.libraryDeps deps
)
623 , solverPkgFlags spkg
626 , confPkgSource
= solverPkgSource spkg
627 , confPkgFlags
= solverPkgFlags spkg
628 , confPkgStanzas
= solverPkgStanzas spkg
630 -- NB: no support for executable dependencies
633 deps
= fmap (concatMap (map configuredId
. mapDep
)) (solverPkgLibDeps spkg
)
635 -- ------------------------------------------------------------
637 -- * Primitives for traversing plans
639 -- ------------------------------------------------------------
643 -- Algorithms to traverse or execute an 'InstallPlan', especially in parallel,
644 -- may make use of the 'Processing' type and the associated operations
645 -- 'ready', 'completed' and 'failed'.
647 -- The 'Processing' type is used to keep track of the state of a traversal and
648 -- includes the set of packages that are in the processing state, e.g. in the
649 -- process of being installed, plus those that have been completed and those
650 -- where processing failed.
652 -- Traversal algorithms start with an 'InstallPlan':
654 -- * Initially there will be certain packages that can be processed immediately
655 -- (since they are configured source packages and have all their dependencies
656 -- installed already). The function 'ready' returns these packages plus a
657 -- 'Processing' state that marks these same packages as being in the
660 -- * The algorithm must now arrange for these packages to be processed
661 -- (possibly in parallel). When a package has completed processing, the
662 -- algorithm needs to know which other packages (if any) are now ready to
663 -- process as a result. The 'completed' function marks a package as completed
664 -- and returns any packages that are newly in the processing state (ie ready
665 -- to process), along with the updated 'Processing' state.
667 -- * If failure is possible then when processing a package fails, the algorithm
668 -- needs to know which other packages have also failed as a result. The
669 -- 'failed' function marks the given package as failed as well as all the
670 -- other packages that depend on the failed package. In addition it returns
671 -- the other failed packages.
673 -- | The 'Processing' type is used to keep track of the state of a traversal
674 -- and includes the set of packages that are in the processing state, e.g. in
675 -- the process of being installed, plus those that have been completed and
676 -- those where processing failed.
677 data Processing
= Processing
!(Set UnitId
) !(Set UnitId
) !(Set UnitId
)
679 -- processing, completed, failed
681 -- | The packages in the plan that are initially ready to be installed.
682 -- That is they are in the configured state and have all their dependencies
683 -- installed already.
685 -- The result is both the packages that are now ready to be installed and also
686 -- a 'Processing' state containing those same packages. The assumption is that
687 -- all the packages that are ready will now be processed and so we can consider
688 -- them to be in the processing state.
690 :: (IsUnit ipkg
, IsUnit srcpkg
)
691 => GenericInstallPlan ipkg srcpkg
692 -> ([GenericReadyPackage srcpkg
], Processing
)
694 assert
(processingInvariant plan processing
) $
695 (readyPackages
, processing
)
699 (Set
.fromList
[nodeKey pkg | pkg
<- readyPackages
])
700 (Set
.fromList
[nodeKey pkg | pkg
<- toList plan
, isInstalled pkg
])
704 | Configured pkg
<- toList plan
705 , all isInstalled
(directDeps plan
(nodeKey pkg
))
708 isInstalled
:: GenericPlanPackage a b
-> Bool
709 isInstalled
(PreExisting
{}) = True
710 isInstalled
(Installed
{}) = True
711 isInstalled _
= False
713 -- | Given a package in the processing state, mark the package as completed
714 -- and return any packages that are newly in the processing state (ie ready to
715 -- process), along with the updated 'Processing' state.
717 :: forall ipkg srcpkg
718 . (IsUnit ipkg
, IsUnit srcpkg
)
719 => GenericInstallPlan ipkg srcpkg
722 -> ([GenericReadyPackage srcpkg
], Processing
)
723 completed plan
(Processing processingSet completedSet failedSet
) pkgid
=
724 assert
(pkgid `Set
.member` processingSet
) $
725 assert
(processingInvariant plan processing
') $
726 ( map asReadyPackage newlyReady
730 completedSet
' = Set
.insert pkgid completedSet
732 -- each direct reverse dep where all direct deps are completed
735 | dep
<- revDirectDeps plan pkgid
737 ((`Set
.member` completedSet
') . nodeKey
)
738 (directDeps plan
(nodeKey dep
))
744 (Set
.delete pkgid processingSet
)
745 (map nodeKey newlyReady
)
746 processing
' = Processing processingSet
' completedSet
' failedSet
748 asReadyPackage
:: GenericPlanPackage ipkg srcpkg
-> GenericReadyPackage srcpkg
749 asReadyPackage
(Configured pkg
) = ReadyPackage pkg
750 asReadyPackage pkg
= internalError
"completed" $ "not in configured state: " ++ displayGenericPlanPackage pkg
753 :: (IsUnit ipkg
, IsUnit srcpkg
)
754 => GenericInstallPlan ipkg srcpkg
757 -> ([srcpkg
], Processing
)
758 failed plan
(Processing processingSet completedSet failedSet
) pkgid
=
759 assert
(pkgid `Set
.member` processingSet
) $
760 assert
(all (`Set
.notMember` processingSet
) (drop 1 newlyFailedIds
)) $
761 assert
(all (`Set
.notMember` completedSet
) (drop 1 newlyFailedIds
)) $
762 -- but note that some newlyFailed may already be in the failed set
763 -- since one package can depend on two packages that both fail and
764 -- so would be in the rev-dep closure for both.
765 assert
(processingInvariant plan processing
') $
766 ( map asConfiguredPackage
(drop 1 newlyFailed
)
770 processingSet
' = Set
.delete pkgid processingSet
771 failedSet
' = failedSet `Set
.union` Set
.fromList newlyFailedIds
772 newlyFailedIds
= map nodeKey newlyFailed
774 fromMaybe (internalError
"failed" "package not in graph") $
775 Graph
.revClosure
(planGraph plan
) [pkgid
]
776 processing
' = Processing processingSet
' completedSet failedSet
'
778 asConfiguredPackage
(Configured pkg
) = pkg
779 asConfiguredPackage pkg
= internalError
"failed" $ "not in configured state: " ++ displayGenericPlanPackage pkg
782 :: (IsUnit ipkg
, IsUnit srcpkg
)
783 => GenericInstallPlan ipkg srcpkg
786 processingInvariant plan
(Processing processingSet completedSet failedSet
) =
787 -- All the packages in the three sets are actually in the graph
788 assert
(Foldable
.all (flip Graph
.member
(planGraph plan
)) processingSet
)
789 $ assert
(Foldable
.all (flip Graph
.member
(planGraph plan
)) completedSet
)
790 $ assert
(Foldable
.all (flip Graph
.member
(planGraph plan
)) failedSet
)
792 -- The processing, completed and failed sets are disjoint from each other
793 assert
(noIntersection processingSet completedSet
)
794 $ assert
(noIntersection processingSet failedSet
)
795 $ assert
(noIntersection failedSet completedSet
)
797 -- Packages that depend on a package that's still processing cannot be
799 assert
(noIntersection
(reverseClosure processingSet
) completedSet
)
801 -- On the other hand, packages that depend on a package that's still
802 -- processing /can/ have failed (since they may have depended on multiple
803 -- packages that were processing, but it only takes one to fail to cause
804 -- knock-on failures) so it is quite possible to have an
805 -- intersection (reverseClosure processingSet) failedSet
807 -- The failed set is upwards closed, i.e. equal to its own rev dep closure
808 assert
(failedSet
== reverseClosure failedSet
)
810 -- All immediate reverse deps of packages that are currently processing
811 -- are not currently being processed (ie not in the processing set).
814 [ rdeppkgid `Set
.notMember` processingSet
815 | pkgid
<- Set
.toList processingSet
818 (internalError
"processingInvariant" "")
820 (Graph
.revNeighbors
(planGraph plan
) pkgid
)
824 -- Packages from the processing or failed sets are only ever in the
828 [ case Graph
.lookup pkgid
(planGraph plan
) of
829 Just
(Configured _
) -> True
830 Just
(PreExisting _
) -> False
831 Just
(Installed _
) -> False
833 | pkgid
<- Set
.toList processingSet
++ Set
.toList failedSet
836 -- We use asserts rather than returning False so that on failure we get
837 -- better details on which bit of the invariant was violated.
843 . fromMaybe (internalError
"processingInvariant" "")
844 . Graph
.revClosure
(planGraph plan
)
846 noIntersection a b
= Set
.null (Set
.intersection a b
)
848 -- ------------------------------------------------------------
850 -- * Traversing plans
852 -- ------------------------------------------------------------
854 -- | Flatten an 'InstallPlan', producing the sequence of source packages in
855 -- the order in which they would be processed when the plan is executed. This
856 -- can be used for simulations or presenting execution dry-runs.
858 -- It is guaranteed to give the same order as using 'execute' (with a serial
859 -- in-order 'JobControl'), which is a reverse topological orderings of the
860 -- source packages in the dependency graph, albeit not necessarily exactly the
861 -- same ordering as that produced by 'reverseTopologicalOrder'.
863 :: (IsUnit ipkg
, IsUnit srcpkg
)
864 => GenericInstallPlan ipkg srcpkg
865 -> [GenericReadyPackage srcpkg
]
866 executionOrder plan
=
867 let (newpkgs
, processing
) = ready plan
868 in tryNewTasks processing newpkgs
870 tryNewTasks _processing
[] = []
871 tryNewTasks processing
(p
: todo
) = waitForTasks processing p todo
873 waitForTasks processing p todo
=
874 p
: tryNewTasks processing
' (todo
++ nextpkgs
)
876 (nextpkgs
, processing
') = completed plan processing
(nodeKey p
)
878 -- ------------------------------------------------------------
882 -- ------------------------------------------------------------
884 -- | The set of results we get from executing an install plan.
885 type BuildOutcomes failure result
= Map UnitId
(Either failure result
)
887 -- | Lookup the build result for a single package.
891 -> BuildOutcomes failure result
892 -> Maybe (Either failure result
)
893 lookupBuildOutcome
= Map
.lookup . installedUnitId
895 -- | Execute an install plan. This traverses the plan in dependency order.
897 -- Executing each individual package can fail and if so all dependents fail
898 -- too. The result for each package is collected as a 'BuildOutcomes' map.
900 -- Visiting each package happens with optional parallelism, as determined by
901 -- the 'JobControl'. By default, after any failure we stop as soon as possible
902 -- (using the 'JobControl' to try to cancel in-progress tasks). This behaviour
903 -- can be reversed to keep going and build as many packages as possible.
905 -- Note that the 'BuildOutcomes' is /not/ guaranteed to cover all the packages
906 -- in the plan. In particular in the default mode where we stop as soon as
907 -- possible after a failure then there may be packages which are skipped and
908 -- these will have no 'BuildOutcome'.
910 :: forall m ipkg srcpkg result failure
915 => JobControl m
(UnitId
, Either failure result
)
917 -- ^ Keep going after failure
918 -> (srcpkg
-> failure
)
919 -- ^ Value for dependents of failed packages
920 -> GenericInstallPlan ipkg srcpkg
921 -> (GenericReadyPackage srcpkg
-> m
(Either failure result
))
922 -> m
(BuildOutcomes failure result
)
923 execute jobCtl keepGoing depFailure plan installPkg
=
924 let (newpkgs
, processing
) = ready plan
925 in tryNewTasks Map
.empty False False processing newpkgs
928 :: BuildOutcomes failure result
932 -> [GenericReadyPackage srcpkg
]
933 -> m
(BuildOutcomes failure result
)
935 tryNewTasks
!results tasksFailed tasksRemaining
!processing newpkgs
936 -- we were in the process of cancelling and now we're finished
937 | tasksFailed
&& not keepGoing
&& not tasksRemaining
=
939 -- we are still in the process of cancelling, wait for remaining tasks
940 | tasksFailed
&& not keepGoing
&& tasksRemaining
=
941 waitForTasks results tasksFailed processing
942 -- no new tasks to do and all tasks are done so we're finished
943 |
null newpkgs
&& not tasksRemaining
=
945 -- no new tasks to do, remaining tasks to wait for
947 waitForTasks results tasksFailed processing
948 -- new tasks to do, spawn them, then wait for tasks to complete
952 [ spawnJob jobCtl
$ do
953 result
<- installPkg pkg
954 return (nodeKey pkg
, result
)
957 waitForTasks results tasksFailed processing
960 :: BuildOutcomes failure result
963 -> m
(BuildOutcomes failure result
)
964 waitForTasks
!results tasksFailed
!processing
= do
965 (pkgid
, result
) <- collectJob jobCtl
969 tasksRemaining
<- remainingJobs jobCtl
977 results
' = Map
.insert pkgid result results
978 (nextpkgs
, processing
') = completed plan processing pkgid
980 -- if this is the first failure and we're not trying to keep going
981 -- then try to cancel as many of the remaining jobs as possible
982 when (not tasksFailed
&& not keepGoing
) $
985 tasksRemaining
<- remainingJobs jobCtl
986 tryNewTasks results
' True tasksRemaining processing
' []
988 (depsfailed
, processing
') = failed plan processing pkgid
989 results
' = Map
.insert pkgid result results `Map
.union` depResults
992 [ (nodeKey deppkg
, Left
(depFailure deppkg
))
993 | deppkg
<- depsfailed
996 -- ------------------------------------------------------------
998 -- * Checking validity of plans
1000 -- ------------------------------------------------------------
1002 -- | A valid installation plan is a set of packages that is closed, acyclic
1003 -- and respects the package state relation.
1005 -- * if the result is @False@ use 'problems' to get a detailed list.
1007 :: (IsUnit ipkg
, IsUnit srcpkg
)
1009 -> Graph
(GenericPlanPackage ipkg srcpkg
)
1012 case problems graph
of
1014 ps
-> internalError loc
('\n' : unlines (map showPlanProblem ps
))
1016 data PlanProblem ipkg srcpkg
1017 = PackageMissingDeps
(GenericPlanPackage ipkg srcpkg
) [UnitId
]
1018 | PackageCycle
[GenericPlanPackage ipkg srcpkg
]
1019 | PackageStateInvalid
1020 (GenericPlanPackage ipkg srcpkg
)
1021 (GenericPlanPackage ipkg srcpkg
)
1024 :: (IsUnit ipkg
, IsUnit srcpkg
)
1025 => PlanProblem ipkg srcpkg
1027 showPlanProblem
(PackageMissingDeps pkg missingDeps
) =
1029 ++ prettyShow
(nodeKey pkg
)
1030 ++ " depends on the following packages which are missing from the plan: "
1031 ++ intercalate
", " (map prettyShow missingDeps
)
1032 showPlanProblem
(PackageCycle cycleGroup
) =
1033 "The following packages are involved in a dependency cycle "
1034 ++ intercalate
", " (map (prettyShow
. nodeKey
) cycleGroup
)
1035 showPlanProblem
(PackageStateInvalid pkg pkg
') =
1037 ++ prettyShow
(nodeKey pkg
)
1039 ++ showPlanPackageTag pkg
1040 ++ " state but it depends on package "
1041 ++ prettyShow
(nodeKey pkg
')
1042 ++ " which is in the "
1043 ++ showPlanPackageTag pkg
'
1046 -- | For an invalid plan, produce a detailed list of problems as human readable
1047 -- error messages. This is mainly intended for debugging purposes.
1048 -- Use 'showPlanProblem' for a human readable explanation.
1050 :: (IsUnit ipkg
, IsUnit srcpkg
)
1051 => Graph
(GenericPlanPackage ipkg srcpkg
)
1052 -> [PlanProblem ipkg srcpkg
]
1054 [ PackageMissingDeps
1057 (fmap nodeKey
. flip Graph
.lookup graph
)
1060 |
(pkg
, missingDeps
) <- Graph
.broken graph
1062 ++ [ PackageCycle cycleGroup
1063 | cycleGroup
<- Graph
.cycles graph
1066 ++ [ PackageInconsistency name inconsistencies
1067 | (name, inconsistencies) <-
1068 dependencyInconsistencies indepGoals graph ]
1069 --TODO: consider re-enabling this one, see SolverInstallPlan
1071 ++ [ PackageStateInvalid pkg pkg
'
1072 | pkg
<- Foldable
.toList graph
1075 (flip Graph
.lookup graph
)
1077 , not (stateDependencyRelation pkg pkg
')
1080 -- | The states of packages have that depend on each other must respect
1081 -- this relation. That is for very case where package @a@ depends on
1082 -- package @b@ we require that @stateDependencyRelation a b = True@.
1083 stateDependencyRelation
1084 :: GenericPlanPackage ipkg srcpkg
1085 -> GenericPlanPackage ipkg srcpkg
1087 stateDependencyRelation PreExisting
{} PreExisting
{} = True
1088 stateDependencyRelation Installed
{} PreExisting
{} = True
1089 stateDependencyRelation Installed
{} Installed
{} = True
1090 stateDependencyRelation Configured
{} PreExisting
{} = True
1091 stateDependencyRelation Configured
{} Installed
{} = True
1092 stateDependencyRelation Configured
{} Configured
{} = True
1093 stateDependencyRelation _ _
= False