Skip to content

Port solver fixes from master into 1.24 #3373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Apr 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0316fad
Make the solver aware of pkg-config constraints
Mar 5, 2016
5783cb2
Make ConflictSet abstract
edsko Mar 19, 2016
67800c1
Remove the 'run tests' step from setup-dev.sh
BardurArantsson Apr 21, 2016
e569752
Add test case for backjumping after enforcing SIR
grayjay Sep 26, 2015
ba1c3aa
Fix construction of conflict sets during SIR
edsko Apr 17, 2016
180497b
Fix construction of conflict sets in cycle check
edsko Apr 17, 2016
48caaba
Explain test in more detail
edsko Mar 12, 2016
4720990
Add failing test case
grayjay Sep 23, 2015
13b80d9
Explain test case in a bit more detail
edsko Mar 19, 2016
61be5fc
Add test case for inconsistent dependency bug
grayjay Sep 26, 2015
33dda64
Use tasty-expected-failure for failing unit test
grayjay Apr 18, 2016
f3022ea
Document test case
edsko Mar 20, 2016
78e2865
Test that setup dependencies of linked packages are linked correctly
grayjay Apr 1, 2016
e7c88a4
Add failing test case for conflict set bug
grayjay Apr 12, 2016
e60ab04
Sort solver unit tests
grayjay Apr 18, 2016
96d8f3f
Add issue numbers to solver unit tests
grayjay Apr 18, 2016
6b438db
Fix link deps
edsko Apr 8, 2016
5594d6a
Expect test to pass
grayjay Apr 18, 2016
465a409
Add test case for backjumping when dependencies are not linked
grayjay Apr 20, 2016
de21c2c
Remove goal reason chains.
kosmikus Apr 20, 2016
a3fccd6
Fix incorrect comment
edsko Apr 21, 2016
ec3b8d1
Use empty POption for unknown packages
edsko Apr 21, 2016
8474308
Fix bug in link validation
edsko Mar 20, 2016
183b54a
Declare indepGoals3 as passing.
edsko Apr 21, 2016
0f576c3
Use larger conflict set when solver chooses wrong version for package…
grayjay Apr 12, 2016
fa1eaf8
Replace toConflictSet by goalVar/varToConflictSet
edsko Apr 22, 2016
e353f71
Document unqualifyDeps
edsko Apr 22, 2016
b74a2ff
Fix (very) minor mistake during merging
edsko Apr 22, 2016
57a1bce
Use zipWithM in linkDeps
edsko Apr 22, 2016
5d814ea
Remove redundant calls to simplifyVar
edsko Apr 22, 2016
d9cfc94
Rename args to linkDeps
edsko Apr 22, 2016
07ab701
Document that qualification is flag independent
edsko Apr 22, 2016
8b1c299
Add two more solver test cases.
kosmikus Apr 22, 2016
4249fb3
Add the current goal to the initial conflict set while backjumping.
kosmikus Apr 22, 2016
a1f8978
Replace Goal with Var where possible; remove Unknown goal reason.
kosmikus Apr 22, 2016
1faa777
Allow to test error messages in the solver tests.
kosmikus Apr 22, 2016
10a345f
Simplify representation of test suites in the solver DSL
grayjay Apr 3, 2016
a60c813
Remove a redundant import.
kosmikus Apr 22, 2016
211b497
Remove redundant constraints.
kosmikus Apr 22, 2016
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
11 changes: 7 additions & 4 deletions cabal-install/Distribution/Client/Configure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Distribution.Client.InstallPlan (InstallPlan)
import Distribution.Client.IndexUtils as IndexUtils
( getSourcePackages, getInstalledPackages )
import Distribution.Client.PackageIndex ( PackageIndex, elemByPackageName )
import Distribution.Client.PkgConfigDb (PkgConfigDb, readPkgConfigDb)
import Distribution.Client.Setup
( ConfigExFlags(..), configureCommand, filterConfigureFlags
, RepoContext(..) )
Expand Down Expand Up @@ -110,11 +111,13 @@ configure verbosity packageDBs repoCtxt comp platform conf

installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf
sourcePkgDb <- getSourcePackages verbosity repoCtxt
pkgConfigDb <- readPkgConfigDb verbosity conf

checkConfigExFlags verbosity installedPkgIndex
(packageIndex sourcePkgDb) configExFlags

progress <- planLocalPackage verbosity comp platform configFlags configExFlags
installedPkgIndex sourcePkgDb
installedPkgIndex sourcePkgDb pkgConfigDb

