@@ -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,80 @@ 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 )
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
+ instance Eq FlagAssignment where
162
+ (==) (FlagAssignment m1) (FlagAssignment m2) = fmap snd m1 == fmap snd m2
163
+
164
+ instance Ord FlagAssignment where
165
+ compare (FlagAssignment m1) (FlagAssignment m2) = fmap snd m1 `compare` fmap snd m2
166
+
167
+ -- | Combines pairs of values contained in the 'FlagAssignment' Map.
168
+ --
169
+ -- The last flag specified takes precedence, and we record the number
170
+ -- of times we have seen the flag.
164
171
--
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.
172
+ combineFlagValues :: (Int , Bool ) -> (Int , Bool ) -> (Int , Bool )
173
+ combineFlagValues (c1, _) (c2, b2) = (c1 + c2, b2)
174
+
175
+ -- The 'Semigroup' instance currently is right-biased.
176
+ --
177
+ -- If duplicate flags are specified, we want the last flag specified to
178
+ -- take precedence and we want to know how many times the flag has been
179
+ -- specified so that we have the option of warning the user about
180
+ -- supplying duplicate flags.
181
+ instance Semigroup FlagAssignment where
182
+ (<>) (FlagAssignment m1) (FlagAssignment m2) = FlagAssignment (Map. unionWith combineFlagValues m1 m2)
183
+
184
+ instance Monoid FlagAssignment where
185
+ mempty = FlagAssignment Map. empty
186
+ mappend = (<>)
168
187
169
188
-- | Construct a 'FlagAssignment' from a list of flag/value pairs.
170
189
--
190
+ -- If duplicate flags occur in the input list, the later entries
191
+ -- in the list will take precedence.
192
+ --
171
193
-- @since 2.2.0
172
194
mkFlagAssignment :: [(FlagName , Bool )] -> FlagAssignment
173
- mkFlagAssignment = FlagAssignment
195
+ mkFlagAssignment = FlagAssignment . Map. fromListWith ( flip combineFlagValues) . fmap ( fmap ( \ b -> ( 1 , b)))
174
196
175
197
-- | Deconstruct a 'FlagAssignment' into a list of flag/value pairs.
176
198
--
177
- -- @ ('mkFlagAssignment' . 'unFlagAssignment') fa == fa @
199
+ -- @ 'null' ('findDuplicateFlagAssignments' fa) ==> ('mkFlagAssignment' . 'unFlagAssignment') fa == fa @
178
200
--
179
201
-- @since 2.2.0
180
202
unFlagAssignment :: FlagAssignment -> [(FlagName , Bool )]
181
- unFlagAssignment ( FlagAssignment xs) = xs
203
+ unFlagAssignment = fmap ( fmap snd ) . Map. toList . getFlagAssignment
182
204
183
205
-- | Test whether 'FlagAssignment' is empty.
184
206
--
185
207
-- @since 2.2.0
186
208
nullFlagAssignment :: FlagAssignment -> Bool
187
- nullFlagAssignment (FlagAssignment [] ) = True
188
- nullFlagAssignment _ = False
209
+ nullFlagAssignment = Map. null . getFlagAssignment
189
210
190
211
-- | Lookup the value for a flag
191
212
--
192
213
-- Returns 'Nothing' if the flag isn't contained in the 'FlagAssignment'.
193
214
--
194
215
-- @since 2.2.0
195
216
lookupFlagAssignment :: FlagName -> FlagAssignment -> Maybe Bool
196
- lookupFlagAssignment fn = lookup fn . unFlagAssignment
217
+ lookupFlagAssignment fn = fmap snd . Map. lookup fn . getFlagAssignment
197
218
198
219
-- | Insert or update the boolean value of a flag.
199
220
--
221
+ -- If the flag is already present in the 'FlagAssigment', the
222
+ -- value will be updated and the fact that multiple values have
223
+ -- been provided for that flag will be recorded so that a
224
+ -- warning can be generated later on.
225
+ --
200
226
-- @since 2.2.0
201
227
insertFlagAssignment :: FlagName -> Bool -> FlagAssignment -> FlagAssignment
202
228
-- TODO: this currently just shadows prior values for an existing flag;
203
229
-- rather than enforcing uniqueness at construction, it's verified lateron via
204
230
-- `D.C.Dependency.configuredPackageProblems`
205
- insertFlagAssignment flag val = mkFlagAssignment . (( flag, val): ) . unFlagAssignment
231
+ insertFlagAssignment flag val = FlagAssignment . Map. insertWith ( flip combineFlagValues) flag ( 1 , val) . getFlagAssignment
206
232
207
233
-- | Remove all flag-assignments from the first 'FlagAssignment' that
208
234
-- are contained in the second 'FlagAssignment'
@@ -214,7 +240,13 @@ insertFlagAssignment flag val = mkFlagAssignment . ((flag,val):) . unFlagAssignm
214
240
--
215
241
-- @since 2.2.0
216
242
diffFlagAssignment :: FlagAssignment -> FlagAssignment -> FlagAssignment
217
- diffFlagAssignment fa1 fa2 = mkFlagAssignment (unFlagAssignment fa1 \\ unFlagAssignment fa2)
243
+ diffFlagAssignment fa1 fa2 = FlagAssignment (Map. difference (getFlagAssignment fa1) (getFlagAssignment fa2))
244
+
245
+ -- | Find the 'FlagName's that have been listed more than once.
246
+ --
247
+ -- @since 2.2.0
248
+ findDuplicateFlagAssignments :: FlagAssignment -> [FlagName ]
249
+ findDuplicateFlagAssignments = Map. keys . Map. filter ((> 1 ) . fst ) . getFlagAssignment
218
250
219
251
-- | @since 2.2.0
220
252
instance Read FlagAssignment where
@@ -235,7 +267,7 @@ dispFlagAssignment = Disp.hsep . map (Disp.text . showFlagValue) . unFlagAssignm
235
267
236
268
-- | Parses a flag assignment.
237
269
parsecFlagAssignment :: ParsecParser FlagAssignment
238
- parsecFlagAssignment = FlagAssignment <$> P. sepBy (onFlag <|> offFlag) P. skipSpaces1
270
+ parsecFlagAssignment = mkFlagAssignment <$> P. sepBy (onFlag <|> offFlag) P. skipSpaces1
239
271
where
240
272
onFlag = do
241
273
P. optional (P. char ' +' )
@@ -248,7 +280,7 @@ parsecFlagAssignment = FlagAssignment <$> P.sepBy (onFlag <|> offFlag) P.skipSpa
248
280
249
281
-- | Parses a flag assignment.
250
282
parseFlagAssignment :: Parse. ReadP r FlagAssignment
251
- parseFlagAssignment = FlagAssignment <$> Parse. sepBy parseFlagValue Parse. skipSpaces1
283
+ parseFlagAssignment = mkFlagAssignment <$> Parse. sepBy parseFlagValue Parse. skipSpaces1
252
284
where
253
285
parseFlagValue =
254
286
(do Parse. optional (Parse. char ' +' )
0 commit comments