Skip to content

Commit 4d51ab9

Browse files
committed
chore(cabal-install-solver): add comments and improve readability
1 parent cb41f5f commit 4d51ab9

File tree

4 files changed

+176
-66
lines changed

4 files changed

+176
-66
lines changed

cabal-install-solver/src/Distribution/Solver/Modular/Builder.hs

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -62,41 +62,48 @@ type LinkingState = M.Map (PN, I) [PackagePath]
6262
-- We also adjust the map of overall goals, and keep track of the
6363
-- reverse dependencies of each of the goals.
6464
extendOpen :: QPN -> [FlaggedDep QPN] -> BuildState -> BuildState
65-
extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs
65+
extendOpen qpn deps buildState@(BS { rdeps = rdeps0, open = goals0 }) = go rdeps0 goals0 deps
6666
where
6767
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)
77-
| qpn == qpn' =
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.
68+
go rdeps goals [] =
69+
buildState { rdeps = rdeps, open = goals }
70+
71+
go rdeps goals ((Flagged fn@(FN qpn' _) fInfo t f) : fdeps) =
72+
go rdeps (FlagGoal fn fInfo t f (flagGR qpn') : goals) fdeps
73+
74+
-- Note: for 'Flagged' goals, we always insert, so later additions win.
75+
-- This is important, because in general, if a goal is inserted twice,
76+
-- the later addition will have better dependency information.
77+
go rdeps goals ((Stanza sn@(SN qpn' _) t) : fdeps) =
78+
go rdeps (StanzaGoal sn t (flagGR qpn') : goals) fdeps
79+
80+
go rdeps goals ((Simple (LDep dr (Dep (PkgComponent qpn' _) _)) c) : fdeps)
81+
| qpn' == qpn =
82+
-- We currently only add a self-dependency to the graph if it is
83+
-- between a package and its setup script. The edge creates a cycle
84+
-- and causes the solver to backtrack and choose a different
85+
-- instance for the setup script. We may need to track other
86+
-- self-dependencies once we implement component-based solving.
8387
case c of
84-
ComponentSetup -> go (M.adjust (addIfAbsent (ComponentSetup, qpn')) qpn g) o ngs
85-
_ -> go 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
88+
ComponentSetup -> go (M.adjust (addIfAbsent (ComponentSetup, qpn)) qpn' rdeps) goals fdeps
89+
_ -> go rdeps goals fdeps
90+
| qpn' `M.member` rdeps =
91+
go (M.adjust (addIfAbsent (c, qpn)) qpn' rdeps) goals fdeps
92+
| otherwise =
93+
-- Note: insert/adjust have different arg order
94+
go (M.insert qpn' [(c, qpn)] rdeps) (PkgGoal qpn' (DependencyGoal dr) : goals) fdeps
95+
96+
go rdeps o ((Simple (LDep _dr (Ext _ext )) _c) : goals) = go rdeps o goals
97+
go rdeps o ((Simple (LDep _dr (Lang _lang)) _c) : goals) = go rdeps o goals
98+
go rdeps o ((Simple (LDep _dr (Pkg _pn _vr)) _c) : goals) = go rdeps o goals
9299

93100
addIfAbsent :: Eq a => a -> [a] -> [a]
94101
addIfAbsent x xs = if x `elem` xs then xs else x : xs
95102

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)
103+
-- GoalReason for a flag or stanza. Each flag/stanza is introduced only by
104+
-- its containing package.
105+
flagGR :: qpn -> GoalReason qpn
106+
flagGR qpn = DependencyGoal (DependencyReason qpn M.empty S.empty)
100107

101108
-- | Given the current scope, qualify all the package names in the given set of
102109
-- dependencies and then extend the set of open goals accordingly.
@@ -127,12 +134,14 @@ build = ana go
127134
go :: Linker BuildState -> TreeF () QGoalReason (Linker BuildState)
128135
go s = addLinking (linkingState s) $ addChildren (buildState s)
129136

137+
-- | Add children to the tree based on the current build state.
130138
addChildren :: BuildState -> TreeF () QGoalReason BuildState
131139

132140
-- If we have a choice between many goals, we just record the choice in
133141
-- the tree. We select each open goal in turn, and before we descend, remove
134142
-- it from the queue of open goals.
135143
addChildren bs@(BS { rdeps = rdm, open = gs, next = Goals })
144+
-- No goals left. We have done.
136145
| L.null gs = DoneF rdm ()
137146
| otherwise = GoalChoiceF rdm $ P.fromList
138147
$ L.map (\ (g, gs') -> (close g, bs { next = OneGoal g, open = gs' }))
@@ -254,16 +263,17 @@ buildTree idx igs =
254263
build Linker {
255264
buildState = BS {
256265
index = idx
257-
, rdeps = M.fromList (L.map (\ qpn -> (qpn, [])) qpns)
258-
, open = L.map topLevelGoal qpns
266+
, rdeps = M.fromList [(qpn, []) | qpn <- qpns]
267+
, open = [ PkgGoal qpn UserGoal | qpn <- qpns ]
259268
, next = Goals
260269
}
261270
, linkingState = M.empty
262271
}
263272
where
264-
topLevelGoal qpn = PkgGoal qpn UserGoal
273+
-- The package names are interpreted as top-level goals in the host stage.
274+
path = PackagePath Stage.Host QualToplevel
275+
qpns = [ Q path pn | pn <- igs ]
265276

266-
qpns = L.map (Q (PackagePath Stage.Host QualToplevel)) igs
267277

268278
{-------------------------------------------------------------------------------
269279
Goals

cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,36 @@ type FlaggedDeps qpn = [FlaggedDep qpn]
9090

9191
-- | Flagged dependencies can either be plain dependency constraints,
9292
-- or flag-dependent dependency trees.
93+
--
94+
-- Note: this is a recursive data structure representing a tree of dependencies.
95+
--
96+
-- Note 2: why LDep contains its own DependencyReason? I am thinking it should
97+
-- be external to this type. Basically you traverse the tree and the flag and
98+
-- stanza choices are the DepedencyReason?
9399
data FlaggedDep qpn
94100
= -- | Dependencies which are conditional on a flag choice.
95-
Flagged (FN qpn) FInfo (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn)
96-
| -- | Dependencies which are conditional on whether or not a stanza
101+
Flagged
102+
(FN qpn)
103+
-- ^ The qualified flag name.
104+
FInfo
105+
-- ^ The flag information.
106+
(FlaggedDeps qpn)
107+
-- ^ Extra dependencies when the flag is true.
108+
(FlaggedDeps qpn)
109+
-- ^ Extra dependencies when the flag is false.
110+
| -- | Dependencies which are conditional on whether or not a stanza.
97111
-- (e.g., a test suite or benchmark) is enabled.
98-
Stanza (SN qpn) (TrueFlaggedDeps qpn)
99-
| -- | Dependencies which are always enabled, for the component 'comp'.
100-
Simple (LDep qpn) Component
112+
Stanza
113+
(SN qpn)
114+
-- ^ The qualified stanza name.
115+
(FlaggedDeps qpn)
116+
-- ^ Extra dependencies when stanza is enabled.
117+
| -- | Dependencies which are always enabled.
118+
Simple
119+
(LDep qpn)
120+
-- ^ The dependency.
121+
Component
122+
-- ^ The component of `qpn` introducing the dependency.
101123
deriving Show
102124

103125
-- | Conservatively flatten out flagged dependencies
@@ -111,16 +133,18 @@ flattenFlaggedDeps = concatMap aux
111133
aux (Stanza _ t) = flattenFlaggedDeps t
112134
aux (Simple d c) = [(d, c)]
113135

114-
type TrueFlaggedDeps qpn = FlaggedDeps qpn
115-
type FalseFlaggedDeps qpn = FlaggedDeps qpn
116-
117136
-- | A 'Dep' labeled with the reason it was introduced.
118137
--
119138
-- 'LDep' intentionally has no 'Functor' instance because the type variable
120139
-- is used both to record the dependencies as well as who's doing the
121140
-- depending; having a 'Functor' instance makes bugs where we don't distinguish
122141
-- these two far too likely. (By rights 'LDep' ought to have two type variables.)
123-
data LDep qpn = LDep (DependencyReason qpn) (Dep qpn)
142+
data LDep qpn
143+
= LDep
144+
(DependencyReason qpn)
145+
-- ^ The reason the dependency was introduced.
146+
(Dep qpn)
147+
-- ^ The dependency itself.
124148
deriving Show
125149

126150
-- | A dependency (constraint) associates a package name with a constrained
@@ -139,21 +163,35 @@ data Dep qpn
139163

140164
-- | An exposed component within a package. This type is used to represent
141165
-- build-depends and build-tool-depends dependencies.
142-
data PkgComponent qpn = PkgComponent qpn ExposedComponent
166+
data PkgComponent qpn
167+
= PkgComponent
168+
qpn
169+
-- ^ The qualified name of the package.
170+
ExposedComponent
171+
-- ^ The component exposed by the package.
143172
deriving (Eq, Ord, Functor, Show)
144173

145174
-- | A component that can be depended upon by another package, i.e., a library
146175
-- or an executable.
147176
data ExposedComponent
148-
= ExposedLib LibraryName
149-
| ExposedExe UnqualComponentName
177+
= -- | A library component
178+
ExposedLib LibraryName
179+
| -- | An executable component
180+
ExposedExe UnqualComponentName
150181
deriving (Eq, Ord, Show)
151182

152183
-- | The reason that a dependency is active. It identifies the package and any
153184
-- flag and stanza choices that introduced the dependency. It contains
154185
-- everything needed for creating ConflictSets or describing conflicts in solver
155186
-- log messages.
156-
data DependencyReason qpn = DependencyReason qpn (Map Flag FlagValue) (S.Set Stanza)
187+
data DependencyReason qpn
188+
= DependencyReason
189+
qpn
190+
-- ^ The qualified name of the dependent package.
191+
(Map Flag FlagValue)
192+
-- ^ The flag choices that introduced the dependency.
193+
(S.Set Stanza)
194+
-- ^ The stanza choices that introduced the dependency.
157195
deriving (Functor, Eq, Show)
158196

159197
-- | Print the reason that a dependency was introduced.

cabal-install-solver/src/Distribution/Solver/Modular/Index.hs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ type Index = Map PN (Map I PInfo)
3131
-- globally, for reasons external to the solver. We currently use this
3232
-- for shadowing which essentially is a GHC limitation, and for
3333
-- installed packages that are broken.
34-
data PInfo = PInfo (FlaggedDeps PN)
35-
(Map ExposedComponent ComponentInfo)
36-
FlagInfo
37-
(Maybe FailReason)
34+
data PInfo = PInfo
35+
(FlaggedDeps PN)
36+
-- ^ The package dependencies, whether they are conditional on a flag, a
37+
-- stanza or always active.
38+
(Map ExposedComponent ComponentInfo)
39+
-- ^ Info associated with each library and executable component.
40+
FlagInfo
41+
--
42+
(Maybe FailReason)
3843

3944
-- | Info associated with each library and executable in a package instance.
4045
data ComponentInfo = ComponentInfo {

cabal-install-solver/src/Distribution/Solver/Modular/Tree.hs

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,52 @@ type Weight = Double
4949
--
5050
-- TODO: The weight type should be changed from [Double] to Double to avoid
5151
-- giving too much weight to preferences that are applied later.
52-
data Tree d c =
53-
-- | Choose a version for a package (or choose to link)
54-
PChoice QPN RevDepMap c (WeightedPSQ [Weight] POption (Tree d c))
52+
--
53+
-- Note: this the tree *of possible choices*, which is used to explore all
54+
-- possible solutions to a given problem. It does not describe a single solution.
55+
data Tree d c
56+
= -- | Choose a version for a package (or choose to link)
57+
PChoice
58+
QPN
59+
-- ^ The package to choose an instance for
60+
RevDepMap
61+
-- ^ The reverse dependency map (FIXME ?)
62+
c
63+
-- ^ Additional data for the choice node
64+
(WeightedPSQ [Weight] POption (Tree d c))
65+
-- ^ Weighted list of possible options (`POption`) paired with the subsequent search tree.
5566

56-
-- | Choose a value for a flag
57-
--
58-
-- The Bool is the default value.
59-
| FChoice QFN RevDepMap c WeakOrTrivial FlagType Bool (WeightedPSQ [Weight] Bool (Tree d c))
67+
| -- | Choose a value for a flag.
68+
FChoice
69+
QFN
70+
-- ^ The flag to choose a value for.
71+
RevDepMap
72+
-- ^ The reverse dependency map (FIXME ?).
73+
c
74+
-- ^ Additional data for the choice node.
75+
WeakOrTrivial
76+
-- ^ Whether the choice should be deferred.
77+
FlagType
78+
-- ^ Whether the flag is manual or automatic.
79+
Bool
80+
-- ^ The flag default value
81+
(WeightedPSQ [Weight] Bool (Tree d c))
82+
-- ^ Weighted list of possible options paired with the subsequent search tree.
6083

61-
-- | Choose whether or not to enable a stanza
62-
| SChoice QSN RevDepMap c WeakOrTrivial (WeightedPSQ [Weight] Bool (Tree d c))
84+
| -- | Choose whether or not to enable a stanza.
85+
SChoice
86+
QSN
87+
-- ^ The stanza to choose to enable or disable.
88+
RevDepMap
89+
-- ^ The reverse dependency map (FIXME ?).
90+
c
91+
-- ^ Additional data for the choice node.
92+
WeakOrTrivial
93+
-- ^ Whether the choice should be deferred.
94+
(WeightedPSQ [Weight] Bool (Tree d c))
95+
-- ^ Weighted list of possible options paired with the subsequent search tree.
6396

64-
-- | Choose which choice to make next
97+
| -- | Choose which choice to make next
6598
--
6699
-- Invariants:
67100
--
@@ -72,13 +105,25 @@ data Tree d c =
72105
-- invariant that the 'QGoalReason' cached in the 'PChoice', 'FChoice'
73106
-- or 'SChoice' directly below a 'GoalChoice' node must equal the reason
74107
-- recorded on that 'GoalChoice' node.
75-
| GoalChoice RevDepMap (PSQ (Goal QPN) (Tree d c))
108+
GoalChoice
109+
RevDepMap
110+
-- ^ The reverse dependency map (FIXME ?).
111+
(PSQ (Goal QPN) (Tree d c))
112+
-- ^ Priority search queue associating a goal with the search tree.
76113

77-
-- | We're done -- we found a solution!
78-
| Done RevDepMap d
114+
| -- | We're done -- we found a solution!
115+
Done
116+
RevDepMap
117+
-- ^ The reverse dependency map (FIXME ?).
118+
d
119+
-- ^ The solution.
79120

80-
-- | We failed to find a solution in this path through the tree
81-
| Fail ConflictSet FailReason
121+
| -- | We failed to find a solution in this path through the tree
122+
Fail
123+
ConflictSet
124+
-- ^ The conflict set.
125+
FailReason
126+
-- ^ The reason for failure.
82127

83128
-- | A package option is a package instance with an optional linking annotation
84129
--
@@ -96,7 +141,12 @@ data Tree d c =
96141
-- dependencies must also be the exact same).
97142
--
98143
-- See <http://www.well-typed.com/blog/2015/03/qualified-goals/> for details.
99-
data POption = POption I (Maybe PackagePath)
144+
data POption
145+
= POption
146+
I
147+
-- ^ The choosen package instance.
148+
(Maybe PackagePath)
149+
-- ^ The package this choice is linked to (if any).
100150
deriving (Eq, Show)
101151

102152
data FailReason = UnsupportedExtension Extension
@@ -132,7 +182,14 @@ data FailReason = UnsupportedExtension Extension
132182
deriving (Eq, Show)
133183

134184
-- | Information about a dependency involved in a conflict, for error messages.
135-
data ConflictingDep = ConflictingDep (DependencyReason QPN) (PkgComponent QPN) CI
185+
data ConflictingDep
186+
= ConflictingDep
187+
(DependencyReason QPN)
188+
-- ^ The reason for the dependency.
189+
(PkgComponent QPN)
190+
-- ^ The component of the package that caused the conflict.
191+
CI
192+
-- ^ The constrained instance.
136193
deriving (Eq, Show)
137194

138195
-- | Functor for the tree type. 'a' is the type of nodes' children. 'd' and 'c'

0 commit comments

Comments
 (0)