diff --git a/cabal-install-solver/src/Distribution/Solver/Modular.hs b/cabal-install-solver/src/Distribution/Solver/Modular.hs index b653d1459ed..12ac50a2698 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular.hs @@ -145,7 +145,7 @@ solve' sc cinfo idx pkgConfigDB pprefs gcs pns = runSolver :: Bool -> SolverConfig -> RetryLog SummarizedMessage SolverFailure (Assignment, RevDepMap) runSolver keepLog sc' = - displayLogMessages keepLog $ + displayLogMessages (reposSearched sc') keepLog $ solve sc' cinfo idx pkgConfigDB pprefs gcs pns createErrorMsg :: SolverFailure diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs index 1eb759bd263..327bc0ea0f7 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Log.hs @@ -23,12 +23,13 @@ data SolverFailure = -- | Postprocesses a log file. This function discards all log messages and -- avoids calling 'showMessages' if the log isn't needed (specified by -- 'keepLog'), for efficiency. -displayLogMessages :: Bool +displayLogMessages :: [String] + -> Bool -> RetryLog Message SolverFailure a -> RetryLog SummarizedMessage SolverFailure a -displayLogMessages keepLog lg = fromProgress $ +displayLogMessages repos keepLog lg = fromProgress $ if keepLog - then summarizeMessages progress + then summarizeMessages repos 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 16a1b1ff13b..30976fb0f72 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs @@ -85,7 +85,8 @@ 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 (EntryUnknownPackage qpn gr repos) = + "unknown package: " ++ showQPN qpn ++ showGR gr ++ showReposSearched repos displayMessage EntrySuccess = "done" displayMessage (EntryFailure c fr) = "fail" ++ showFR c fr displayMessage (EntrySkipMany qsn b cs) = "skipping: " ++ showOptions qsn b ++ " " ++ showConflicts cs @@ -99,8 +100,8 @@ displayMessage (EntryRejectMany qpn is c fr) = "rejecting: " ++ showOptions qpn -- 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. -summarizeMessages :: Progress Message a b -> Progress SummarizedMessage a b -summarizeMessages = go 0 +summarizeMessages :: [String] -> Progress Message a b -> Progress SummarizedMessage a b +summarizeMessages repos = 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'. @@ -126,7 +127,7 @@ summarizeMessages = go 0 Step (SummarizedMsg $ AtLevel l (EntryTryingNewP qpn' i gr)) (go l ms) go !l (Step (Next (Goal (P qpn) gr)) (Step (Failure _c UnknownPackage) ms)) = - Step (SummarizedMsg $ AtLevel l (EntryUnknownPackage qpn gr)) (go l ms) + Step (SummarizedMsg $ AtLevel l (EntryUnknownPackage qpn gr repos)) (go l ms) -- standard display go !l (Step Enter ms) = go (l+1) ms @@ -295,6 +296,13 @@ showGR :: QGoalReason -> String showGR UserGoal = " (user goal)" showGR (DependencyGoal dr) = " (dependency of " ++ showDependencyReason dr ++ ")" +-- | List the repositories that were searched for a package that turned out to +-- be unknown. This helps the user notice when @active-repositories@ is set to +-- a nonstandard value that excludes a package from the search. +showReposSearched :: [String] -> String +showReposSearched [] = "" +showReposSearched repos = "; searched repositories: " ++ L.intercalate ", " repos + showFR :: ConflictSet -> FailReason -> String showFR _ (UnsupportedExtension ext) = " (conflict: requires " ++ showUnsupportedExtension ext ++ ")" showFR _ (UnsupportedLanguage lang) = " (conflict: requires " ++ showUnsupportedLanguage lang ++ ")" diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs index f2197379076..bdfde81d5bb 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs @@ -74,7 +74,10 @@ data SolverConfig = SolverConfig { solveExecutables :: SolveExecutables, goalOrder :: Maybe (Variable QPN -> Variable QPN -> Ordering), solverVerbosity :: VerbosityLevel, - pruneAfterFirstSuccess :: PruneAfterFirstSuccess + pruneAfterFirstSuccess :: PruneAfterFirstSuccess, + -- | The names of the repositories that were searched for packages. Used to + -- enrich the @unknown package@ failure message. + reposSearched :: [String] } -- | Whether to remove all choices after the first successful choice at each diff --git a/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs b/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs index 747db0a88ae..39d61fe6512 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/SummarizedMessage.hs @@ -37,7 +37,7 @@ data Entry | EntryTryingS QSN Bool | EntryRejectMany QPN [POption] ConflictSet FailReason | EntrySkipMany QPN [POption] (Set CS.Conflict) - | EntryUnknownPackage QPN (GoalReason QPN) + | EntryUnknownPackage QPN (GoalReason QPN) [String] | EntrySuccess | EntryFailure ConflictSet FailReason diff --git a/cabal-install/src/Distribution/Client/Dependency.hs b/cabal-install/src/Distribution/Client/Dependency.hs index fc447247d69..1503db176a7 100644 --- a/cabal-install/src/Distribution/Client/Dependency.hs +++ b/cabal-install/src/Distribution/Client/Dependency.hs @@ -57,6 +57,7 @@ module Distribution.Client.Dependency , setSolveExecutables , setGoalOrder , setSolverVerbosity + , setActiveRepositories , removeLowerBounds , removeUpperBounds , addDefaultSetupDependencies @@ -82,6 +83,7 @@ import Distribution.Client.Types , RelaxDepSubject (..) , RelaxDeps (..) , RelaxedDep (..) + , RepoName (..) , SourcePackageDb (SourcePackageDb) , UnresolvedPkgLoc , UnresolvedSourcePackage @@ -212,6 +214,9 @@ data DepResolverParams = DepResolverParams , depResolverGoalOrder :: Maybe (Variable QPN -> Variable QPN -> Ordering) -- ^ Function to override the solver's goal-ordering heuristics. , depResolverVerbosity :: VerbosityLevel + , depResolverActiveRepos :: [RepoName] + -- ^ The names of the repositories that were searched for packages. Used to + -- enrich the @unknown package@ solver error message. } showDepResolverParams :: DepResolverParams -> String @@ -309,6 +314,7 @@ basicDepResolverParams installedPkgIndex sourcePkgIndex = , depResolverSolveExecutables = SolveExecutables True , depResolverGoalOrder = Nothing , depResolverVerbosity = Normal + , depResolverActiveRepos = [] } addTargets @@ -444,6 +450,12 @@ setSolverVerbosity verbosity params = { depResolverVerbosity = verbosity } +setActiveRepositories :: [RepoName] -> DepResolverParams -> DepResolverParams +setActiveRepositories repos params = + params + { depResolverActiveRepos = repos + } + dependOnWiredIns :: CompilerInfo -> DepResolverParams -> DepResolverParams dependOnWiredIns compiler params = addConstraints extraConstraints params where @@ -882,6 +894,7 @@ resolveDependencies platform comp pkgConfigDB params = do order verbosity (PruneAfterFirstSuccess False) + (map unRepoName activeRepos) ) platform comp @@ -915,6 +928,7 @@ resolveDependencies platform comp pkgConfigDB params = do solveExes order verbosity + activeRepos ) = if isJust (compilerInfoWiredInUnitIds comp) || asBool (depResolverAllowBootLibInstalls params) then dependOnWiredIns comp params @@ -1236,6 +1250,7 @@ resolveWithoutDependencies _onlyConstrained _order _verbosity + _activeRepos ) = collectEithers $ map selectPackage (Set.toList targets) where diff --git a/cabal-install/src/Distribution/Client/IndexUtils.hs b/cabal-install/src/Distribution/Client/IndexUtils.hs index 70f1119c1ea..2aac83ab522 100644 --- a/cabal-install/src/Distribution/Client/IndexUtils.hs +++ b/cabal-install/src/Distribution/Client/IndexUtils.hs @@ -24,6 +24,7 @@ module Distribution.Client.IndexUtils , getSourcePackagesAtIndexState , ActiveRepos , filterSkippedActiveRepos + , activeReposNames , applyStrategy , addIndex , deprecationAwareStrategy diff --git a/cabal-install/src/Distribution/Client/IndexUtils/ActiveRepos.hs b/cabal-install/src/Distribution/Client/IndexUtils/ActiveRepos.hs index 3698e0638ad..245193260b7 100644 --- a/cabal-install/src/Distribution/Client/IndexUtils/ActiveRepos.hs +++ b/cabal-install/src/Distribution/Client/IndexUtils/ActiveRepos.hs @@ -4,6 +4,7 @@ module Distribution.Client.IndexUtils.ActiveRepos ( ActiveRepos (..) , defaultActiveRepos , filterSkippedActiveRepos + , activeReposNames , ActiveRepoEntry (..) , CombineStrategy (..) , organizeByRepos @@ -45,6 +46,9 @@ filterSkippedActiveRepos repos@(ActiveRepos entries) notSkipped (ActiveRepo _ CombineStrategySkip) = False notSkipped _ = True +activeReposNames :: ActiveRepos -> [RepoName] +activeReposNames (ActiveRepos entries) = [r | ActiveRepo r _ <- entries] + instance Binary ActiveRepos instance Structured ActiveRepos instance NFData ActiveRepos diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 336411c9158..6cca4e97679 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -854,6 +854,7 @@ rebuildInstallPlan pkgConfigDB localPackages localPackagesEnabledStanzas + (IndexUtils.activeReposNames ar) case planOrError of Left msg -> do reportPlanningFailure projectConfig compiler platform localPackages @@ -1335,6 +1336,7 @@ planPackages -> Maybe PkgConfigDb -> [PackageSpecifier UnresolvedSourcePackage] -> Map PackageName (Map OptionalStanza Bool) + -> [RepoName] -> Progress String String SolverInstallPlan planPackages verbosity @@ -1345,7 +1347,8 @@ planPackages sourcePkgDb pkgConfigDB localPackages - pkgStanzasEnable = + pkgStanzasEnable + activeRepos = resolveDependencies platform (compilerInfo comp) @@ -1375,6 +1378,7 @@ planPackages . setAllowBootLibInstalls solverSettingAllowBootLibInstalls . setOnlyConstrained solverSettingOnlyConstrained . setSolverVerbosity (verbosityLevel verbosity) + . setActiveRepositories activeRepos . setPreferenceDefault ( case solverSettingPreferVersion of PreferOldest -> PreferAllOldest diff --git a/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/cabal.project b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/cabal.project new file mode 100644 index 00000000000..295806d7e8a --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/cabal.project @@ -0,0 +1,2 @@ +packages: fake-pkg +active-repositories: test-local-repo diff --git a/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/cabal.test.hs b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/cabal.test.hs new file mode 100644 index 00000000000..0849c9413bf --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/cabal.test.hs @@ -0,0 +1,7 @@ +import Test.Cabal.Prelude + +main = cabalTest . recordMode DoNotRecord $ do + withRepo "repo" $ do + res <- fails $ cabal' "v2-build" ["fake-pkg"] + assertOutputContains "unknown package: p" res + assertOutputContains "searched repositories: test-local-repo" res diff --git a/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/fake-pkg/Lib.hs b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/fake-pkg/Lib.hs new file mode 100644 index 00000000000..6d85a26fe10 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/fake-pkg/Lib.hs @@ -0,0 +1 @@ +module Lib where diff --git a/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/fake-pkg/fake-pkg.cabal b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/fake-pkg/fake-pkg.cabal new file mode 100644 index 00000000000..dc74772ca32 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/fake-pkg/fake-pkg.cabal @@ -0,0 +1,8 @@ +cabal-version: 1.12 +name: fake-pkg +version: 1.0 +build-type: Simple + +library + build-depends: base, p + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/repo/q-1.0/Foo.hs b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/repo/q-1.0/Foo.hs new file mode 100644 index 00000000000..efbf93bbde8 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/repo/q-1.0/Foo.hs @@ -0,0 +1 @@ +module Foo where diff --git a/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/repo/q-1.0/q.cabal b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/repo/q-1.0/q.cabal new file mode 100644 index 00000000000..6c6da3c6962 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/UnknownPackageRepos/repo/q-1.0/q.cabal @@ -0,0 +1,9 @@ +cabal-version: 1.12 +name: q +version: 1.0 +build-type: Simple + +library + exposed-modules: Foo + build-depends: base + default-language: Haskell2010 diff --git a/changelog.d/12278.md b/changelog.d/12278.md new file mode 100644 index 00000000000..a32406870e9 --- /dev/null +++ b/changelog.d/12278.md @@ -0,0 +1,20 @@ +--- +synopsis: List searched repositories in "unknown package" solver errors +packages: [cabal-install] +prs: 12278 +issues: 7661 +--- + +When dependency solving fails because a package cannot be found, the solver +now reports which repositories were searched alongside the usual "unknown +package" message: + +```diff +- [__1] unknown package: badpackage (dependency of fake-package) ++ [__1] unknown package: badpackage (dependency of fake-package); searched repositories: hackage.haskell.org +``` + +This makes it easier to notice when `active-repositories` is set to a +nonstandard value that excludes a package from the search even though it is +available on Hackage. When the default repositories are in use, the message +simply lists them, so there is no extra noise beyond a short suffix.