1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE DeriveFoldable #-}
3 {-# LANGUAGE DeriveFunctor #-}
4 {-# LANGUAGE DeriveGeneric #-}
5 {-# LANGUAGE DeriveTraversable #-}
6 {-# LANGUAGE ScopedTypeVariables #-}
8 module Distribution
.Types
.CondTree
28 import Distribution
.Compat
.Prelude
31 import Distribution
.Types
.Condition
33 import qualified Distribution
.Compat
.Lens
as L
35 -- | A 'CondTree' is used to represent the conditional structure of
36 -- a Cabal file, reflecting a syntax element subject to constraints,
37 -- and then any number of sub-elements which may be enabled subject
38 -- to some condition. Both @a@ and @c@ are usually 'Monoid's.
40 -- To be more concrete, consider the following fragment of a @Cabal@
44 -- build-depends: base >= 4.0
46 -- build-depends: base >= 4.2
49 -- One way to represent this is to have @'CondTree' 'ConfVar'
50 -- ['Dependency'] 'BuildInfo'@. Here, 'condTreeData' represents
51 -- the actual fields which are not behind any conditional, while
52 -- 'condTreeComponents' recursively records any further fields
53 -- which are behind a conditional. 'condTreeConstraints' records
54 -- the constraints (in this case, @base >= 4.0@) which would
55 -- be applied if you use this syntax; in general, this is
56 -- derived off of 'targetBuildInfo' (perhaps a good refactoring
57 -- would be to convert this into an opaque type, with a smart
58 -- constructor that pre-computes the dependencies.)
59 data CondTree v c a
= CondNode
61 , condTreeConstraints
:: c
62 , condTreeComponents
:: [CondBranch v c a
]
64 deriving (Show, Eq
, Typeable
, Data
, Generic
, Functor
, Foldable
, Traversable
)
66 instance (Binary v
, Binary c
, Binary a
) => Binary
(CondTree v c a
)
67 instance (Structured v
, Structured c
, Structured a
) => Structured
(CondTree v c a
)
68 instance (NFData v
, NFData c
, NFData a
) => NFData
(CondTree v c a
) where rnf
= genericRnf
70 instance (Semigroup a
, Semigroup c
) => Semigroup
(CondTree v c a
) where
71 (CondNode a c bs
) <> (CondNode a
' c
' bs
') = CondNode
(a
<> a
') (c
<> c
') (bs
<> bs
')
73 instance (Semigroup a
, Semigroup c
, Monoid a
, Monoid c
) => Monoid
(CondTree v c a
) where
75 mempty
= CondNode mempty mempty mempty
77 -- | A 'CondBranch' represents a conditional branch, e.g., @if
78 -- flag(foo)@ on some syntax @a@. It also has an optional false
80 data CondBranch v c a
= CondBranch
81 { condBranchCondition
:: Condition v
82 , condBranchIfTrue
:: CondTree v c a
83 , condBranchIfFalse
:: Maybe (CondTree v c a
)
85 deriving (Show, Eq
, Typeable
, Data
, Generic
, Functor
, Traversable
)
87 -- This instance is written by hand because GHC 8.0.1/8.0.2 infinite
88 -- loops when trying to derive it with optimizations. See
89 -- https://gitlab.haskell.org/ghc/ghc/-/issues/13056
90 instance Foldable
(CondBranch v c
) where
91 foldMap f
(CondBranch _ c Nothing
) = foldMap f c
92 foldMap f
(CondBranch _ c
(Just a
)) = foldMap f c `mappend` foldMap f a
94 instance (Binary v
, Binary c
, Binary a
) => Binary
(CondBranch v c a
)
95 instance (Structured v
, Structured c
, Structured a
) => Structured
(CondBranch v c a
)
96 instance (NFData v
, NFData c
, NFData a
) => NFData
(CondBranch v c a
) where rnf
= genericRnf
98 condIfThen
:: Condition v
-> CondTree v c a
-> CondBranch v c a
99 condIfThen c t
= CondBranch c t Nothing
101 condIfThenElse
:: Condition v
-> CondTree v c a
-> CondTree v c a
-> CondBranch v c a
102 condIfThenElse c t e
= CondBranch c t
(Just e
)
107 -> (Condition v
-> Condition w
)
110 mapCondTree fa fc fcnd
(CondNode a c ifs
) =
111 CondNode
(fa a
) (fc c
) (map g ifs
)
113 g
(CondBranch cnd t me
) =
116 (mapCondTree fa fc fcnd t
)
117 (fmap (mapCondTree fa fc fcnd
) me
)
119 mapTreeConstrs
:: (c
-> d
) -> CondTree v c a
-> CondTree v d a
120 mapTreeConstrs f
= mapCondTree
id f
id
122 mapTreeConds
:: (Condition v
-> Condition w
) -> CondTree v c a
-> CondTree w c a
123 mapTreeConds f
= mapCondTree
id id f
125 mapTreeData
:: (a
-> b
) -> CondTree v c a
-> CondTree v c b
126 mapTreeData f
= mapCondTree f
id id
128 -- | @@Traversal@@ for the variables
129 traverseCondTreeV
:: L
.Traversal
(CondTree v c a
) (CondTree w c a
) v w
130 traverseCondTreeV f
(CondNode a c ifs
) =
131 CondNode a c
<$> traverse
(traverseCondBranchV f
) ifs
133 -- | @@Traversal@@ for the variables
134 traverseCondBranchV
:: L
.Traversal
(CondBranch v c a
) (CondBranch w c a
) v w
135 traverseCondBranchV f
(CondBranch cnd t me
) =
138 <*> traverseCondTreeV f t
139 <*> traverse
(traverseCondTreeV f
) me
141 -- | @@Traversal@@ for the aggregated constraints
142 traverseCondTreeC
:: L
.Traversal
(CondTree v c a
) (CondTree v d a
) c d
143 traverseCondTreeC f
(CondNode a c ifs
) =
144 CondNode a
<$> f c
<*> traverse
(traverseCondBranchC f
) ifs
146 -- | @@Traversal@@ for the aggregated constraints
147 traverseCondBranchC
:: L
.Traversal
(CondBranch v c a
) (CondBranch v d a
) c d
148 traverseCondBranchC f
(CondBranch cnd t me
) =
150 <$> traverseCondTreeC f t
151 <*> traverse
(traverseCondTreeC f
) me
153 -- | Extract the condition matched by the given predicate from a cond tree.
155 -- We use this mainly for extracting buildable conditions (see the Note in
156 -- Distribution.PackageDescription.Configuration), but the function is in fact
158 extractCondition
:: Eq v
=> (a
-> Bool) -> CondTree v c a
-> Condition v
159 extractCondition p
= go
162 |
not (p x
) = Lit
False
163 |
otherwise = goList cs
166 goList
(CondBranch c t e
: cs
) =
169 ce
= maybe (Lit
True) go e
171 ((c `cAnd` ct
) `cOr`
(CNot c `cAnd` ce
)) `cAnd` goList cs
173 -- | Flattens a CondTree using a partial flag assignment. When a condition
174 -- cannot be evaluated, both branches are ignored.
176 :: (Semigroup a
, Semigroup d
)
177 => (v
-> Either v
Bool)
180 simplifyCondTree env
(CondNode a d ifs
) =
181 foldl (<>) (d
, a
) $ mapMaybe (simplifyCondBranch env
) ifs
183 -- | Realizes a 'CondBranch' using partial flag assignment. When a condition
184 -- cannot be evaluated, returns 'Nothing'.
186 :: (Semigroup a
, Semigroup d
)
187 => (v
-> Either v
Bool)
190 simplifyCondBranch env
(CondBranch cnd t me
) =
191 case simplifyCondition cnd env
of
192 (Lit
True, _
) -> Just
$ simplifyCondTree env t
193 (Lit
False, _
) -> fmap (simplifyCondTree env
) me
196 -- | Flatten a CondTree. This will resolve the CondTree by taking all
197 -- possible paths into account. Note that since branches represent exclusive
198 -- choices this may not result in a \"sane\" result.
199 ignoreConditions
:: (Semigroup a
, Semigroup c
) => CondTree v c a
-> (a
, c
)
200 ignoreConditions
(CondNode a c ifs
) = foldl (<>) (a
, c
) $ concatMap f ifs
202 f
(CondBranch _ t me
) =
204 : maybeToList (fmap ignoreConditions me
)
206 -- | Flatten a CondTree. This will traverse the CondTree by taking all
207 -- possible paths into account, but merging inclusive when two paths
208 -- may co-exist, and exclusively when the paths are an if/else
209 foldCondTree
:: forall b c a v
. b
-> ((c
, a
) -> b
) -> (b
-> b
-> b
) -> (b
-> b
-> b
) -> CondTree v c a
-> b
210 foldCondTree e u mergeInclusive mergeExclusive
= goTree
212 goTree
:: CondTree v c a
-> b
213 goTree
(CondNode a c ifs
) = u
(c
, a
) `mergeInclusive`
foldl goBranch e ifs
214 goBranch
:: b
-> CondBranch v c a
-> b
215 goBranch acc
(CondBranch _ t mt
) = mergeInclusive acc
(maybe (goTree t
) (mergeExclusive
(goTree t
) . goTree
) mt
)