1 {-# LANGUAGE ScopedTypeVariables #-}
2 module Distribution
.Solver
.Modular
.Builder
(
4 , splits
-- for testing
7 -- Building the search tree.
9 -- In this phase, we build a search tree that is too large, i.e, it contains
10 -- invalid solutions. We keep track of the open goals at each point. We
11 -- nondeterministically pick an open goal (via a goal choice node), create
12 -- subtrees according to the index and the available solutions, and extend the
13 -- set of open goals by superficially looking at the dependencies recorded in
16 -- For each goal, we keep track of all the *reasons* why it is being
17 -- introduced. These are for debugging and error messages, mainly. A little bit
18 -- of care has to be taken due to the way we treat flags. If a package has
19 -- flag-guarded dependencies, we cannot introduce them immediately. Instead, we
20 -- store the entire dependency.
22 import qualified Data
.List
as L
23 import qualified Data
.Map
as M
24 import qualified Data
.Set
as S
27 import qualified Distribution
.Solver
.Modular
.ConflictSet
as CS
28 import Distribution
.Solver
.Modular
.Dependency
29 import Distribution
.Solver
.Modular
.Flag
30 import Distribution
.Solver
.Modular
.Index
31 import Distribution
.Solver
.Modular
.Package
32 import qualified Distribution
.Solver
.Modular
.PSQ
as P
33 import Distribution
.Solver
.Modular
.Tree
34 import qualified Distribution
.Solver
.Modular
.WeightedPSQ
as W
36 import Distribution
.Solver
.Types
.ComponentDeps
37 import Distribution
.Solver
.Types
.PackagePath
38 import Distribution
.Solver
.Types
.Settings
40 -- | All state needed to build and link the search tree. It has a type variable
41 -- because the linking phase doesn't need to know about the state used to build
43 data Linker a
= Linker
{
45 linkingState
:: LinkingState
48 -- | The state needed to build the search tree without creating any linked nodes.
49 data BuildState
= BS
{
50 index :: Index
, -- ^ information about packages and their dependencies
51 rdeps
:: RevDepMap
, -- ^ set of all package goals, completed and open, with reverse dependencies
52 open
:: [OpenGoal
], -- ^ set of still open goals (flag and package goals)
53 next :: BuildType
, -- ^ kind of node to generate next
54 qualifyOptions
:: QualifyOptions
-- ^ qualification options
57 -- | Map of available linking targets.
58 type LinkingState
= M
.Map
(PN
, I
) [PackagePath
]
60 -- | Extend the set of open goals with the new goals listed.
62 -- We also adjust the map of overall goals, and keep track of the
63 -- reverse dependencies of each of the goals.
64 extendOpen
:: QPN
-> [FlaggedDep QPN
] -> BuildState
-> BuildState
65 extendOpen qpn
' gs s
@(BS
{ rdeps
= gs
', open
= o
' }) = go gs
' o
' gs
67 go
:: RevDepMap
-> [OpenGoal
] -> [FlaggedDep QPN
] -> BuildState
68 go g o
[] = s
{ rdeps
= g
, open
= o
}
69 go g o
((Flagged fn
@(FN qpn _
) fInfo t f
) : ngs
) =
70 go g
(FlagGoal fn fInfo t f
(flagGR qpn
) : o
) ngs
71 -- Note: for 'Flagged' goals, we always insert, so later additions win.
72 -- This is important, because in general, if a goal is inserted twice,
73 -- the later addition will have better dependency information.
74 go g o
((Stanza sn
@(SN qpn _
) t
) : ngs
) =
75 go g
(StanzaGoal sn t
(flagGR qpn
) : o
) ngs
76 go g o
((Simple
(LDep dr
(Dep
(PkgComponent qpn _
) _
)) c
) : ngs
)
78 -- We currently only add a self-dependency to the graph if it is
79 -- between a package and its setup script. The edge creates a cycle
80 -- and causes the solver to backtrack and choose a different
81 -- instance for the setup script. We may need to track other
82 -- self-dependencies once we implement component-based solving.
84 ComponentSetup
-> go
(M
.adjust
(addIfAbsent
(ComponentSetup
, qpn
')) qpn g
) o ngs
86 | qpn `M
.member` g
= go
(M
.adjust
(addIfAbsent
(c
, qpn
')) qpn g
) o ngs
87 |
otherwise = go
(M
.insert qpn
[(c
, qpn
')] g
) (PkgGoal qpn
(DependencyGoal dr
) : o
) ngs
88 -- code above is correct; insert/adjust have different arg order
89 go g o
((Simple
(LDep _dr
(Ext _ext
)) _
) : ngs
) = go g o ngs
90 go g o
((Simple
(LDep _dr
(Lang _lang
))_
) : ngs
) = go g o ngs
91 go g o
((Simple
(LDep _dr
(Pkg _pn _vr
))_
) : ngs
) = go g o ngs
93 addIfAbsent
:: Eq a
=> a
-> [a
] -> [a
]
94 addIfAbsent x xs
= if x `
elem` xs
then xs
else x
: xs
96 -- GoalReason for a flag or stanza. Each flag/stanza is introduced only by
97 -- its containing package.
98 flagGR
:: qpn
-> GoalReason qpn
99 flagGR qpn
= DependencyGoal
(DependencyReason qpn M
.empty S
.empty)
101 -- | Given the current scope, qualify all the package names in the given set of
102 -- dependencies and then extend the set of open goals accordingly.
103 scopedExtendOpen
:: QPN
-> FlaggedDeps PN
-> FlagInfo
->
104 BuildState
-> BuildState
105 scopedExtendOpen qpn fdeps fdefs s
= extendOpen qpn gs s
107 -- Qualify all package names
108 qfdeps
= qualifyDeps
(qualifyOptions s
) qpn fdeps
109 -- Introduce all package flags
110 qfdefs
= L
.map (\ (fn
, b
) -> Flagged
(FN qpn fn
) b
[] []) $ M
.toList fdefs
111 -- Combine new package and flag goals
112 gs
= qfdefs
++ qfdeps
115 -- In the expression @qfdefs ++ qfdeps@ above, flags occur potentially
116 -- multiple times, both via the flag declaration and via dependencies.
118 -- | Datatype that encodes what to build next
120 Goals
-- ^ build a goal choice node
121 | OneGoal OpenGoal
-- ^ build a node for this goal
122 | Instance QPN PInfo
-- ^ build a tree for a concrete instance
124 build
:: Linker BuildState
-> Tree
() QGoalReason
127 go
:: Linker BuildState
-> TreeF
() QGoalReason
(Linker BuildState
)
128 go s
= addLinking
(linkingState s
) $ addChildren
(buildState s
)
130 addChildren
:: BuildState
-> TreeF
() QGoalReason BuildState
132 -- If we have a choice between many goals, we just record the choice in
133 -- the tree. We select each open goal in turn, and before we descend, remove
134 -- it from the queue of open goals.
135 addChildren bs
@(BS
{ rdeps
= rdm
, open
= gs
, next = Goals
})
136 | L
.null gs
= DoneF rdm
()
137 |
otherwise = GoalChoiceF rdm
$ P
.fromList
138 $ L
.map (\ (g
, gs
') -> (close g
, bs
{ next = OneGoal g
, open
= gs
' }))
141 -- If we have already picked a goal, then the choice depends on the kind
144 -- For a package, we look up the instances available in the global info,
145 -- and then handle each instance in turn.
146 addChildren bs
@(BS
{ rdeps
= rdm
, index = idx
, next = OneGoal
(PkgGoal qpn
@(Q _ pn
) gr
) }) =
147 case M
.lookup pn idx
of
149 (varToConflictSet
(P qpn
) `CS
.union` goalReasonToConflictSetWithConflict qpn gr
)
151 Just pis
-> PChoiceF qpn rdm gr
(W
.fromList
(L
.map (\ (i
, info
) ->
152 ([], POption i Nothing
, bs
{ next = Instance qpn info
}))
154 -- TODO: data structure conversion is rather ugly here
156 -- For a flag, we create only two subtrees, and we create them in the order
157 -- that is indicated by the flag default.
158 addChildren bs
@(BS
{ rdeps
= rdm
, next = OneGoal
(FlagGoal qfn
@(FN qpn _
) (FInfo b m w
) t f gr
) }) =
159 FChoiceF qfn rdm gr weak m b
(W
.fromList
160 [([if b
then 0 else 1], True, (extendOpen qpn t bs
) { next = Goals
}),
161 ([if b
then 1 else 0], False, (extendOpen qpn f bs
) { next = Goals
})])
163 trivial
= L
.null t
&& L
.null f
164 weak
= WeakOrTrivial
$ unWeakOrTrivial w || trivial
166 -- For a stanza, we also create only two subtrees. The order is initially
167 -- False, True. This can be changed later by constraints (force enabling
168 -- the stanza by replacing the False branch with failure) or preferences
169 -- (try enabling the stanza if possible by moving the True branch first).
171 addChildren bs
@(BS
{ rdeps
= rdm
, next = OneGoal
(StanzaGoal qsn
@(SN qpn _
) t gr
) }) =
172 SChoiceF qsn rdm gr trivial
(W
.fromList
173 [([0], False, bs
{ next = Goals
}),
174 ([1], True, (extendOpen qpn t bs
) { next = Goals
})])
176 trivial
= WeakOrTrivial
(L
.null t
)
178 -- For a particular instance, we change the state: we update the scope,
179 -- and furthermore we update the set of goals.
181 -- TODO: We could inline this above.
182 addChildren bs
@(BS
{ next = Instance qpn
(PInfo fdeps _ fdefs _
) }) =
183 addChildren
((scopedExtendOpen qpn fdeps fdefs bs
)
186 {-------------------------------------------------------------------------------
188 -------------------------------------------------------------------------------}
190 -- | Introduce link nodes into the tree
192 -- Linking is a phase that adapts package choice nodes and adds the option to
193 -- link wherever appropriate: Package goals are called "related" if they are for
194 -- the same instance of the same package (but have different prefixes). A link
195 -- option is available in a package choice node whenever we can choose an
196 -- instance that has already been chosen for a related goal at a higher position
197 -- in the tree. We only create link options for related goals that are not
198 -- themselves linked, because the choice to link to a linked goal is the same as
199 -- the choice to link to the target of that goal's linking.
201 -- The code here proceeds by maintaining a finite map recording choices that
202 -- have been made at higher positions in the tree. For each pair of package name
203 -- and instance, it stores the prefixes at which we have made a choice for this
204 -- package instance. Whenever we make an unlinked choice, we extend the map.
205 -- Whenever we find a choice, we look into the map in order to find out what
206 -- link options we have to add.
208 -- A separate tree traversal would be simpler. However, 'addLinking' creates
209 -- linked nodes from existing unlinked nodes, which leads to sharing between the
210 -- nodes. If we copied the nodes when they were full trees of type
211 -- 'Tree () QGoalReason', then the sharing would cause a space leak during
212 -- exploration of the tree. Instead, we only copy the 'BuildState', which is
213 -- relatively small, while the tree is being constructed. See
214 -- https://github.com/haskell/cabal/issues/2899
215 addLinking
:: LinkingState
-> TreeF
() c a
-> TreeF
() c
(Linker a
)
216 -- The only nodes of interest are package nodes
217 addLinking ls
(PChoiceF qpn
@(Q pp pn
) rdm gr cs
) =
218 let linkedCs
= fmap (\bs
-> Linker bs ls
) $
219 W
.fromList
$ concatMap (linkChoices ls qpn
) (W
.toList cs
)
220 unlinkedCs
= W
.mapWithKey goP cs
221 allCs
= unlinkedCs `W
.union` linkedCs
223 -- Recurse underneath package choices. Here we just need to make sure
224 -- that we record the package choice so that it is available below
225 goP
:: POption
-> a
-> Linker a
226 goP
(POption i Nothing
) bs
= Linker bs
$ M
.insertWith
(++) (pn
, i
) [pp
] ls
227 goP _ _
= alreadyLinked
228 in PChoiceF qpn rdm gr allCs
229 addLinking ls t
= fmap (\bs
-> Linker bs ls
) t
231 linkChoices
:: forall a w
. LinkingState
235 linkChoices related
(Q _pp pn
) (weight
, POption i Nothing
, subtree
) =
236 L
.map aux
(M
.findWithDefault
[] (pn
, i
) related
)
238 aux
:: PackagePath
-> (w
, POption
, a
)
239 aux pp
= (weight
, POption i
(Just pp
), subtree
)
240 linkChoices _ _
(_
, POption _
(Just _
), _
) =
244 alreadyLinked
= error "addLinking called on tree that already contains linked nodes"
246 -------------------------------------------------------------------------------
248 -- | Interface to the tree builder. Just takes an index and a list of package names,
249 -- and computes the initial state and then the tree from there.
250 buildTree
:: Index
-> IndependentGoals
-> [PN
] -> Tree
() QGoalReason
251 buildTree idx
(IndependentGoals ind
) igs
=
255 , rdeps
= M
.fromList
(L
.map (\ qpn
-> (qpn
, [])) qpns
)
256 , open
= L
.map topLevelGoal qpns
258 , qualifyOptions
= defaultQualifyOptions idx
260 , linkingState
= M
.empty
263 topLevelGoal qpn
= PkgGoal qpn UserGoal
265 qpns | ind
= L
.map makeIndependent igs
266 |
otherwise = L
.map (Q
(PackagePath DefaultNamespace QualToplevel
)) igs
268 {-------------------------------------------------------------------------------
270 -------------------------------------------------------------------------------}
272 -- | Information needed about a dependency before it is converted into a Goal.
274 FlagGoal
(FN QPN
) FInfo
(FlaggedDeps QPN
) (FlaggedDeps QPN
) QGoalReason
275 | StanzaGoal
(SN QPN
) (FlaggedDeps QPN
) QGoalReason
276 | PkgGoal QPN QGoalReason
278 -- | Closes a goal, i.e., removes all the extraneous information that we
279 -- need only during the build phase.
280 close
:: OpenGoal
-> Goal QPN
281 close
(FlagGoal qfn _ _ _ gr
) = Goal
(F qfn
) gr
282 close
(StanzaGoal qsn _ gr
) = Goal
(S qsn
) gr
283 close
(PkgGoal qpn gr
) = Goal
(P qpn
) gr
285 {-------------------------------------------------------------------------------
287 -------------------------------------------------------------------------------}
289 -- | Pairs each element of a list with the list resulting from removal of that
290 -- element from the original list.
291 splits
:: [a
] -> [(a
, [a
])]
294 go
:: ([a
] -> [a
]) -> [a
] -> [(a
, [a
])]
296 go f
(x
: xs
) = (x
, f xs
) : go
(f
. (x
:)) xs