Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions Cabal-syntax/src/Distribution/Version.hs
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,25 @@ import Distribution.Types.VersionRange
-- | This is the converse of 'isAnyVersion'. It check if the version range is
-- empty, if there is no possible version that satisfies the version range.
--
-- For example this is @True@ (for all @v@):
-- For example, @\<v && >v@ is no version for all @v@.
--
-- > isNoVersion (EarlierVersion v `IntersectVersionRanges` LaterVersion v)
-- >>> ordNub [isNoVersion (earlierVersion v `intersectVersionRanges` laterVersion v) | v <- mkVersion <$> [[0],[1]]]
-- [True]
--
-- >>> isNoVersion <$> [noVersion, anyVersion]
-- [True,False]
isNoVersion :: VersionRange -> Bool
isNoVersion vr = case asVersionIntervals vr of
[] -> True
_ -> False

-- | Is this version range in fact just a specific version?
--
-- For example the version range @\">= 3 && <= 3\"@ contains only the version
-- For example the version range @\>= 3 && <= 3@ contains only the version
-- @3@.
--
-- >>> isSpecificVersion (orLaterVersion (mkVersion [3]) `intersectVersionRanges` orEarlierVersion (mkVersion [3]))
-- Just (mkVersion [3])
isSpecificVersion :: VersionRange -> Maybe Version
isSpecificVersion vr = case asVersionIntervals vr of
[VersionInterval (LowerBound v InclusiveBound) (UpperBound v' InclusiveBound)]
Expand Down Expand Up @@ -222,5 +229,6 @@ transformCaretLower = hyloVersionRange embed projectVersionRange
-- >>> :set -XScopedTypeVariables
-- >>> import Distribution.Parsec
-- >>> import Distribution.Pretty
-- >>> import Distribution.Utils.Generic (ordNub)
-- >>>
-- >>> mapVR f xs = [pretty $ f v| Just v <- simpleParsec <$> xs]
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,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 = " (not a user-provided goal nor mentioned as a constraint when reject-unconstrained-dependencies=all)"
showFR _ Shadowed = " (shadowed by another installed package with same version)"
showFR _ (Broken u) = " (package is broken, missing dependency " ++ prettyShow u ++ ")"
showFR _ UnknownPackage = " (unknown package)"
Expand Down
30 changes: 25 additions & 5 deletions cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
#ifdef DEBUG_TRACETREE
{-# OPTIONS_GHC -Wno-orphans #-}
#endif
Expand All @@ -18,6 +19,7 @@ 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)
Expand All @@ -41,6 +43,7 @@ import Distribution.Solver.Modular.PSQ (PSQ)
import Distribution.Solver.Modular.RetryLog
import Distribution.Solver.Modular.Tree
import qualified Distribution.Solver.Modular.PSQ as PSQ
import Distribution.Solver.Types.PackageConstraint

import Distribution.Simple.Setup (BooleanFlag(..))

Expand Down Expand Up @@ -140,15 +143,13 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
prunePhase = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) .
(case onlyConstrained sc of
OnlyConstrainedAll ->
P.onlyConstrained pkgIsExplicit
P.onlyConstrained (`S.member` versionConstrainedOrGoals)
OnlyConstrainedNone ->
id)
buildPhase = buildTree idx (independentGoals sc) (S.toList userGoals)

allExplicit = M.keysSet userConstraints `S.union` userGoals

pkgIsExplicit :: PN -> Bool
pkgIsExplicit pn = S.member pn allExplicit
versionConstrained = filterVersion isVersionConstrained userConstraints
versionConstrainedOrGoals = versionConstrained `S.union` userGoals

-- When --reorder-goals is set, we use preferReallyEasyGoalChoices, which
-- prefers (keeps) goals only if the have 0 or 1 enabled choice.
Expand All @@ -166,6 +167,25 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
| asBool (reorderGoals sc) = P.preferReallyEasyGoalChoices
| otherwise = id {- P.firstGoal -}

-- | Keep package names of constraints that satisfy the predicate.
filterVersion :: (LabeledPackageConstraint -> Bool) -> M.Map PN [LabeledPackageConstraint] -> Set PN
filterVersion versionFilter = M.keysSet . M.filter (not . null) . M.map (filter versionFilter)

normalise :: VersionRange -> VersionRange
normalise = fromVersionIntervals . toVersionIntervals

-- | Unconstrained with a version range @>=0@ or @<0@ or their flag equivalents
-- and constrained by other versions ranges.
--
-- Both the @-any@ and @-none@ flags are considered unconstrained, because they
-- don't actually constrain the version of the package. The @-any@ flag allows
-- any version, and the @-none@ flag effectively excludes a package.
isVersionConstrained :: LabeledPackageConstraint -> Bool
isVersionConstrained (LabeledPackageConstraint (PackageConstraint _ c) _) = case c of
PackagePropertyVersion (normalise -> vr) -> not (isAnyVersion vr || isNoVersion vr)
-- `PackagePropertyFlags` @-any@ and @-none@ are covered below.
_ -> False

-- | Dump solver tree to a file (in debugging mode)
--
-- This only does something if the @debug-tracetree@ configure argument was
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ tests =
"Could not resolve dependencies:\n"
++ "[__0] trying: A-3 (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 (not a user-provided goal nor mentioned as a constraint"
++ " when reject-unconstrained-dependencies=all)\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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cabal-version: 2.2
name: a
version: 0

library
build-depends:
some-lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# tar
# tar
# tar
# cabal v2-update
Downloading the latest package list from test-local-repo
# cabal v2-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 when reject-unconstrained-dependencies=all)
[__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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# tar
# tar
# tar
# cabal v2-update
Downloading the latest package list from test-local-repo
# cabal v2-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 when reject-unconstrained-dependencies=all)
[__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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# tar
# tar
# tar
# cabal v2-update
Downloading the latest package list from test-local-repo
# cabal v2-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 when reject-unconstrained-dependencies=all)
[__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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# tar
# tar
# tar
# cabal v2-update
Downloading the latest package list from test-local-repo
# cabal v2-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 when reject-unconstrained-dependencies=all)
[__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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# tar
# tar
# tar
# cabal v2-update
Downloading the latest package list from test-local-repo
# cabal v2-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 when reject-unconstrained-dependencies=all)
[__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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
packages: .
constraints: some-lib +some-flag
reject-unconstrained-dependencies: all
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Test.Cabal.Prelude
main = do
cabalTest . recordMode RecordMarked $ withRepo "repo" $ do
res <- fails $ cabal' "v2-build" ["all", "--dry-run"]
assertOutputContains "not a user-provided goal" res

-- The following all check version ranges that don't have a version
-- constrained dependency one way or another.

cabalTest' "any-flag" . recordMode RecordMarked $ withRepo "repo" $ do
res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--constraint", "some-lib -any"]
assertOutputContains "not a user-provided goal" res

cabalTest' "any-version" . recordMode RecordMarked $ withRepo "repo" $ do
res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--constraint", "some-lib >=0"]
assertOutputContains "not a user-provided goal" res

cabalTest' "none-flag" . recordMode RecordMarked $ withRepo "repo" $ do
res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--constraint", "some-lib -none"]
assertOutputContains "not a user-provided goal" res

cabalTest' "none-version" . recordMode RecordMarked $ withRepo "repo" $ do
res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--constraint", "some-lib <0"]
assertOutputContains "not a user-provided goal" res
11 changes: 11 additions & 0 deletions changelog.d/12191.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
synopsis: Reject dependencies with unconstrained versions
packages: [cabal-install]
prs: 12191
issues: 12190
---

Change the behaviour of `--reject-unconstrained-dependencies=all` so that it is
not satisfied by flag constraints. Only version constraints should satisfy this
check and even then these need to reject at least some versions so `>=0` or
`-any` and `<0` or `-none` as version constraint will also fail the check.
Loading