1
1
{-# LANGUAGE ScopedTypeVariables #-}
2
- module Distribution.Solver.Modular.Builder (buildTree ) where
2
+ module Distribution.Solver.Modular.Builder (
3
+ buildTree
4
+ , splits -- for testing
5
+ ) where
3
6
4
7
-- Building the search tree.
5
8
--
@@ -24,12 +27,10 @@ import Distribution.Solver.Modular.Dependency
24
27
import Distribution.Solver.Modular.Flag
25
28
import Distribution.Solver.Modular.Index
26
29
import Distribution.Solver.Modular.Package
27
- import Distribution.Solver.Modular.PSQ (PSQ )
28
30
import qualified Distribution.Solver.Modular.PSQ as P
29
31
import Distribution.Solver.Modular.Tree
30
32
import qualified Distribution.Solver.Modular.WeightedPSQ as W
31
33
32
- import Distribution.Solver.Types.ComponentDeps (Component )
33
34
import Distribution.Solver.Types.PackagePath
34
35
import Distribution.Solver.Types.Settings
35
36
@@ -43,11 +44,11 @@ data Linker a = Linker {
43
44
44
45
-- | The state needed to build the search tree without creating any linked nodes.
45
46
data BuildState = BS {
46
- index :: Index , -- ^ information about packages and their dependencies
47
- rdeps :: RevDepMap , -- ^ set of all package goals, completed and open, with reverse dependencies
48
- open :: PSQ ( OpenGoal () ) () , -- ^ set of still open goals (flag and package goals)
49
- next :: BuildType , -- ^ kind of node to generate next
50
- qualifyOptions :: QualifyOptions -- ^ qualification options
47
+ index :: Index , -- ^ information about packages and their dependencies
48
+ rdeps :: RevDepMap , -- ^ set of all package goals, completed and open, with reverse dependencies
49
+ open :: [ OpenGoal ], -- ^ set of still open goals (flag and package goals)
50
+ next :: BuildType , -- ^ kind of node to generate next
51
+ qualifyOptions :: QualifyOptions -- ^ qualification options
51
52
}
52
53
53
54
-- | Map of available linking targets.
@@ -57,34 +58,33 @@ type LinkingState = Map (PN, I) [PackagePath]
57
58
--
58
59
-- We also adjust the map of overall goals, and keep track of the
59
60
-- reverse dependencies of each of the goals.
60
- extendOpen :: QPN -> [OpenGoal Component ] -> BuildState -> BuildState
61
+ extendOpen :: QPN -> [PotentialGoal ] -> BuildState -> BuildState
61
62
extendOpen qpn' gs s@ (BS { rdeps = gs', open = o' }) = go gs' o' gs
62
63
where
63
- go :: RevDepMap -> PSQ ( OpenGoal () ) () -> [OpenGoal Component ] -> BuildState
64
- go g o [] = s { rdeps = g, open = o }
65
- go g o (ng @ ( OpenGoal (Flagged _ _ _ _ ) _gr ) : ngs) = go g (cons' ng () o) ngs
64
+ go :: RevDepMap -> [ OpenGoal ] -> [PotentialGoal ] -> BuildState
65
+ go g o [] = s { rdeps = g, open = o }
66
+ go g o (( PotentialGoal (Flagged fn fInfo t f ) gr ) : ngs) = go g (FlagGoal fn fInfo t f gr : o) ngs
66
67
-- Note: for 'Flagged' goals, we always insert, so later additions win.
67
68
-- This is important, because in general, if a goal is inserted twice,
68
69
-- the later addition will have better dependency information.
69
- go g o (ng @ ( OpenGoal (Stanza _ _ ) _gr ) : ngs) = go g (cons' ng () o) ngs
70
- go g o (ng @ ( OpenGoal (Simple (Dep _ qpn _) c) _gr ) : ngs)
70
+ go g o (( PotentialGoal (Stanza sn t) gr ) : ngs) = go g (StanzaGoal sn t gr : o) ngs
71
+ go g o (( PotentialGoal (Simple (Dep _ qpn _) c) gr ) : ngs)
71
72
| qpn == qpn' = go g o ngs
72
73
-- we ignore self-dependencies at this point; TODO: more care may be needed
73
74
| qpn `M.member` g = go (M. adjust (addIfAbsent (c, qpn')) qpn g) o ngs
74
- | otherwise = go (M. insert qpn [(c, qpn')] g) (cons' ng () o) ngs
75
+ | otherwise = go (M. insert qpn [(c, qpn')] g) (PkgGoal qpn gr : o) ngs
75
76
-- code above is correct; insert/adjust have different arg order
76
- go g o ( ( OpenGoal (Simple (Ext _ext ) _) _gr) : ngs) = go g o ngs
77
- go g o ( ( OpenGoal (Simple (Lang _lang)_) _gr) : ngs) = go g o ngs
78
- go g o ( ( OpenGoal (Simple (Pkg _pn _vr)_) _gr) : ngs)= go g o ngs
77
+ go g o (( PotentialGoal (Simple (Ext _ext ) _) _gr) : ngs) = go g o ngs
78
+ go g o (( PotentialGoal (Simple (Lang _lang)_) _gr) : ngs) = go g o ngs
79
+ go g o (( PotentialGoal (Simple (Pkg _pn _vr)_) _gr) : ngs) = go g o ngs
79
80
80
- cons' = P. cons . forgetCompOpenGoal
81
81
82
82
addIfAbsent :: Eq a => a -> [a ] -> [a ]
83
83
addIfAbsent x xs = if x `elem` xs then xs else x : xs
84
84
85
85
-- | Given the current scope, qualify all the package names in the given set of
86
86
-- dependencies and then extend the set of open goals accordingly.
87
- scopedExtendOpen :: QPN -> I -> QGoalReason -> FlaggedDeps Component PN -> FlagInfo ->
87
+ scopedExtendOpen :: QPN -> I -> QGoalReason -> FlaggedDeps PN -> FlagInfo ->
88
88
BuildState -> BuildState
89
89
scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s
90
90
where
@@ -93,7 +93,7 @@ scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s
93
93
-- Introduce all package flags
94
94
qfdefs = L. map (\ (fn, b) -> Flagged (FN (PI qpn i) fn) b [] [] ) $ M. toList fdefs
95
95
-- Combine new package and flag goals
96
- gs = L. map (flip OpenGoal gr) (qfdefs ++ qfdeps)
96
+ gs = L. map (flip PotentialGoal gr) (qfdefs ++ qfdeps)
97
97
-- NOTE:
98
98
--
99
99
-- In the expression @qfdefs ++ qfdeps@ above, flags occur potentially
@@ -112,10 +112,9 @@ scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s
112
112
113
113
-- | Datatype that encodes what to build next
114
114
data BuildType =
115
- Goals -- ^ build a goal choice node
116
- | OneGoal ( OpenGoal () ) -- ^ build a node for this goal
115
+ Goals -- ^ build a goal choice node
116
+ | OneGoal OpenGoal -- ^ build a node for this goal
117
117
| Instance QPN I PInfo QGoalReason -- ^ build a tree for a concrete instance
118
- deriving Show
119
118
120
119
build :: Linker BuildState -> Tree () QGoalReason
121
120
build = ana go
@@ -129,23 +128,17 @@ addChildren :: BuildState -> TreeF () QGoalReason BuildState
129
128
-- the tree. We select each open goal in turn, and before we descend, remove
130
129
-- it from the queue of open goals.
131
130
addChildren bs@ (BS { rdeps = rdm, open = gs, next = Goals })
132
- | P .null gs = DoneF rdm ()
133
- | otherwise = GoalChoiceF rdm $ P. mapKeys close
134
- $ P. mapWithKey (\ g (_sc , gs') -> bs { next = OneGoal g, open = gs' })
135
- $ P. splits gs
131
+ | L .null gs = DoneF rdm ()
132
+ | otherwise = GoalChoiceF rdm $ P. fromList
133
+ $ L. map (\ (g , gs') -> (close g, bs { next = OneGoal g, open = gs' }) )
134
+ $ splits gs
136
135
137
136
-- If we have already picked a goal, then the choice depends on the kind
138
137
-- of goal.
139
138
--
140
139
-- For a package, we look up the instances available in the global info,
141
140
-- and then handle each instance in turn.
142
- addChildren (BS { index = _ , next = OneGoal (OpenGoal (Simple (Ext _ ) _) _ ) }) =
143
- error " Distribution.Solver.Modular.Builder: addChildren called with Ext goal"
144
- addChildren (BS { index = _ , next = OneGoal (OpenGoal (Simple (Lang _ ) _) _ ) }) =
145
- error " Distribution.Solver.Modular.Builder: addChildren called with Lang goal"
146
- addChildren (BS { index = _ , next = OneGoal (OpenGoal (Simple (Pkg _ _ ) _) _ ) }) =
147
- error " Distribution.Solver.Modular.Builder: addChildren called with Pkg goal"
148
- addChildren bs@ (BS { rdeps = rdm, index = idx, next = OneGoal (OpenGoal (Simple (Dep _ qpn@ (Q _ pn) _) _) gr) }) =
141
+ addChildren bs@ (BS { rdeps = rdm, index = idx, next = OneGoal (PkgGoal qpn@ (Q _ pn) gr) }) =
149
142
-- If the package does not exist in the index, we construct an emty PChoiceF node for it
150
143
-- After all, we have no choices here. Alternatively, we could immediately construct
151
144
-- a Fail node here, but that would complicate the construction of conflict sets.
@@ -160,10 +153,10 @@ addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (OpenGoal (Simple
160
153
161
154
-- For a flag, we create only two subtrees, and we create them in the order
162
155
-- that is indicated by the flag default.
163
- addChildren bs@ (BS { rdeps = rdm, next = OneGoal (OpenGoal ( Flagged qfn@ (FN (PI qpn _) _) (FInfo b m w) t f) gr) }) =
156
+ addChildren bs@ (BS { rdeps = rdm, next = OneGoal (FlagGoal qfn@ (FN (PI qpn _) _) (FInfo b m w) t f gr) }) =
164
157
FChoiceF qfn rdm gr weak m b (W. fromList
165
- [([if b then 0 else 1 ], True , (extendOpen qpn (L. map (flip OpenGoal (FDependency qfn True )) t) bs) { next = Goals }),
166
- ([if b then 1 else 0 ], False , (extendOpen qpn (L. map (flip OpenGoal (FDependency qfn False )) f) bs) { next = Goals })])
158
+ [([if b then 0 else 1 ], True , (extendOpen qpn (L. map (flip PotentialGoal (FDependency qfn True )) t) bs) { next = Goals }),
159
+ ([if b then 1 else 0 ], False , (extendOpen qpn (L. map (flip PotentialGoal (FDependency qfn False )) f) bs) { next = Goals })])
167
160
where
168
161
trivial = L. null t && L. null f
169
162
weak = WeakOrTrivial $ unWeakOrTrivial w || trivial
@@ -173,10 +166,10 @@ addChildren bs@(BS { rdeps = rdm, next = OneGoal (OpenGoal (Flagged qfn@(FN (PI
173
166
-- the stanza by replacing the False branch with failure) or preferences
174
167
-- (try enabling the stanza if possible by moving the True branch first).
175
168
176
- addChildren bs@ (BS { rdeps = rdm, next = OneGoal (OpenGoal ( Stanza qsn@ (SN (PI qpn _) _) t) gr) }) =
169
+ addChildren bs@ (BS { rdeps = rdm, next = OneGoal (StanzaGoal qsn@ (SN (PI qpn _) _) t gr) }) =
177
170
SChoiceF qsn rdm gr trivial (W. fromList
178
- [([0 ], False , bs { next = Goals }),
179
- ([1 ], True , (extendOpen qpn (L. map (flip OpenGoal (SDependency qsn)) t) bs) { next = Goals })])
171
+ [([0 ], False , bs { next = Goals }),
172
+ ([1 ], True , (extendOpen qpn (L. map (flip PotentialGoal (SDependency qsn)) t) bs) { next = Goals })])
180
173
where
181
174
trivial = WeakOrTrivial (L. null t)
182
175
@@ -258,16 +251,49 @@ buildTree idx (IndependentGoals ind) igs =
258
251
buildState = BS {
259
252
index = idx
260
253
, rdeps = M. fromList (L. map (\ qpn -> (qpn, [] )) qpns)
261
- , open = P. fromList ( L. map ( \ qpn -> ( topLevelGoal qpn, () )) qpns)
254
+ , open = L. map topLevelGoal qpns
262
255
, next = Goals
263
256
, qualifyOptions = defaultQualifyOptions idx
264
257
}
265
258
, linkingState = M. empty
266
259
}
267
260
where
268
- -- Should a top-level goal allowed to be an executable style
269
- -- dependency? Well, I don't think it would make much difference
270
- topLevelGoal qpn = OpenGoal (Simple (Dep False {- not exe -} qpn (Constrained [] )) () ) UserGoal
261
+ topLevelGoal qpn = PkgGoal qpn UserGoal
271
262
272
263
qpns | ind = makeIndependent igs
273
264
| otherwise = L. map (Q (PackagePath DefaultNamespace QualToplevel )) igs
265
+
266
+ {- ------------------------------------------------------------------------------
267
+ Goals
268
+ -------------------------------------------------------------------------------}
269
+
270
+ -- | Information needed about a dependency before it is converted into a Goal.
271
+ -- Not all PotentialGoals correspond to Goals. For example, PotentialGoals can
272
+ -- represent pkg-config or language extension dependencies.
273
+ data PotentialGoal = PotentialGoal (FlaggedDep QPN ) QGoalReason
274
+
275
+ -- | Like a PotentialGoal, except that it always introduces a new Goal.
276
+ data OpenGoal =
277
+ FlagGoal (FN QPN ) FInfo (FlaggedDeps QPN ) (FlaggedDeps QPN ) QGoalReason
278
+ | StanzaGoal (SN QPN ) (FlaggedDeps QPN ) QGoalReason
279
+ | PkgGoal QPN QGoalReason
280
+
281
+ -- | Closes a goal, i.e., removes all the extraneous information that we
282
+ -- need only during the build phase.
283
+ close :: OpenGoal -> Goal QPN
284
+ close (FlagGoal qfn _ _ _ gr) = Goal (F qfn) gr
285
+ close (StanzaGoal qsn _ gr) = Goal (S qsn) gr
286
+ close (PkgGoal qpn gr) = Goal (P qpn) gr
287
+
288
+ {- ------------------------------------------------------------------------------
289
+ Auxiliary
290
+ -------------------------------------------------------------------------------}
291
+
292
+ -- | Pairs each element of a list with the list resulting from removal of that
293
+ -- element from the original list.
294
+ splits :: [a ] -> [(a , [a ])]
295
+ splits = go id
296
+ where
297
+ go :: ([a ] -> [a ]) -> [a ] -> [(a , [a ])]
298
+ go _ [] = []
299
+ go f (x : xs) = (x, f xs) : go (f . (x : )) xs
0 commit comments