notice verbosity "Resolving dependencies..."
maybePlan <- foldProgress logMsg (return . Left) (return . Right)
Expand Down Expand Up @@ -280,10 +283,10 @@ planLocalPackage :: Verbosity -> Compiler
-> ConfigFlags -> ConfigExFlags
-> InstalledPackageIndex
-> SourcePackageDb
-> PkgConfigDb
-> IO (Progress String String InstallPlan)
planLocalPackage verbosity comp platform configFlags configExFlags
installedPkgIndex
(SourcePackageDb _ packagePrefs) = do
installedPkgIndex (SourcePackageDb _ packagePrefs) pkgConfigDb = do
pkg <- readPackageDescription verbosity =<< defaultPackageDesc verbosity
solver <- chooseSolver verbosity (fromFlag $ configSolver configExFlags)
(compilerInfo comp)
Expand Down Expand Up @@ -337,7 +340,7 @@ planLocalPackage verbosity comp platform configFlags configExFlags
(SourcePackageDb mempty packagePrefs)
[SpecificSourcePackage localPkg]

return (resolveDependencies platform (compilerInfo comp) solver resolverParams)
return (resolveDependencies platform (compilerInfo comp) pkgConfigDb solver resolverParams)


-- | Call an installer for an 'SourcePackage' but override the configure
Expand Down
8 changes: 5 additions & 3 deletions cabal-install/Distribution/Client/Dependency.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import Distribution.Simple.PackageIndex (InstalledPackageIndex)
import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex
import qualified Distribution.Client.InstallPlan as InstallPlan
import Distribution.Client.InstallPlan (InstallPlan)
import Distribution.Client.PkgConfigDb (PkgConfigDb)
import Distribution.Client.Types
( SourcePackageDb(SourcePackageDb), SourcePackage(..)
, ConfiguredPackage(..), ConfiguredId(..)
Expand Down Expand Up @@ -553,25 +554,26 @@ runSolver Modular = modularResolver
--
resolveDependencies :: Platform
-> CompilerInfo
-> PkgConfigDb
-> Solver
-> DepResolverParams
-> Progress String String InstallPlan

--TODO: is this needed here? see dontUpgradeNonUpgradeablePackages
resolveDependencies platform comp _solver params
resolveDependencies platform comp _pkgConfigDB _solver params
| null (depResolverTargets params)
= return (validateSolverResult platform comp indGoals [])
where
indGoals = depResolverIndependentGoals params

resolveDependencies platform comp solver params =
resolveDependencies platform comp pkgConfigDB solver params =

Step (showDepResolverParams finalparams)
$ fmap (validateSolverResult platform comp indGoals)
$ runSolver solver (SolverConfig reorderGoals indGoals noReinstalls
shadowing strFlags maxBkjumps)
platform comp installedPkgIndex sourcePkgIndex
preferences constraints targets
pkgConfigDB preferences constraints targets
where

finalparams @ (DepResolverParams
Expand Down
4 changes: 2 additions & 2 deletions cabal-install/Distribution/Client/Dependency/Modular.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ import Distribution.System
-- | Ties the two worlds together: classic cabal-install vs. the modular
-- solver. Performs the necessary translations before and after.
modularResolver :: SolverConfig -> DependencyResolver
modularResolver sc (Platform arch os) cinfo iidx sidx pprefs pcs pns =
modularResolver sc (Platform arch os) cinfo iidx sidx pkgConfigDB pprefs pcs pns =
fmap (uncurry postprocess) $ -- convert install plan
logToProgress (maxBackjumps sc) $ -- convert log format into progress format
solve sc cinfo idx pprefs gcs pns
solve sc cinfo idx pkgConfigDB pprefs gcs pns
where
-- Indices have to be converted into solver-specific uniform index.
idx = convPIs os arch cinfo (shadowPkgs sc) (strongFlags sc) iidx sidx
Expand Down
19 changes: 12 additions & 7 deletions cabal-install/Distribution/Client/Dependency/Modular/Assignment.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Distribution.Client.Dependency.Modular.Configured
import Distribution.Client.Dependency.Modular.Dependency
import Distribution.Client.Dependency.Modular.Flag
import Distribution.Client.Dependency.Modular.Package
import Distribution.Client.Dependency.Modular.Version

-- | A (partial) package assignment. Qualified package names
-- are associated with instances.
Expand Down Expand Up @@ -62,19 +63,23 @@ data PreAssignment = PA PPreAssignment FAssignment SAssignment
-- or the successfully extended assignment.
extend :: (Extension -> Bool) -- ^ is a given extension supported
-> (Language -> Bool) -- ^ is a given language supported
-> Goal QPN
-> (PN -> VR -> Bool) -- ^ is a given pkg-config requirement satisfiable
-> Var QPN
-> PPreAssignment -> [Dep QPN] -> Either (ConflictSet QPN, [Dep QPN]) PPreAssignment
extend extSupported langSupported goal@(Goal var _) = foldM extendSingle
extend extSupported langSupported pkgPresent var = foldM extendSingle
where

extendSingle :: PPreAssignment -> Dep QPN
-> Either (ConflictSet QPN, [Dep QPN]) PPreAssignment
extendSingle a (Ext ext ) =
if extSupported ext then Right a
else Left (toConflictSet goal, [Ext ext])
else Left (varToConflictSet var, [Ext ext])
extendSingle a (Lang lang) =
if langSupported lang then Right a
else Left (toConflictSet goal, [Lang lang])
else Left (varToConflictSet var, [Lang lang])
extendSingle a (Pkg pn vr) =
if pkgPresent pn vr then Right a
else Left (varToConflictSet var, [Pkg pn vr])
extendSingle a (Dep qpn ci) =
let ci' = M.findWithDefault (Constrained []) qpn a
in case (\ x -> M.insert qpn x a) <$> merge ci' ci of
Expand All @@ -85,9 +90,9 @@ extend extSupported langSupported goal@(Goal var _) = foldM extendSingle
-- making a choice pkg == instance, and pkg => pkg == instance is a part
-- of the conflict, then this info is clear from the context and does not
-- have to be repeated.
simplify v (Fixed _ (Goal var' _)) c | v == var && var' == var = [c]
simplify v c (Fixed _ (Goal var' _)) | v == var && var' == var = [c]
simplify _ c d = [c, d]
simplify v (Fixed _ var') c | v == var && var' == var = [c]
simplify v c (Fixed _ var') | v == var && var' == var = [c]
simplify _ c d = [c, d]

-- | Delivers an ordered list of fully configured packages.
--
Expand Down
34 changes: 21 additions & 13 deletions cabal-install/Distribution/Client/Dependency/Modular/Builder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,13 @@ extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs
-- code above is correct; insert/adjust have different arg order
go g o ( (OpenGoal (Simple (Ext _ext ) _) _gr) : ngs) = go g o ngs
go g o ( (OpenGoal (Simple (Lang _lang)_) _gr) : ngs) = go g o ngs
go g o ( (OpenGoal (Simple (Pkg _pn _vr)_) _gr) : ngs)= go g o ngs

cons' = P.cons . forgetCompOpenGoal

-- | Given the current scope, qualify all the package names in the given set of
-- dependencies and then extend the set of open goals accordingly.
scopedExtendOpen :: QPN -> I -> QGoalReasonChain -> FlaggedDeps Component PN -> FlagInfo ->
scopedExtendOpen :: QPN -> I -> QGoalReason -> FlaggedDeps Component PN -> FlagInfo ->
BuildState -> BuildState
scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s
where
Expand Down Expand Up @@ -96,13 +97,13 @@ scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s
data BuildType =
Goals -- ^ build a goal choice node
| OneGoal (OpenGoal ()) -- ^ build a node for this goal
| Instance QPN I PInfo QGoalReasonChain -- ^ build a tree for a concrete instance
| Instance QPN I PInfo QGoalReason -- ^ build a tree for a concrete instance
deriving Show

build :: BuildState -> Tree QGoalReasonChain
build :: BuildState -> Tree QGoalReason
build = ana go
where
go :: BuildState -> TreeF QGoalReasonChain BuildState
go :: BuildState -> TreeF QGoalReason BuildState

-- If we have a choice between many goals, we just record the choice in
-- the tree. We select each open goal in turn, and before we descend, remove
Expand All @@ -121,9 +122,16 @@ build = ana go
error "Distribution.Client.Dependency.Modular.Builder: build.go called with Ext goal"
go (BS { index = _ , next = OneGoal (OpenGoal (Simple (Lang _ ) _) _ ) }) =
error "Distribution.Client.Dependency.Modular.Builder: build.go called with Lang goal"
go (BS { index = _ , next = OneGoal (OpenGoal (Simple (Pkg _ _ ) _) _ ) }) =
error "Distribution.Client.Dependency.Modular.Builder: build.go called with Pkg goal"
go bs@(BS { index = idx, next = OneGoal (OpenGoal (Simple (Dep qpn@(Q _ pn) _) _) gr) }) =
-- If the package does not exist in the index, we construct an emty PChoiceF node for it
-- After all, we have no choices here. Alternatively, we could immediately construct
-- a Fail node here, but that would complicate the construction of conflict sets.
-- We will probably want to give this case special treatment when generating error
-- messages though.
case M.lookup pn idx of
Nothing -> FailF (toConflictSet (Goal (P qpn) gr)) (BuildFailureNotInIndex pn)
Nothing -> PChoiceF qpn gr (P.fromList [])
Just pis -> PChoiceF qpn gr (P.fromList (L.map (\ (i, info) ->
(POption i Nothing, bs { next = Instance qpn i info gr }))
(M.toList pis)))
Expand All @@ -135,8 +143,8 @@ build = ana go
-- TODO: Should we include the flag default in the tree?
go bs@(BS { next = OneGoal (OpenGoal (Flagged qfn@(FN (PI qpn _) _) (FInfo b m w) t f) gr) }) =
FChoiceF qfn gr (w || trivial) m (P.fromList (reorder b
[(True, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True : gr)) t) bs) { next = Goals }),
(False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False : gr)) f) bs) { next = Goals })]))
[(True, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True )) t) bs) { next = Goals }),
(False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False)) f) bs) { next = Goals })]))
where
reorder True = id
reorder False = reverse
Expand All @@ -149,22 +157,22 @@ build = ana go

