Skip to content

Commit 7c50909

Browse files
committed
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
1 parent 6cc9c63 commit 7c50909

7 files changed

Lines changed: 156 additions & 26 deletions

File tree

cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import Distribution.Solver.Types.PackagePath
2323
import Distribution.Solver.Types.PackagePreferences
2424
import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb)
2525
import Distribution.Solver.Types.LabeledPackageConstraint
26+
import Distribution.Solver.Types.PackageConstraint (PackageConstraint(..), PackageProperty(..))
27+
import Distribution.Types.VersionRange (normaliseVersionRange, projectVersionRange, VersionRangeF(ThisVersionF))
2628
import Distribution.Solver.Types.Settings
2729
import Distribution.Solver.Types.Variable
2830

@@ -140,17 +142,14 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
140142
validateTree cinfo idx pkgConfigDB
141143
prunePhase = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) .
142144
(case onlyConstrained sc of
143-
OnlyConstrainedAll ->
144-
P.onlyConstrained pkgIsExplicit
145-
OnlyConstrainedNone ->
146-
id)
145+
OnlyConstrainedEq -> P.onlyConstrained (`S.member` allExplicitEq)
146+
OnlyConstrainedAll -> P.onlyConstrained (`S.member` allExplicit)
147+
OnlyConstrainedNone -> id)
147148
buildPhase = buildTree idx (independentGoals sc) (S.toList userGoals)
148149

150+
allExplicitEq = M.keysSet (filterThisVersion userConstraints) `S.union` userGoals
149151
allExplicit = M.keysSet userConstraints `S.union` userGoals
150152

151-
pkgIsExplicit :: PN -> Bool
152-
pkgIsExplicit pn = S.member pn allExplicit
153-
154153
-- When --reorder-goals is set, we use preferReallyEasyGoalChoices, which
155154
-- prefers (keeps) goals only if the have 0 or 1 enabled choice.
156155
--
@@ -167,6 +166,14 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
167166
| asBool (reorderGoals sc) = P.preferReallyEasyGoalChoices
168167
| otherwise = id {- P.firstGoal -}
169168

169+
-- | Keep version ranges that normalise to equality version constraints (== v).
170+
filterThisVersion :: M.Map PN [LabeledPackageConstraint] -> M.Map PN [LabeledPackageConstraint]
171+
filterThisVersion = M.filter (not . null) . M.map (filter isThisVersion) where
172+
isThisVersion lpc
173+
| LabeledPackageConstraint (PackageConstraint _ (PackagePropertyVersion vr)) _ <- lpc
174+
, ThisVersionF _ <- projectVersionRange $ normaliseVersionRange vr = True
175+
| otherwise = False
176+
170177
-- | Dump solver tree to a file (in debugging mode)
171178
--
172179
-- This only does something if the @debug-tracetree@ configure argument was

cabal-install-solver/src/Distribution/Solver/Types/Settings.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ newtype AllowBootLibInstalls = AllowBootLibInstalls Bool
6161
data OnlyConstrained
6262
= OnlyConstrainedNone
6363
| OnlyConstrainedAll
64+
| OnlyConstrainedEq
6465
deriving (Eq, Generic, Show)
6566

6667
newtype EnableBackjumping = EnableBackjumping Bool
@@ -108,12 +109,14 @@ instance NFData AllowBootLibInstalls
108109
instance NFData OnlyConstrained
109110

110111
instance Pretty OnlyConstrained where
112+
pretty OnlyConstrainedEq = PP.text "eq"
111113
pretty OnlyConstrainedAll = PP.text "all"
112114
pretty OnlyConstrainedNone = PP.text "none"
113115

