From 83858e70c91f61a2ffa9be44630b76dd86a6bf70 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 30 Jan 2026 09:19:56 -0500 Subject: [PATCH 01/25] filter == version constraints - Update test for == constraint check - Add OnlyConstrainedEq - Test --reject-unconstrained-dependencies=eq - Add haddocks for eq option - Remove pkgIsExplicit - One-line keepNonEmpty - Use pattern guard - Top level filterThisVersion - Name tests --- .../src/Distribution/Solver/Modular/Solver.hs | 21 ++++-- .../src/Distribution/Solver/Types/Settings.hs | 5 +- .../src/Distribution/Client/Setup.hs | 4 +- .../MultiPkg/cabal.multipkg-all.out | 37 ++++++++++ .../MultiPkg/cabal.multipkg-eq.out | 29 ++++++++ .../RequireExplicit/MultiPkg/cabal.test.hs | 74 ++++++++++++++++--- doc/cabal-project-description-file.rst | 12 +-- 7 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs index d16fb37af37..ce069a65d8e 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs @@ -23,6 +23,8 @@ import Distribution.Solver.Types.PackagePath import Distribution.Solver.Types.PackagePreferences import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb) import Distribution.Solver.Types.LabeledPackageConstraint +import Distribution.Solver.Types.PackageConstraint (PackageConstraint(..), PackageProperty(..)) +import Distribution.Types.VersionRange (normaliseVersionRange, projectVersionRange, VersionRangeF(ThisVersionF)) import Distribution.Solver.Types.Settings import Distribution.Solver.Types.Variable @@ -140,17 +142,14 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals = validateTree cinfo idx pkgConfigDB prunePhase = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) . (case onlyConstrained sc of - OnlyConstrainedAll -> - P.onlyConstrained pkgIsExplicit - OnlyConstrainedNone -> - id) + OnlyConstrainedEq -> P.onlyConstrained (`S.member` allExplicitEq) + OnlyConstrainedAll -> P.onlyConstrained (`S.member` allExplicit) + OnlyConstrainedNone -> id) buildPhase = buildTree idx (independentGoals sc) (S.toList userGoals) + allExplicitEq = M.keysSet (filterThisVersion userConstraints) `S.union` userGoals allExplicit = M.keysSet userConstraints `S.union` userGoals - pkgIsExplicit :: PN -> Bool - pkgIsExplicit pn = S.member pn allExplicit - -- When --reorder-goals is set, we use preferReallyEasyGoalChoices, which -- prefers (keeps) goals only if the have 0 or 1 enabled choice. -- @@ -167,6 +166,14 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals = | asBool (reorderGoals sc) = P.preferReallyEasyGoalChoices | otherwise = id {- P.firstGoal -} +-- | Keep version ranges that normalise to equality version constraints (== v). +filterThisVersion :: M.Map PN [LabeledPackageConstraint] -> M.Map PN [LabeledPackageConstraint] +filterThisVersion = M.filter (not . null) . M.map (filter isThisVersion) where + isThisVersion lpc + | LabeledPackageConstraint (PackageConstraint _ (PackagePropertyVersion vr)) _ <- lpc + , ThisVersionF _ <- projectVersionRange $ normaliseVersionRange vr = True + | otherwise = False + -- | Dump solver tree to a file (in debugging mode) -- -- This only does something if the @debug-tracetree@ configure argument was diff --git a/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs b/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs index 8d70237a33a..c1e68fc6828 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs @@ -61,6 +61,7 @@ newtype AllowBootLibInstalls = AllowBootLibInstalls Bool data OnlyConstrained = OnlyConstrainedNone | OnlyConstrainedAll + | OnlyConstrainedEq deriving (Eq, Generic, Show) newtype EnableBackjumping = EnableBackjumping Bool @@ -108,12 +109,14 @@ instance NFData AllowBootLibInstalls instance NFData OnlyConstrained instance Pretty OnlyConstrained where + pretty OnlyConstrainedEq = PP.text "eq" pretty OnlyConstrainedAll = PP.text "all" pretty OnlyConstrainedNone = PP.text "none" instance Parsec OnlyConstrained where parsec = P.choice - [ P.string "all" >> return OnlyConstrainedAll + [ P.string "eq" >> return OnlyConstrainedEq + , P.string "all" >> return OnlyConstrainedAll , P.string "none" >> return OnlyConstrainedNone ] diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs index 67dc3e7fa2f..646ee311723 100644 --- a/cabal-install/src/Distribution/Client/Setup.hs +++ b/cabal-install/src/Distribution/Client/Setup.hs @@ -3695,9 +3695,9 @@ optionSolverFlags getoc setoc ( reqArg - "none|all" + "none|all|eq" ( parsecToReadE - (const "reject-unconstrained-dependencies must be 'none' or 'all'") + (const "reject-unconstrained-dependencies must be 'none', 'all', or 'eq'") (toFlag `fmap` parsec) ) (flagToList . fmap prettyShow) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out new file mode 100644 index 00000000000..59c7495fb33 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out @@ -0,0 +1,37 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: other-lib (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set) +[__1] fail (backjumping, conflict set: b, other-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: b:some-exe:exe.some-exe (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set) +[__1] fail (backjumping, conflict set: b, b:some-exe:exe.some-exe) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), b:some-exe:exe.some-exe (1) +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out new file mode 100644 index 00000000000..b697bc8fec7 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out @@ -0,0 +1,29 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: a-0 (user goal) +[__1] next goal: some-lib (dependency of a) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set) +[__1] fail (backjumping, conflict set: a, some-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: a (2), some-lib (1) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs index 271bddebf0b..1317eb6a7aa 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs @@ -1,16 +1,68 @@ import Test.Cabal.Prelude + -- See #4332, dep solving output is not deterministic -main = cabalTest . recordMode DoNotRecord $ withRepo "repo" $ do - -- other-lib is a dependency of b, but it's not listed in cabal.project - res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "some-exe -any"] - assertOutputContains "not a user-provided goal" res - -- and some-exe is a build-tool dependency of b, again not listed - res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "other-lib -any"] - assertOutputContains "not a user-provided goal" res +assertErr = assertOutputContains "not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set" + +constraint x = "--constraint=" ++ x + +main = do + cabalTest' "multipkg-all" . withRepo "repo" $ do + let opts = + [ "--dry-run" + , "--reject-unconstrained-dependencies=all" + ] + + -- other-lib is a dependency of b, but it's not listed in cabal.project + res <- fails $ cabal' "build" ("all" : opts ++ [constraint "some-exe -any"]) + assertErr res + + -- and some-exe is a build-tool dependency of b, again not listed + res <- fails $ cabal' "build" ("all" : opts ++ [constraint "other-lib -any"]) + assertErr res + + -- everything's listed, good to go + cabal "build" $ + "all" + : opts + ++ [ constraint "other-lib -any" + , constraint "some-exe -any" + ] + + -- a depends on b, but b is a local dependency, so it gets a pass + cabal "build" $ + "a" + : opts + ++ [ constraint "other-lib -any" + , constraint "some-exe -any" + ] + + cabalTest' "multipkg-eq" . withRepo "repo" $ do + -- all the options except for those about some-lib + let opts = + [ "all" + , "--dry-run" + , "--reject-unconstrained-dependencies=eq" + , constraint "other-lib ==1.0" + , constraint "some-exe ==1.0" + ] + + -- everything's listed as == constraint + cabal "build" (opts ++ [constraint "some-lib ==1.0"]) - -- everything's listed, good to go - cabal "v2-build" ["all", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "other-lib -any", "--constraint", "some-exe -any"] + -- everything's listed as == constraint when normalised + cabal "build" $ + opts + ++ [ constraint "some-lib ==1.0" + , constraint "some-lib >0" + , constraint "some-lib <2" + ] - -- a depends on b, but b is a local dependency, so it gets a pass - cabal "v2-build" ["a", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "other-lib -any", "--constraint", "some-exe -any"] + -- not everything's listed as == constraint when normalised + res <- + fails . cabal' "build" $ + opts + ++ [ constraint "some-lib >0" + , constraint "some-lib <2" + ] + assertErr res diff --git a/doc/cabal-project-description-file.rst b/doc/cabal-project-description-file.rst index adae4514b14..ea707b10e38 100644 --- a/doc/cabal-project-description-file.rst +++ b/doc/cabal-project-description-file.rst @@ -790,8 +790,8 @@ The following settings control the behavior of the dependency solver: active-repositories: :none -.. cfg-field:: reject-unconstrained-dependencies: all, none - --reject-unconstrained-dependencies=[all|none] +.. cfg-field:: reject-unconstrained-dependencies: eq, all, none + --reject-unconstrained-dependencies=[eq|all|none] :synopsis: Restrict the solver to packages that have constraints on them. :default: none @@ -801,9 +801,11 @@ The following settings control the behavior of the dependency solver: aware of in a build plan. If you wish to restrict the build plan to a closed set of packages (e.g., from a freeze file), use this flag. - When set to `all`, all non-local packages that aren't goals must be - explicitly constrained. When set to `none`, the solver will - consider all packages. + When set to `eq` or `all`, all non-local packages that aren't goals must be + explicitly constrained. where `all` accepts any version constraint and `eq` + accepts version ranges that normalise to an equality constraint. + + When set to `none`, the solver will consider all packages. .. _package-configuration-options: From efd9e259ec1c879c7ad62974484efce80e713d30 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 1 Feb 2026 21:03:34 -0500 Subject: [PATCH 02/25] Show flag setting in FailReason --- .../src/Distribution/Solver/Modular/Message.hs | 8 +++++++- .../src/Distribution/Solver/Modular/Preference.hs | 7 ++++--- .../src/Distribution/Solver/Modular/Solver.hs | 8 +++++--- .../src/Distribution/Solver/Modular/Tree.hs | 3 ++- .../RequireExplicit/MultiPkg/cabal.multipkg-all.out | 4 ++-- .../RequireExplicit/MultiPkg/cabal.multipkg-eq.out | 2 +- .../PackageTests/RequireExplicit/MultiPkg/cabal.test.hs | 9 ++++----- 7 files changed, 25 insertions(+), 16 deletions(-) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs index 9cc4234e66e..e9d516d5549 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs @@ -56,6 +56,7 @@ import Distribution.Types.UnqualComponentName ( unUnqualComponentName ) import Text.PrettyPrint ( nest, render ) +import Distribution.Solver.Types.Settings (OnlyConstrained(..)) -- A data type to hold log information from the modular solver. data Message = @@ -311,7 +312,7 @@ showFR _ (PackageRequiresMissingComponent qpn comp) = " (requires " ++ showExpos showFR _ (PackageRequiresPrivateComponent qpn comp) = " (requires " ++ showExposedComponent comp ++ " from " ++ showQPN qpn ++ ", but the component is private)" showFR _ (PackageRequiresUnbuildableComponent qpn comp) = " (requires " ++ showExposedComponent comp ++ " from " ++ showQPN qpn ++ ", but the component is not buildable in the current environment)" showFR _ CannotReinstall = " (avoiding to reinstall a package with same version but new dependencies)" -showFR _ NotExplicit = " (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set)" +showFR _ (NotExplicit oc) = showNotExplicit oc showFR _ Shadowed = " (shadowed by another installed package with same version)" showFR _ (Broken u) = " (package is broken, missing dependency " ++ prettyShow u ++ ")" showFR _ UnknownPackage = " (unknown package)" @@ -334,6 +335,11 @@ showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CH showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")" showFR _ EmptyGoalChoice = " (INTERNAL ERROR: EMPTY GOAL CHOICE)" +showNotExplicit :: OnlyConstrained -> String +showNotExplicit oc = if oc == OnlyConstrainedNone + then " (INTERNAL ERROR: NOT EXPLICIT when reject-unconstrained-dependencies=" ++ prettyShow oc ++ " was set)" + else " (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=" ++ prettyShow oc ++ " was set)" + showExposedComponent :: ExposedComponent -> String showExposedComponent (ExposedLib LMainLibName) = "library" showExposedComponent (ExposedLib (LSubLibName name)) = "library '" ++ unUnqualComponentName name ++ "'" diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs index e33eb09524f..c6f5b5928d2 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs @@ -43,6 +43,7 @@ import Distribution.Solver.Modular.Tree import Distribution.Solver.Modular.Version import qualified Distribution.Solver.Modular.ConflictSet as CS import qualified Distribution.Solver.Modular.WeightedPSQ as W +import Distribution.Solver.Types.Settings (OnlyConstrained(..)) -- | Update the weights of children under 'PChoice' nodes. 'addWeights' takes a -- list of weight-calculating functions in order to avoid sorting the package @@ -351,13 +352,13 @@ avoidReinstalls p = go go x = x -- | Require all packages to be mentioned in a constraint or as a goal. -onlyConstrained :: (PN -> Bool) -> EndoTreeTrav d QGoalReason -onlyConstrained p = go +onlyConstrained :: OnlyConstrained -> (PN -> Bool) -> EndoTreeTrav d QGoalReason +onlyConstrained oc p = go where go (PChoiceF v@(Q _ pn) _ gr _) | not (p pn) = FailF (varToConflictSet (P v) `CS.union` goalReasonToConflictSetWithConflict v gr) - NotExplicit + (NotExplicit oc) go x = x diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs index ce069a65d8e..637087c0bd5 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} #ifdef DEBUG_TRACETREE {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -Wno-orphans #-} @@ -11,6 +12,7 @@ module Distribution.Solver.Modular.Solver import Distribution.Solver.Compat.Prelude import Prelude () +import Data.Function ((&)) import qualified Data.Map as M import qualified Data.List as L @@ -141,9 +143,9 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals = validateLinking idx . validateTree cinfo idx pkgConfigDB prunePhase = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) . - (case onlyConstrained sc of - OnlyConstrainedEq -> P.onlyConstrained (`S.member` allExplicitEq) - OnlyConstrainedAll -> P.onlyConstrained (`S.member` allExplicit) + (let oc = onlyConstrained sc in oc & \case + OnlyConstrainedEq -> P.onlyConstrained oc (`S.member` allExplicitEq) + OnlyConstrainedAll -> P.onlyConstrained oc (`S.member` allExplicit) OnlyConstrainedNone -> id) buildPhase = buildTree idx (independentGoals sc) (S.toList userGoals) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Tree.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Tree.hs index a845ad6ef9d..3b6f19ea35e 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Tree.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Tree.hs @@ -36,6 +36,7 @@ import Distribution.Solver.Types.PackagePath import Distribution.Types.PkgconfigVersionRange import Distribution.Types.UnitId (UnitId) import Language.Haskell.Extension (Extension, Language) +import Distribution.Solver.Types.Settings (OnlyConstrained(..)) type Weight = Double @@ -112,7 +113,7 @@ data FailReason = UnsupportedExtension Extension | PackageRequiresPrivateComponent QPN ExposedComponent | PackageRequiresUnbuildableComponent QPN ExposedComponent | CannotReinstall - | NotExplicit + | NotExplicit OnlyConstrained | Shadowed | Broken UnitId | UnknownPackage diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out index 59c7495fb33..79c7e4d3b6e 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-all.out @@ -6,7 +6,7 @@ Error: [Cabal-7107] Could not resolve dependencies: [__0] trying: b-0 (user goal) [__1] next goal: other-lib (dependency of b) -[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=all was set) [__1] fail (backjumping, conflict set: b, other-lib) After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) # cabal build @@ -15,7 +15,7 @@ Error: [Cabal-7107] Could not resolve dependencies: [__0] trying: b-0 (user goal) [__1] next goal: b:some-exe:exe.some-exe (dependency of b) -[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=all was set) [__1] fail (backjumping, conflict set: b, b:some-exe:exe.some-exe) After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), b:some-exe:exe.some-exe (1) # cabal build diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out index b697bc8fec7..2fb70f302d6 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.multipkg-eq.out @@ -24,6 +24,6 @@ Error: [Cabal-7107] Could not resolve dependencies: [__0] trying: a-0 (user goal) [__1] next goal: some-lib (dependency of a) -[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=eq was set) [__1] fail (backjumping, conflict set: a, some-lib) After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: a (2), some-lib (1) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs index 1317eb6a7aa..096c80ddf33 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs @@ -2,7 +2,7 @@ import Test.Cabal.Prelude -- See #4332, dep solving output is not deterministic -assertErr = assertOutputContains "not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set" +assertErr x = assertOutputContains ("not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=" ++ x ++ " was set") constraint x = "--constraint=" ++ x @@ -15,12 +15,11 @@ main = do -- other-lib is a dependency of b, but it's not listed in cabal.project res <- fails $ cabal' "build" ("all" : opts ++ [constraint "some-exe -any"]) - assertErr res + assertErr "all" res -- and some-exe is a build-tool dependency of b, again not listed res <- fails $ cabal' "build" ("all" : opts ++ [constraint "other-lib -any"]) - assertErr res - + assertErr "all" res -- everything's listed, good to go cabal "build" $ "all" @@ -65,4 +64,4 @@ main = do ++ [ constraint "some-lib >0" , constraint "some-lib <2" ] - assertErr res + assertErr "eq" res From 0b3ae5d709919b500c058383ab621d20e72694a8 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sun, 1 Feb 2026 22:05:32 -0500 Subject: [PATCH 03/25] Add changelog --- changelog.d/pr-11470.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 changelog.d/pr-11470.md diff --git a/changelog.d/pr-11470.md b/changelog.d/pr-11470.md new file mode 100644 index 00000000000..c6f300c7953 --- /dev/null +++ b/changelog.d/pr-11470.md @@ -0,0 +1,14 @@ +--- +synopsis: Add eq as an option to the flag reject-unconstrained-dependencies +packages: [cabal-install, cabal-install-solver] +prs: 11470 +issues: 11468 +--- + +Extend the flag `reject-unconstrained-dependencies` to have options +`none|all|eq`. + +When set to `eq` or `all`, all non-local packages that aren't goals must be +explicitly constrained. The difference between `all` and `eq` is that `all` +accepts any version constraint (even `>0`) but `eq` accepts only version ranges +that normalise to an equality constraint (== v). From fbd6834a74ae026b591c20bd9940d2279dd58bc7 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 6 Feb 2026 14:49:12 -0500 Subject: [PATCH 04/25] Add tests using projects --- .../RequireExplicit/MultiPkg/cabal.test.hs | 5 +- .../MultiPkgInProject/a/a.cabal | 8 +++ .../MultiPkgInProject/b/b.cabal | 10 +++ .../cabal.all.no.exe-any.project | 4 ++ .../cabal.all.no.lib-any.project | 4 ++ .../cabal.all.no.lib-exe-any.project | 5 ++ .../cabal.eq.go.eq-and-range.project | 8 +++ .../MultiPkgInProject/cabal.eq.go.eq.project | 6 ++ .../cabal.eq.no.eq-and-range.project | 6 ++ .../cabal.eq.no.exe-eq.project | 4 ++ .../cabal.eq.no.lib-eq.project | 4 ++ .../cabal.eq.no.lib-exe-eq.project | 5 ++ .../cabal.eq.no.lib-range.project | 5 ++ .../MultiPkgInProject/cabal.multipkg-all.out | 29 +++++++++ .../MultiPkgInProject/cabal.multipkg-eq.out | 65 +++++++++++++++++++ .../MultiPkgInProject/cabal.project | 3 + .../MultiPkgInProject/cabal.test.hs | 22 +++++++ .../repo/other-lib-1.0/other-lib.cabal | 7 ++ .../repo/some-exe-1.0/some-exe.cabal | 6 ++ .../repo/some-lib-1.0/some-lib.cabal | 7 ++ 20 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/a/a.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/b/b.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.exe-any.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-any.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-exe-any.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq-and-range.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.eq-and-range.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.exe-eq.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-eq.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-exe-eq.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-range.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-all.out create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-eq.out create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/other-lib-1.0/other-lib.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-exe-1.0/some-exe.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-lib-1.0/some-lib.cabal diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs index 096c80ddf33..d5b95d3dc1a 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs @@ -20,6 +20,7 @@ main = do -- and some-exe is a build-tool dependency of b, again not listed res <- fails $ cabal' "build" ("all" : opts ++ [constraint "other-lib -any"]) assertErr "all" res + -- everything's listed, good to go cabal "build" $ "all" @@ -54,7 +55,7 @@ main = do opts ++ [ constraint "some-lib ==1.0" , constraint "some-lib >0" - , constraint "some-lib <2" + , constraint "some-lib <=2" ] -- not everything's listed as == constraint when normalised @@ -62,6 +63,6 @@ main = do fails . cabal' "build" $ opts ++ [ constraint "some-lib >0" - , constraint "some-lib <2" + , constraint "some-lib <=2" ] assertErr "eq" res diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/a/a.cabal b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/a/a.cabal new file mode 100644 index 00000000000..c2a690ed917 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/a/a.cabal @@ -0,0 +1,8 @@ +cabal-version: 2.2 +name: a +version: 0 + +library + build-depends: + some-lib, + b diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/b/b.cabal b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/b/b.cabal new file mode 100644 index 00000000000..662e0df30e1 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/b/b.cabal @@ -0,0 +1,10 @@ +cabal-version: 2.2 +name: b +version: 0 + +library + build-tool-depends: + some-exe:some-exe -any + build-depends: + some-lib, + other-lib diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.exe-any.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.exe-any.project new file mode 100644 index 00000000000..6ba82928d5c --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.exe-any.project @@ -0,0 +1,4 @@ +import: cabal.project +reject-unconstrained-dependencies: all +constraints: + some-exe -any diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-any.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-any.project new file mode 100644 index 00000000000..a080a63105c --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-any.project @@ -0,0 +1,4 @@ +import: cabal.project +reject-unconstrained-dependencies: all +constraints: + some-lib -any diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-exe-any.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-exe-any.project new file mode 100644 index 00000000000..e0c161140ee --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.all.no.lib-exe-any.project @@ -0,0 +1,5 @@ +import: cabal.project +reject-unconstrained-dependencies: all +constraints: + some-exe -any + , some-lib -any diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq-and-range.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq-and-range.project new file mode 100644 index 00000000000..dd735205611 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq-and-range.project @@ -0,0 +1,8 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-lib >0 + , some-lib <=2 + , some-lib ==1.0 + , some-exe ==1.0 + , other-lib ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq.project new file mode 100644 index 00000000000..f3fac72ab8d --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.go.eq.project @@ -0,0 +1,6 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-lib ==1.0 + , some-exe ==1.0 + , other-lib ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.eq-and-range.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.eq-and-range.project new file mode 100644 index 00000000000..04256acab13 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.eq-and-range.project @@ -0,0 +1,6 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-lib >0 + , some-lib <=2 + , some-lib ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.exe-eq.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.exe-eq.project new file mode 100644 index 00000000000..262e65ba310 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.exe-eq.project @@ -0,0 +1,4 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-exe ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-eq.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-eq.project new file mode 100644 index 00000000000..f3183329eeb --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-eq.project @@ -0,0 +1,4 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-lib ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-exe-eq.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-exe-eq.project new file mode 100644 index 00000000000..e785abb2ef4 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-exe-eq.project @@ -0,0 +1,5 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-exe ==1.0 + , some-lib ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-range.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-range.project new file mode 100644 index 00000000000..30df1fec381 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.eq.no.lib-range.project @@ -0,0 +1,5 @@ +import: cabal.project +reject-unconstrained-dependencies: eq +constraints: + some-lib >0 + , some-lib <=2 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-all.out b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-all.out new file mode 100644 index 00000000000..2724f12564a --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-all.out @@ -0,0 +1,29 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: a-0 (user goal) +[__1] next goal: some-lib (dependency of a) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=all was set) +[__1] fail (backjumping, conflict set: a, some-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: a (2), some-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: other-lib (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=all was set) +[__1] fail (backjumping, conflict set: b, other-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: other-lib (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=all was set) +[__1] fail (backjumping, conflict set: b, other-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-eq.out b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-eq.out new file mode 100644 index 00000000000..de658864711 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.multipkg-eq.out @@ -0,0 +1,65 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: other-lib (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=eq was set) +[__1] fail (backjumping, conflict set: b, other-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: a-0 (user goal) +[__1] next goal: some-lib (dependency of a) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=eq was set) +[__1] fail (backjumping, conflict set: a, some-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: a (2), some-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: other-lib (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=eq was set) +[__1] fail (backjumping, conflict set: b, other-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: b-0 (user goal) +[__1] next goal: other-lib (dependency of b) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=eq was set) +[__1] fail (backjumping, conflict set: b, other-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1) +# cabal build +Resolving dependencies... +Error: [Cabal-7107] +Could not resolve dependencies: +[__0] trying: a-0 (user goal) +[__1] next goal: some-lib (dependency of a) +[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=eq was set) +[__1] fail (backjumping, conflict set: a, some-lib) +After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: a (2), some-lib (1) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.project b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.project new file mode 100644 index 00000000000..32e0ad6fdeb --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.project @@ -0,0 +1,3 @@ +packages: + ./a + ./b diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs new file mode 100644 index 00000000000..b0754463162 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs @@ -0,0 +1,22 @@ +import Test.Cabal.Prelude + +-- See #4332, dep solving output is not deterministic +-- other-lib is a dependency of b, but it's not listed in cabal.project +-- and some-exe is a build-tool dependency of b, again not listed + +assertErr x = assertOutputContains ("not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=" ++ x ++ " was set") +constraint x = "--constraint=" ++ x +opts = [ "all", "--dry-run" ] + +main = do + cabalTest' "multipkg-all" . withRepo "repo" $ do + let proj n = "--project-file=cabal.all" <.> n <.> ".project" : opts + let no n = assertErr "all" =<< fails (cabal' "build" $ proj n) + mapM_ (no . ("no" <.>)) ["exe-any", "lib-any", "lib-exe-any"] + + cabalTest' "multipkg-eq" . withRepo "repo" $ do + let proj n = "--project-file=cabal.eq" <.> n <.> ".project" : opts + let no n = assertErr "eq" =<< fails (cabal' "build" $ proj n) + let go n = cabal' "build" $ proj n + mapM_ (go . ("go" <.>)) ["eq-and-range", "eq"] + mapM_ (no . ("no" <.>)) ["eq-and-range", "exe-eq", "lib-eq", "lib-exe-eq", "lib-range"] diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/other-lib-1.0/other-lib.cabal b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/other-lib-1.0/other-lib.cabal new file mode 100644 index 00000000000..f0d38525d02 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/other-lib-1.0/other-lib.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.2 +name: other-lib +version: 1.0 + +library + exposed-modules: + Foo diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-exe-1.0/some-exe.cabal b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-exe-1.0/some-exe.cabal new file mode 100644 index 00000000000..93f0fbb5b6d --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-exe-1.0/some-exe.cabal @@ -0,0 +1,6 @@ +cabal-version: 2.2 +name: some-exe +version: 1.0 + +executable some-exe + main-is: Foo.hs diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-lib-1.0/some-lib.cabal b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-lib-1.0/some-lib.cabal new file mode 100644 index 00000000000..99958826d9e --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/repo/some-lib-1.0/some-lib.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.2 +name: some-lib +version: 1.0 + +library + exposed-modules: + Foo From 68723ce2769093b32e0c8c7482151a75bb0ad237 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 6 Feb 2026 17:01:12 -0500 Subject: [PATCH 05/25] SkipIfOSX --- .../PackageTests/RequireExplicit/MultiPkg/cabal.test.hs | 8 ++++++++ .../RequireExplicit/MultiPkgInProject/cabal.test.hs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs index d5b95d3dc1a..3328aaac141 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs @@ -7,6 +7,14 @@ assertErr x = assertOutputContains ("not a user-provided goal nor mentioned as a constraint x = "--constraint=" ++ x main = do + skipIfOSX "macOS has a different solver output format" + -- - - other-lib-1.0 (lib) (requires build) + -- - - some-exe-1.0 (exe:some-exe) (requires build) + -- - some-lib-1.0 (lib) (requires build) + -- + - some-exe-1.0 (exe:some-exe) (requires build) + -- + - other-lib-1.0 (lib) (requires build) + -- - b-0 (lib) (first run) + -- - a-0 (lib) (first run) cabalTest' "multipkg-all" . withRepo "repo" $ do let opts = [ "--dry-run" diff --git a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs index b0754463162..4b07df86756 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs @@ -9,6 +9,14 @@ constraint x = "--constraint=" ++ x opts = [ "all", "--dry-run" ] main = do + skipIfOSX "macOS has a different solver output format" + -- - - other-lib-1.0 (lib) (requires build) + -- - - some-exe-1.0 (exe:some-exe) (requires build) + -- - some-lib-1.0 (lib) (requires build) + -- + - some-exe-1.0 (exe:some-exe) (requires build) + -- + - other-lib-1.0 (lib) (requires build) + -- - b-0 (lib) (first run) + -- - a-0 (lib) (first run) cabalTest' "multipkg-all" . withRepo "repo" $ do let proj n = "--project-file=cabal.all" <.> n <.> ".project" : opts let no n = assertErr "all" =<< fails (cabal' "build" $ proj n) From 6a332f7cca139a4b417671a46936624723a88152 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Sat, 7 Feb 2026 08:39:22 -0500 Subject: [PATCH 06/25] Update expectation in solver unit test - Use onlyConstrainedAll in solver unit tests - Add unit tests for =eq and =none - Add whenNone, whenAll and whenEq - Enhance the failure message expectation --- .../Solver/Modular/DSL/TestCaseUtils.hs | 6 +- .../Distribution/Solver/Modular/Solver.hs | 119 ++++++++++++++---- 2 files changed, 96 insertions(+), 29 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs index 3fe0eb6a339..c1f80908851 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs @@ -79,9 +79,9 @@ allowBootLibInstalls :: SolverTest -> SolverTest allowBootLibInstalls test = test{testAllowBootLibInstalls = AllowBootLibInstalls True} -onlyConstrained :: SolverTest -> SolverTest -onlyConstrained test = - test{testOnlyConstrained = OnlyConstrainedAll} +onlyConstrained :: OnlyConstrained -> SolverTest -> SolverTest +onlyConstrained oc test = + test{testOnlyConstrained = oc} disableBackjumping :: SolverTest -> SolverTest disableBackjumping test = diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 691d9b1d39e..fa6b9754e7e 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} -- | This is a set of unit tests for the dependency solver, -- which uses the solver DSL ("UnitTests.Distribution.Solver.Modular.DSL") @@ -23,10 +24,12 @@ import Language.Haskell.Extension ) -- cabal-install + import Distribution.Solver.Types.Flag import Distribution.Solver.Types.OptionalStanza import Distribution.Solver.Types.PackageConstraint import qualified Distribution.Solver.Types.PackagePath as P +import Distribution.Solver.Types.Settings (OnlyConstrained (..)) import UnitTests.Distribution.Solver.Modular.DSL import UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils @@ -234,32 +237,96 @@ tests = ] , testGroup "reject-unconstrained" - [ runTest $ - onlyConstrained $ - mkTest db12 "missing syb" ["E"] $ - solverFailure (isInfixOf "not a user-provided goal") - , runTest $ - onlyConstrained $ - mkTest db12 "all goals" ["E", "syb"] $ - solverSuccess [("E", 1), ("syb", 2)] - , runTest $ - onlyConstrained $ - mkTest db17 "backtracking" ["A", "B"] $ - solverSuccess [("A", 2), ("B", 1)] - , runTest $ - onlyConstrained $ - mkTest db17 "failure message" ["A"] $ - solverFailure $ - isInfixOf $ - "Could not resolve dependencies:\n" - ++ "[__0] trying: A-3.0.0 (user goal)\n" - ++ "[__1] next goal: C (dependency of A)\n" - ++ "[__1] fail (not a user-provided goal nor mentioned as a constraint, " - ++ "but reject-unconstrained-dependencies was set)\n" - ++ "[__1] fail (backjumping, conflict set: A, C)\n" - ++ "After searching the rest of the dependency tree exhaustively, " - ++ "these were the goals I've had most trouble fulfilling: A, C, B" - ] + $ let solverMsg condition = + "Could not resolve dependencies:\n" + ++ "[__0] trying: A-3.0.0 (user goal)\n" + ++ "[__1] next goal: C (dependency of A)\n" + ++ "[__1] fail (not a user-provided goal nor mentioned as a constraint, " + ++ "but reject-unconstrained-dependencies=" + ++ condition + ++ " was set)\n" + ++ "[__1] fail (backjumping, conflict set: A, C)\n" + ++ "After searching the rest of the dependency tree exhaustively, " + ++ "these were the goals I've had most trouble fulfilling: A, C, B" + whenNone = onlyConstrained OnlyConstrainedNone + whenAll = onlyConstrained OnlyConstrainedAll + whenEq = onlyConstrained OnlyConstrainedEq + in [ testGroup + "=none" + [ runTest $ + whenNone $ + mkTest db12 "goal E" ["E"] $ + solverSuccess [("E", 1), ("syb", 2)] + , runTest $ + whenNone $ + mkTest db12 "all goals" ["E", "syb"] $ + solverSuccess [("E", 1), ("syb", 2)] + , runTest $ + whenNone $ + mkTest db17 "goal A B: backtracking" ["A", "B"] $ + solverSuccess [("A", 3), ("B", 1), ("C", 1)] + , runTest $ + whenNone $ + mkTest db17 "goal A" ["A"] $ + solverSuccess [("A", 3), ("B", 1), ("C", 1)] + ] + , testGroup + "=all" + [ runTest $ + whenAll $ + mkTest db12 "goal E: missing syb" ["E"] $ + solverFailure + ( \m -> + all + (`isInfixOf` m) + [ "next goal: syb (dependency of E)" + , "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=all was set" + ] + ) + , runTest $ + whenAll $ + mkTest db12 "all goals" ["E", "syb"] $ + solverSuccess [("E", 1), ("syb", 2)] + , runTest $ + whenAll $ + mkTest db17 "goal A B: backtracking" ["A", "B"] $ + solverSuccess [("A", 2), ("B", 1)] + , runTest $ + whenAll $ + mkTest db17 "goal A: failure message" ["A"] $ + solverFailure $ + isInfixOf $ + solverMsg "all" + ] + , testGroup "=eq" $ + let eGoalFailure m = + all + (`isInfixOf` m) + [ "next goal: E.base (dependency of E)" + , "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=eq was set" + ] + in [ runTest $ + whenEq $ + mkTest db12 "goal E: missing syb" ["E"] $ + solverFailure eGoalFailure + , runTest $ + whenEq $ + mkTest db12 "all goals" ["E", "syb"] $ + solverFailure eGoalFailure + , runTest $ + whenEq $ + mkTest db17 "goal A B: backtracking" ["A", "B"] $ + solverSuccess [("A", 2), ("B", 1)] + , runTest $ + whenEq $ + mkTest db17 "goal A: failure message" ["A"] $ + solverFailure $ + isInfixOf $ + solverMsg "eq" + ] + ] , testGroup "Cycles" [ runTest $ mkTest db14 "simpleCycle1" ["A"] anySolverFailure From a7dce12f922a2ad3a71411b8a9eca018ddb3030e Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Mon, 9 Feb 2026 14:57:13 -0500 Subject: [PATCH 07/25] Add constrained solving tests --- .../Solver/Modular/DSL/TestCaseUtils.hs | 2 +- .../Distribution/Solver/Modular/Solver.hs | 176 ++++++++++++------ 2 files changed, 124 insertions(+), 54 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs index c1f80908851..c0d011b4d95 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs @@ -2,7 +2,7 @@ -- | Utilities for creating HUnit test cases with the solver DSL. module UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils - ( SolverTest + ( SolverTest (..) , SolverResult (..) , maxBackjumps , disableFineGrainedConflicts diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index fa6b9754e7e..109405be5da 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -17,6 +17,8 @@ import Test.Tasty as TF import Test.Tasty.ExpectedFailure -- Cabal + +import qualified Distribution.Version as C import Language.Haskell.Extension ( Extension (..) , KnownExtension (..) @@ -31,7 +33,7 @@ import Distribution.Solver.Types.PackageConstraint import qualified Distribution.Solver.Types.PackagePath as P import Distribution.Solver.Types.Settings (OnlyConstrained (..)) import UnitTests.Distribution.Solver.Modular.DSL -import UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils +import UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils hiding (testMinimizeConflictSet) tests :: [TF.TestTree] tests = @@ -251,31 +253,49 @@ tests = whenNone = onlyConstrained OnlyConstrainedNone whenAll = onlyConstrained OnlyConstrainedAll whenEq = onlyConstrained OnlyConstrainedEq + eqConstraints = + [ ExVersionConstraint (ScopeAnyQualifier "B") (C.thisVersion $ mkSimpleVersion 1) + , ExVersionConstraint (ScopeAnyQualifier "C") (C.thisVersion $ mkSimpleVersion 1) + ] + gtConstraints = + [ ExVersionConstraint (ScopeAnyQualifier "B") (C.laterVersion $ mkSimpleVersion 0) + , ExVersionConstraint (ScopeAnyQualifier "C") (C.laterVersion $ mkSimpleVersion 0) + ] in [ testGroup "=none" - [ runTest $ - whenNone $ - mkTest db12 "goal E" ["E"] $ - solverSuccess [("E", 1), ("syb", 2)] - , runTest $ - whenNone $ - mkTest db12 "all goals" ["E", "syb"] $ - solverSuccess [("E", 1), ("syb", 2)] - , runTest $ - whenNone $ - mkTest db17 "goal A B: backtracking" ["A", "B"] $ - solverSuccess [("A", 3), ("B", 1), ("C", 1)] - , runTest $ - whenNone $ - mkTest db17 "goal A" ["A"] $ - solverSuccess [("A", 3), ("B", 1), ("C", 1)] + [ runTest . whenNone $ + mkTest + db12 + "goal E" + ["E"] + (solverSuccess [("E", 1), ("syb", 2)]) + , runTest . whenNone $ + mkTest + db12 + "all goals" + ["E", "syb"] + (solverSuccess [("E", 1), ("syb", 2)]) + , runTest . whenNone $ + mkTest + db17 + "goal A B backtracking" + ["A", "B"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + , runTest . whenNone $ + mkTest + db17 + "goal A" + ["A"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) ] , testGroup "=all" - [ runTest $ - whenAll $ - mkTest db12 "goal E: missing syb" ["E"] $ - solverFailure + [ runTest . whenAll $ + mkTest + db12 + "goal E missing syb" + ["E"] + ( solverFailure ( \m -> all (`isInfixOf` m) @@ -284,20 +304,37 @@ tests = , "but reject-unconstrained-dependencies=all was set" ] ) - , runTest $ - whenAll $ - mkTest db12 "all goals" ["E", "syb"] $ - solverSuccess [("E", 1), ("syb", 2)] - , runTest $ - whenAll $ - mkTest db17 "goal A B: backtracking" ["A", "B"] $ - solverSuccess [("A", 2), ("B", 1)] - , runTest $ - whenAll $ - mkTest db17 "goal A: failure message" ["A"] $ - solverFailure $ - isInfixOf $ - solverMsg "all" + ) + , runTest . whenAll $ + mkTest + db12 + "all goals" + ["E", "syb"] + (solverSuccess [("E", 1), ("syb", 2)]) + , runTest . whenAll $ + mkTest + db17 + "goal A B backtracking" + ["A", "B"] + (solverSuccess [("A", 2), ("B", 1)]) + , runTest . whenAll $ + mkTest + db17 + "goal A failure message" + ["A"] + (solverFailure . isInfixOf $ solverMsg "all") + , runTest . whenAll $ + ( mkTest db17 "goal A with B ==1, C ==1" ["A"] $ + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = eqConstraints + } + , runTest . whenAll $ + ( mkTest db17 "goal A with B >0, C >0" ["A"] $ + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = gtConstraints + } ] , testGroup "=eq" $ let eGoalFailure m = @@ -307,24 +344,57 @@ tests = , "not a user-provided goal nor mentioned as a constraint" , "but reject-unconstrained-dependencies=eq was set" ] - in [ runTest $ - whenEq $ - mkTest db12 "goal E: missing syb" ["E"] $ - solverFailure eGoalFailure - , runTest $ - whenEq $ - mkTest db12 "all goals" ["E", "syb"] $ - solverFailure eGoalFailure - , runTest $ - whenEq $ - mkTest db17 "goal A B: backtracking" ["A", "B"] $ - solverSuccess [("A", 2), ("B", 1)] - , runTest $ - whenEq $ - mkTest db17 "goal A: failure message" ["A"] $ - solverFailure $ - isInfixOf $ - solverMsg "eq" + in [ runTest . whenEq $ + mkTest + db12 + "goal E missing syb" + ["E"] + (solverFailure eGoalFailure) + , runTest . whenEq $ + mkTest + db12 + "all goals" + ["E", "syb"] + (solverFailure eGoalFailure) + , runTest . whenEq $ + mkTest + db17 + "goal A B backtracking" + ["A", "B"] + (solverSuccess [("A", 2), ("B", 1)]) + , runTest . whenEq $ + mkTest + db17 + "goal A failure message" + ["A"] + (solverFailure . isInfixOf $ solverMsg "eq") + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B ==1, C ==1" + ["A"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = eqConstraints + } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B >0, C >0 failure message" + ["A"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + [ "next goal: C (dependency of A)" + , "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=eq was set" + ] + ) + ) + ) + { testConstraints = gtConstraints + } ] ] , testGroup From 80a6beb3bd862b9fe0838e8c357a6db41fa47cff Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Mon, 9 Feb 2026 17:35:40 -0500 Subject: [PATCH 08/25] Add mkConstraint --- .../Distribution/Solver/Modular/Solver.hs | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 109405be5da..857b019a49d 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -253,13 +253,21 @@ tests = whenNone = onlyConstrained OnlyConstrainedNone whenAll = onlyConstrained OnlyConstrainedAll whenEq = onlyConstrained OnlyConstrainedEq + eq1 = C.thisVersion $ mkSimpleVersion 1 + gt0 = C.laterVersion $ C.mkVersion [0] + eqGt = C.intersectVersionRanges eq1 gt0 + mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr eqConstraints = - [ ExVersionConstraint (ScopeAnyQualifier "B") (C.thisVersion $ mkSimpleVersion 1) - , ExVersionConstraint (ScopeAnyQualifier "C") (C.thisVersion $ mkSimpleVersion 1) + [ mkConstraint "B" eq1 + , mkConstraint "C" eq1 ] gtConstraints = - [ ExVersionConstraint (ScopeAnyQualifier "B") (C.laterVersion $ mkSimpleVersion 0) - , ExVersionConstraint (ScopeAnyQualifier "C") (C.laterVersion $ mkSimpleVersion 0) + [ mkConstraint "B" gt0 + , mkConstraint "C" gt0 + ] + eqGtConstraints = + [ mkConstraint "B" eqGt + , mkConstraint "C" eqGt ] in [ testGroup "=none" @@ -335,6 +343,12 @@ tests = ) { testConstraints = gtConstraints } + , runTest . whenAll $ + ( mkTest db17 "goal A with B >0 && ==1, C >0 && ==1" ["A"] $ + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = eqGtConstraints + } ] , testGroup "=eq" $ let eGoalFailure m = @@ -395,6 +409,15 @@ tests = ) { testConstraints = gtConstraints } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B >0 && ==1, C >0 && ==1" + ["A"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = eqGtConstraints + } ] ] , testGroup From 863b9239021febcca6ac0e144f67007fe4992aba Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 07:24:28 -0500 Subject: [PATCH 09/25] Use normalise not normaliseVersionRange --- .../src/Distribution/Solver/Modular/Solver.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs index 637087c0bd5..49a99627f1f 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs @@ -21,12 +21,12 @@ import Distribution.Verbosity import Distribution.Compiler (CompilerInfo) +import Distribution.Version import Distribution.Solver.Types.PackagePath import Distribution.Solver.Types.PackagePreferences import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb) import Distribution.Solver.Types.LabeledPackageConstraint import Distribution.Solver.Types.PackageConstraint (PackageConstraint(..), PackageProperty(..)) -import Distribution.Types.VersionRange (normaliseVersionRange, projectVersionRange, VersionRangeF(ThisVersionF)) import Distribution.Solver.Types.Settings import Distribution.Solver.Types.Variable @@ -171,9 +171,10 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals = -- | Keep version ranges that normalise to equality version constraints (== v). filterThisVersion :: M.Map PN [LabeledPackageConstraint] -> M.Map PN [LabeledPackageConstraint] filterThisVersion = M.filter (not . null) . M.map (filter isThisVersion) where + normalise = fromVersionIntervals . toVersionIntervals isThisVersion lpc | LabeledPackageConstraint (PackageConstraint _ (PackagePropertyVersion vr)) _ <- lpc - , ThisVersionF _ <- projectVersionRange $ normaliseVersionRange vr = True + , ThisVersionF _ <- projectVersionRange $ normalise vr = True | otherwise = False -- | Dump solver tree to a file (in debugging mode) From dc3922ea327e37c4ce0eeceb8ce9387316aacc21 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 09:49:20 -0500 Subject: [PATCH 10/25] Add >= version constraint check --- .../Distribution/Solver/Modular/Solver.hs | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 857b019a49d..ebfa01e4bf2 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -255,6 +255,7 @@ tests = whenEq = onlyConstrained OnlyConstrainedEq eq1 = C.thisVersion $ mkSimpleVersion 1 gt0 = C.laterVersion $ C.mkVersion [0] + ge1 = C.orLaterVersion $ C.mkVersion [1] eqGt = C.intersectVersionRanges eq1 gt0 mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr eqConstraints = @@ -269,6 +270,10 @@ tests = [ mkConstraint "B" eqGt , mkConstraint "C" eqGt ] + geConstraints = + [ mkConstraint "B" ge1 + , mkConstraint "C" ge1 + ] in [ testGroup "=none" [ runTest . whenNone $ @@ -301,7 +306,7 @@ tests = [ runTest . whenAll $ mkTest db12 - "goal E missing syb" + "goal E missing syb failure" ["E"] ( solverFailure ( \m -> @@ -328,7 +333,7 @@ tests = , runTest . whenAll $ mkTest db17 - "goal A failure message" + "goal A failure" ["A"] (solverFailure . isInfixOf $ solverMsg "all") , runTest . whenAll $ @@ -349,6 +354,12 @@ tests = ) { testConstraints = eqGtConstraints } + , runTest . whenAll $ + ( mkTest db17 "goal A with B >=1, C >=1" ["A"] $ + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = geConstraints + } ] , testGroup "=eq" $ let eGoalFailure m = @@ -361,13 +372,13 @@ tests = in [ runTest . whenEq $ mkTest db12 - "goal E missing syb" + "goal E missing syb failure" ["E"] (solverFailure eGoalFailure) , runTest . whenEq $ mkTest db12 - "all goals" + "all goals failure" ["E", "syb"] (solverFailure eGoalFailure) , runTest . whenEq $ @@ -379,7 +390,7 @@ tests = , runTest . whenEq $ mkTest db17 - "goal A failure message" + "goal A failure" ["A"] (solverFailure . isInfixOf $ solverMsg "eq") , runTest . whenEq $ @@ -394,7 +405,7 @@ tests = , runTest . whenEq $ ( mkTest db17 - "goal A with B >0, C >0 failure message" + "goal A with B >0, C >0 failure" ["A"] ( solverFailure ( \m -> @@ -418,6 +429,24 @@ tests = ) { testConstraints = eqGtConstraints } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B >=1, C >=1 failure" + ["A"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + [ "next goal: C (dependency of A)" + , "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=eq was set" + ] + ) + ) + ) + { testConstraints = geConstraints + } ] ] , testGroup From 89a9ffcbedcbaeacf1877f73c17e029553fc5dae Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 09:56:22 -0500 Subject: [PATCH 11/25] Finer grained test groups; pass and rejects --- .../Distribution/Solver/Modular/Solver.hs | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index ebfa01e4bf2..1dcc29e52fc 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -304,21 +304,6 @@ tests = , testGroup "=all" [ runTest . whenAll $ - mkTest - db12 - "goal E missing syb failure" - ["E"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - [ "next goal: syb (dependency of E)" - , "not a user-provided goal nor mentioned as a constraint" - , "but reject-unconstrained-dependencies=all was set" - ] - ) - ) - , runTest . whenAll $ mkTest db12 "all goals" @@ -330,12 +315,6 @@ tests = "goal A B backtracking" ["A", "B"] (solverSuccess [("A", 2), ("B", 1)]) - , runTest . whenAll $ - mkTest - db17 - "goal A failure" - ["A"] - (solverFailure . isInfixOf $ solverMsg "all") , runTest . whenAll $ ( mkTest db17 "goal A with B ==1, C ==1" ["A"] $ (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) @@ -361,60 +340,93 @@ tests = { testConstraints = geConstraints } ] + , testGroup + "=all rejects" + [ runTest . whenAll $ + mkTest + db12 + "goal E missing syb" + ["E"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + [ "next goal: syb (dependency of E)" + , "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=all was set" + ] + ) + ) + , runTest . whenAll $ + mkTest + db17 + "goal A" + ["A"] + (solverFailure . isInfixOf $ solverMsg "all") + ] , testGroup "=eq" $ - let eGoalFailure m = + [ runTest . whenEq $ + mkTest + db17 + "goal A B backtracking" + ["A", "B"] + (solverSuccess [("A", 2), ("B", 1)]) + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B ==1, C ==1" + ["A"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = eqConstraints + } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B >0 && ==1, C >0 && ==1" + ["A"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = eqGtConstraints + } + ] + , testGroup "=eq rejects" $ + let neq = + [ "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=eq was set" + ] + eGoalFailure m = all (`isInfixOf` m) - [ "next goal: E.base (dependency of E)" - , "not a user-provided goal nor mentioned as a constraint" - , "but reject-unconstrained-dependencies=eq was set" - ] + ("next goal: E.base (dependency of E)" : neq) in [ runTest . whenEq $ mkTest db12 - "goal E missing syb failure" + "goal E missing syb" ["E"] (solverFailure eGoalFailure) , runTest . whenEq $ mkTest db12 - "all goals failure" + "all goals" ["E", "syb"] (solverFailure eGoalFailure) , runTest . whenEq $ mkTest db17 - "goal A B backtracking" - ["A", "B"] - (solverSuccess [("A", 2), ("B", 1)]) - , runTest . whenEq $ - mkTest - db17 - "goal A failure" + "goal A" ["A"] (solverFailure . isInfixOf $ solverMsg "eq") , runTest . whenEq $ ( mkTest db17 - "goal A with B ==1, C ==1" - ["A"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) - { testConstraints = eqConstraints - } - , runTest . whenEq $ - ( mkTest - db17 - "goal A with B >0, C >0 failure" + "goal A with B >0, C >0" ["A"] ( solverFailure ( \m -> all (`isInfixOf` m) - [ "next goal: C (dependency of A)" - , "not a user-provided goal nor mentioned as a constraint" - , "but reject-unconstrained-dependencies=eq was set" - ] + ("next goal: C (dependency of A)" : neq) ) ) ) @@ -423,25 +435,13 @@ tests = , runTest . whenEq $ ( mkTest db17 - "goal A with B >0 && ==1, C >0 && ==1" - ["A"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) - { testConstraints = eqGtConstraints - } - , runTest . whenEq $ - ( mkTest - db17 - "goal A with B >=1, C >=1 failure" + "goal A with B >=1, C >=1" ["A"] ( solverFailure ( \m -> all (`isInfixOf` m) - [ "next goal: C (dependency of A)" - , "not a user-provided goal nor mentioned as a constraint" - , "but reject-unconstrained-dependencies=eq was set" - ] + ("next goal: C (dependency of A)" : neq) ) ) ) From 9be9909cffc0ee24f345c58e561726e3cf7f7033 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 11:31:51 -0500 Subject: [PATCH 12/25] Add <= tests --- .../Distribution/Solver/Modular/Solver.hs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 1dcc29e52fc..1950917db79 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -256,6 +256,8 @@ tests = eq1 = C.thisVersion $ mkSimpleVersion 1 gt0 = C.laterVersion $ C.mkVersion [0] ge1 = C.orLaterVersion $ C.mkVersion [1] + -- TODO: Find out why <=1 is not the same as <=1.0.0 + le1 = C.orEarlierVersion $ C.mkVersion [1, 0, 0] eqGt = C.intersectVersionRanges eq1 gt0 mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr eqConstraints = @@ -274,6 +276,10 @@ tests = [ mkConstraint "B" ge1 , mkConstraint "C" ge1 ] + leConstraints = + [ mkConstraint "B" le1 + , mkConstraint "C" le1 + ] in [ testGroup "=none" [ runTest . whenNone $ @@ -339,6 +345,12 @@ tests = ) { testConstraints = geConstraints } + , runTest . whenAll $ + ( mkTest db17 "goal A with B <=1, C <=1" ["A"] $ + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = leConstraints + } ] , testGroup "=all rejects" @@ -447,6 +459,21 @@ tests = ) { testConstraints = geConstraints } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B <=1, C <=1" + ["A"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + ("next goal: C (dependency of A)" : neq) + ) + ) + ) + { testConstraints = leConstraints + } ] ] , testGroup From 6e0c97c82faff3f0ee0cf250aa133503f35b2acc Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 11:42:55 -0500 Subject: [PATCH 13/25] Combine >= with <= tests --- .../Distribution/Solver/Modular/Solver.hs | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 1950917db79..ceea6db54fb 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -272,12 +272,8 @@ tests = [ mkConstraint "B" eqGt , mkConstraint "C" eqGt ] - geConstraints = + geleConstraints = [ mkConstraint "B" ge1 - , mkConstraint "C" ge1 - ] - leConstraints = - [ mkConstraint "B" le1 , mkConstraint "C" le1 ] in [ testGroup @@ -340,16 +336,10 @@ tests = { testConstraints = eqGtConstraints } , runTest . whenAll $ - ( mkTest db17 "goal A with B >=1, C >=1" ["A"] $ - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) - { testConstraints = geConstraints - } - , runTest . whenAll $ - ( mkTest db17 "goal A with B <=1, C <=1" ["A"] $ + ( mkTest db17 "goal A with B >=1, C <=1" ["A"] $ (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) ) - { testConstraints = leConstraints + { testConstraints = geleConstraints } ] , testGroup @@ -447,22 +437,7 @@ tests = , runTest . whenEq $ ( mkTest db17 - "goal A with B >=1, C >=1" - ["A"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - ("next goal: C (dependency of A)" : neq) - ) - ) - ) - { testConstraints = geConstraints - } - , runTest . whenEq $ - ( mkTest - db17 - "goal A with B <=1, C <=1" + "goal A with B >=1, C <=1" ["A"] ( solverFailure ( \m -> @@ -472,7 +447,7 @@ tests = ) ) ) - { testConstraints = leConstraints + { testConstraints = geleConstraints } ] ] From b9ac5cc366f98dd8a322867dca3a095d3cac2aa2 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 11:49:09 -0500 Subject: [PATCH 14/25] Test >0 && ==1 and also ==1 && >0 --- .../UnitTests/Distribution/Solver/Modular/Solver.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index ceea6db54fb..6e39eb37177 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -258,7 +258,13 @@ tests = ge1 = C.orLaterVersion $ C.mkVersion [1] -- TODO: Find out why <=1 is not the same as <=1.0.0 le1 = C.orEarlierVersion $ C.mkVersion [1, 0, 0] + + -- ==1 && >0 eqGt = C.intersectVersionRanges eq1 gt0 + + -- ==1 && >0 + gtEq = C.intersectVersionRanges gt0 eq1 + mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr eqConstraints = [ mkConstraint "B" eq1 @@ -270,7 +276,7 @@ tests = ] eqGtConstraints = [ mkConstraint "B" eqGt - , mkConstraint "C" eqGt + , mkConstraint "C" gtEq ] geleConstraints = [ mkConstraint "B" ge1 @@ -330,7 +336,7 @@ tests = { testConstraints = gtConstraints } , runTest . whenAll $ - ( mkTest db17 "goal A with B >0 && ==1, C >0 && ==1" ["A"] $ + ( mkTest db17 "goal A with B ==1 && >0, C >0 && ==1" ["A"] $ (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) ) { testConstraints = eqGtConstraints @@ -385,7 +391,7 @@ tests = , runTest . whenEq $ ( mkTest db17 - "goal A with B >0 && ==1, C >0 && ==1" + "goal A with B ==1 && >0, C >0 && ==1" ["A"] (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) ) From c37addb2395aeeb044a30d76f8badc24adcb5e74 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 13:27:20 -0500 Subject: [PATCH 15/25] Test B <=1 && >=1, C <=1 && >=1 --- .../Distribution/Solver/Modular/Solver.hs | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 6e39eb37177..bf99abfa14f 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -265,23 +265,35 @@ tests = -- ==1 && >0 gtEq = C.intersectVersionRanges gt0 eq1 + -- <=1 && >=1 + lege1 = C.intersectVersionRanges le1 ge1 + mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr + -- B ==1, C ==1 eqConstraints = [ mkConstraint "B" eq1 , mkConstraint "C" eq1 ] + -- B >0, C >0 gtConstraints = [ mkConstraint "B" gt0 , mkConstraint "C" gt0 ] + -- B ==1 && >0, C >0 && ==1 eqGtConstraints = [ mkConstraint "B" eqGt , mkConstraint "C" gtEq ] + -- B >=1, C <=1 geleConstraints = [ mkConstraint "B" ge1 , mkConstraint "C" le1 ] + -- B <=1 && >=1, C <=1 && >=1 + legeConstraints = + [ mkConstraint "B" lege1 + , mkConstraint "C" lege1 + ] in [ testGroup "=none" [ runTest . whenNone $ @@ -347,31 +359,40 @@ tests = ) { testConstraints = geleConstraints } - ] - , testGroup - "=all rejects" - [ runTest . whenAll $ - mkTest - db12 - "goal E missing syb" - ["E"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - [ "next goal: syb (dependency of E)" - , "not a user-provided goal nor mentioned as a constraint" - , "but reject-unconstrained-dependencies=all was set" - ] - ) - ) , runTest . whenAll $ - mkTest - db17 - "goal A" - ["A"] - (solverFailure . isInfixOf $ solverMsg "all") + ( mkTest + db17 + "goal A with B <=1 && >=1, C <=1 && >=1" + ["A"] + (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + ) + { testConstraints = legeConstraints + } ] + , testGroup "=all rejects" $ + let nall = + [ "not a user-provided goal nor mentioned as a constraint" + , "but reject-unconstrained-dependencies=all was set" + ] + in [ runTest . whenAll $ + mkTest + db12 + "goal E missing syb" + ["E"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + ("next goal: syb (dependency of E)" : nall) + ) + ) + , runTest . whenAll $ + mkTest + db17 + "goal A" + ["A"] + (solverFailure . isInfixOf $ solverMsg "all") + ] , testGroup "=eq" $ [ runTest . whenEq $ mkTest @@ -455,6 +476,21 @@ tests = ) { testConstraints = geleConstraints } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B <=1 && >=1, C <=1 && >=1" + ["A"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + ("next goal: C (dependency of A)" : neq) + ) + ) + ) + { testConstraints = legeConstraints + } ] ] , testGroup From 12e9f2101779df1b52e3504875bd9abcd0c1d219 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 13:37:15 -0500 Subject: [PATCH 16/25] Reuse solutions --- .../Distribution/Solver/Modular/Solver.hs | 108 ++++-------------- 1 file changed, 21 insertions(+), 87 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index bf99abfa14f..34db7d61574 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -294,78 +294,39 @@ tests = [ mkConstraint "B" lege1 , mkConstraint "C" lege1 ] + + solveABC = solverSuccess [("A", 3), ("B", 1), ("C", 1)] + solveAB = solverSuccess [("A", 2), ("B", 1)] + solveEsyb = solverSuccess [("E", 1), ("syb", 2)] in [ testGroup "=none" - [ runTest . whenNone $ - mkTest - db12 - "goal E" - ["E"] - (solverSuccess [("E", 1), ("syb", 2)]) - , runTest . whenNone $ - mkTest - db12 - "all goals" - ["E", "syb"] - (solverSuccess [("E", 1), ("syb", 2)]) - , runTest . whenNone $ - mkTest - db17 - "goal A B backtracking" - ["A", "B"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - , runTest . whenNone $ - mkTest - db17 - "goal A" - ["A"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) + [ runTest . whenNone $ mkTest db12 "goal E" ["E"] solveEsyb + , runTest . whenNone $ mkTest db12 "all goals" ["E", "syb"] solveEsyb + , runTest . whenNone $ mkTest db17 "goal A B backtracking" ["A", "B"] solveABC + , runTest . whenNone $ mkTest db17 "goal A" ["A"] solveABC ] , testGroup "=all" - [ runTest . whenAll $ - mkTest - db12 - "all goals" - ["E", "syb"] - (solverSuccess [("E", 1), ("syb", 2)]) - , runTest . whenAll $ - mkTest - db17 - "goal A B backtracking" - ["A", "B"] - (solverSuccess [("A", 2), ("B", 1)]) + [ runTest . whenAll $ mkTest db12 "all goals" ["E", "syb"] solveEsyb + , runTest . whenAll $ mkTest db17 "goal A B backtracking" ["A", "B"] solveAB , runTest . whenAll $ - ( mkTest db17 "goal A with B ==1, C ==1" ["A"] $ - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B ==1, C ==1" ["A"] solveABC) { testConstraints = eqConstraints } , runTest . whenAll $ - ( mkTest db17 "goal A with B >0, C >0" ["A"] $ - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B >0, C >0" ["A"] solveABC) { testConstraints = gtConstraints } , runTest . whenAll $ - ( mkTest db17 "goal A with B ==1 && >0, C >0 && ==1" ["A"] $ - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B ==1 && >0, C >0 && ==1" ["A"] solveABC) { testConstraints = eqGtConstraints } , runTest . whenAll $ - ( mkTest db17 "goal A with B >=1, C <=1" ["A"] $ - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B >=1, C <=1" ["A"] solveABC) { testConstraints = geleConstraints } , runTest . whenAll $ - ( mkTest - db17 - "goal A with B <=1 && >=1, C <=1 && >=1" - ["A"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] solveABC) { testConstraints = legeConstraints } ] @@ -394,28 +355,13 @@ tests = (solverFailure . isInfixOf $ solverMsg "all") ] , testGroup "=eq" $ - [ runTest . whenEq $ - mkTest - db17 - "goal A B backtracking" - ["A", "B"] - (solverSuccess [("A", 2), ("B", 1)]) + [ runTest . whenEq $ mkTest db17 "goal A B backtracking" ["A", "B"] solveAB , runTest . whenEq $ - ( mkTest - db17 - "goal A with B ==1, C ==1" - ["A"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B ==1, C ==1" ["A"] solveABC) { testConstraints = eqConstraints } , runTest . whenEq $ - ( mkTest - db17 - "goal A with B ==1 && >0, C >0 && ==1" - ["A"] - (solverSuccess [("A", 3), ("B", 1), ("C", 1)]) - ) + (mkTest db17 "goal A with B ==1 && >0, C >0 && ==1" ["A"] solveABC) { testConstraints = eqGtConstraints } ] @@ -429,23 +375,11 @@ tests = (`isInfixOf` m) ("next goal: E.base (dependency of E)" : neq) in [ runTest . whenEq $ - mkTest - db12 - "goal E missing syb" - ["E"] - (solverFailure eGoalFailure) + mkTest db12 "goal E missing syb" ["E"] (solverFailure eGoalFailure) , runTest . whenEq $ - mkTest - db12 - "all goals" - ["E", "syb"] - (solverFailure eGoalFailure) + mkTest db12 "all goals" ["E", "syb"] (solverFailure eGoalFailure) , runTest . whenEq $ - mkTest - db17 - "goal A" - ["A"] - (solverFailure . isInfixOf $ solverMsg "eq") + mkTest db17 "goal A" ["A"] (solverFailure . isInfixOf $ solverMsg "eq") , runTest . whenEq $ ( mkTest db17 From ce82b624eb221877d414b4bf66989ce10314d4f0 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 13:44:42 -0500 Subject: [PATCH 17/25] Test >0 && ==1 && <2 --- .../Distribution/Solver/Modular/Solver.hs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 34db7d61574..c07790e79ac 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -255,6 +255,7 @@ tests = whenEq = onlyConstrained OnlyConstrainedEq eq1 = C.thisVersion $ mkSimpleVersion 1 gt0 = C.laterVersion $ C.mkVersion [0] + lt2 = C.earlierVersion $ C.mkVersion [2] ge1 = C.orLaterVersion $ C.mkVersion [1] -- TODO: Find out why <=1 is not the same as <=1.0.0 le1 = C.orEarlierVersion $ C.mkVersion [1, 0, 0] @@ -262,12 +263,15 @@ tests = -- ==1 && >0 eqGt = C.intersectVersionRanges eq1 gt0 - -- ==1 && >0 + -- >0 && ==1 gtEq = C.intersectVersionRanges gt0 eq1 -- <=1 && >=1 lege1 = C.intersectVersionRanges le1 ge1 + -- >0 && ==1 && <2 + gtEqLt = C.intersectVersionRanges gtEq lt2 + mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr -- B ==1, C ==1 eqConstraints = @@ -294,6 +298,11 @@ tests = [ mkConstraint "B" lege1 , mkConstraint "C" lege1 ] + -- B >0 && ==1 && <2, C >0 && ==1 && <2 + gtEqLtConstraints = + [ mkConstraint "B" gtEqLt + , mkConstraint "C" gtEqLt + ] solveABC = solverSuccess [("A", 3), ("B", 1), ("C", 1)] solveAB = solverSuccess [("A", 2), ("B", 1)] @@ -329,6 +338,10 @@ tests = (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] solveABC) { testConstraints = legeConstraints } + , runTest . whenAll $ + (mkTest db17 "goal A with B >0 && ==1 && <2, C >0 && ==1 && <2" ["A"] solveABC) + { testConstraints = gtEqLtConstraints + } ] , testGroup "=all rejects" $ let nall = @@ -364,6 +377,10 @@ tests = (mkTest db17 "goal A with B ==1 && >0, C >0 && ==1" ["A"] solveABC) { testConstraints = eqGtConstraints } + , runTest . whenEq $ + (mkTest db17 "goal A with B >0 && ==1 && <2, C >0 && ==1 && <2" ["A"] solveABC) + { testConstraints = gtEqLtConstraints + } ] , testGroup "=eq rejects" $ let neq = From 44d43290ba8dac1ea68124811fb1cf3e33b840b8 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 13:46:52 -0500 Subject: [PATCH 18/25] Lines between commented bindings --- .../Distribution/Solver/Modular/Solver.hs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index c07790e79ac..8dc7df2ba85 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -253,11 +253,21 @@ tests = whenNone = onlyConstrained OnlyConstrainedNone whenAll = onlyConstrained OnlyConstrainedAll whenEq = onlyConstrained OnlyConstrainedEq + + -- ==1 eq1 = C.thisVersion $ mkSimpleVersion 1 + + -- >0 gt0 = C.laterVersion $ C.mkVersion [0] + + -- <2 lt2 = C.earlierVersion $ C.mkVersion [2] + + -- >=1 ge1 = C.orLaterVersion $ C.mkVersion [1] + -- TODO: Find out why <=1 is not the same as <=1.0.0 + -- <=1 le1 = C.orEarlierVersion $ C.mkVersion [1, 0, 0] -- ==1 && >0 @@ -273,31 +283,37 @@ tests = gtEqLt = C.intersectVersionRanges gtEq lt2 mkConstraint pkg vr = ExVersionConstraint (ScopeAnyQualifier pkg) vr + -- B ==1, C ==1 eqConstraints = [ mkConstraint "B" eq1 , mkConstraint "C" eq1 ] + -- B >0, C >0 gtConstraints = [ mkConstraint "B" gt0 , mkConstraint "C" gt0 ] + -- B ==1 && >0, C >0 && ==1 eqGtConstraints = [ mkConstraint "B" eqGt , mkConstraint "C" gtEq ] + -- B >=1, C <=1 geleConstraints = [ mkConstraint "B" ge1 , mkConstraint "C" le1 ] + -- B <=1 && >=1, C <=1 && >=1 legeConstraints = [ mkConstraint "B" lege1 , mkConstraint "C" lege1 ] + -- B >0 && ==1 && <2, C >0 && ==1 && <2 gtEqLtConstraints = [ mkConstraint "B" gtEqLt From 55c1dfae9dff588f2280f86c23eb3ac2f4dd7e59 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 13:53:23 -0500 Subject: [PATCH 19/25] Add test for B ==1 || >0, C >0 || ==1 --- .../Distribution/Solver/Modular/Solver.hs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 8dc7df2ba85..66c68878dcd 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -273,9 +273,15 @@ tests = -- ==1 && >0 eqGt = C.intersectVersionRanges eq1 gt0 + -- ==1 | >0 + eqOrGt = C.unionVersionRanges eq1 gt0 + -- >0 && ==1 gtEq = C.intersectVersionRanges gt0 eq1 + -- >0 || ==1 + gtOrEq = C.unionVersionRanges gt0 eq1 + -- <=1 && >=1 lege1 = C.intersectVersionRanges le1 ge1 @@ -302,6 +308,12 @@ tests = , mkConstraint "C" gtEq ] + -- B ==1 || >0, C >0 || ==1 + eqOrGtConstraints = + [ mkConstraint "B" eqOrGt + , mkConstraint "C" gtOrEq + ] + -- B >=1, C <=1 geleConstraints = [ mkConstraint "B" ge1 @@ -358,6 +370,10 @@ tests = (mkTest db17 "goal A with B >0 && ==1 && <2, C >0 && ==1 && <2" ["A"] solveABC) { testConstraints = gtEqLtConstraints } + , runTest . whenAll $ + (mkTest db17 "goal A with B ==1 || >0, C >0 || ==1" ["A"] solveABC) + { testConstraints = eqOrGtConstraints + } ] , testGroup "=all rejects" $ let nall = @@ -458,6 +474,21 @@ tests = ) { testConstraints = legeConstraints } + , runTest . whenEq $ + ( mkTest + db17 + "goal A with B ==1 || >0, C >0 || ==1" + ["A"] + ( solverFailure + ( \m -> + all + (`isInfixOf` m) + ("next goal: C (dependency of A)" : neq) + ) + ) + ) + { testConstraints = eqOrGtConstraints + } ] ] , testGroup From 6da8c5ca3c8bd1a0db6eb481f645ba6a4236e983 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 10 Feb 2026 13:56:16 -0500 Subject: [PATCH 20/25] Add eqFailure --- .../Distribution/Solver/Modular/Solver.hs | 59 ++++--------------- 1 file changed, 11 insertions(+), 48 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 66c68878dcd..ce9e6f04dc2 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -423,6 +423,13 @@ tests = all (`isInfixOf` m) ("next goal: E.base (dependency of E)" : neq) + eqFailure = + solverFailure + ( \m -> + all + (`isInfixOf` m) + ("next goal: C (dependency of A)" : neq) + ) in [ runTest . whenEq $ mkTest db12 "goal E missing syb" ["E"] (solverFailure eGoalFailure) , runTest . whenEq $ @@ -430,63 +437,19 @@ tests = , runTest . whenEq $ mkTest db17 "goal A" ["A"] (solverFailure . isInfixOf $ solverMsg "eq") , runTest . whenEq $ - ( mkTest - db17 - "goal A with B >0, C >0" - ["A"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - ("next goal: C (dependency of A)" : neq) - ) - ) - ) + (mkTest db17 "goal A with B >0, C >0" ["A"] eqFailure) { testConstraints = gtConstraints } , runTest . whenEq $ - ( mkTest - db17 - "goal A with B >=1, C <=1" - ["A"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - ("next goal: C (dependency of A)" : neq) - ) - ) - ) + (mkTest db17 "goal A with B >=1, C <=1" ["A"] eqFailure) { testConstraints = geleConstraints } , runTest . whenEq $ - ( mkTest - db17 - "goal A with B <=1 && >=1, C <=1 && >=1" - ["A"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - ("next goal: C (dependency of A)" : neq) - ) - ) - ) + (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] eqFailure) { testConstraints = legeConstraints } , runTest . whenEq $ - ( mkTest - db17 - "goal A with B ==1 || >0, C >0 || ==1" - ["A"] - ( solverFailure - ( \m -> - all - (`isInfixOf` m) - ("next goal: C (dependency of A)" : neq) - ) - ) - ) + (mkTest db17 "goal A with B ==1 || >0, C >0 || ==1" ["A"] eqFailure) { testConstraints = eqOrGtConstraints } ] From 9454ebba8a0918c8421d50bb4781ae6871ae55c6 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 11 Feb 2026 08:49:20 -0500 Subject: [PATCH 21/25] Add test for ^>=1 constraint --- .../Distribution/Solver/Modular/Solver.hs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index ce9e6f04dc2..115e6100b9f 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -266,6 +266,9 @@ tests = -- >=1 ge1 = C.orLaterVersion $ C.mkVersion [1] + -- \^>=1 + ce1 = C.majorBoundVersion $ mkSimpleVersion 1 + -- TODO: Find out why <=1 is not the same as <=1.0.0 -- <=1 le1 = C.orEarlierVersion $ C.mkVersion [1, 0, 0] @@ -320,6 +323,12 @@ tests = , mkConstraint "C" le1 ] + -- B ^>=1, C <=1 + celeConstraints = + [ mkConstraint "B" ce1 + , mkConstraint "C" ce1 + ] + -- B <=1 && >=1, C <=1 && >=1 legeConstraints = [ mkConstraint "B" lege1 @@ -362,6 +371,10 @@ tests = (mkTest db17 "goal A with B >=1, C <=1" ["A"] solveABC) { testConstraints = geleConstraints } + , runTest . whenAll $ + (mkTest db17 "goal A with B ^>=1, C ^>=1" ["A"] solveABC) + { testConstraints = celeConstraints + } , runTest . whenAll $ (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] solveABC) { testConstraints = legeConstraints @@ -444,6 +457,10 @@ tests = (mkTest db17 "goal A with B >=1, C <=1" ["A"] eqFailure) { testConstraints = geleConstraints } + , runTest . whenEq $ + (mkTest db17 "goal A with B ^>=1, C ^>=1" ["A"] eqFailure) + { testConstraints = celeConstraints + } , runTest . whenEq $ (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] eqFailure) { testConstraints = legeConstraints From 9e3fcf95d4d4a2457d1badf50fa343c73c859609 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 11 Feb 2026 08:51:31 -0500 Subject: [PATCH 22/25] Change s/all goals/goal all/ --- .../tests/UnitTests/Distribution/Solver/Modular/Solver.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 115e6100b9f..44e38e31b87 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -347,13 +347,13 @@ tests = in [ testGroup "=none" [ runTest . whenNone $ mkTest db12 "goal E" ["E"] solveEsyb - , runTest . whenNone $ mkTest db12 "all goals" ["E", "syb"] solveEsyb + , runTest . whenNone $ mkTest db12 "goal all" ["E", "syb"] solveEsyb , runTest . whenNone $ mkTest db17 "goal A B backtracking" ["A", "B"] solveABC , runTest . whenNone $ mkTest db17 "goal A" ["A"] solveABC ] , testGroup "=all" - [ runTest . whenAll $ mkTest db12 "all goals" ["E", "syb"] solveEsyb + [ runTest . whenAll $ mkTest db12 "goal all" ["E", "syb"] solveEsyb , runTest . whenAll $ mkTest db17 "goal A B backtracking" ["A", "B"] solveAB , runTest . whenAll $ (mkTest db17 "goal A with B ==1, C ==1" ["A"] solveABC) @@ -446,7 +446,7 @@ tests = in [ runTest . whenEq $ mkTest db12 "goal E missing syb" ["E"] (solverFailure eGoalFailure) , runTest . whenEq $ - mkTest db12 "all goals" ["E", "syb"] (solverFailure eGoalFailure) + mkTest db12 "goal all" ["E", "syb"] (solverFailure eGoalFailure) , runTest . whenEq $ mkTest db17 "goal A" ["A"] (solverFailure . isInfixOf $ solverMsg "eq") , runTest . whenEq $ From 7e29815aa2fe14342e292104f29f651b3bc6a2a6 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 11 Feb 2026 09:26:28 -0500 Subject: [PATCH 23/25] Add test for ==1 || ==2 --- .../Distribution/Solver/Modular/Solver.hs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 44e38e31b87..1e341123f22 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -254,8 +254,9 @@ tests = whenAll = onlyConstrained OnlyConstrainedAll whenEq = onlyConstrained OnlyConstrainedEq - -- ==1 + -- ==1, ==2 eq1 = C.thisVersion $ mkSimpleVersion 1 + eq2 = C.thisVersion $ mkSimpleVersion 2 -- >0 gt0 = C.laterVersion $ C.mkVersion [0] @@ -276,7 +277,7 @@ tests = -- ==1 && >0 eqGt = C.intersectVersionRanges eq1 gt0 - -- ==1 | >0 + -- ==1 || >0 eqOrGt = C.unionVersionRanges eq1 gt0 -- >0 && ==1 @@ -285,6 +286,9 @@ tests = -- >0 || ==1 gtOrEq = C.unionVersionRanges gt0 eq1 + -- ==1 || ==2 + eqOrEq = C.unionVersionRanges eq1 eq2 + -- <=1 && >=1 lege1 = C.intersectVersionRanges le1 ge1 @@ -329,6 +333,12 @@ tests = , mkConstraint "C" ce1 ] + -- B ==1 || ==2, C ==1 || ==2 + eqOrEqConstraints = + [ mkConstraint "B" eqOrEq + , mkConstraint "C" eqOrEq + ] + -- B <=1 && >=1, C <=1 && >=1 legeConstraints = [ mkConstraint "B" lege1 @@ -379,6 +389,10 @@ tests = (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] solveABC) { testConstraints = legeConstraints } + , runTest . whenAll $ + (mkTest db17 "goal A with B ==1 || ==2, C ==1 || ==2" ["A"] solveABC) + { testConstraints = eqOrEqConstraints + } , runTest . whenAll $ (mkTest db17 "goal A with B >0 && ==1 && <2, C >0 && ==1 && <2" ["A"] solveABC) { testConstraints = gtEqLtConstraints @@ -461,6 +475,10 @@ tests = (mkTest db17 "goal A with B ^>=1, C ^>=1" ["A"] eqFailure) { testConstraints = celeConstraints } + , runTest . whenEq $ + (mkTest db17 "goal A with B ==1 || ==2, C ==1 || ==2" ["A"] eqFailure) + { testConstraints = eqOrEqConstraints + } , runTest . whenEq $ (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] eqFailure) { testConstraints = legeConstraints From 40304a1b82e62c9daecdc9fd42756b34b746b0e7 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 11 Feb 2026 09:38:35 -0500 Subject: [PATCH 24/25] Add test for ==1 || >1 and ==1 || <1 --- .../Distribution/Solver/Modular/Solver.hs | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 1e341123f22..293a4a16697 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -258,15 +258,21 @@ tests = eq1 = C.thisVersion $ mkSimpleVersion 1 eq2 = C.thisVersion $ mkSimpleVersion 2 - -- >0 + -- >0, >1 gt0 = C.laterVersion $ C.mkVersion [0] + gt1 = C.laterVersion $ C.mkVersion [1] - -- <2 + -- <1, <2 + lt1 = C.earlierVersion $ C.mkVersion [2] lt2 = C.earlierVersion $ C.mkVersion [2] -- >=1 ge1 = C.orLaterVersion $ C.mkVersion [1] + -- ==1 || >1, ==1 || <1 + eqOrGt1 = C.unionVersionRanges eq1 gt1 + eqOrLt1 = C.unionVersionRanges eq1 lt1 + -- \^>=1 ce1 = C.majorBoundVersion $ mkSimpleVersion 1 @@ -278,13 +284,13 @@ tests = eqGt = C.intersectVersionRanges eq1 gt0 -- ==1 || >0 - eqOrGt = C.unionVersionRanges eq1 gt0 + eq1OrGt0 = C.unionVersionRanges eq1 gt0 -- >0 && ==1 gtEq = C.intersectVersionRanges gt0 eq1 -- >0 || ==1 - gtOrEq = C.unionVersionRanges gt0 eq1 + gt0OrEq1 = C.unionVersionRanges gt0 eq1 -- ==1 || ==2 eqOrEq = C.unionVersionRanges eq1 eq2 @@ -317,8 +323,8 @@ tests = -- B ==1 || >0, C >0 || ==1 eqOrGtConstraints = - [ mkConstraint "B" eqOrGt - , mkConstraint "C" gtOrEq + [ mkConstraint "B" eq1OrGt0 + , mkConstraint "C" gt0OrEq1 ] -- B >=1, C <=1 @@ -333,6 +339,12 @@ tests = , mkConstraint "C" ce1 ] + -- B ==1 || >1, C ==1 || <1 + eqOrGt1Constraints = + [ mkConstraint "B" eqOrGt1 + , mkConstraint "C" eqOrLt1 + ] + -- B ==1 || ==2, C ==1 || ==2 eqOrEqConstraints = [ mkConstraint "B" eqOrEq @@ -389,6 +401,10 @@ tests = (mkTest db17 "goal A with B <=1 && >=1, C <=1 && >=1" ["A"] solveABC) { testConstraints = legeConstraints } + , runTest . whenAll $ + (mkTest db17 "goal A with B ==1 || >1, C ==1 || <1" ["A"] solveABC) + { testConstraints = eqOrGt1Constraints + } , runTest . whenAll $ (mkTest db17 "goal A with B ==1 || ==2, C ==1 || ==2" ["A"] solveABC) { testConstraints = eqOrEqConstraints @@ -475,6 +491,10 @@ tests = (mkTest db17 "goal A with B ^>=1, C ^>=1" ["A"] eqFailure) { testConstraints = celeConstraints } + , runTest . whenEq $ + (mkTest db17 "goal A with B ==1 || >1, C ==1 || <1" ["A"] eqFailure) + { testConstraints = eqOrGt1Constraints + } , runTest . whenEq $ (mkTest db17 "goal A with B ==1 || ==2, C ==1 || ==2" ["A"] eqFailure) { testConstraints = eqOrEqConstraints From 3e4a2ef9f36c7d955ae45069e7ffb3a366793626 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 11 Feb 2026 09:52:11 -0500 Subject: [PATCH 25/25] Add VersionEqualitiesInPackages test --- .../VersionEqualitiesInPackages/a/a.cabal | 8 +++++ .../VersionEqualitiesInPackages/b/b.cabal | 10 +++++++ .../VersionEqualitiesInPackages/cabal.out | 29 +++++++++++++++++++ .../VersionEqualitiesInPackages/cabal.project | 8 +++++ .../VersionEqualitiesInPackages/cabal.test.hs | 17 +++++++++++ .../repo/other-lib-1.0/other-lib.cabal | 7 +++++ .../repo/some-exe-1.0/some-exe.cabal | 6 ++++ .../repo/some-lib-1.0/some-lib.cabal | 7 +++++ 8 files changed, 92 insertions(+) create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/a/a.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/b/b.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.out create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.project create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/other-lib-1.0/other-lib.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-exe-1.0/some-exe.cabal create mode 100644 cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-lib-1.0/some-lib.cabal diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/a/a.cabal b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/a/a.cabal new file mode 100644 index 00000000000..1619977533b --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/a/a.cabal @@ -0,0 +1,8 @@ +cabal-version: 2.2 +name: a +version: 0 + +library + build-depends: + some-lib ==1.0, + b ==0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/b/b.cabal b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/b/b.cabal new file mode 100644 index 00000000000..73e6daaf543 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/b/b.cabal @@ -0,0 +1,10 @@ +cabal-version: 2.2 +name: b +version: 0 + +library + build-tool-depends: + some-exe:some-exe ==1.0 + build-depends: + some-lib ==1.0, + other-lib ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.out b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.out new file mode 100644 index 00000000000..35275627893 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.out @@ -0,0 +1,29 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following would be built: + - other-lib-1.0 (lib) (requires build) + - some-exe-1.0 (exe:some-exe) (requires build) + - some-lib-1.0 (lib) (requires build) + - b-0 (lib) (first run) + - a-0 (lib) (first run) diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.project b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.project new file mode 100644 index 00000000000..3a6a6dc5014 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.project @@ -0,0 +1,8 @@ +packages: + ./a + ./b + +constraints: + some-lib ==1.0 + , other-lib ==1.0 + , some-exe ==1.0 diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.test.hs b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.test.hs new file mode 100644 index 00000000000..474ee805a73 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/cabal.test.hs @@ -0,0 +1,17 @@ +import Test.Cabal.Prelude + + +main = do + skipIfOSX "macOS has a different solver output format" + -- - - other-lib-1.0 (lib) (requires build) + -- - - some-exe-1.0 (exe:some-exe) (requires build) + -- - some-lib-1.0 (lib) (requires build) + -- + - some-exe-1.0 (exe:some-exe) (requires build) + -- + - other-lib-1.0 (lib) (requires build) + -- - b-0 (lib) (first run) + -- - a-0 (lib) (first run) + cabalTest . withRepo "repo" $ do + let opts = [ "all", "--dry-run" ] + cabal "build" $ opts + cabal "build" $ "--reject-unconstrained-dependencies=all" : opts + cabal "build" $ "--reject-unconstrained-dependencies=eq" : opts diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/other-lib-1.0/other-lib.cabal b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/other-lib-1.0/other-lib.cabal new file mode 100644 index 00000000000..f0d38525d02 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/other-lib-1.0/other-lib.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.2 +name: other-lib +version: 1.0 + +library + exposed-modules: + Foo diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-exe-1.0/some-exe.cabal b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-exe-1.0/some-exe.cabal new file mode 100644 index 00000000000..93f0fbb5b6d --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-exe-1.0/some-exe.cabal @@ -0,0 +1,6 @@ +cabal-version: 2.2 +name: some-exe +version: 1.0 + +executable some-exe + main-is: Foo.hs diff --git a/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-lib-1.0/some-lib.cabal b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-lib-1.0/some-lib.cabal new file mode 100644 index 00000000000..99958826d9e --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/VersionEqualitiesInPackages/repo/some-lib-1.0/some-lib.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.2 +name: some-lib +version: 1.0 + +library + exposed-modules: + Foo