make LTS branch pre-releases
[cabal.git] / cabal-install-solver / src / Distribution / Solver / Modular / Validate.hs
blob4af149b31cfc0d21a1f226340c1207022248ce6c
1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 {-# LANGUAGE MultiParamTypeClasses #-}
3 module Distribution.Solver.Modular.Validate (validateTree) where
5 -- Validation of the tree.
6 --
7 -- The task here is to make sure all constraints hold. After validation, any
8 -- assignment returned by exploration of the tree should be a complete valid
9 -- assignment, i.e., actually constitute a solution.
11 import Control.Monad (foldM, mzero, liftM2)
12 import Control.Monad.Reader (MonadReader, Reader, runReader, local, asks)
13 import Data.Either (lefts)
14 import Data.Function (on)
16 import qualified Data.List as L
17 import qualified Data.Set as S
19 import Language.Haskell.Extension (Extension, Language)
21 import Data.Map.Strict as M
22 import Distribution.Compiler (CompilerInfo(..))
24 import Distribution.Solver.Modular.Assignment
25 import qualified Distribution.Solver.Modular.ConflictSet as CS
26 import Distribution.Solver.Modular.Dependency
27 import Distribution.Solver.Modular.Flag
28 import Distribution.Solver.Modular.Index
29 import Distribution.Solver.Modular.Package
30 import Distribution.Solver.Modular.Tree
31 import Distribution.Solver.Modular.Version
32 import qualified Distribution.Solver.Modular.WeightedPSQ as W
34 import Distribution.Solver.Types.PackagePath
35 import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb, pkgConfigPkgIsPresent)
36 import Distribution.Types.LibraryName
37 import Distribution.Types.PkgconfigVersionRange
39 -- In practice, most constraints are implication constraints (IF we have made
40 -- a number of choices, THEN we also have to ensure that). We call constraints
41 -- that for which the preconditions are fulfilled ACTIVE. We maintain a set
42 -- of currently active constraints that we pass down the node.
44 -- We aim at detecting inconsistent states as early as possible.
46 -- Whenever we make a choice, there are two things that need to happen:
48 -- (1) We must check that the choice is consistent with the currently
49 -- active constraints.
51 -- (2) The choice increases the set of active constraints. For the new
52 -- active constraints, we must check that they are consistent with
53 -- the current state.
55 -- We can actually merge (1) and (2) by saying the current choice is
56 -- a new active constraint, fixing the choice.
58 -- If a test fails, we have detected an inconsistent state. We can
59 -- disable the current subtree and do not have to traverse it any further.
61 -- We need a good way to represent the current state, i.e., the current
62 -- set of active constraints. Since the main situation where we have to
63 -- search in it is (1), it seems best to store the state by package: for
64 -- every package, we store which versions are still allowed. If for any
65 -- package, we have inconsistent active constraints, we can also stop.
66 -- This is a particular way to read task (2):
68 -- (2, weak) We only check if the new constraints are consistent with
69 -- the choices we've already made, and add them to the active set.
71 -- (2, strong) We check if the new constraints are consistent with the
72 -- choices we've already made, and the constraints we already have.
74 -- It currently seems as if we're implementing the weak variant. However,
75 -- when used together with 'preferEasyGoalChoices', we will find an
76 -- inconsistent state in the very next step.
78 -- What do we do about flags?
80 -- Like for packages, we store the flag choices we have already made.
81 -- Now, regarding (1), we only have to test whether we've decided the
82 -- current flag before. Regarding (2), the interesting bit is in discovering
83 -- the new active constraints. To this end, we look up the constraints for
84 -- the package the flag belongs to, and traverse its flagged dependencies.
85 -- Wherever we find the flag in question, we start recording dependencies
86 -- underneath as new active dependencies. If we encounter other flags, we
87 -- check if we've chosen them already and either proceed or stop.
89 -- | The state needed during validation.
90 data ValidateState = VS {
91 supportedExt :: Extension -> Bool,
92 supportedLang :: Language -> Bool,
93 presentPkgs :: Maybe (PkgconfigName -> PkgconfigVersionRange -> Bool),
94 index :: Index,
96 -- Saved, scoped, dependencies. Every time 'validate' makes a package choice,
97 -- it qualifies the package's dependencies and saves them in this map. Then
98 -- the qualified dependencies are available for subsequent flag and stanza
99 -- choices for the same package.
100 saved :: Map QPN (FlaggedDeps QPN),
102 pa :: PreAssignment,
104 -- Map from package name to the components that are provided by the chosen
105 -- instance of that package, and whether those components are visible and
106 -- buildable.
107 availableComponents :: Map QPN (Map ExposedComponent ComponentInfo),
109 -- Map from package name to the components that are required from that
110 -- package.
111 requiredComponents :: Map QPN ComponentDependencyReasons,
113 qualifyOptions :: QualifyOptions
116 newtype Validate a = Validate (Reader ValidateState a)
117 deriving (Functor, Applicative, Monad, MonadReader ValidateState)
119 runValidate :: Validate a -> ValidateState -> a
120 runValidate (Validate r) = runReader r
122 -- | A preassignment comprises knowledge about variables, but not
123 -- necessarily fixed values.
124 data PreAssignment = PA PPreAssignment FAssignment SAssignment
126 -- | A (partial) package preassignment. Qualified package names
127 -- are associated with MergedPkgDeps.
128 type PPreAssignment = Map QPN MergedPkgDep
130 -- | A dependency on a component, including its DependencyReason.
131 data PkgDep = PkgDep (DependencyReason QPN) (PkgComponent QPN) CI
133 -- | Map from component name to one of the reasons that the component is
134 -- required.
135 type ComponentDependencyReasons = Map ExposedComponent (DependencyReason QPN)
137 -- | MergedPkgDep records constraints about the instances that can still be
138 -- chosen, and in the extreme case fixes a concrete instance. Otherwise, it is a
139 -- list of version ranges paired with the goals / variables that introduced
140 -- them. It also records whether a package is a build-tool dependency, for each
141 -- reason that it was introduced.
143 -- It is important to store the component name with the version constraint, for
144 -- error messages, because whether something is a build-tool dependency affects
145 -- its qualifier, which affects which constraint is applied.
146 data MergedPkgDep =
147 MergedDepFixed ExposedComponent (DependencyReason QPN) I
148 | MergedDepConstrained [VROrigin]
150 -- | Version ranges paired with origins.
151 type VROrigin = (VR, ExposedComponent, DependencyReason QPN)
153 -- | The information needed to create a 'Fail' node.
154 type Conflict = (ConflictSet, FailReason)
156 validate :: Tree d c -> Validate (Tree d c)
157 validate = go
158 where
159 go :: Tree d c -> Validate (Tree d c)
161 go (PChoice qpn rdm gr ts) = PChoice qpn rdm gr <$> W.traverseWithKey (\k -> goP qpn k . go) ts
162 go (FChoice qfn rdm gr b m d ts) =
164 -- Flag choices may occur repeatedly (because they can introduce new constraints
165 -- in various places). However, subsequent choices must be consistent. We thereby
166 -- collapse repeated flag choice nodes.
167 PA _ pfa _ <- asks pa -- obtain current flag-preassignment
168 case M.lookup qfn pfa of
169 Just rb -> -- flag has already been assigned; collapse choice to the correct branch
170 case W.lookup rb ts of
171 Just t -> goF qfn rb (go t)
172 Nothing -> return $ Fail (varToConflictSet (F qfn)) (MalformedFlagChoice qfn)
173 Nothing -> -- flag choice is new, follow both branches
174 FChoice qfn rdm gr b m d <$> W.traverseWithKey (\k -> goF qfn k . go) ts
175 go (SChoice qsn rdm gr b ts) =
177 -- Optional stanza choices are very similar to flag choices.
178 PA _ _ psa <- asks pa -- obtain current stanza-preassignment
179 case M.lookup qsn psa of
180 Just rb -> -- stanza choice has already been made; collapse choice to the correct branch
181 case W.lookup rb ts of
182 Just t -> goS qsn rb (go t)
183 Nothing -> return $ Fail (varToConflictSet (S qsn)) (MalformedStanzaChoice qsn)
184 Nothing -> -- stanza choice is new, follow both branches
185 SChoice qsn rdm gr b <$> W.traverseWithKey (\k -> goS qsn k . go) ts
187 -- We don't need to do anything for goal choices or failure nodes.
188 go (GoalChoice rdm ts) = GoalChoice rdm <$> traverse go ts
189 go (Done rdm s ) = pure (Done rdm s)
190 go (Fail c fr ) = pure (Fail c fr)
192 -- What to do for package nodes ...
193 goP :: QPN -> POption -> Validate (Tree d c) -> Validate (Tree d c)
194 goP qpn@(Q _pp pn) (POption i _) r = do
195 PA ppa pfa psa <- asks pa -- obtain current preassignment
196 extSupported <- asks supportedExt -- obtain the supported extensions
197 langSupported <- asks supportedLang -- obtain the supported languages
198 pkgPresent <- asks presentPkgs -- obtain the present pkg-config pkgs
199 idx <- asks index -- obtain the index
200 svd <- asks saved -- obtain saved dependencies
201 aComps <- asks availableComponents
202 rComps <- asks requiredComponents
203 qo <- asks qualifyOptions
204 -- obtain dependencies and index-dictated exclusions introduced by the choice
205 let (PInfo deps comps _ mfr) = idx ! pn ! i
206 -- qualify the deps in the current scope
207 let qdeps = qualifyDeps qo qpn deps
208 -- the new active constraints are given by the instance we have chosen,
209 -- plus the dependency information we have for that instance
210 let newactives = extractAllDeps pfa psa qdeps
211 -- We now try to extend the partial assignment with the new active constraints.
212 let mnppa = extend extSupported langSupported pkgPresent newactives
213 =<< extendWithPackageChoice (PI qpn i) ppa
214 -- In case we continue, we save the scoped dependencies
215 let nsvd = M.insert qpn qdeps svd
216 case mfr of
217 Just fr -> -- The index marks this as an invalid choice. We can stop.
218 return (Fail (varToConflictSet (P qpn)) fr)
219 Nothing ->
220 let newDeps :: Either Conflict (PPreAssignment, Map QPN ComponentDependencyReasons)
221 newDeps = do
222 nppa <- mnppa
223 rComps' <- extendRequiredComponents qpn aComps rComps newactives
224 checkComponentsInNewPackage (M.findWithDefault M.empty qpn rComps) qpn comps
225 return (nppa, rComps')
226 in case newDeps of
227 Left (c, fr) -> -- We have an inconsistency. We can stop.
228 return (Fail c fr)
229 Right (nppa, rComps') -> -- We have an updated partial assignment for the recursive validation.
230 local (\ s -> s { pa = PA nppa pfa psa
231 , saved = nsvd
232 , availableComponents = M.insert qpn comps aComps
233 , requiredComponents = rComps'
234 }) r
236 -- What to do for flag nodes ...
237 goF :: QFN -> Bool -> Validate (Tree d c) -> Validate (Tree d c)
238 goF qfn@(FN qpn _f) b r = do
239 PA ppa pfa psa <- asks pa -- obtain current preassignment
240 extSupported <- asks supportedExt -- obtain the supported extensions
241 langSupported <- asks supportedLang -- obtain the supported languages
242 pkgPresent <- asks presentPkgs -- obtain the present pkg-config pkgs
243 svd <- asks saved -- obtain saved dependencies
244 aComps <- asks availableComponents
245 rComps <- asks requiredComponents
246 -- Note that there should be saved dependencies for the package in question,
247 -- because while building, we do not choose flags before we see the packages
248 -- that define them.
249 let qdeps = svd ! qpn
250 -- We take the *saved* dependencies, because these have been qualified in the
251 -- correct scope.
253 -- Extend the flag assignment
254 let npfa = M.insert qfn b pfa
255 -- We now try to get the new active dependencies we might learn about because
256 -- we have chosen a new flag.
257 let newactives = extractNewDeps (F qfn) b npfa psa qdeps
258 mNewRequiredComps = extendRequiredComponents qpn aComps rComps newactives
259 -- As in the package case, we try to extend the partial assignment.
260 let mnppa = extend extSupported langSupported pkgPresent newactives ppa
261 case liftM2 (,) mnppa mNewRequiredComps of
262 Left (c, fr) -> return (Fail c fr) -- inconsistency found
263 Right (nppa, rComps') ->
264 local (\ s -> s { pa = PA nppa npfa psa, requiredComponents = rComps' }) r
266 -- What to do for stanza nodes (similar to flag nodes) ...
267 goS :: QSN -> Bool -> Validate (Tree d c) -> Validate (Tree d c)
268 goS qsn@(SN qpn _f) b r = do
269 PA ppa pfa psa <- asks pa -- obtain current preassignment
270 extSupported <- asks supportedExt -- obtain the supported extensions
271 langSupported <- asks supportedLang -- obtain the supported languages
272 pkgPresent <- asks presentPkgs -- obtain the present pkg-config pkgs
273 svd <- asks saved -- obtain saved dependencies
274 aComps <- asks availableComponents
275 rComps <- asks requiredComponents
276 -- Note that there should be saved dependencies for the package in question,
277 -- because while building, we do not choose flags before we see the packages
278 -- that define them.
279 let qdeps = svd ! qpn
280 -- We take the *saved* dependencies, because these have been qualified in the
281 -- correct scope.
283 -- Extend the flag assignment
284 let npsa = M.insert qsn b psa
285 -- We now try to get the new active dependencies we might learn about because
286 -- we have chosen a new flag.
287 let newactives = extractNewDeps (S qsn) b pfa npsa qdeps
288 mNewRequiredComps = extendRequiredComponents qpn aComps rComps newactives
289 -- As in the package case, we try to extend the partial assignment.
290 let mnppa = extend extSupported langSupported pkgPresent newactives ppa
291 case liftM2 (,) mnppa mNewRequiredComps of
292 Left (c, fr) -> return (Fail c fr) -- inconsistency found
293 Right (nppa, rComps') ->
294 local (\ s -> s { pa = PA nppa pfa npsa, requiredComponents = rComps' }) r
296 -- | Check that a newly chosen package instance contains all components that
297 -- are required from that package so far. The components must also be visible
298 -- and buildable.
299 checkComponentsInNewPackage :: ComponentDependencyReasons
300 -> QPN
301 -> Map ExposedComponent ComponentInfo
302 -> Either Conflict ()
303 checkComponentsInNewPackage required qpn providedComps =
304 case M.toList $ deleteKeys (M.keys providedComps) required of
305 (missingComp, dr) : _ ->
306 Left $ mkConflict missingComp dr NewPackageIsMissingRequiredComponent
307 [] ->
308 let failures = lefts
309 [ case () of
310 _ | compIsVisible compInfo == IsVisible False ->
311 Left $ mkConflict comp dr NewPackageHasPrivateRequiredComponent
312 | compIsBuildable compInfo == IsBuildable False ->
313 Left $ mkConflict comp dr NewPackageHasUnbuildableRequiredComponent
314 | otherwise -> Right ()
315 | let merged = M.intersectionWith (,) required providedComps
316 , (comp, (dr, compInfo)) <- M.toList merged ]
317 in case failures of
318 failure : _ -> Left failure
319 [] -> Right ()
320 where
321 mkConflict :: ExposedComponent
322 -> DependencyReason QPN
323 -> (ExposedComponent -> DependencyReason QPN -> FailReason)
324 -> Conflict
325 mkConflict comp dr mkFailure =
326 (CS.insert (P qpn) (dependencyReasonToConflictSet dr), mkFailure comp dr)
328 deleteKeys :: Ord k => [k] -> Map k v -> Map k v
329 deleteKeys ks m = L.foldr M.delete m ks
331 -- | We try to extract as many concrete dependencies from the given flagged
332 -- dependencies as possible. We make use of all the flag knowledge we have
333 -- already acquired.
334 extractAllDeps :: FAssignment -> SAssignment -> FlaggedDeps QPN -> [LDep QPN]
335 extractAllDeps fa sa deps = do
336 d <- deps
337 case d of
338 Simple sd _ -> return sd
339 Flagged qfn _ td fd -> case M.lookup qfn fa of
340 Nothing -> mzero
341 Just True -> extractAllDeps fa sa td
342 Just False -> extractAllDeps fa sa fd
343 Stanza qsn td -> case M.lookup qsn sa of
344 Nothing -> mzero
345 Just True -> extractAllDeps fa sa td
346 Just False -> []
348 -- | We try to find new dependencies that become available due to the given
349 -- flag or stanza choice. We therefore look for the choice in question, and then call
350 -- 'extractAllDeps' for everything underneath.
351 extractNewDeps :: Var QPN -> Bool -> FAssignment -> SAssignment -> FlaggedDeps QPN -> [LDep QPN]
352 extractNewDeps v b fa sa = go
353 where
354 go :: FlaggedDeps QPN -> [LDep QPN]
355 go deps = do
356 d <- deps
357 case d of
358 Simple _ _ -> mzero
359 Flagged qfn' _ td fd
360 | v == F qfn' -> if b then extractAllDeps fa sa td else extractAllDeps fa sa fd
361 | otherwise -> case M.lookup qfn' fa of
362 Nothing -> mzero
363 Just True -> go td
364 Just False -> go fd
365 Stanza qsn' td
366 | v == S qsn' -> if b then extractAllDeps fa sa td else []
367 | otherwise -> case M.lookup qsn' sa of
368 Nothing -> mzero
369 Just True -> go td
370 Just False -> []
372 -- | Extend a package preassignment.
374 -- Takes the variable that causes the new constraints, a current preassignment
375 -- and a set of new dependency constraints.
377 -- We're trying to extend the preassignment with each dependency one by one.
378 -- Each dependency is for a particular variable. We check if we already have
379 -- constraints for that variable in the current preassignment. If so, we're
380 -- trying to merge the constraints.
382 -- Either returns a witness of the conflict that would arise during the merge,
383 -- or the successfully extended assignment.
384 extend :: (Extension -> Bool) -- ^ is a given extension supported
385 -> (Language -> Bool) -- ^ is a given language supported
386 -> Maybe (PkgconfigName -> PkgconfigVersionRange -> Bool) -- ^ is a given pkg-config requirement satisfiable
387 -> [LDep QPN]
388 -> PPreAssignment
389 -> Either Conflict PPreAssignment
390 extend extSupported langSupported pkgPresent newactives ppa = foldM extendSingle ppa newactives
391 where
393 extendSingle :: PPreAssignment -> LDep QPN -> Either Conflict PPreAssignment
394 extendSingle a (LDep dr (Ext ext )) =
395 if extSupported ext then Right a
396 else Left (dependencyReasonToConflictSet dr, UnsupportedExtension ext)
397 extendSingle a (LDep dr (Lang lang)) =
398 if langSupported lang then Right a
399 else Left (dependencyReasonToConflictSet dr, UnsupportedLanguage lang)
400 extendSingle a (LDep dr (Pkg pn vr)) =
401 case (\f -> f pn vr) <$> pkgPresent of
402 Just True -> Right a
403 Just False -> Left (dependencyReasonToConflictSet dr, MissingPkgconfigPackage pn vr)
404 Nothing -> Left (dependencyReasonToConflictSet dr, MissingPkgconfigProgram pn vr)
405 extendSingle a (LDep dr (Dep dep@(PkgComponent qpn _) ci)) =
406 let mergedDep = M.findWithDefault (MergedDepConstrained []) qpn a
407 in case (\ x -> M.insert qpn x a) <$> merge mergedDep (PkgDep dr dep ci) of
408 Left (c, (d, d')) -> Left (c, ConflictingConstraints d d')
409 Right x -> Right x
411 -- | Extend a package preassignment with a package choice. For example, when
412 -- the solver chooses foo-2.0, it tries to add the constraint foo==2.0.
414 -- TODO: The new constraint is implemented as a dependency from foo to foo's
415 -- main library. That isn't correct, because foo might only be needed as a build
416 -- tool dependency. The implementation may need to change when we support
417 -- component-based dependency solving.
418 extendWithPackageChoice :: PI QPN -> PPreAssignment -> Either Conflict PPreAssignment
419 extendWithPackageChoice (PI qpn i) ppa =
420 let mergedDep = M.findWithDefault (MergedDepConstrained []) qpn ppa
421 newChoice = PkgDep (DependencyReason qpn M.empty S.empty)
422 (PkgComponent qpn (ExposedLib LMainLibName))
423 (Fixed i)
424 in case (\ x -> M.insert qpn x ppa) <$> merge mergedDep newChoice of
425 Left (c, (d, _d')) -> -- Don't include the package choice in the
426 -- FailReason, because it is redundant.
427 Left (c, NewPackageDoesNotMatchExistingConstraint d)
428 Right x -> Right x
430 -- | Merge constrained instances. We currently adopt a lazy strategy for
431 -- merging, i.e., we only perform actual checking if one of the two choices
432 -- is fixed. If the merge fails, we return a conflict set indicating the
433 -- variables responsible for the failure, as well as the two conflicting
434 -- fragments.
436 -- Note that while there may be more than one conflicting pair of version
437 -- ranges, we only return the first we find.
439 -- The ConflictingDeps are returned in order, i.e., the first describes the
440 -- conflicting part of the MergedPkgDep, and the second describes the PkgDep.
442 -- TODO: Different pairs might have different conflict sets. We're
443 -- obviously interested to return a conflict that has a "better" conflict
444 -- set in the sense the it contains variables that allow us to backjump
445 -- further. We might apply some heuristics here, such as to change the
446 -- order in which we check the constraints.
447 merge :: MergedPkgDep -> PkgDep -> Either (ConflictSet, (ConflictingDep, ConflictingDep)) MergedPkgDep
448 merge (MergedDepFixed comp1 vs1 i1) (PkgDep vs2 (PkgComponent p comp2) ci@(Fixed i2))
449 | i1 == i2 = Right $ MergedDepFixed comp1 vs1 i1
450 | otherwise =
451 Left ( (CS.union `on` dependencyReasonToConflictSet) vs1 vs2
452 , ( ConflictingDep vs1 (PkgComponent p comp1) (Fixed i1)
453 , ConflictingDep vs2 (PkgComponent p comp2) ci ) )
455 merge (MergedDepFixed comp1 vs1 i@(I v _)) (PkgDep vs2 (PkgComponent p comp2) ci@(Constrained vr))
456 | checkVR vr v = Right $ MergedDepFixed comp1 vs1 i
457 | otherwise =
458 Left ( createConflictSetForVersionConflict p v vs1 vr vs2
459 , ( ConflictingDep vs1 (PkgComponent p comp1) (Fixed i)
460 , ConflictingDep vs2 (PkgComponent p comp2) ci ) )
462 merge (MergedDepConstrained vrOrigins) (PkgDep vs2 (PkgComponent p comp2) ci@(Fixed i@(I v _))) =
463 go vrOrigins -- I tried "reverse vrOrigins" here, but it seems to slow things down ...
464 where
465 go :: [VROrigin] -> Either (ConflictSet, (ConflictingDep, ConflictingDep)) MergedPkgDep
466 go [] = Right (MergedDepFixed comp2 vs2 i)
467 go ((vr, comp1, vs1) : vros)
468 | checkVR vr v = go vros
469 | otherwise =
470 Left ( createConflictSetForVersionConflict p v vs2 vr vs1
471 , ( ConflictingDep vs1 (PkgComponent p comp1) (Constrained vr)
472 , ConflictingDep vs2 (PkgComponent p comp2) ci ) )
474 merge (MergedDepConstrained vrOrigins) (PkgDep vs2 (PkgComponent _ comp2) (Constrained vr)) =
475 Right (MergedDepConstrained $
477 -- TODO: This line appends the new version range, to preserve the order used
478 -- before a refactoring. Consider prepending the version range, if there is
479 -- no negative performance impact.
480 vrOrigins ++ [(vr, comp2, vs2)])
482 -- | Creates a conflict set representing a conflict between a version constraint
483 -- and the fixed version chosen for a package.
484 createConflictSetForVersionConflict :: QPN
485 -> Ver
486 -> DependencyReason QPN
487 -> VR
488 -> DependencyReason QPN
489 -> ConflictSet
490 createConflictSetForVersionConflict pkg
491 conflictingVersion
492 versionDR@(DependencyReason p1 _ _)
493 conflictingVersionRange
494 versionRangeDR@(DependencyReason p2 _ _) =
495 let hasFlagsOrStanzas (DependencyReason _ fs ss) = not (M.null fs) || not (S.null ss)
497 -- The solver currently only optimizes the case where there is a conflict
498 -- between the version chosen for a package and a version constraint that
499 -- is not under any flags or stanzas. Here is how we check for this case:
501 -- (1) Choosing a specific version for a package foo is implemented as
502 -- adding a dependency from foo to that version of foo (See
503 -- extendWithPackageChoice), so we check that the DependencyReason
504 -- contains the current package and no flag or stanza choices.
506 -- (2) We check that the DependencyReason for the version constraint also
507 -- contains no flag or stanza choices.
509 -- When these criteria are not met, we fall back to calling
510 -- dependencyReasonToConflictSet.
511 if p1 == pkg && not (hasFlagsOrStanzas versionDR) && not (hasFlagsOrStanzas versionRangeDR)
512 then let cs1 = dependencyReasonToConflictSetWithVersionConflict
514 (CS.OrderedVersionRange conflictingVersionRange)
515 versionDR
516 cs2 = dependencyReasonToConflictSetWithVersionConstraintConflict
517 pkg conflictingVersion versionRangeDR
518 in cs1 `CS.union` cs2
519 else dependencyReasonToConflictSet versionRangeDR `CS.union` dependencyReasonToConflictSet versionDR
521 -- | Takes a list of new dependencies and uses it to try to update the map of
522 -- known component dependencies. It returns a failure when a new dependency
523 -- requires a component that is missing, private, or unbuildable in a previously
524 -- chosen package.
525 extendRequiredComponents :: QPN -- ^ package we extend
526 -> Map QPN (Map ExposedComponent ComponentInfo)
527 -> Map QPN ComponentDependencyReasons
528 -> [LDep QPN]
529 -> Either Conflict (Map QPN ComponentDependencyReasons)
530 extendRequiredComponents eqpn available = foldM extendSingle
531 where
532 extendSingle :: Map QPN ComponentDependencyReasons
533 -> LDep QPN
534 -> Either Conflict (Map QPN ComponentDependencyReasons)
535 extendSingle required (LDep dr (Dep (PkgComponent qpn comp) _)) =
536 let compDeps = M.findWithDefault M.empty qpn required
537 success = Right $ M.insertWith M.union qpn (M.insert comp dr compDeps) required
538 in -- Only check for the existence of the component if its package has
539 -- already been chosen.
540 case M.lookup qpn available of
541 Just comps ->
542 case M.lookup comp comps of
543 Nothing ->
544 Left $ mkConflict qpn comp dr PackageRequiresMissingComponent
545 Just compInfo
546 | compIsVisible compInfo == IsVisible False
547 , eqpn /= qpn -- package components can depend on other components
549 Left $ mkConflict qpn comp dr PackageRequiresPrivateComponent
550 | compIsBuildable compInfo == IsBuildable False ->
551 Left $ mkConflict qpn comp dr PackageRequiresUnbuildableComponent
552 | otherwise -> success
553 Nothing -> success
554 extendSingle required _ = Right required
556 mkConflict :: QPN
557 -> ExposedComponent
558 -> DependencyReason QPN
559 -> (QPN -> ExposedComponent -> FailReason)
560 -> Conflict
561 mkConflict qpn comp dr mkFailure =
562 (CS.insert (P qpn) (dependencyReasonToConflictSet dr), mkFailure qpn comp)
565 -- | Interface.
566 validateTree :: CompilerInfo -> Index -> Maybe PkgConfigDb -> Tree d c -> Tree d c
567 validateTree cinfo idx pkgConfigDb t = runValidate (validate t) VS {
568 supportedExt = maybe (const True) -- if compiler has no list of extensions, we assume everything is supported
569 (\ es -> let s = S.fromList es in \ x -> S.member x s)
570 (compilerInfoExtensions cinfo)
571 , supportedLang = maybe (const True)
572 (flip L.elem) -- use list lookup because language list is small and no Ord instance
573 (compilerInfoLanguages cinfo)
574 , presentPkgs = pkgConfigPkgIsPresent <$> pkgConfigDb
575 , index = idx
576 , saved = M.empty
577 , pa = PA M.empty M.empty M.empty
578 , availableComponents = M.empty
579 , requiredComponents = M.empty
580 , qualifyOptions = defaultQualifyOptions idx