Skip to content

Commit b0b0386

Browse files
committed
Improve algorithm for choosing flags with './Setup configure'
Cabal previously tried all flag combinations, which was too slow. The new algorithm assigns one flag at a time, and backtracks when a flag introduces a dependency that is unavailable. This change also fixes a space leak.
1 parent 74e9383 commit b0b0386

File tree

1 file changed

+35
-27
lines changed

1 file changed

+35
-27
lines changed

Cabal/Distribution/PackageDescription/Configuration.hs

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ instance Monoid d => Mon.Monoid (DepTestRslt d) where
189189
mappend (MissingDeps d) (MissingDeps d') = MissingDeps (d `mappend` d')
190190

191191

192-
data BT a = BTN a | BTB (BT a) (BT a) -- very simple binary tree
192+
data Tree a = Tree a [Tree a]
193193

194194

195195
-- | Try to find a flag assignment that satisfies the constraints of all trees.
@@ -224,10 +224,7 @@ resolveWithFlags ::
224224
-> Either [Dependency] (TargetSet PDTagged, FlagAssignment)
225225
-- ^ Either the missing dependencies (error case), or a pair of
226226
-- (set of build targets with dependencies, chosen flag assignments)
227-
resolveWithFlags dom os arch impl constrs trees checkDeps =
228-
case try dom [] of
229-
Right r -> Right r
230-
Left dbt -> Left $ findShortest dbt
227+
resolveWithFlags dom os arch impl constrs trees checkDeps = explore $ build dom []
231228
where
232229
extraConstrs = toDepMap constrs
233230

@@ -241,44 +238,53 @@ resolveWithFlags dom os arch impl constrs trees checkDeps =
241238
-- either succeeds or returns a binary tree with the missing dependencies
242239
-- encountered in each run. Since the tree is constructed lazily, we
243240
-- avoid some computation overhead in the successful case.
244-
try [] flags =
241+
explore :: Tree FlagAssignment
242+
-> Either [Dependency] (TargetSet PDTagged, FlagAssignment)
243+
explore (Tree flags ts) =
245244
let targetSet = TargetSet $ flip map simplifiedTrees $
246245
-- apply additional constraints to all dependencies
247246
first (`constrainBy` extraConstrs) .
248247
simplifyCondTree (env flags)
249248
deps = overallDependencies targetSet
250249
in case checkDeps (fromDepMap deps) of
251-
DepOk -> Right (targetSet, flags)
252-
MissingDeps mds -> Left (BTN mds)
253-
254-
try ((n, vals):rest) flags =
255-
tryAll $ map (\v -> try rest ((n, v):flags)) vals
256-
250+
DepOk | null ts -> Right (targetSet, flags)
251+
| otherwise -> tryAll $ map explore ts
252+
MissingDeps mds -> Left mds
253+
254+
build :: [(FlagName, [Bool])]
255+
-> FlagAssignment
256+
-> Tree FlagAssignment
257+
build [] flags = Tree flags []
258+
build ((n, vals):rest) flags =
259+
Tree flags $ map (\v -> build rest ((n, v):flags)) vals
260+
261+
tryAll :: [Either [a] b] -> Either [a] b
257262
tryAll = foldr mp mz
258263

259264
-- special version of `mplus' for our local purposes
260-
mp (Left xs) (Left ys) = (Left (BTB xs ys))
261-
mp (Left _) m@(Right _) = m
265+
mp :: Either [a] b -> Either [a] b -> Either [a] b
262266
mp m@(Right _) _ = m
267+
mp _ m@(Right _) = m
268+
mp (Left xs) (Left ys) = let shortest = findShortest xs ys
269+
in shortest `seq` Left shortest
263270

264271
-- `mzero'
265-
mz = Left (BTN [])
272+
mz :: Either [a] b
273+
mz = Left []
266274

275+
env :: FlagAssignment -> FlagName -> Either FlagName Bool
267276
env flags flag = (maybe (Left flag) Right . lookup flag) flags
268277

269278
-- for the error case we inspect our lazy tree of missing dependencies and
270279
-- pick the shortest list of missing dependencies
271-
findShortest (BTN x) = x
272-
findShortest (BTB lt rt) =
273-
let l = findShortest lt
274-
r = findShortest rt
275-
in case (l,r) of
276-
([], xs) -> xs -- [] is too short
277-
(xs, []) -> xs
278-
([x], _) -> [x] -- single elem is optimum
279-
(_, [x]) -> [x]
280-
(xs, ys) -> if lazyLengthCmp xs ys
281-
then xs else ys
280+
findShortest l r =
281+
case (l,r) of
282+
([], xs) -> xs -- [] is too short
283+
(xs, []) -> xs
284+
([x], _) -> [x] -- single elem is optimum
285+
(_, [x]) -> [x]
286+
(xs, ys) -> if lazyLengthCmp xs ys
287+
then xs else ys
282288
-- lazy variant of @\xs ys -> length xs <= length ys@
283289
lazyLengthCmp [] _ = True
284290
lazyLengthCmp _ [] = False
@@ -301,6 +307,8 @@ toDepMap ds =
301307
fromDepMap :: DependencyMap -> [Dependency]
302308
fromDepMap m = [ Dependency p vr | (p,vr) <- toList (unDependencyMap m) ]
303309

310+
-- | Flattens a CondTree using a partial flag assignment. When a condition
311+
-- cannot be evaluated, both branches are ignored.
304312
simplifyCondTree :: (Monoid a, Monoid d) =>
305313
(v -> Either v Bool)
306314
-> CondTree v d a
@@ -312,7 +320,7 @@ simplifyCondTree env (CondNode a d ifs) =
312320
case simplifyCondition cnd env of
313321
(Lit True, _) -> Just $ simplifyCondTree env t
314322
(Lit False, _) -> fmap (simplifyCondTree env) me
315-
_ -> error $ "Environment not defined for all free vars"
323+
_ -> Nothing
316324

317325
-- | Flatten a CondTree. This will resolve the CondTree by taking all
318326
-- possible paths into account. Note that since branches represent exclusive

0 commit comments

Comments
 (0)