From aecf41efc6aa4398916d59f7024d220d17f416de Mon Sep 17 00:00:00 2001 From: Yvan Sraka Date: Fri, 27 Jun 2025 13:04:44 +1000 Subject: [PATCH] Refactor cabal-install solver config log output The main goal is to add an intermediate log message type to the processing of the solver log. There are zero known changes to the cabal solver's output. Co-Authored-By: Erik de Castro Lopo --- .../cabal-install-solver.cabal | 1 + .../src/Distribution/Solver/Modular.hs | 62 ++++--- .../src/Distribution/Solver/Modular/Log.hs | 15 +- .../Distribution/Solver/Modular/Message.hs | 167 ++++++++++-------- .../Solver/Types/DependencyResolver.hs | 13 +- .../Solver/Types/SummarizedMessage.hs | 48 +++++ .../src/Distribution/Client/Dependency.hs | 64 ++++--- changelog.d/pr-10854 | 7 + 8 files changed, 250 insertions(+), 127 deletions(-) create mode 100644 cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs create mode 100644 changelog.d/pr-10854 diff --git a/cabal-install-solver/cabal-install-solver.cabal b/cabal-install-solver/cabal-install-solver.cabal index 7e4386b149f..87c43723165 100644 --- a/cabal-install-solver/cabal-install-solver.cabal +++ b/cabal-install-solver/cabal-install-solver.cabal @@ -95,6 +95,7 @@ library Distribution.Solver.Types.SolverId Distribution.Solver.Types.SolverPackage Distribution.Solver.Types.SourcePackage + Distribution.Solver.Types.SummarizedMessage Distribution.Solver.Types.Variable build-depends: diff --git a/cabal-install-solver/src/Distribution/Solver/Modular.hs b/cabal-install-solver/src/Distribution/Solver/Modular.hs index 9111b2d78d0..a4baebf496c 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular.hs @@ -27,8 +27,14 @@ import Distribution.Solver.Modular.ConfiguredConversion ( convCP ) import qualified Distribution.Solver.Modular.ConflictSet as CS import Distribution.Solver.Modular.Dependency -import Distribution.Solver.Modular.Flag -import Distribution.Solver.Modular.Index + ( Var(..), + showVar, + ConflictMap, + ConflictSet, + showConflictSet, + RevDepMap ) +import Distribution.Solver.Modular.Flag ( SN(SN), FN(FN) ) +import Distribution.Solver.Modular.Index ( Index ) import Distribution.Solver.Modular.IndexConversion ( convPIs ) import Distribution.Solver.Modular.Log @@ -36,25 +42,38 @@ import Distribution.Solver.Modular.Log import Distribution.Solver.Modular.Package ( PN ) import Distribution.Solver.Modular.RetryLog + ( RetryLog, + toProgress, + fromProgress, + retry, + failWith, + continueWith ) import Distribution.Solver.Modular.Solver ( SolverConfig(..), PruneAfterFirstSuccess(..), solve ) import Distribution.Solver.Types.DependencyResolver + ( DependencyResolver ) import Distribution.Solver.Types.LabeledPackageConstraint + ( LabeledPackageConstraint, unlabelPackageConstraint ) import Distribution.Solver.Types.PackageConstraint -import Distribution.Solver.Types.PackagePath + ( PackageConstraint(..), scopeToPackageName ) +import Distribution.Solver.Types.PackagePath ( QPN ) import Distribution.Solver.Types.PackagePreferences + ( PackagePreferences ) import Distribution.Solver.Types.PkgConfigDb ( PkgConfigDb ) import Distribution.Solver.Types.Progress -import Distribution.Solver.Types.Variable + ( Progress(..), foldProgress ) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage(StringMsg) ) +import Distribution.Solver.Types.Variable ( Variable(..) ) import Distribution.System ( Platform(..) ) import Distribution.Simple.Setup ( BooleanFlag(..) ) import Distribution.Simple.Utils - ( ordNubBy ) -import Distribution.Verbosity - + ( ordNubBy ) +import Distribution.Verbosity ( normal, verbose ) +import Distribution.Solver.Modular.Message ( renderSummarizedMessage ) -- | Ties the two worlds together: classic cabal-install vs. the modular -- solver. Performs the necessary translations before and after. @@ -120,21 +139,21 @@ solve' :: SolverConfig -> (PN -> PackagePreferences) -> Map PN [LabeledPackageConstraint] -> Set PN - -> Progress String String (Assignment, RevDepMap) + -> Progress SummarizedMessage String (Assignment, RevDepMap) solve' sc cinfo idx pkgConfigDB pprefs gcs pns = toProgress $ retry (runSolver printFullLog sc) createErrorMsg where runSolver :: Bool -> SolverConfig - -> RetryLog String SolverFailure (Assignment, RevDepMap) + -> RetryLog SummarizedMessage SolverFailure (Assignment, RevDepMap) runSolver keepLog sc' = displayLogMessages keepLog $ solve sc' cinfo idx pkgConfigDB pprefs gcs pns createErrorMsg :: SolverFailure - -> RetryLog String String (Assignment, RevDepMap) + -> RetryLog SummarizedMessage String (Assignment, RevDepMap) createErrorMsg failure@(ExhaustiveSearch cs cm) = if asBool $ minimizeConflictSet sc - then continueWith ("Found no solution after exhaustively searching the " + then continueWith (mkStringMsg $ "Found no solution after exhaustively searching the " ++ "dependency tree. Rerunning the dependency solver " ++ "to minimize the conflict set ({" ++ showConflictSet cs ++ "}).") $ @@ -155,7 +174,7 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = rerunSolverForErrorMsg cs ++ finalErrorMsg sc failure createErrorMsg failure@BackjumpLimitReached = continueWith - ("Backjump limit reached. Rerunning dependency solver to generate " + (mkStringMsg $ "Backjump limit reached. Rerunning dependency solver to generate " ++ "a final conflict set for the search tree containing the " ++ "first backjump.") $ retry (runSolver printFullLog sc { pruneAfterFirstSuccess = PruneAfterFirstSuccess True }) $ @@ -181,13 +200,16 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = -- original goal order. goalOrder' = preferGoalsFromConflictSet cs <> fromMaybe mempty (goalOrder sc) - in unlines ("Could not resolve dependencies:" : messages (toProgress (runSolver True sc'))) + in unlines ("Could not resolve dependencies:" : map renderSummarizedMessage (messages (toProgress (runSolver True sc')))) printFullLog = solverVerbosity sc >= verbose messages :: Progress step fail done -> [step] messages = foldProgress (:) (const []) (const []) +mkStringMsg :: String -> SummarizedMessage +mkStringMsg msg = StringMsg msg + -- | Try to remove variables from the given conflict set to create a minimal -- conflict set. -- @@ -219,11 +241,11 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = -- solver to add new unnecessary variables to the conflict set. This function -- discards the result from any run that adds new variables to the conflict -- set, but the end result may not be completely minimized. -tryToMinimizeConflictSet :: forall a . (SolverConfig -> RetryLog String SolverFailure a) +tryToMinimizeConflictSet :: forall a . (SolverConfig -> RetryLog SummarizedMessage SolverFailure a) -> SolverConfig -> ConflictSet -> ConflictMap - -> RetryLog String SolverFailure a + -> RetryLog SummarizedMessage SolverFailure a tryToMinimizeConflictSet runSolver sc cs cm = foldl (\r v -> retryNoSolution r $ tryToRemoveOneVar v) (fromProgress $ Fail $ ExhaustiveSearch cs cm) @@ -249,14 +271,14 @@ tryToMinimizeConflictSet runSolver sc cs cm = tryToRemoveOneVar :: Var QPN -> ConflictSet -> ConflictMap - -> RetryLog String SolverFailure a + -> RetryLog SummarizedMessage SolverFailure a tryToRemoveOneVar v smallestKnownCS smallestKnownCM -- Check whether v is still present, because it may have already been -- removed in a previous solver rerun. | not (v `CS.member` smallestKnownCS) = fromProgress $ Fail $ ExhaustiveSearch smallestKnownCS smallestKnownCM | otherwise = - continueWith ("Trying to remove variable " ++ varStr ++ " from the " + continueWith (mkStringMsg $ "Trying to remove variable " ++ varStr ++ " from the " ++ "conflict set.") $ retry (runSolver sc') $ \case err@(ExhaustiveSearch cs' _) @@ -268,14 +290,14 @@ tryToMinimizeConflictSet runSolver sc cs cm = ++ "conflict set." in -- Use the new conflict set, even if v wasn't removed, -- because other variables may have been removed. - failWith (msg ++ " Continuing with " ++ showCS cs' ++ ".") err + failWith (mkStringMsg $ msg ++ " Continuing with " ++ showCS cs' ++ ".") err | otherwise -> - failWith ("Failed to find a smaller conflict set. The new " + failWith (mkStringMsg $ "Failed to find a smaller conflict set. The new " ++ "conflict set is not a subset of the previous " ++ "conflict set: " ++ showCS cs') $ ExhaustiveSearch smallestKnownCS smallestKnownCM BackjumpLimitReached -> - failWith "Reached backjump limit while minimizing conflict set." + failWith (mkStringMsg "Reached backjump limit while minimizing conflict set.") BackjumpLimitReached where varStr = "\"" ++ showVar v ++ "\"" diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs index 321a051070b..1eb759bd263 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs @@ -7,11 +7,14 @@ import Prelude () import Distribution.Solver.Compat.Prelude import Distribution.Solver.Types.Progress - -import Distribution.Solver.Modular.Dependency -import Distribution.Solver.Modular.Message + ( Progress(Done, Fail), foldProgress ) +import Distribution.Solver.Modular.ConflictSet + ( ConflictMap, ConflictSet ) import Distribution.Solver.Modular.RetryLog - + ( RetryLog, toProgress, fromProgress ) +import Distribution.Solver.Modular.Message (Message, summarizeMessages) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage(..) ) -- | Information about a dependency solver failure. data SolverFailure = ExhaustiveSearch ConflictSet ConflictMap @@ -22,10 +25,10 @@ data SolverFailure = -- 'keepLog'), for efficiency. displayLogMessages :: Bool -> RetryLog Message SolverFailure a - -> RetryLog String SolverFailure a + -> RetryLog SummarizedMessage SolverFailure a displayLogMessages keepLog lg = fromProgress $ if keepLog - then showMessages progress + then summarizeMessages progress else foldProgress (const id) Fail Done progress where progress = toProgress lg diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs index 2bc28286df0..d6ffadf0abf 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs @@ -1,9 +1,9 @@ {-# LANGUAGE BangPatterns #-} -{-# LANGUAGE LambdaCase #-} module Distribution.Solver.Modular.Message ( Message(..), - showMessages + summarizeMessages, + renderSummarizedMessage, ) where import qualified Data.List as L @@ -14,26 +14,50 @@ import qualified Data.Set as S import Data.Maybe (catMaybes, mapMaybe, isJust) import Prelude hiding (pi) -import Distribution.Pretty (prettyShow) -- from Cabal +import Distribution.Pretty ( prettyShow ) -- from Cabal import qualified Distribution.Solver.Modular.ConflictSet as CS import Distribution.Solver.Modular.Dependency -import Distribution.Solver.Modular.Flag ( QFN, QSN ) -import qualified Distribution.Solver.Modular.Flag as Flag ( showQFN, showQFNBool, showQSN, showQSNBool ) + ( Var(P), + ConflictSet, + showConflictSet, + QGoalReason, + GoalReason(DependencyGoal, UserGoal), + Goal(Goal), + DependencyReason(DependencyReason), + ExposedComponent(..), + PkgComponent(PkgComponent), + CI(Constrained, Fixed), + showDependencyReason ) +import Distribution.Solver.Modular.Flag + ( QSN, QFN, showQFNBool, showQSNBool, showQFN, showQSN ) import Distribution.Solver.Modular.MessageUtils - (showUnsupportedExtension, showUnsupportedLanguage) + ( showUnsupportedExtension, showUnsupportedLanguage ) import Distribution.Solver.Modular.Package + ( PI(PI), showI, showPI ) import Distribution.Solver.Modular.Tree - ( FailReason(..), POption(..), ConflictingDep(..) ) + ( FailReason(..), POption(..), ConflictingDep(..) ) import Distribution.Solver.Modular.Version + ( VR, Ver, showVer, showVR, (.||.) ) + import Distribution.Solver.Types.ConstraintSource + ( ConstraintSource (..), showConstraintSource ) import Distribution.Solver.Types.PackagePath + ( QPN, Qualified(Q), showQPN ) import Distribution.Solver.Types.Progress -import Distribution.Solver.Types.ProjectConfigPath (docProjectConfigPathFailReason) + ( Progress(..) ) +import Distribution.Solver.Types.ProjectConfigPath + ( docProjectConfigPathFailReason) +import Distribution.Solver.Types.SummarizedMessage + ( Entry(..), EntryAtLevel(..), SummarizedMessage(..) ) import Distribution.Types.LibraryName + ( LibraryName(LSubLibName, LMainLibName) ) import Distribution.Types.UnqualComponentName -import Text.PrettyPrint (nest, render) + ( unUnqualComponentName ) + +import Text.PrettyPrint ( nest, render ) +-- A data type to hold log information from the modular solver. data Message = Enter -- ^ increase indentation level | Leave -- ^ decrease indentation level @@ -45,51 +69,81 @@ data Message = | Success | Failure ConflictSet FailReason --- | Transforms the structured message type to actual messages (strings). +renderSummarizedMessage :: SummarizedMessage -> String +renderSummarizedMessage (SummarizedMsg i) = displayMessageAtLevel i +renderSummarizedMessage (StringMsg s) = s + +displayMessageAtLevel :: EntryAtLevel -> String +displayMessageAtLevel (AtLevel l msg) = + let s = show l + in "[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ displayMessage msg + +displayMessage :: Entry -> String +displayMessage (EntryPackageGoal qpn gr) = "next goal: " ++ showQPN qpn ++ showGR gr +displayMessage (EntryRejectF qfn b c fr) = "rejecting: " ++ showQFNBool qfn b ++ showFR c fr +displayMessage (EntryRejectS qsn b c fr) = "rejecting: " ++ showQSNBool qsn b ++ showFR c fr +displayMessage (EntrySkipping cs) = "skipping: " ++ showConflicts cs +displayMessage (EntryTryingF qfn b) = "trying: " ++ showQFNBool qfn b +displayMessage (EntryTryingP qpn i) = "trying: " ++ showOption qpn i +displayMessage (EntryTryingNewP qpn i gr) = "trying: " ++ showOption qpn i ++ showGR gr +displayMessage (EntryTryingS qsn b) = "trying: " ++ showQSNBool qsn b +displayMessage (EntryUnknownPackage qpn gr) = "unknown package: " ++ showQPN qpn ++ showGR gr +displayMessage EntrySuccess = "done" +displayMessage (EntryFailure c fr) = "fail" ++ showFR c fr +displayMessage (EntrySkipMany qsn b cs) = "skipping: " ++ showOptions qsn b ++ " " ++ showConflicts cs +-- Instead of displaying `aeson-1.0.2.1, aeson-1.0.2.0, aeson-1.0.1.0, ...`, +-- the following line aims to display `aeson: 1.0.2.1, 1.0.2.0, 1.0.1.0, ...`. +-- +displayMessage (EntryRejectMany qpn is c fr) = "rejecting: " ++ showOptions qpn is ++ showFR c fr + +-- | Transforms the structured message type to actual messages (SummarizedMessage s). -- -- The log contains level numbers, which are useful for any trace that involves -- backtracking, because only the level numbers will allow to keep track of -- backjumps. -showMessages :: Progress Message a b -> Progress String a b -showMessages = go 0 +summarizeMessages :: Progress Message a b -> Progress SummarizedMessage a b +summarizeMessages = go 0 where -- 'go' increments the level for a recursive call when it encounters -- 'TryP', 'TryF', or 'TryS' and decrements the level when it encounters 'Leave'. - go :: Int -> Progress Message a b -> Progress String a b + go :: Int -> Progress Message a b -> Progress SummarizedMessage a b go !_ (Done x) = Done x go !_ (Fail x) = Fail x + -- complex patterns go !l (Step (TryP qpn i) (Step Enter (Step (Failure c fr) (Step Leave ms)))) = goPReject l qpn [i] c fr ms + go !l (Step (TryP qpn i) (Step Enter (Step (Skip conflicts) (Step Leave ms)))) = goPSkip l qpn [i] conflicts ms + go !l (Step (TryF qfn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) = - (atLevel l $ blurbQFNBool Rejecting qfn b ++ showFR c fr) (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryRejectF qfn b c fr)) (go l ms) + go !l (Step (TryS qsn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) = - (atLevel l $ blurbQSNBool Rejecting qsn b ++ showFR c fr) (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryRejectS qsn b c fr)) (go l ms) + + -- "Trying ..." message when a new goal is started go !l (Step (Next (Goal (P _ ) gr)) (Step (TryP qpn' i) ms@(Step Enter (Step (Next _) _)))) = - (atLevel l $ blurbOption Trying qpn' i ++ showGR gr) (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryTryingNewP qpn' i gr)) (go l ms) + go !l (Step (Next (Goal (P qpn) gr)) (Step (Failure _c UnknownPackage) ms)) = - atLevel l ("unknown package: " ++ showQPN qpn ++ showGR gr) $ go l ms + Step (SummarizedMsg $ AtLevel l $ (EntryUnknownPackage qpn gr)) (go l ms) + -- standard display go !l (Step Enter ms) = go (l+1) ms go !l (Step Leave ms) = go (l-1) ms - go !l (Step (TryP qpn i) ms) = (atLevel l $ blurbOption Trying qpn i) (go l ms) - go !l (Step (TryF qfn b) ms) = (atLevel l $ blurbQFNBool Trying qfn b) (go l ms) - go !l (Step (TryS qsn b) ms) = (atLevel l $ blurbQSNBool Trying qsn b) (go l ms) - go !l (Step (Next (Goal (P qpn) gr)) ms) = (atLevel l $ showPackageGoal qpn gr) (go l ms) - go !l (Step (Next _) ms) = go l ms -- ignore flag goals in the log - go !l (Step (Skip conflicts) ms) = - -- 'Skip' should always be handled by 'goPSkip' in the case above. - (atLevel l $ blurb Skipping ++ showConflicts conflicts) (go l ms) - go !l (Step (Success) ms) = (atLevel l $ "done") (go l ms) - go !l (Step (Failure c fr) ms) = (atLevel l $ showFailure c fr) (go l ms) - - showPackageGoal :: QPN -> QGoalReason -> String - showPackageGoal qpn gr = "next goal: " ++ showQPN qpn ++ showGR gr - - showFailure :: ConflictSet -> FailReason -> String - showFailure c fr = "fail" ++ showFR c fr + + go !l (Step (TryP qpn i) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryTryingP qpn i)) (go l ms) + go !l (Step (TryF qfn b) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryTryingF qfn b)) (go l ms) + go !l (Step (TryS qsn b) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryTryingS qsn b)) (go l ms) + go !l (Step (Next (Goal (P qpn) gr)) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryPackageGoal qpn gr)) (go l ms) + go !l (Step (Next _) ms) = go l ms -- ignore flag goals in the log + + -- 'Skip' should always be handled by 'goPSkip' in the case above. + go !l (Step (Skip conflicts) ms) = Step (SummarizedMsg $ AtLevel l $ (EntrySkipping conflicts)) (go l ms) + go !l (Step (Success) ms) = Step (SummarizedMsg $ AtLevel l $ EntrySuccess) (go l ms) + go !l (Step (Failure c fr) ms) = Step (SummarizedMsg $ AtLevel l $ (EntryFailure c fr)) (go l ms) -- special handler for many subsequent package rejections goPReject :: Int @@ -98,14 +152,13 @@ showMessages = go 0 -> ConflictSet -> FailReason -> Progress Message a b - -> Progress String a b + -> Progress SummarizedMessage a b goPReject l qpn is c fr (Step (TryP qpn' i) (Step Enter (Step (Failure _ fr') (Step Leave ms)))) | qpn == qpn' && fr == fr' = -- By prepending (i : is) we reverse the order of the instances. goPReject l qpn (i : is) c fr ms goPReject l qpn is c fr ms = - (atLevel l $ blurbOptions Rejecting qpn (reverse is) ++ showFR c fr) - (go l ms) + Step (SummarizedMsg $ AtLevel l $ (EntryRejectMany qpn (reverse is) c fr)) (go l ms) -- Handle many subsequent skipped package instances. goPSkip :: Int @@ -113,25 +166,18 @@ showMessages = go 0 -> [POption] -> Set CS.Conflict -> Progress Message a b - -> Progress String a b + -> Progress SummarizedMessage a b goPSkip l qpn is conflicts (Step (TryP qpn' i) (Step Enter (Step (Skip conflicts') (Step Leave ms)))) | qpn == qpn' && conflicts == conflicts' = -- By prepending (i : is) we reverse the order of the instances. goPSkip l qpn (i : is) conflicts ms goPSkip l qpn is conflicts ms = - let msg = blurbOptions Skipping qpn (reverse is) ++ showConflicts conflicts - in atLevel l msg (go l ms) - - -- write a message with the current level number - atLevel :: Int -> String -> Progress String a b -> Progress String a b - atLevel l x xs = - let s = show l - in Step ("[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ x) xs + Step (SummarizedMsg $ AtLevel l $ (EntrySkipMany qpn (reverse is) conflicts)) (go l ms) -- | Display the set of 'Conflicts' for a skipped package version. showConflicts :: Set CS.Conflict -> String showConflicts conflicts = - " (has the same characteristics that caused the previous version to fail: " + "(has the same characteristics that caused the previous version to fail: " ++ conflictMsg ++ ")" where conflictMsg :: String @@ -213,29 +259,6 @@ data MergedPackageConflict = MergedPackageConflict { , versionConflict :: Maybe VR } -data ProgressAction = - Trying - | Skipping - | Rejecting - -blurb :: ProgressAction -> String -blurb = \case - Trying -> "trying: " - Skipping -> "skipping: " - Rejecting -> "rejecting: " - -blurbQFNBool :: ProgressAction -> QFN -> Bool -> String -blurbQFNBool a q b = blurb a ++ Flag.showQFNBool q b - -blurbQSNBool :: ProgressAction -> QSN -> Bool -> String -blurbQSNBool a q b = blurb a ++ Flag.showQSNBool q b - -blurbOption :: ProgressAction -> QPN -> POption -> String -blurbOption a q p = blurb a ++ showOption q p - -blurbOptions :: ProgressAction -> QPN -> [POption] -> String -blurbOptions a q ps = blurb a ++ showOptions q ps - showOption :: QPN -> POption -> String showOption qpn@(Q _pp pn) (POption i linkedTo) = case linkedTo of @@ -306,8 +329,8 @@ showFR _ (UnsupportedSpecVer ver) = " (unsupported spec-version " ++ pre -- The following are internal failures. They should not occur. In the -- interest of not crashing unnecessarily, we still just print an error -- message though. -showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ Flag.showQFN qfn ++ ")" -showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ Flag.showQSN qsn ++ ")" +showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")" +showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")" showFR _ EmptyGoalChoice = " (INTERNAL ERROR: EMPTY GOAL CHOICE)" showExposedComponent :: ExposedComponent -> String @@ -332,7 +355,9 @@ showConflictingDep (ConflictingDep dr (PkgComponent qpn comp) ci) = componentStr ++ showVR vr -- $setup +-- >>> import Distribution.Solver.Modular.Package -- >>> import Distribution.Solver.Types.PackagePath +-- >>> import Distribution.Types.PackageName -- >>> import Distribution.Types.Version -- >>> import Distribution.Types.UnitId -- >>> let foobarPN = PackagePath DefaultNamespace QualToplevel diff --git a/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs b/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs index 139a6d2b33d..956a4e14849 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs @@ -2,17 +2,22 @@ module Distribution.Solver.Types.DependencyResolver ( DependencyResolver ) where -import Distribution.Solver.Compat.Prelude +import Distribution.Solver.Compat.Prelude ( Maybe, String, Set ) import Prelude () import Distribution.Solver.Types.LabeledPackageConstraint + ( LabeledPackageConstraint ) import Distribution.Solver.Types.PkgConfigDb ( PkgConfigDb ) import Distribution.Solver.Types.PackagePreferences + ( PackagePreferences ) import Distribution.Solver.Types.PackageIndex ( PackageIndex ) import Distribution.Solver.Types.Progress + ( Progress ) import Distribution.Solver.Types.ResolverPackage -import Distribution.Solver.Types.SourcePackage - + ( ResolverPackage ) +import Distribution.Solver.Types.SourcePackage ( SourcePackage ) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage(..) ) import Distribution.Simple.PackageIndex ( InstalledPackageIndex ) import Distribution.Package ( PackageName ) import Distribution.Compiler ( CompilerInfo ) @@ -34,4 +39,4 @@ type DependencyResolver loc = Platform -> (PackageName -> PackagePreferences) -> [LabeledPackageConstraint] -> Set PackageName - -> Progress String String [ResolverPackage loc] + -> Progress SummarizedMessage String [ResolverPackage loc] diff --git a/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs b/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs new file mode 100644 index 00000000000..747db0a88ae --- /dev/null +++ b/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs @@ -0,0 +1,48 @@ +module Distribution.Solver.Types.SummarizedMessage + ( Entry(..) + , EntryAtLevel(..) + , SummarizedMessage(..) + ) where + +import Prelude () +import Distribution.Solver.Compat.Prelude hiding (fail) + +import Distribution.Solver.Modular.Tree + ( FailReason(..), POption(..) ) +import Distribution.Solver.Types.PackagePath ( QPN ) +import Distribution.Solver.Modular.Flag ( QSN, QFN ) +import Distribution.Solver.Modular.Dependency + ( ConflictSet, QGoalReason, GoalReason ) +import qualified Distribution.Solver.Modular.ConflictSet as CS + +-- The following types are used to encode log messages from the +-- dependency solver so they can be easily displayed in the module +-- `Distribution.Solver.Modular.Message`. +-- +-- These types are an intermediate representation of the solver log. +-- The log is converted from a list of Message to a list of +-- SummarizedMessage to a list of String. Message is very similar to +-- the structure of the search tree but difficult to read, and +-- SummarizedMessage has the structure that is shown to users. + +-- Encoding of solver messages. +data Entry + = EntryPackageGoal QPN QGoalReason + | EntryRejectF QFN Bool ConflictSet FailReason + | EntryRejectS QSN Bool ConflictSet FailReason + | EntrySkipping (Set CS.Conflict) + | EntryTryingF QFN Bool + | EntryTryingP QPN POption + | EntryTryingNewP QPN POption (GoalReason QPN) + | EntryTryingS QSN Bool + | EntryRejectMany QPN [POption] ConflictSet FailReason + | EntrySkipMany QPN [POption] (Set CS.Conflict) + | EntryUnknownPackage QPN (GoalReason QPN) + | EntrySuccess + | EntryFailure ConflictSet FailReason + +-- Encode the level at which the solver message occurred. +data EntryAtLevel = AtLevel Int Entry + +-- Messages from the solver. +data SummarizedMessage = SummarizedMsg EntryAtLevel | StringMsg String diff --git a/cabal-install/src/Distribution/Client/Dependency.hs b/cabal-install/src/Distribution/Client/Dependency.hs index d59bc611c44..594afb9e24f 100644 --- a/cabal-install/src/Distribution/Client/Dependency.hs +++ b/cabal-install/src/Distribution/Client/Dependency.hs @@ -123,6 +123,12 @@ import Distribution.Solver.Modular , SolverConfig (..) , modularResolver ) +import Distribution.Solver.Modular.Message + ( renderSummarizedMessage + ) +import Distribution.Solver.Types.SummarizedMessage + ( SummarizedMessage (..) + ) import Distribution.System ( Platform ) @@ -152,6 +158,8 @@ import Distribution.Solver.Types.ResolverPackage import Distribution.Solver.Types.Settings import Distribution.Solver.Types.SolverId import Distribution.Solver.Types.SolverPackage + ( SolverPackage (SolverPackage) + ) import Distribution.Solver.Types.SourcePackage import Distribution.Solver.Types.Variable @@ -790,32 +798,33 @@ resolveDependencies resolveDependencies platform comp pkgConfigDB params = Step (showDepResolverParams finalparams) $ fmap (validateSolverResult platform comp indGoals) $ - runSolver - ( SolverConfig - reordGoals - cntConflicts - fineGrained - minimize - indGoals - noReinstalls - shadowing - strFlags - onlyConstrained_ - maxBkjumps - enableBj - solveExes - order - verbosity - (PruneAfterFirstSuccess False) - ) - platform - comp - installedPkgIndex - sourcePkgIndex - pkgConfigDB - preferences - constraints - targets + formatProgress $ + runSolver + ( SolverConfig + reordGoals + cntConflicts + fineGrained + minimize + indGoals + noReinstalls + shadowing + strFlags + onlyConstrained_ + maxBkjumps + enableBj + solveExes + order + verbosity + (PruneAfterFirstSuccess False) + ) + platform + comp + installedPkgIndex + sourcePkgIndex + pkgConfigDB + preferences + constraints + targets where finalparams@( DepResolverParams targets @@ -844,6 +853,9 @@ resolveDependencies platform comp pkgConfigDB params = then params else dontInstallNonReinstallablePackages params + formatProgress :: Progress SummarizedMessage String a -> Progress String String a + formatProgress p = foldProgress (\x xs -> Step (renderSummarizedMessage x) xs) Fail Done p + preferences :: PackageName -> PackagePreferences preferences = interpretPackagesPreference targets defpref prefs diff --git a/changelog.d/pr-10854 b/changelog.d/pr-10854 new file mode 100644 index 00000000000..5209a98c68c --- /dev/null +++ b/changelog.d/pr-10854 @@ -0,0 +1,7 @@ +synopsis: Refactor cabal-install solver config log output +packages: cabal-install-solver +prs: #10854 + +This refactoring is mainly cosmetic changes to the modular solver's +code base. The main change is separating the detection of errors from +the reporting of errors.