114116
instance Parsec OnlyConstrained where
115117
parsec = P.choice
116-
[ P.string "all" >> return OnlyConstrainedAll
118+
[ P.string "eq" >> return OnlyConstrainedEq
119+
, P.string "all" >> return OnlyConstrainedAll
117120
, P.string "none" >> return OnlyConstrainedNone
118121
]
119122

cabal-install/src/Distribution/Client/Setup.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3692,9 +3692,9 @@ optionSolverFlags
36923692
getoc
36933693
setoc
36943694
( reqArg
3695-
"none|all"
3695+
"none|all|eq"
36963696
( parsecToReadE
3697-
(const "reject-unconstrained-dependencies must be 'none' or 'all'")
3697+
(const "reject-unconstrained-dependencies must be 'none', 'all', or 'eq'")
36983698
(toFlag `fmap` parsec)
36993699
)
37003700
(flagToList . fmap prettyShow)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# cabal v2-update
2+
Downloading the latest package list from test-local-repo
3+
# cabal build
4+
Resolving dependencies...
5+
Error: [Cabal-7107]
6+
Could not resolve dependencies:
7+
[__0] trying: b-0 (user goal)
8+
[__1] next goal: other-lib (dependency of b)
9+
[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set)
10+
[__1] fail (backjumping, conflict set: b, other-lib)
11+
After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: b (2), other-lib (1)
12+
# cabal build
13+
Resolving dependencies...
14+
Error: [Cabal-7107]
15+
Could not resolve dependencies:
16+
[__0] trying: b-0 (user goal)
17+
[__1] next goal: b:some-exe:exe.some-exe (dependency of b)
18+
[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set)
19+
[__1] fail (backjumping, conflict set: b, b:some-exe:exe.some-exe)
20+
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)
21+
# cabal build
22+
Resolving dependencies...
23+
Build profile: -w ghc-<GHCVER> -O1
24+
In order, the following would be built:
25+
- other-lib-1.0 (lib) (requires build)
26+
- some-exe-1.0 (exe:some-exe) (requires build)
27+
- some-lib-1.0 (lib) (requires build)
28+
- b-0 (lib) (first run)
29+
- a-0 (lib) (first run)
30+
# cabal build
31+
Build profile: -w ghc-<GHCVER> -O1
32+
In order, the following would be built:
33+
- other-lib-1.0 (lib) (requires build)
34+
- some-exe-1.0 (exe:some-exe) (requires build)
35+
- some-lib-1.0 (lib) (requires build)
36+
- b-0 (lib) (first run)
37+
- a-0 (lib) (first run)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# cabal v2-update
2+
Downloading the latest package list from test-local-repo
3+
# cabal build
4+
Resolving dependencies...
5+
Build profile: -w ghc-<GHCVER> -O1
6+
In order, the following would be built:
7+
- other-lib-1.0 (lib) (requires build)
8+
- some-exe-1.0 (exe:some-exe) (requires build)
9+
- some-lib-1.0 (lib) (requires build)
10+
- b-0 (lib) (first run)
11+
- a-0 (lib) (first run)
12+
# cabal build
13+
Resolving dependencies...
14+
Build profile: -w ghc-<GHCVER> -O1
15+
In order, the following would be built:
16+
- other-lib-1.0 (lib) (requires build)
17+
- some-exe-1.0 (exe:some-exe) (requires build)
18+
- some-lib-1.0 (lib) (requires build)
19+
- b-0 (lib) (first run)
20+
- a-0 (lib) (first run)
21+
# cabal build
22+
Resolving dependencies...
23+
Error: [Cabal-7107]
24+
Could not resolve dependencies:
25+
[__0] trying: a-0 (user goal)
26+
[__1] next goal: some-lib (dependency of a)
27+
[__1] fail (not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set)
28+
[__1] fail (backjumping, conflict set: a, some-lib)
29+
After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: a (2), some-lib (1)
Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,68 @@
11
import Test.Cabal.Prelude
2+
23
-- See #4332, dep solving output is not deterministic
3-
main = cabalTest . recordMode DoNotRecord $ withRepo "repo" $ do
4-
-- other-lib is a dependency of b, but it's not listed in cabal.project
5-
res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "some-exe -any"]
6-
assertOutputContains "not a user-provided goal" res
74

