1 {-# LANGUAGE ScopedTypeVariables #-}
2 -- | Reordering or pruning the tree in order to prefer or make certain choices.
3 module Distribution
.Solver
.Modular
.Preference
8 , enforcePackageConstraints
9 , enforceSingleInstanceRestriction
11 , preferBaseGoalChoice
13 , preferPackagePreferences
14 , preferReallyEasyGoalChoices
17 , pruneAfterFirstSuccess
21 import Distribution
.Solver
.Compat
.Prelude
23 import qualified Data
.List
as L
24 import qualified Data
.Map
as M
25 import Control
.Monad
.Trans
.Reader
(Reader
, runReader
, ask
, local
)
27 import Distribution
.PackageDescription
(lookupFlagAssignment
, unFlagAssignment
) -- from Cabal
29 import Distribution
.Solver
.Types
.Flag
30 import Distribution
.Solver
.Types
.InstalledPreference
31 import Distribution
.Solver
.Types
.LabeledPackageConstraint
32 import Distribution
.Solver
.Types
.OptionalStanza
33 import Distribution
.Solver
.Types
.PackageConstraint
34 import Distribution
.Solver
.Types
.PackagePath
35 import Distribution
.Solver
.Types
.PackagePreferences
36 import Distribution
.Solver
.Types
.Variable
38 import Distribution
.Solver
.Modular
.Dependency
39 import Distribution
.Solver
.Modular
.Flag
40 import Distribution
.Solver
.Modular
.Package
41 import qualified Distribution
.Solver
.Modular
.PSQ
as P
42 import Distribution
.Solver
.Modular
.Tree
43 import Distribution
.Solver
.Modular
.Version
44 import qualified Distribution
.Solver
.Modular
.ConflictSet
as CS
45 import qualified Distribution
.Solver
.Modular
.WeightedPSQ
as W
47 -- | Update the weights of children under 'PChoice' nodes. 'addWeights' takes a
48 -- list of weight-calculating functions in order to avoid sorting the package
49 -- choices multiple times. Each function takes the package name, sorted list of
50 -- children's versions, and package option. 'addWeights' prepends the new
51 -- weights to the existing weights, which gives precedence to preferences that
53 addWeights
:: [PN
-> [Ver
] -> POption
-> Weight
] -> EndoTreeTrav d c
56 go
:: TreeF d c
(Tree d c
) -> TreeF d c
(Tree d c
)
57 go
(PChoiceF qpn
@(Q _ pn
) rdm x cs
) =
58 let sortedVersions
= L
.sortBy (flip compare) $ L
.map version
(W
.keys cs
)
59 weights k
= [f pn sortedVersions k | f
<- fs
]
61 elemsToWhnf
:: [a
] -> ()
62 elemsToWhnf
= foldr seq ()
64 -- Evaluate the children's versions before evaluating any of the
65 -- subtrees, so that 'sortedVersions' doesn't hold onto all of the
66 -- subtrees (referenced by cs) and cause a space leak.
67 (elemsToWhnf sortedVersions `
seq`
68 W
.mapWeightsWithKey
(\k w
-> weights k
++ w
) cs
)
71 addWeight
:: (PN
-> [Ver
] -> POption
-> Weight
) -> EndoTreeTrav d c
72 addWeight f
= addWeights
[f
]
74 version
:: POption
-> Ver
75 version
(POption
(I v _
) _
) = v
77 -- | Prefer to link packages whenever possible.
78 preferLinked
:: EndoTreeTrav d c
79 preferLinked
= addWeight
(const (const linked
))
81 linked
(POption _ Nothing
) = 1
82 linked
(POption _
(Just _
)) = 0
84 -- Works by setting weights on choice nodes. Also applies stanza preferences.
85 preferPackagePreferences
:: (PN
-> PackagePreferences
) -> EndoTreeTrav d c
86 preferPackagePreferences pcs
=
87 preferPackageStanzaPreferences pcs
.
88 -- Each package is assigned a list of weights (currently three of them),
89 -- and options are ordered by comparison of these lists.
91 -- The head of the list (and thus the top priority for ordering)
92 -- is whether the package version is "preferred"
93 -- (https://hackage.haskell.org/packages/preferred-versions).
95 -- The next two elements depend on 'PackagePreferences'.
96 -- For 'PreferInstalled' they are whether the version is installed (0 or 1)
97 -- and how close is the version to the latest one (between 0.0 and 1.0).
98 -- For 'PreferLatest' the weights are the same, but swapped, so that
99 -- ordering considers how new is the package first.
100 -- For 'PreferOldest' one weight measures how close is the version to the
101 -- the oldest one possible (between 0.0 and 1.0) and another checks whether
102 -- the version is installed (0 or 1).
104 \pn _ opt
-> preferred pn opt
105 , \pn vs opt
-> case preference pn
of
106 PreferInstalled
-> installed opt
107 PreferLatest
-> latest vs opt
108 PreferOldest
-> oldest vs opt
109 , \pn vs opt
-> case preference pn
of
110 PreferInstalled
-> latest vs opt
111 PreferLatest
-> installed opt
112 PreferOldest
-> installed opt
115 -- Prefer packages with higher version numbers over packages with
116 -- lower version numbers.
117 latest
:: [Ver
] -> POption
-> Weight
118 latest sortedVersions opt
=
119 let l
= length sortedVersions
120 index = fromMaybe l
$ L
.findIndex (<= version opt
) sortedVersions
121 in fromIntegral index / fromIntegral l
123 -- Prefer packages with lower version numbers over packages with
124 -- higher version numbers.
125 oldest
:: [Ver
] -> POption
-> Weight
126 oldest sortedVersions opt
= 1 - latest sortedVersions opt
128 preference
:: PN
-> InstalledPreference
130 let PackagePreferences _ ipref _
= pcs pn
133 -- | Prefer versions satisfying more preferred version ranges.
134 preferred
:: PN
-> POption
-> Weight
136 let PackagePreferences vrs _ _
= pcs pn
137 in fromIntegral . negate . L
.length $
138 L
.filter (flip checkVR
(version opt
)) vrs
140 -- Prefer installed packages over non-installed packages.
141 installed
:: POption
-> Weight
142 installed
(POption
(I _
(Inst _
)) _
) = 0
145 -- | Traversal that tries to establish package stanza enable\/disable
146 -- preferences. Works by reordering the branches of stanza choices.
147 -- Note that this works on packages lower in the path as well as at the top level.
148 -- This is because stanza preferences apply to local packages only
149 -- and for local packages, a single version is fixed, which means
150 -- (for now) that all stanza preferences must be uniform at all levels.
151 -- Further, even when we can have multiple versions of the same package,
152 -- the build plan will be more efficient if we can attempt to keep
153 -- stanza preferences aligned at all levels.
154 preferPackageStanzaPreferences
:: (PN
-> PackagePreferences
) -> EndoTreeTrav d c
155 preferPackageStanzaPreferences pcs
= go
157 go
(SChoiceF qsn
@(SN
(Q _pp pn
) s
) rdm gr _tr ts
)
158 | enableStanzaPref pn s
=
159 -- move True case first to try enabling the stanza
160 let ts
' = W
.mapWeightsWithKey
(\k w
-> weight k
: w
) ts
161 weight k
= if k
then 0 else 1
162 -- defer the choice by setting it to weak
163 in SChoiceF qsn rdm gr
(WeakOrTrivial
True) ts
'
166 enableStanzaPref
:: PN
-> OptionalStanza
-> Bool
167 enableStanzaPref pn s
=
168 let PackagePreferences _ _ spref
= pcs pn
171 -- | Helper function that tries to enforce a single package constraint on a
172 -- given instance for a P-node. Translates the constraint into a
173 -- tree-transformer that either leaves the subtree untouched, or replaces it
174 -- with an appropriate failure node.
175 processPackageConstraintP
:: forall d c
. QPN
178 -> LabeledPackageConstraint
181 processPackageConstraintP qpn c i
(LabeledPackageConstraint
(PackageConstraint scope prop
) src
) r
=
182 if constraintScopeMatches scope qpn
186 go
:: I
-> PackageProperty
-> Tree d c
187 go
(I v _
) (PackagePropertyVersion vr
)
189 |
otherwise = Fail c
(GlobalConstraintVersion vr src
)
190 go _ PackagePropertyInstalled
192 |
otherwise = Fail c
(GlobalConstraintInstalled src
)
193 go _ PackagePropertySource
195 |
otherwise = Fail c
(GlobalConstraintSource src
)
198 -- | Helper function that tries to enforce a single package constraint on a
199 -- given flag setting for an F-node. Translates the constraint into a
200 -- tree-transformer that either leaves the subtree untouched, or replaces it
201 -- with an appropriate failure node.
202 processPackageConstraintF
:: forall d c
. QPN
206 -> LabeledPackageConstraint
209 processPackageConstraintF qpn f c b
' (LabeledPackageConstraint
(PackageConstraint scope prop
) src
) r
=
210 if constraintScopeMatches scope qpn
214 go
:: PackageProperty
-> Tree d c
215 go
(PackagePropertyFlags fa
) =
216 case lookupFlagAssignment f fa
of
218 Just b | b
== b
' -> r
219 |
otherwise -> Fail c
(GlobalConstraintFlag src
)
222 -- | Helper function that tries to enforce a single package constraint on a
223 -- given flag setting for an F-node. Translates the constraint into a
224 -- tree-transformer that either leaves the subtree untouched, or replaces it
225 -- with an appropriate failure node.
226 processPackageConstraintS
:: forall d c
. QPN
230 -> LabeledPackageConstraint
233 processPackageConstraintS qpn s c b
' (LabeledPackageConstraint
(PackageConstraint scope prop
) src
) r
=
234 if constraintScopeMatches scope qpn
238 go
:: PackageProperty
-> Tree d c
239 go
(PackagePropertyStanzas ss
) =
240 if not b
' && s `
elem` ss
then Fail c
(GlobalConstraintFlag src
)
244 -- | Traversal that tries to establish various kinds of user constraints. Works
245 -- by selectively disabling choices that have been ruled out by global user
247 enforcePackageConstraints
:: M
.Map PN
[LabeledPackageConstraint
]
249 enforcePackageConstraints pcs
= go
251 go
(PChoiceF qpn
@(Q _ pn
) rdm gr ts
) =
252 let c
= varToConflictSet
(P qpn
)
253 -- compose the transformation functions for each of the relevant constraint
254 g
= \ (POption i _
) -> foldl (\ h pc
-> h
. processPackageConstraintP qpn c i pc
)
256 (M
.findWithDefault
[] pn pcs
)
257 in PChoiceF qpn rdm gr
(W
.mapWithKey g ts
)
258 go
(FChoiceF qfn
@(FN qpn
@(Q _ pn
) f
) rdm gr tr m d ts
) =
259 let c
= varToConflictSet
(F qfn
)
260 -- compose the transformation functions for each of the relevant constraint
261 g
= \ b
-> foldl (\ h pc
-> h
. processPackageConstraintF qpn f c b pc
)
263 (M
.findWithDefault
[] pn pcs
)
264 in FChoiceF qfn rdm gr tr m d
(W
.mapWithKey g ts
)
265 go
(SChoiceF qsn
@(SN qpn
@(Q _ pn
) f
) rdm gr tr ts
) =
266 let c
= varToConflictSet
(S qsn
)
267 -- compose the transformation functions for each of the relevant constraint
268 g
= \ b
-> foldl (\ h pc
-> h
. processPackageConstraintS qpn f c b pc
)
270 (M
.findWithDefault
[] pn pcs
)
271 in SChoiceF qsn rdm gr tr
(W
.mapWithKey g ts
)
274 -- | Transformation that tries to enforce the rule that manual flags can only be
277 -- If there are no constraints on a manual flag, this function prunes all but
278 -- the default value. If there are constraints, then the flag is allowed to have
279 -- the values specified by the constraints. Note that the type used for flag
280 -- values doesn't need to be Bool.
282 -- This function makes an exception for the case where there are multiple goals
283 -- for a single package (with different qualifiers), and flag constraints for
284 -- manual flag x only apply to some of those goals. In that case, we allow the
285 -- unconstrained goals to use the default value for x OR any of the values in
286 -- the constraints on x (even though the constraints don't apply), in order to
287 -- allow the unconstrained goals to be linked to the constrained goals. See
288 -- https://github.com/haskell/cabal/issues/4299. Removing the single instance
289 -- restriction (SIR) would also fix #4299, so we may want to remove this
290 -- exception and only let the user toggle manual flags if we remove the SIR.
292 -- This function does not enforce any of the constraints, since that is done by
293 -- 'enforcePackageConstraints'.
294 enforceManualFlags
:: M
.Map PN
[LabeledPackageConstraint
] -> EndoTreeTrav d c
295 enforceManualFlags pcs
= go
297 go
(FChoiceF qfn
@(FN
(Q _ pn
) fn
) rdm gr tr Manual d ts
) =
298 FChoiceF qfn rdm gr tr Manual d
$
299 let -- A list of all values specified by constraints on 'fn'.
300 -- We ignore the constraint scope in order to handle issue #4299.
301 flagConstraintValues
:: [Bool]
302 flagConstraintValues
=
304 |
let lpcs
= M
.findWithDefault
[] pn pcs
305 , (LabeledPackageConstraint
(PackageConstraint _
(PackagePropertyFlags fa
)) _
) <- lpcs
306 , (fn
', flagVal
) <- unFlagAssignment fa
309 -- Prune flag values that are not the default and do not match any
310 -- of the constraints.
311 restrictToggling
:: Eq a
=> a
-> [a
] -> a
-> Tree d c
-> Tree d c
312 restrictToggling flagDefault constraintVals flagVal r
=
313 if flagVal `
elem` constraintVals || flagVal
== flagDefault
315 else Fail
(varToConflictSet
(F qfn
)) ManualFlag
317 in W
.mapWithKey
(restrictToggling d flagConstraintValues
) ts
320 -- | Avoid reinstalls.
322 -- This is a tricky strategy. If a package version is installed already and the
323 -- same version is available from a repo, the repo version will never be chosen.
324 -- This would result in a reinstall (either destructively, or potentially,
325 -- shadowing). The old instance won't be visible or even present anymore, but
326 -- other packages might have depended on it.
328 -- TODO: It would be better to actually check the reverse dependencies of installed
329 -- packages. If they're not depended on, then reinstalling should be fine. Even if
330 -- they are, perhaps this should just result in trying to reinstall those other
331 -- packages as well. However, doing this all neatly in one pass would require to
332 -- change the builder, or at least to change the goal set after building.
333 avoidReinstalls
:: (PN
-> Bool) -> EndoTreeTrav d c
334 avoidReinstalls p
= go
336 go
(PChoiceF qpn
@(Q _ pn
) rdm gr cs
)
337 | p pn
= PChoiceF qpn rdm gr disableReinstalls
338 |
otherwise = PChoiceF qpn rdm gr cs
341 let installed
= [ v |
(_
, POption
(I v
(Inst _
)) _
, _
) <- W
.toList cs
]
342 in W
.mapWithKey
(notReinstall installed
) cs
344 notReinstall vs
(POption
(I v InRepo
) _
) _ | v `
elem` vs
=
345 Fail
(varToConflictSet
(P qpn
)) CannotReinstall
350 -- | Require all packages to be mentioned in a constraint or as a goal.
351 onlyConstrained
:: (PN
-> Bool) -> EndoTreeTrav d QGoalReason
352 onlyConstrained p
= go
354 go
(PChoiceF v
@(Q _ pn
) _ gr _
) |
not (p pn
)
356 (varToConflictSet
(P v
) `CS
.union` goalReasonToConflictSetWithConflict v gr
)
361 -- | Sort all goals using the provided function.
362 sortGoals
:: (Variable QPN
-> Variable QPN
-> Ordering) -> EndoTreeTrav d c
363 sortGoals variableOrder
= go
365 go
(GoalChoiceF rdm xs
) = GoalChoiceF rdm
(P
.sortByKeys goalOrder xs
)
368 goalOrder
:: Goal QPN
-> Goal QPN
-> Ordering
369 goalOrder
= variableOrder `on`
(varToVariable
. goalToVar
)
371 varToVariable
:: Var QPN
-> Variable QPN
372 varToVariable
(P qpn
) = PackageVar qpn
373 varToVariable
(F
(FN qpn fn
)) = FlagVar qpn fn
374 varToVariable
(S
(SN qpn stanza
)) = StanzaVar qpn stanza
376 -- | Reduce the branching degree of the search tree by removing all choices
377 -- after the first successful choice at each level. The returned tree is the
378 -- minimal subtree containing the path to the first backjump.
379 pruneAfterFirstSuccess
:: EndoTreeTrav d c
380 pruneAfterFirstSuccess
= go
382 go
(PChoiceF qpn rdm gr ts
) = PChoiceF qpn rdm gr
(W
.takeUntil active ts
)
383 go
(FChoiceF qfn rdm gr w m d ts
) = FChoiceF qfn rdm gr w m d
(W
.takeUntil active ts
)
384 go
(SChoiceF qsn rdm gr w ts
) = SChoiceF qsn rdm gr w
(W
.takeUntil active ts
)
387 -- | Always choose the first goal in the list next, abandoning all
390 -- This is unnecessary for the default search strategy, because
391 -- it descends only into the first goal choice anyway,
392 -- but may still make sense to just reduce the tree size a bit.
393 firstGoal
:: EndoTreeTrav d c
396 go
(GoalChoiceF rdm xs
) = GoalChoiceF rdm
(P
.firstOnly xs
)
398 -- Note that we keep empty choice nodes, because they mean success.
400 -- | Transformation that tries to make a decision on base as early as
401 -- possible by pruning all other goals when base is available. In nearly
402 -- all cases, there's a single choice for the base package. Also, fixing
403 -- base early should lead to better error messages.
404 preferBaseGoalChoice
:: EndoTreeTrav d c
405 preferBaseGoalChoice
= go
407 go
(GoalChoiceF rdm xs
) = GoalChoiceF rdm
(P
.filterIfAnyByKeys isBase xs
)
410 isBase
:: Goal QPN
-> Bool
411 isBase
(Goal
(P
(Q _pp pn
)) _
) = unPN pn
== "base"
414 -- | Deal with setup and build-tool-depends dependencies after regular dependencies,
415 -- so we will link setup/exe dependencies against package dependencies when possible
416 deferSetupExeChoices
:: EndoTreeTrav d c
417 deferSetupExeChoices
= go
419 go
(GoalChoiceF rdm xs
) = GoalChoiceF rdm
(P
.preferByKeys noSetupOrExe xs
)
422 noSetupOrExe
:: Goal QPN
-> Bool
423 noSetupOrExe
(Goal
(P
(Q
(PackagePath _ns
(QualSetup _
)) _
)) _
) = False
424 noSetupOrExe
(Goal
(P
(Q
(PackagePath _ns
(QualExe _ _
)) _
)) _
) = False
425 noSetupOrExe _
= True
427 -- | Transformation that tries to avoid making weak flag choices early.
428 -- Weak flags are trivial flags (not influencing dependencies) or such
429 -- flags that are explicitly declared to be weak in the index.
430 deferWeakFlagChoices
:: EndoTreeTrav d c
431 deferWeakFlagChoices
= go
433 go
(GoalChoiceF rdm xs
) = GoalChoiceF rdm
(P
.prefer noWeakFlag
(P
.prefer noWeakStanza xs
))
436 noWeakStanza
:: Tree d c
-> Bool
437 noWeakStanza
(SChoice _ _ _
(WeakOrTrivial
True) _
) = False
438 noWeakStanza _
= True
440 noWeakFlag
:: Tree d c
-> Bool
441 noWeakFlag
(FChoice _ _ _
(WeakOrTrivial
True) _ _ _
) = False
444 -- | Transformation that prefers goals with lower branching degrees.
446 -- When a goal choice node has at least one goal with zero or one children, this
447 -- function prunes all other goals. This transformation can help the solver find
448 -- a solution in fewer steps by allowing it to backtrack sooner when it is
449 -- exploring a subtree with no solutions. However, each step is more expensive.
450 preferReallyEasyGoalChoices
:: EndoTreeTrav d c
451 preferReallyEasyGoalChoices
= go
453 go
(GoalChoiceF rdm xs
) = GoalChoiceF rdm
(P
.filterIfAny zeroOrOneChoices xs
)
456 -- | Monad used internally in enforceSingleInstanceRestriction
458 -- For each package instance we record the goal for which we picked a concrete
459 -- instance. The SIR means that for any package instance there can only be one.
460 type EnforceSIR
= Reader
(Map
(PI PN
) QPN
)
462 -- | Enforce ghc's single instance restriction
464 -- From the solver's perspective, this means that for any package instance
465 -- (that is, package name + package version) there can be at most one qualified
466 -- goal resolving to that instance (there may be other goals _linking_ to that
467 -- instance however).
468 enforceSingleInstanceRestriction
:: Tree d c
-> Tree d c
469 enforceSingleInstanceRestriction
= (`runReader` M
.empty) . go
471 go
:: Tree d c
-> EnforceSIR
(Tree d c
)
473 -- We just verify package choices.
474 go
(PChoice qpn rdm gr cs
) =
475 PChoice qpn rdm gr
<$> sequenceA
(W
.mapWithKey
(goP qpn
) (fmap go cs
))
476 go
(FChoice qfn rdm y t m d ts
) =
477 FChoice qfn rdm y t m d
<$> traverse go ts
478 go
(SChoice qsn rdm y t ts
) =
479 SChoice qsn rdm y t
<$> traverse go ts
480 go
(GoalChoice rdm ts
) =
481 GoalChoice rdm
<$> traverse go ts
482 go x
@(Fail _ _
) = return x
483 go x
@(Done _ _
) = return x
486 goP
:: QPN
-> POption
-> EnforceSIR
(Tree d c
) -> EnforceSIR
(Tree d c
)
487 goP qpn
@(Q _ pn
) (POption i linkedTo
) r
= do
490 case (linkedTo
, M
.lookup inst env
) of
492 -- For linked nodes we don't check anything
494 (Nothing
, Nothing
) ->
495 -- Not linked, not already used
496 local
(M
.insert inst qpn
) r
497 (Nothing
, Just qpn
') -> do
498 -- Not linked, already used. This is an error
499 return $ Fail
(CS
.union (varToConflictSet
(P qpn
)) (varToConflictSet
(P qpn
'))) MultipleInstances