@@ -16,6 +16,7 @@ module Distribution.Types.GenericPackageDescription (
16
16
lookupFlagAssignment ,
17
17
insertFlagAssignment ,
18
18
diffFlagAssignment ,
19
+ findDuplicateFlagAssignments ,
19
20
nullFlagAssignment ,
20
21
showFlagValue ,
21
22
dispFlagAssignment ,
@@ -25,11 +26,11 @@ module Distribution.Types.GenericPackageDescription (
25
26
) where
26
27
27
28
import Prelude ()
28
- import Data.List ((\\) )
29
29
import Distribution.Compat.Prelude
30
30
import Distribution.Utils.ShortText
31
31
import Distribution.Utils.Generic (lowercase )
32
32
import qualified Text.PrettyPrint as Disp
33
+ import qualified Data.Map as Map
33
34
import qualified Distribution.Compat.ReadP as Parse
34
35
import qualified Distribution.Compat.Parsec as P
35
36
import Distribution.Compat.ReadP ((+++) )
@@ -154,55 +155,74 @@ instance Text FlagName where
154
155
-- discovered during configuration. For example @--flags=foo --flags=-bar@
155
156
-- becomes @[("foo", True), ("bar", False)]@
156
157
--
157
- newtype FlagAssignment = FlagAssignment [( FlagName , Bool )]
158
- deriving (Binary ,Eq ,Ord , Semigroup , Monoid )
158
+ newtype FlagAssignment = FlagAssignment { getFlagAssignment :: Map. Map FlagName ( Int , Bool ) }
159
+ deriving (Binary ,Eq ,Ord )
159
160
160
- -- TODO: the Semigroup/Monoid/Ord/Eq instances would benefit from
161
- -- [(FlagName,Bool)] being in a normal form, i.e. sorted. We could
162
- -- e.g. switch to a `Data.Map.Map` representation, but see duplicates
163
- -- check in `configuredPackageProblems`.
161
+ -- | Combines pairs of values contained in the 'FlagAssignment' Map.
164
162
--
165
- -- Also, the 'Semigroup' instance currently is left-biased as entries
166
- -- in the left-hand 'FlagAssignment' shadow those occuring in the
167
- -- right-hand side 'FlagAssignment' for the same flagnames.
163
+ -- The last flag specified takes precedence, and we record the number
164
+ -- of times we have seen the flag.
165
+ --
166
+ combineFlagValues :: (Int , Bool ) -> (Int , Bool ) -> (Int , Bool )
167
+ combineFlagValues (c1, _) (c2, b2) = (c1 + c2, b2)
168
+
169
+ -- The 'Semigroup' instance currently is right-biased.
170
+ --
171
+ -- If duplicate flags are specified, we want the last flag specified to
172
+ -- take precedence and we want to know how many times the flag has been
173
+ -- specified so that we have the option of warning the user about
174
+ -- supplying duplicate flags.
175
+ instance Semigroup FlagAssignment where
176
+ (<>) (FlagAssignment m1) (FlagAssignment m2) = FlagAssignment (Map. unionWith combineFlagValues m1 m2)
177
+
178
+ instance Monoid FlagAssignment where
179
+ mempty = FlagAssignment Map. empty
180
+ mappend = (<>)
168
181
169
182
-- | Construct a 'FlagAssignment' from a list of flag/value pairs.
170
183
--
184
+ -- If duplicate flags occur in the input list, the later entries
185
+ -- in the list will take precedence.
186
+ --
171
187
-- @since 2.2.0
172
188
mkFlagAssignment :: [(FlagName , Bool )] -> FlagAssignment
173
- mkFlagAssignment = FlagAssignment
189
+ mkFlagAssignment = FlagAssignment . Map. fromListWith ( flip combineFlagValues) . fmap ( fmap ( \ b -> ( 1 , b)))
174
190
175
191
-- | Deconstruct a 'FlagAssignment' into a list of flag/value pairs.
176
192
--
177
- -- @ ('mkFlagAssignment' . 'unFlagAssignment') fa == fa @
193
+ -- @ 'null' ('findDuplicateFlagAssignments' fa) ==> ('mkFlagAssignment' . 'unFlagAssignment') fa == fa @
178
194
--
179
195
-- @since 2.2.0
180
196
unFlagAssignment :: FlagAssignment -> [(FlagName , Bool )]
181
- unFlagAssignment ( FlagAssignment xs) = xs
197
+ unFlagAssignment = fmap ( fmap snd ) . Map. toList . getFlagAssignment
182
198
183
199
-- | Test whether 'FlagAssignment' is empty.
184
200
--
185
201
-- @since 2.2.0
186
202
nullFlagAssignment :: FlagAssignment -> Bool
187
- nullFlagAssignment (FlagAssignment [] ) = True
188
- nullFlagAssignment _ = False
203
+ nullFlagAssignment = Map. null . getFlagAssignment
189
204
190
205
-- | Lookup the value for a flag
191
206
--
192
207
-- Returns 'Nothing' if the flag isn't contained in the 'FlagAssignment'.
193
208
--
194
209
-- @since 2.2.0
195
210
lookupFlagAssignment :: FlagName -> FlagAssignment -> Maybe Bool
196
- lookupFlagAssignment fn = lookup fn . unFlagAssignment
211
+ lookupFlagAssignment fn = fmap snd . Map. lookup fn . getFlagAssignment
197
212
198
213
-- | Insert or update the boolean value of a flag.
199
214
--
215
+ -- If the flag is already present in the 'FlagAssigment', the
216
+ -- value will be updated and the fact that multiple values have
217
+ -- been provided for that flag will be recorded so that a
218
+ -- warning can be generated later on.
219
+ --
200
220
-- @since 2.2.0
201
221
insertFlagAssignment :: FlagName -> Bool -> FlagAssignment -> FlagAssignment
202
222
-- TODO: this currently just shadows prior values for an existing flag;
203
223
-- rather than enforcing uniqueness at construction, it's verified lateron via
204
224
-- `D.C.Dependency.configuredPackageProblems`
205
- insertFlagAssignment flag val = mkFlagAssignment . (( flag, val): ) . unFlagAssignment
225
+ insertFlagAssignment flag val = FlagAssignment . Map. insertWith ( flip combineFlagValues) flag ( 1 , val) . getFlagAssignment
206
226
207
227
-- | Remove all flag-assignments from the first 'FlagAssignment' that
208
228
-- are contained in the second 'FlagAssignment'
@@ -214,7 +234,10 @@ insertFlagAssignment flag val = mkFlagAssignment . ((flag,val):) . unFlagAssignm
214
234
--
215
235
-- @since 2.2.0
216
236
diffFlagAssignment :: FlagAssignment -> FlagAssignment -> FlagAssignment
217
- diffFlagAssignment fa1 fa2 = mkFlagAssignment (unFlagAssignment fa1 \\ unFlagAssignment fa2)
237
+ diffFlagAssignment fa1 fa2 = FlagAssignment (Map. difference (getFlagAssignment fa1) (getFlagAssignment fa2))
238
+
239
+ findDuplicateFlagAssignments :: FlagAssignment -> [FlagName ]
240
+ findDuplicateFlagAssignments = Map. keys . Map. filter ((> 1 ) . fst ) . getFlagAssignment
218
241
219
242
-- | @since 2.2.0
220
243
instance Read FlagAssignment where
@@ -235,7 +258,7 @@ dispFlagAssignment = Disp.hsep . map (Disp.text . showFlagValue) . unFlagAssignm
235
258
236
259
-- | Parses a flag assignment.
237
260
parsecFlagAssignment :: ParsecParser FlagAssignment
238
- parsecFlagAssignment = FlagAssignment <$> P. sepBy (onFlag <|> offFlag) P. skipSpaces1
261
+ parsecFlagAssignment = mkFlagAssignment <$> P. sepBy (onFlag <|> offFlag) P. skipSpaces1
239
262
where
240
263
onFlag = do
241
264
P. optional (P. char ' +' )
@@ -248,7 +271,7 @@ parsecFlagAssignment = FlagAssignment <$> P.sepBy (onFlag <|> offFlag) P.skipSpa
248
271
249
272
-- | Parses a flag assignment.
250
273
parseFlagAssignment :: Parse. ReadP r FlagAssignment
251
- parseFlagAssignment = FlagAssignment <$> Parse. sepBy parseFlagValue Parse. skipSpaces1
274
+ parseFlagAssignment = mkFlagAssignment <$> Parse. sepBy parseFlagValue Parse. skipSpaces1
252
275
where
253
276
parseFlagValue =
254
277
(do Parse. optional (Parse. char ' +' )
0 commit comments