8-
-- and some-exe is a build-tool dependency of b, again not listed
9-
res <- fails $ cabal' "v2-build" ["all", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "other-lib -any"]
10-
assertOutputContains "not a user-provided goal" res
5+
assertErr = assertOutputContains "not a user-provided goal nor mentioned as a constraint, but reject-unconstrained-dependencies was set"
6+
7+
constraint x = "--constraint=" ++ x
8+
9+
main = do
10+
cabalTest' "multipkg-all" . withRepo "repo" $ do
11+
let opts =
12+
[ "--dry-run"
13+
, "--reject-unconstrained-dependencies=all"
14+
]
15+
16+
-- other-lib is a dependency of b, but it's not listed in cabal.project
17+
res <- fails $ cabal' "build" ("all" : opts ++ [constraint "some-exe -any"])
18+
assertErr res
19+
20+
-- and some-exe is a build-tool dependency of b, again not listed
21+
res <- fails $ cabal' "build" ("all" : opts ++ [constraint "other-lib -any"])
22+
assertErr res
23+
24+
-- everything's listed, good to go
25+
cabal "build" $
26+
"all"
27+
: opts
28+
++ [ constraint "other-lib -any"
29+
, constraint "some-exe -any"
30+
]
31+
32+
-- a depends on b, but b is a local dependency, so it gets a pass
33+
cabal "build" $
34+
"a"
35+
: opts
36+
++ [ constraint "other-lib -any"
37+
, constraint "some-exe -any"
38+
]
39+
40+
cabalTest' "multipkg-eq" . withRepo "repo" $ do
41+
-- all the options except for those about some-lib
42+
let opts =
43+
[ "all"
44+
, "--dry-run"
45+
, "--reject-unconstrained-dependencies=eq"
46+
, constraint "other-lib ==1.0"
47+
, constraint "some-exe ==1.0"
48+
]
49+
50+
-- everything's listed as == constraint
51+
cabal "build" (opts ++ [constraint "some-lib ==1.0"])
1152

12-
-- everything's listed, good to go
13-
cabal "v2-build" ["all", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "other-lib -any", "--constraint", "some-exe -any"]
53+
-- everything's listed as == constraint when normalised
54+
cabal "build" $
55+
opts
56+
++ [ constraint "some-lib ==1.0"
57+
, constraint "some-lib >0"
58+
, constraint "some-lib <2"
59+
]
1460

15-
-- a depends on b, but b is a local dependency, so it gets a pass
16-
cabal "v2-build" ["a", "--dry-run", "--reject-unconstrained-dependencies", "all", "--constraint", "other-lib -any", "--constraint", "some-exe -any"]
61+
-- not everything's listed as == constraint when normalised
62+
res <-
63+
fails . cabal' "build" $
64+
opts
65+
++ [ constraint "some-lib >0"
66+
, constraint "some-lib <2"
67+
]
68+
assertErr res

doc/cabal-project-description-file.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -790,8 +790,8 @@ The following settings control the behavior of the dependency solver:
790790
active-repositories: :none
791791

792792

793-
.. cfg-field:: reject-unconstrained-dependencies: all, none
794-
--reject-unconstrained-dependencies=[all|none]
793+
.. cfg-field:: reject-unconstrained-dependencies: eq, all, none
794+
--reject-unconstrained-dependencies=[eq|all|none]
795795
:synopsis: Restrict the solver to packages that have constraints on them.
796796

797797
:default: none
@@ -801,9 +801,11 @@ The following settings control the behavior of the dependency solver:
801801
aware of in a build plan. If you wish to restrict the build plan to
802802
a closed set of packages (e.g., from a freeze file), use this flag.
803803

804-
When set to `all`, all non-local packages that aren't goals must be
805-
explicitly constrained. When set to `none`, the solver will
806-
consider all packages.
804+
When set to `eq` or `all`, all non-local packages that aren't goals must be
805+
explicitly constrained. where `all` accepts any version constraint and `eq`
806+
accepts version ranges that normalise to an equality constraint.
807+
808+
When set to `none`, the solver will consider all packages.
807809

808810
.. _package-configuration-options:
809811

0 commit comments

Comments
 (0)