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 d16fb37af37..49a99627f1f 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 @@ -19,10 +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.Solver.Types.Settings import Distribution.Solver.Types.Variable @@ -139,18 +143,15 @@ 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 - OnlyConstrainedAll -> - P.onlyConstrained pkgIsExplicit - OnlyConstrainedNone -> - id) + (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) + 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 +168,15 @@ 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 + normalise = fromVersionIntervals . toVersionIntervals + isThisVersion lpc + | LabeledPackageConstraint (PackageConstraint _ (PackagePropertyVersion vr)) _ <- lpc + , ThisVersionF _ <- projectVersionRange $ normalise 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/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-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-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs index 3fe0eb6a339..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 @@ -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..293a4a16697 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") @@ -16,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 (..) @@ -23,12 +26,14 @@ 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 +import UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils hiding (testMinimizeConflictSet) tests :: [TF.TestTree] tests = @@ -234,32 +239,276 @@ 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 + + -- ==1, ==2 + eq1 = C.thisVersion $ mkSimpleVersion 1 + eq2 = C.thisVersion $ mkSimpleVersion 2 + + -- >0, >1 + gt0 = C.laterVersion $ C.mkVersion [0] + gt1 = C.laterVersion $ C.mkVersion [1] + + -- <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 + + -- TODO: Find out why <=1 is not the same as <=1.0.0 + -- <=1 + le1 = C.orEarlierVersion $ C.mkVersion [1, 0, 0] + + -- ==1 && >0 + eqGt = C.intersectVersionRanges eq1 gt0 + + -- ==1 || >0 + eq1OrGt0 = C.unionVersionRanges eq1 gt0 + + -- >0 && ==1 + gtEq = C.intersectVersionRanges gt0 eq1 + + -- >0 || ==1 + gt0OrEq1 = C.unionVersionRanges gt0 eq1 + + -- ==1 || ==2 + eqOrEq = C.unionVersionRanges eq1 eq2 + + -- <=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 = + [ 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 || >0, C >0 || ==1 + eqOrGtConstraints = + [ mkConstraint "B" eq1OrGt0 + , mkConstraint "C" gt0OrEq1 + ] + + -- B >=1, C <=1 + geleConstraints = + [ mkConstraint "B" ge1 + , mkConstraint "C" le1 + ] + + -- B ^>=1, C <=1 + celeConstraints = + [ mkConstraint "B" ce1 + , 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 + , mkConstraint "C" eqOrEq + ] + + -- 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 + , mkConstraint "C" gtEqLt + ] + + 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"] 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 "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) + { testConstraints = eqConstraints + } + , runTest . whenAll $ + (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"] solveABC) + { testConstraints = eqGtConstraints + } + , runTest . whenAll $ + (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 + } + , 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 + } + , runTest . whenAll $ + (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 = + [ "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 db17 "goal A B backtracking" ["A", "B"] solveAB + , runTest . whenEq $ + (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"] 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 = + [ "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)" : 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 $ + mkTest db12 "goal all" ["E", "syb"] (solverFailure eGoalFailure) + , runTest . whenEq $ + mkTest db17 "goal A" ["A"] (solverFailure . isInfixOf $ solverMsg "eq") + , runTest . whenEq $ + (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"] 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 = eqOrGt1Constraints + } + , 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 + } + , runTest . whenEq $ + (mkTest db17 "goal A with B ==1 || >0, C >0 || ==1" ["A"] eqFailure) + { testConstraints = eqOrGtConstraints + } + ] + ] , testGroup "Cycles" [ runTest $ mkTest db14 "simpleCycle1" ["A"] anySolverFailure 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..79c7e4d3b6e --- /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=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: 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=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 +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..2fb70f302d6 --- /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=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 271bddebf0b..3328aaac141 100644 --- a/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkg/cabal.test.hs @@ -1,16 +1,76 @@ 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 x = assertOutputContains ("not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies=" ++ x ++ " was set") + +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" + , "--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 "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 "all" 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 "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..4b07df86756 --- /dev/null +++ b/cabal-testsuite/PackageTests/RequireExplicit/MultiPkgInProject/cabal.test.hs @@ -0,0 +1,30 @@ +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 + 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) + 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 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 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). 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: