Skip to content

Commit d89087b

Browse files
authored
Merge pull request #9560 from cabalism/fix/format-rejections-9559
Add a format rejections function
2 parents f01e000 + f80bb15 commit d89087b

File tree

4 files changed

+177
-21
lines changed

4 files changed

+177
-21
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ doctest :
9494
$(DOCTEST) Cabal-syntax
9595
$(DOCTEST) Cabal-described
9696
$(DOCTEST) --build-depends=QuickCheck Cabal
97+
$(DOCTEST) cabal-install-solver
9798
$(DOCTEST) cabal-install
9899

99100
# This is not run as part of validate.sh (we need hackage-security, which is tricky to get).

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

Lines changed: 106 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE LambdaCase #-}
3+
{-# LANGUAGE ViewPatterns #-}
24

35
module Distribution.Solver.Modular.Message (
46
Message(..),
57
showMessages
68
) where
79

10+
import Data.Maybe (isJust)
811
import qualified Data.List as L
912
import Data.Map (Map)
1013
import qualified Data.Map as M
@@ -17,7 +20,8 @@ import Distribution.Pretty (prettyShow) -- from Cabal
1720

1821
import qualified Distribution.Solver.Modular.ConflictSet as CS
1922
import Distribution.Solver.Modular.Dependency
20-
import Distribution.Solver.Modular.Flag
23+
import Distribution.Solver.Modular.Flag ( QFN, QSN )
24+
import qualified Distribution.Solver.Modular.Flag as Flag ( showQFN, showQFNBool, showQSN, showQSNBool )
2125
import Distribution.Solver.Modular.MessageUtils
2226
(showUnsupportedExtension, showUnsupportedLanguage)
2327
import Distribution.Solver.Modular.Package
@@ -60,24 +64,24 @@ showMessages = go 0
6064
go !l (Step (TryP qpn i) (Step Enter (Step (Skip conflicts) (Step Leave ms)))) =
6165
goPSkip l qpn [i] conflicts ms
6266
go !l (Step (TryF qfn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =
63-
(atLevel l $ "rejecting: " ++ showQFNBool qfn b ++ showFR c fr) (go l ms)
67+
(atLevel l $ blurbQFNBool Rejecting qfn b ++ showFR c fr) (go l ms)
6468
go !l (Step (TryS qsn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =
65-
(atLevel l $ "rejecting: " ++ showQSNBool qsn b ++ showFR c fr) (go l ms)
69+
(atLevel l $ blurbQSNBool Rejecting qsn b ++ showFR c fr) (go l ms)
6670
go !l (Step (Next (Goal (P _ ) gr)) (Step (TryP qpn' i) ms@(Step Enter (Step (Next _) _)))) =
67-
(atLevel l $ "trying: " ++ showQPNPOpt qpn' i ++ showGR gr) (go l ms)
71+
(atLevel l $ blurbOption Trying qpn' i ++ showGR gr) (go l ms)
6872
go !l (Step (Next (Goal (P qpn) gr)) (Step (Failure _c UnknownPackage) ms)) =
6973
atLevel l ("unknown package: " ++ showQPN qpn ++ showGR gr) $ go l ms
7074
-- standard display
7175
go !l (Step Enter ms) = go (l+1) ms
7276
go !l (Step Leave ms) = go (l-1) ms
73-
go !l (Step (TryP qpn i) ms) = (atLevel l $ "trying: " ++ showQPNPOpt qpn i) (go l ms)
74-
go !l (Step (TryF qfn b) ms) = (atLevel l $ "trying: " ++ showQFNBool qfn b) (go l ms)
75-
go !l (Step (TryS qsn b) ms) = (atLevel l $ "trying: " ++ showQSNBool qsn b) (go l ms)
77+
go !l (Step (TryP qpn i) ms) = (atLevel l $ blurbOption Trying qpn i) (go l ms)
78+
go !l (Step (TryF qfn b) ms) = (atLevel l $ blurbQFNBool Trying qfn b) (go l ms)
79+
go !l (Step (TryS qsn b) ms) = (atLevel l $ blurbQSNBool Trying qsn b) (go l ms)
7680
go !l (Step (Next (Goal (P qpn) gr)) ms) = (atLevel l $ showPackageGoal qpn gr) (go l ms)
7781
go !l (Step (Next _) ms) = go l ms -- ignore flag goals in the log
7882
go !l (Step (Skip conflicts) ms) =
7983
-- 'Skip' should always be handled by 'goPSkip' in the case above.
80-
(atLevel l $ "skipping: " ++ showConflicts conflicts) (go l ms)
84+
(atLevel l $ blurb Skipping ++ showConflicts conflicts) (go l ms)
8185
go !l (Step (Success) ms) = (atLevel l $ "done") (go l ms)
8286
go !l (Step (Failure c fr) ms) = (atLevel l $ showFailure c fr) (go l ms)
8387

@@ -96,9 +100,12 @@ showMessages = go 0
96100
-> Progress Message a b
97101
-> Progress String a b
98102
goPReject l qpn is c fr (Step (TryP qpn' i) (Step Enter (Step (Failure _ fr') (Step Leave ms))))
99-
| qpn == qpn' && fr == fr' = goPReject l qpn (i : is) c fr ms
103+
| qpn == qpn' && fr == fr' =
104+
-- By prepending (i : is) we reverse the order of the instances.
105+
goPReject l qpn (i : is) c fr ms
100106
goPReject l qpn is c fr ms =
101-
(atLevel l $ "rejecting: " ++ L.intercalate ", " (map (showQPNPOpt qpn) (reverse is)) ++ showFR c fr) (go l ms)
107+
(atLevel l $ blurbOptions Rejecting qpn (reverse is) ++ showFR c fr)
108+
(go l ms)
102109

103110
-- Handle many subsequent skipped package instances.
104111
goPSkip :: Int
@@ -108,11 +115,11 @@ showMessages = go 0
108115
-> Progress Message a b
109116
-> Progress String a b
110117
goPSkip l qpn is conflicts (Step (TryP qpn' i) (Step Enter (Step (Skip conflicts') (Step Leave ms))))
111-
| qpn == qpn' && conflicts == conflicts' = goPSkip l qpn (i : is) conflicts ms
118+
| qpn == qpn' && conflicts == conflicts' =
119+
-- By prepending (i : is) we reverse the order of the instances.
120+
goPSkip l qpn (i : is) conflicts ms
112121
goPSkip l qpn is conflicts ms =
113-
let msg = "skipping: "
114-
++ L.intercalate ", " (map (showQPNPOpt qpn) (reverse is))
115-
++ showConflicts conflicts
122+
let msg = blurbOptions Skipping qpn (reverse is) ++ showConflicts conflicts
116123
in atLevel l msg (go l ms)
117124

118125
-- write a message with the current level number
@@ -206,12 +213,83 @@ data MergedPackageConflict = MergedPackageConflict {
206213
, versionConflict :: Maybe VR
207214
}
208215

209-
showQPNPOpt :: QPN -> POption -> String
210-
showQPNPOpt qpn@(Q _pp pn) (POption i linkedTo) =
216+
data ProgressAction =
217+
Trying
218+
| Skipping
219+
| Rejecting
220+
221+
blurb :: ProgressAction -> String
222+
blurb = \case
223+
Trying -> "trying: "
224+
Skipping -> "skipping: "
225+
Rejecting -> "rejecting: "
226+
227+
blurbQFNBool :: ProgressAction -> QFN -> Bool -> String
228+
blurbQFNBool a q b = blurb a ++ Flag.showQFNBool q b
229+
230+
blurbQSNBool :: ProgressAction -> QSN -> Bool -> String
231+
blurbQSNBool a q b = blurb a ++ Flag.showQSNBool q b
232+
233+
blurbOption :: ProgressAction -> QPN -> POption -> String
234+
blurbOption a q p = blurb a ++ showOption q p
235+
236+
blurbOptions :: ProgressAction -> QPN -> [POption] -> String
237+
blurbOptions a q ps = blurb a ++ showIsOrVs q (tryVs ps)
238+
239+
showOption :: QPN -> POption -> String
240+
showOption qpn@(Q _pp pn) (POption i linkedTo) =
211241
case linkedTo of
212242
Nothing -> showPI (PI qpn i) -- Consistent with prior to POption
213243
Just pp' -> showQPN qpn ++ "~>" ++ showPI (PI (Q pp' pn) i)
214244

245+
-- | A list of versions, or a list of instances.
246+
data IsOrVs = Is [POption] | Vs [Ver] deriving Show
247+
248+
-- | Try to convert a list of options to a list of versions, or a list of
249+
-- instances if any of the options is linked or installed. Singleton lists or
250+
-- empty lists are always converted to Is.
251+
-- >>> tryVs [v0, v1]
252+
-- Vs [mkVersion [0],mkVersion [1]]
253+
-- >>> tryVs [v0]
254+
-- Is [POption (I (mkVersion [0]) InRepo) Nothing]
255+
-- >>> tryVs [i0, i1]
256+
-- Is [POption (I (mkVersion [0]) (Inst (UnitId "foo-bar-0-inplace"))) Nothing,POption (I (mkVersion [1]) (Inst (UnitId "foo-bar-1-inplace"))) Nothing]
257+
-- >>> tryVs [i0, v1]
258+
-- Is [POption (I (mkVersion [0]) (Inst (UnitId "foo-bar-0-inplace"))) Nothing,POption (I (mkVersion [1]) InRepo) Nothing]
259+
-- >>> tryVs [v0, i1]
260+
-- Is [POption (I (mkVersion [0]) InRepo) Nothing,POption (I (mkVersion [1]) (Inst (UnitId "foo-bar-1-inplace"))) Nothing]
261+
-- >>> tryVs [i0]
262+
-- Is [POption (I (mkVersion [0]) (Inst (UnitId "foo-bar-0-inplace"))) Nothing]
263+
-- >>> tryVs []
264+
-- Is []
265+
tryVs :: [POption] -> IsOrVs
266+
tryVs xs@[] = Is xs
267+
tryVs xs@[_] = Is xs
268+
tryVs xs
269+
| any (\(POption (instI -> b0) (isJust -> b1)) -> b0 || b1) xs = Is xs
270+
| otherwise =
271+
let (vs, is) = L.partition ((== InRepo) . snd) [(v, l) | POption i _ <- xs, let I v l = i]
272+
in if null is then Vs (fst `map` vs) else Is xs
273+
274+
-- | Shows a list of versions in a human-friendly way, abbreviated. Shows a list
275+
-- of instances in full.
276+
-- >>> showIsOrVs foobarQPN $ tryVs [v0, v1]
277+
-- "foo-bar; 0, 1"
278+
-- >>> showIsOrVs foobarQPN $ tryVs [v0]
279+
-- "foo-bar-0"
280+
-- >>> showIsOrVs foobarQPN $ tryVs [i0, i1]
281+
-- "foo-bar-0/installed-inplace, foo-bar-1/installed-inplace"
282+
-- >>> showIsOrVs foobarQPN $ tryVs [i0, v1]
283+
-- "foo-bar-0/installed-inplace, foo-bar-1"
284+
-- >>> showIsOrVs foobarQPN $ tryVs [v0, i1]
285+
-- "foo-bar-0, foo-bar-1/installed-inplace"
286+
-- >>> showIsOrVs foobarQPN $ tryVs []
287+
-- "unexpected empty list of versions"
288+
showIsOrVs :: QPN -> IsOrVs -> String
289+
showIsOrVs _ (Is []) = "unexpected empty list of versions"
290+
showIsOrVs q (Is xs) = L.intercalate ", " (showOption q `map` xs)
291+
showIsOrVs q (Vs xs) = showQPN q ++ "; " ++ L.intercalate ", " (showVer `map` xs)
292+
215293
showGR :: QGoalReason -> String
216294
showGR UserGoal = " (user goal)"
217295
showGR (DependencyGoal dr) = " (dependency of " ++ showDependencyReason dr ++ ")"
@@ -246,8 +324,8 @@ showFR _ (UnsupportedSpecVer ver) = " (unsupported spec-version " ++ pre
246324
-- The following are internal failures. They should not occur. In the
247325
-- interest of not crashing unnecessarily, we still just print an error
248326
-- message though.
249-
showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")"
250-
showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")"
327+
showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ Flag.showQFN qfn ++ ")"
328+
showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ Flag.showQSN qsn ++ ")"
251329
showFR _ EmptyGoalChoice = " (INTERNAL ERROR: EMPTY GOAL CHOICE)"
252330

253331
showExposedComponent :: ExposedComponent -> String
@@ -270,3 +348,13 @@ showConflictingDep (ConflictingDep dr (PkgComponent qpn comp) ci) =
270348
showQPN qpn ++ componentStr ++ "==" ++ showI i
271349
Constrained vr -> showDependencyReason dr ++ " => " ++ showQPN qpn ++
272350
componentStr ++ showVR vr
351+
352+
-- $setup
353+
-- >>> import Distribution.Solver.Types.PackagePath
354+
-- >>> import Distribution.Types.Version
355+
-- >>> import Distribution.Types.UnitId
356+
-- >>> let foobarQPN = Q (PackagePath DefaultNamespace QualToplevel) (mkPackageName "foo-bar")
357+
-- >>> let v0 = POption (I (mkVersion [0]) InRepo) Nothing
358+
-- >>> let v1 = POption (I (mkVersion [1]) InRepo) Nothing
359+
-- >>> let i0 = POption (I (mkVersion [0]) (Inst $ mkUnitId "foo-bar-0-inplace")) Nothing
360+
-- >>> let i1 = POption (I (mkVersion [1]) (Inst $ mkUnitId "foo-bar-1-inplace")) Nothing

cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ tests =
615615
, "[__2] unknown package: unknown2 (dependency of B)"
616616
, "[__2] fail (backjumping, conflict set: B, unknown2)"
617617
, "[__1] fail (backjumping, conflict set: A, B, unknown1, unknown2)"
618-
, "[__0] skipping: A-3.0.0, A-2.0.0 (has the same characteristics that "
618+
, "[__0] skipping: A; 3.0.0, 2.0.0 (has the same characteristics that "
619619
++ "caused the previous version to fail: depends on 'B')"
620620
, "[__0] trying: A-1.0.0"
621621
, "[__1] done"
@@ -644,7 +644,7 @@ tests =
644644
, "[__1] next goal: B (dependency of A)"
645645
, "[__1] rejecting: B-11.0.0 (conflict: A => B==14.0.0)"
646646
, "[__1] fail (backjumping, conflict set: A, B)"
647-
, "[__0] skipping: A-3.0.0, A-2.0.0 (has the same characteristics that "
647+
, "[__0] skipping: A; 3.0.0, 2.0.0 (has the same characteristics that "
648648
++ "caused the previous version to fail: depends on 'B' but excludes "
649649
++ "version 11.0.0)"
650650
, "[__0] trying: A-1.0.0"
@@ -769,7 +769,7 @@ tests =
769769
, "[__2] next goal: C (dependency of A)"
770770
, "[__2] rejecting: C-2.0.0 (conflict: A => C==1.0.0)"
771771
, "[__2] fail (backjumping, conflict set: A, C)"
772-
, "[__0] skipping: A-3.0.0, A-2.0.0 (has the same characteristics that caused the "
772+
, "[__0] skipping: A; 3.0.0, 2.0.0 (has the same characteristics that caused the "
773773
++ "previous version to fail: depends on 'C' but excludes version 2.0.0)"
774774
, "[__0] trying: A-1.0.0"
775775
, "[__1] next goal: C (dependency of A)"
@@ -912,6 +912,51 @@ tests =
912912
msg = "rejecting: other-package-2.0.0/installed-AbCdEfGhIj0123456789"
913913
in mkTest db "show full installed package ABI hash (issue #5892)" ["my-package"] $
914914
solverFailure (isInfixOf msg)
915+
, testGroup
916+
"package versions abbreviation (issue #9559.)"
917+
[ runTest $
918+
let db =
919+
[ Right $ exAv "A" 1 []
920+
, Right $ exAv "A" 2 []
921+
, Right $ exAv "B" 1 [ExFix "A" 3]
922+
]
923+
rejecting = "rejecting: A-2.0.0"
924+
skipping = "skipping: A-1.0.0"
925+
in mkTest db "show skipping singleton" ["B"] $
926+
solverFailure (\msg -> rejecting `isInfixOf` msg && skipping `isInfixOf` msg)
927+
, runTest $
928+
let db =
929+
[ Left $ exInst "A" 1 "A-1.0.0" []
930+
, Left $ exInst "A" 2 "A-2.0.0" []
931+
, Right $ exAv "B" 1 [ExFix "A" 3]
932+
]
933+
rejecting = "rejecting: A-2.0.0/installed-2.0.0"
934+
skipping = "skipping: A-1.0.0/installed-1.0.0"
935+
in mkTest db "show skipping singleton, installed" ["B"] $
936+
solverFailure (\msg -> rejecting `isInfixOf` msg && skipping `isInfixOf` msg)
937+
, runTest $
938+
let db =
939+
[ Right $ exAv "A" 1 []
940+
, Right $ exAv "A" 2 []
941+
, Right $ exAv "A" 3 []
942+
, Right $ exAv "B" 1 [ExFix "A" 4]
943+
]
944+
rejecting = "rejecting: A-3.0.0"
945+
skipping = "skipping: A; 2.0.0, 1.0.0"
946+
in mkTest db "show skipping versions list" ["B"] $
947+
solverFailure (\msg -> rejecting `isInfixOf` msg && skipping `isInfixOf` msg)
948+
, runTest $
949+
let db =
950+
[ Left $ exInst "A" 1 "A-1.0.0" []
951+
, Left $ exInst "A" 2 "A-2.0.0" []
952+
, Left $ exInst "A" 3 "A-3.0.0" []
953+
, Right $ exAv "B" 1 [ExFix "A" 4]
954+
]
955+
rejecting = "rejecting: A-3.0.0/installed-3.0.0"
956+
skipping = "skipping: A-2.0.0/installed-2.0.0, A-1.0.0/installed-1.0.0"
957+
in mkTest db "show skipping versions list, installed" ["B"] $
958+
solverFailure (\msg -> rejecting `isInfixOf` msg && skipping `isInfixOf` msg)
959+
]
915960
]
916961
]
917962
where

changelog.d/pr-9560

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
synopsis: Shorten solver rejection messages by removing repetition
2+
packages: cabal-install-solver
3+
prs: #9560
4+
issues: #9559 #4251
5+
6+
description: {
7+
8+
As before, we show a single rejection as hyphenated package-version.
9+
10+
For multiple rejections, we show a list of versions preceded by package
11+
semicolon, a much shorter rendering of the same information.
12+
13+
```diff
14+
- [__0] rejecting: pandoc-3.1.8, pandoc-3.1.7, pandoc-3.1.6.2, pandoc-3.1.6.1,
15+
- pandoc-3.1.6, pandoc-3.1.5, pandoc-3.1.4, pandoc-3.1.3, pandoc-3.1.2,
16+
- pandoc-3.1.1, pandoc-3.1, pandoc-3.0.1, pandoc-3.0, pandoc-2.19.2,
17+
- pandoc-2.19.1, pandoc-2.19, pandoc-2.18, pandoc-2.17.1.1, pandoc-2.17.1,
18+
+ [__0] rejecting: pandoc; 3.1.8, 3.1.7, 3.1.6.2, 3.1.6.1, 3.1.6, 3.1.5, 3.1.4,
19+
+ 3.1.3, 3.1.2, 3.1.1, 3.1, 3.0.1, 3.0, 2.19.2, 2.19.1, 2.19, 2.18, 2.17.1.1,
20+
```
21+
22+
}

0 commit comments

Comments
 (0)