go bs@(BS { next = OneGoal (OpenGoal (Stanza qsn@(SN (PI qpn _) _) t) gr) }) =
SChoiceF qsn gr trivial (P.fromList
[(False, bs { next = Goals }),
(True, (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn : gr)) t) bs) { next = Goals })])
[(False, bs { next = Goals }),
(True, (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn)) t) bs) { next = Goals })])
where
trivial = L.null t

-- For a particular instance, we change the state: we update the scope,
-- and furthermore we update the set of goals.
--
-- TODO: We could inline this above.
go bs@(BS { next = Instance qpn i (PInfo fdeps fdefs _) gr }) =
go ((scopedExtendOpen qpn i (PDependency (PI qpn i) : gr) fdeps fdefs bs)
go bs@(BS { next = Instance qpn i (PInfo fdeps fdefs _) _gr }) =
go ((scopedExtendOpen qpn i (PDependency (PI qpn i)) fdeps fdefs bs)
{ next = Goals })

-- | Interface to the tree builder. Just takes an index and a list of package names,
-- and computes the initial state and then the tree from there.
buildTree :: Index -> Bool -> [PN] -> Tree QGoalReasonChain
buildTree :: Index -> Bool -> [PN] -> Tree QGoalReason
buildTree idx ind igs =
build BS {
index = idx
Expand All @@ -174,7 +182,7 @@ buildTree idx ind igs =
, qualifyOptions = defaultQualifyOptions idx
}
where
topLevelGoal qpn = OpenGoal (Simple (Dep qpn (Constrained [])) ()) [UserGoal]
topLevelGoal qpn = OpenGoal (Simple (Dep qpn (Constrained [])) ()) UserGoal

qpns | ind = makeIndependent igs
| otherwise = L.map (Q (PP DefaultNamespace Unqualified)) igs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{-# LANGUAGE CPP #-}
-- | Conflict sets
--
-- Intended for double import
--
-- > import Distribution.Client.Dependency.Modular.ConflictSet (ConflictSet)
-- > import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS
module Distribution.Client.Dependency.Modular.ConflictSet (
ConflictSet -- opaque
, showCS
-- Set-like operations
, toList
, union
, unions
, insert
, empty
, singleton
, member
, filter
, fromList
) where

import Prelude hiding (filter)
import Data.List (intercalate)
import Data.Set (Set)
import qualified Data.Set as S

import Distribution.Client.Dependency.Modular.Package
import Distribution.Client.Dependency.Modular.Var

-- | The set of variables involved in a solver conflict
--
-- Since these variables should be preprocessed in some way, this type is
-- kept abstract.
newtype ConflictSet qpn = CS { fromConflictSet :: Set (Var qpn) }
deriving (Eq, Ord, Show)

showCS :: ConflictSet QPN -> String
showCS = intercalate ", " . map showVar . toList

{-------------------------------------------------------------------------------
Set-like operations
-------------------------------------------------------------------------------}

toList :: ConflictSet qpn -> [Var qpn]
toList = S.toList . fromConflictSet

union :: Ord qpn => ConflictSet qpn -> ConflictSet qpn -> ConflictSet qpn
union (CS a) (CS b) = CS (a `S.union` b)

unions :: Ord qpn => [ConflictSet qpn] -> ConflictSet qpn
unions = CS . S.unions . map fromConflictSet

insert :: Ord qpn => Var qpn -> ConflictSet qpn -> ConflictSet qpn
insert var (CS set) = CS (S.insert (simplifyVar var) set)

empty :: ConflictSet qpn
empty = CS S.empty

singleton :: Var qpn -> ConflictSet qpn
singleton = CS . S.singleton . simplifyVar

member :: Ord qpn => Var qpn -> ConflictSet qpn -> Bool
member var (CS set) = S.member (simplifyVar var) set

#if MIN_VERSION_containers(0,5,0)
filter :: (Var qpn -> Bool) -> ConflictSet qpn -> ConflictSet qpn
#else
filter :: Ord qpn => (Var qpn -> Bool) -> ConflictSet qpn -> ConflictSet qpn
#endif
filter p (CS set) = CS $ S.filter p set

fromList :: Ord qpn => [Var qpn] -> ConflictSet qpn
fromList = CS . S.fromList . map simplifyVar
Loading