Skip to content

Commit 38c8035

Browse files
committed
Solver: Detect cycles between packages and their setup scripts (fixes #4161).
The solver already detected cycles involving more than one package, but it allowed dependencies between components within a package. This commit treats a dependency between a package's setup script and library as a cycle in order to allow the solver to backtrack and try to break the cycle. A more thorough solution would involve tracking all dependencies between components, as in #4087. This commit also fixes the internal error in issue #4980.
1 parent 0c602fd commit 38c8035

File tree

2 files changed

+35
-2
lines changed
  • cabal-install

2 files changed

+35
-2
lines changed

cabal-install/Distribution/Solver/Modular/Builder.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import qualified Distribution.Solver.Modular.PSQ as P
3232
import Distribution.Solver.Modular.Tree
3333
import qualified Distribution.Solver.Modular.WeightedPSQ as W
3434

35+
import Distribution.Solver.Types.ComponentDeps
3536
import Distribution.Solver.Types.PackagePath
3637
import Distribution.Solver.Types.Settings
3738

@@ -72,8 +73,15 @@ extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs
7273
go g o ((Stanza sn@(SN qpn _) t) : ngs) =
7374
go g (StanzaGoal sn t (flagGR qpn) : o) ngs
7475
go g o ((Simple (LDep dr (Dep _ qpn _)) c) : ngs)
75-
| qpn == qpn' = go g o ngs
76-
-- we ignore self-dependencies at this point; TODO: more care may be needed
76+
| qpn == qpn' =
77+
-- We currently only add a self-dependency to the graph if it is
78+
-- between a package and its setup script. The edge creates a cycle
79+
-- and causes the solver to backtrack and choose a different
80+
-- instance for the setup script. We may need to track other
81+
-- self-dependencies once we implement component-based solving.
82+
case c of
83+
ComponentSetup -> go (M.adjust (addIfAbsent (ComponentSetup, qpn')) qpn g) o ngs
84+
_ -> go g o ngs
7785
| qpn `M.member` g = go (M.adjust (addIfAbsent (c, qpn')) qpn g) o ngs
7886
| otherwise = go (M.insert qpn [(c, qpn')] g) (PkgGoal qpn (DependencyGoal dr) : o) ngs
7987
-- code above is correct; insert/adjust have different arg order

cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ tests = [
159159
, runTest $ mkTest db15 "cycleThroughSetupDep3" ["C"] (solverSuccess [("C", 2), ("D", 1)])
160160
, runTest $ mkTest db15 "cycleThroughSetupDep4" ["D"] (solverSuccess [("D", 1)])
161161
, runTest $ mkTest db15 "cycleThroughSetupDep5" ["E"] (solverSuccess [("C", 2), ("D", 1), ("E", 1)])
162+
, runTest $ issue4161 "detect cycle between package and its setup script"
162163
, runTest $ testCyclicDependencyErrorMessages "cyclic dependency error messages"
163164
]
164165
, testGroup "Extensions" [
@@ -783,6 +784,30 @@ db15 = [
783784
, Right $ exAv "E" 1 [ExFix "C" 2]
784785
]
785786

787+
-- | Detect a cycle between a package and its setup script.
788+
--
789+
-- This type of cycle can easily occur when new-build adds default setup
790+
-- dependencies to packages without custom-setup stanzas. For example, cabal
791+
-- adds 'time' as a setup dependency for 'time'. The solver should detect the
792+
-- cycle when it attempts to link the setup and non-setup instances of the
793+
-- package and then choose a different version for the setup dependency.
794+
issue4161 :: String -> SolverTest
795+
issue4161 name =
796+
mkTest db name ["target"] $
797+
SolverResult checkFullLog $ Right [("target", 1), ("time", 2)]
798+
where
799+
db :: ExampleDb
800+
db = [
801+
Right $ exAv "target" 1 [ExFix "time" 2]
802+
, Right $ exAv "time" 2 [] `withSetupDeps` [ExAny "time"]
803+
, Left $ exInst "time" 1 "time-2-inst" []
804+
]
805+
806+
checkFullLog :: [String] -> Bool
807+
checkFullLog = any $ isInfixOf $
808+
"rejecting: time:setup.time~>time-2.0.0 (cyclic dependencies; "
809+
++ "conflict set: time:setup.time)"
810+
786811
-- | Packages pkg-A, pkg-B, and pkg-C form a cycle. The solver should backtrack
787812
-- as soon as it chooses the last package in the cycle, to avoid searching parts
788813
-- of the tree that have no solution. Since there is no way to break the cycle,

0 commit comments

Comments
 (0)