diff --git a/cabal-install/Distribution/Solver/Modular/Builder.hs b/cabal-install/Distribution/Solver/Modular/Builder.hs index 12edea9792a..71406897502 100644 --- a/cabal-install/Distribution/Solver/Modular/Builder.hs +++ b/cabal-install/Distribution/Solver/Modular/Builder.hs @@ -1,5 +1,8 @@ {-# LANGUAGE ScopedTypeVariables #-} -module Distribution.Solver.Modular.Builder (buildTree) where +module Distribution.Solver.Modular.Builder ( + buildTree + , splits -- for testing + ) where -- Building the search tree. -- @@ -24,12 +27,10 @@ import Distribution.Solver.Modular.Dependency import Distribution.Solver.Modular.Flag import Distribution.Solver.Modular.Index import Distribution.Solver.Modular.Package -import Distribution.Solver.Modular.PSQ (PSQ) import qualified Distribution.Solver.Modular.PSQ as P import Distribution.Solver.Modular.Tree import qualified Distribution.Solver.Modular.WeightedPSQ as W -import Distribution.Solver.Types.ComponentDeps (Component) import Distribution.Solver.Types.PackagePath import Distribution.Solver.Types.Settings @@ -43,11 +44,11 @@ data Linker a = Linker { -- | The state needed to build the search tree without creating any linked nodes. data BuildState = BS { - index :: Index, -- ^ information about packages and their dependencies - rdeps :: RevDepMap, -- ^ set of all package goals, completed and open, with reverse dependencies - open :: PSQ (OpenGoal ()) (), -- ^ set of still open goals (flag and package goals) - next :: BuildType, -- ^ kind of node to generate next - qualifyOptions :: QualifyOptions -- ^ qualification options + index :: Index, -- ^ information about packages and their dependencies + rdeps :: RevDepMap, -- ^ set of all package goals, completed and open, with reverse dependencies + open :: [OpenGoal], -- ^ set of still open goals (flag and package goals) + next :: BuildType, -- ^ kind of node to generate next + qualifyOptions :: QualifyOptions -- ^ qualification options } -- | Map of available linking targets. @@ -57,34 +58,33 @@ type LinkingState = Map (PN, I) [PackagePath] -- -- We also adjust the map of overall goals, and keep track of the -- reverse dependencies of each of the goals. -extendOpen :: QPN -> [OpenGoal Component] -> BuildState -> BuildState +extendOpen :: QPN -> [PotentialGoal] -> BuildState -> BuildState extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs where - go :: RevDepMap -> PSQ (OpenGoal ()) () -> [OpenGoal Component] -> BuildState - go g o [] = s { rdeps = g, open = o } - go g o (ng@(OpenGoal (Flagged _ _ _ _) _gr) : ngs) = go g (cons' ng () o) ngs + go :: RevDepMap -> [OpenGoal] -> [PotentialGoal] -> BuildState + go g o [] = s { rdeps = g, open = o } + go g o ((PotentialGoal (Flagged fn fInfo t f) gr) : ngs) = go g (FlagGoal fn fInfo t f gr : o) ngs -- Note: for 'Flagged' goals, we always insert, so later additions win. -- This is important, because in general, if a goal is inserted twice, -- the later addition will have better dependency information. - go g o (ng@(OpenGoal (Stanza _ _ ) _gr) : ngs) = go g (cons' ng () o) ngs - go g o (ng@(OpenGoal (Simple (Dep _ qpn _) c) _gr) : ngs) + go g o ((PotentialGoal (Stanza sn t) gr) : ngs) = go g (StanzaGoal sn t gr : o) ngs + go g o ((PotentialGoal (Simple (Dep _ qpn _) c) gr) : ngs) | qpn == qpn' = go g o ngs -- we ignore self-dependencies at this point; TODO: more care may be needed | qpn `M.member` g = go (M.adjust (addIfAbsent (c, qpn')) qpn g) o ngs - | otherwise = go (M.insert qpn [(c, qpn')] g) (cons' ng () o) ngs + | otherwise = go (M.insert qpn [(c, qpn')] g) (PkgGoal qpn gr : o) ngs -- 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 + go g o ((PotentialGoal (Simple (Ext _ext ) _) _gr) : ngs) = go g o ngs + go g o ((PotentialGoal (Simple (Lang _lang)_) _gr) : ngs) = go g o ngs + go g o ((PotentialGoal (Simple (Pkg _pn _vr)_) _gr) : ngs) = go g o ngs - cons' = P.cons . forgetCompOpenGoal addIfAbsent :: Eq a => a -> [a] -> [a] addIfAbsent x xs = if x `elem` xs then xs else x : xs -- | 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 -> QGoalReason -> FlaggedDeps Component PN -> FlagInfo -> +scopedExtendOpen :: QPN -> I -> QGoalReason -> FlaggedDeps PN -> FlagInfo -> BuildState -> BuildState scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s where @@ -93,7 +93,7 @@ scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s -- Introduce all package flags qfdefs = L.map (\ (fn, b) -> Flagged (FN (PI qpn i) fn) b [] []) $ M.toList fdefs -- Combine new package and flag goals - gs = L.map (flip OpenGoal gr) (qfdefs ++ qfdeps) + gs = L.map (flip PotentialGoal gr) (qfdefs ++ qfdeps) -- NOTE: -- -- In the expression @qfdefs ++ qfdeps@ above, flags occur potentially @@ -112,10 +112,9 @@ scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s -- | Datatype that encodes what to build next data BuildType = - Goals -- ^ build a goal choice node - | OneGoal (OpenGoal ()) -- ^ build a node for this goal + Goals -- ^ build a goal choice node + | OneGoal OpenGoal -- ^ build a node for this goal | Instance QPN I PInfo QGoalReason -- ^ build a tree for a concrete instance - deriving Show build :: Linker BuildState -> Tree () QGoalReason build = ana go @@ -129,23 +128,17 @@ addChildren :: BuildState -> TreeF () QGoalReason BuildState -- the tree. We select each open goal in turn, and before we descend, remove -- it from the queue of open goals. addChildren bs@(BS { rdeps = rdm, open = gs, next = Goals }) - | P.null gs = DoneF rdm () - | otherwise = GoalChoiceF rdm $ P.mapKeys close - $ P.mapWithKey (\ g (_sc, gs') -> bs { next = OneGoal g, open = gs' }) - $ P.splits gs + | L.null gs = DoneF rdm () + | otherwise = GoalChoiceF rdm $ P.fromList + $ L.map (\ (g, gs') -> (close g, bs { next = OneGoal g, open = gs' })) + $ splits gs -- If we have already picked a goal, then the choice depends on the kind -- of goal. -- -- For a package, we look up the instances available in the global info, -- and then handle each instance in turn. -addChildren (BS { index = _ , next = OneGoal (OpenGoal (Simple (Ext _ ) _) _ ) }) = - error "Distribution.Solver.Modular.Builder: addChildren called with Ext goal" -addChildren (BS { index = _ , next = OneGoal (OpenGoal (Simple (Lang _ ) _) _ ) }) = - error "Distribution.Solver.Modular.Builder: addChildren called with Lang goal" -addChildren (BS { index = _ , next = OneGoal (OpenGoal (Simple (Pkg _ _ ) _) _ ) }) = - error "Distribution.Solver.Modular.Builder: addChildren called with Pkg goal" -addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (OpenGoal (Simple (Dep _ qpn@(Q _ pn) _) _) gr) }) = +addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (PkgGoal 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. @@ -160,10 +153,10 @@ addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (OpenGoal (Simple -- For a flag, we create only two subtrees, and we create them in the order -- that is indicated by the flag default. -addChildren bs@(BS { rdeps = rdm, next = OneGoal (OpenGoal (Flagged qfn@(FN (PI qpn _) _) (FInfo b m w) t f) gr) }) = +addChildren bs@(BS { rdeps = rdm, next = OneGoal (FlagGoal qfn@(FN (PI qpn _) _) (FInfo b m w) t f gr) }) = FChoiceF qfn rdm gr weak m b (W.fromList - [([if b then 0 else 1], True, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True )) t) bs) { next = Goals }), - ([if b then 1 else 0], False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False)) f) bs) { next = Goals })]) + [([if b then 0 else 1], True, (extendOpen qpn (L.map (flip PotentialGoal (FDependency qfn True )) t) bs) { next = Goals }), + ([if b then 1 else 0], False, (extendOpen qpn (L.map (flip PotentialGoal (FDependency qfn False)) f) bs) { next = Goals })]) where trivial = L.null t && L.null f weak = WeakOrTrivial $ unWeakOrTrivial w || trivial @@ -173,10 +166,10 @@ addChildren bs@(BS { rdeps = rdm, next = OneGoal (OpenGoal (Flagged qfn@(FN (PI -- the stanza by replacing the False branch with failure) or preferences -- (try enabling the stanza if possible by moving the True branch first). -addChildren bs@(BS { rdeps = rdm, next = OneGoal (OpenGoal (Stanza qsn@(SN (PI qpn _) _) t) gr) }) = +addChildren bs@(BS { rdeps = rdm, next = OneGoal (StanzaGoal qsn@(SN (PI qpn _) _) t gr) }) = SChoiceF qsn rdm gr trivial (W.fromList - [([0], False, bs { next = Goals }), - ([1], True, (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn)) t) bs) { next = Goals })]) + [([0], False, bs { next = Goals }), + ([1], True, (extendOpen qpn (L.map (flip PotentialGoal (SDependency qsn)) t) bs) { next = Goals })]) where trivial = WeakOrTrivial (L.null t) @@ -258,16 +251,49 @@ buildTree idx (IndependentGoals ind) igs = buildState = BS { index = idx , rdeps = M.fromList (L.map (\ qpn -> (qpn, [])) qpns) - , open = P.fromList (L.map (\ qpn -> (topLevelGoal qpn, ())) qpns) + , open = L.map topLevelGoal qpns , next = Goals , qualifyOptions = defaultQualifyOptions idx } , linkingState = M.empty } where - -- Should a top-level goal allowed to be an executable style - -- dependency? Well, I don't think it would make much difference - topLevelGoal qpn = OpenGoal (Simple (Dep False {- not exe -} qpn (Constrained [])) ()) UserGoal + topLevelGoal qpn = PkgGoal qpn UserGoal qpns | ind = makeIndependent igs | otherwise = L.map (Q (PackagePath DefaultNamespace QualToplevel)) igs + +{------------------------------------------------------------------------------- + Goals +-------------------------------------------------------------------------------} + +-- | Information needed about a dependency before it is converted into a Goal. +-- Not all PotentialGoals correspond to Goals. For example, PotentialGoals can +-- represent pkg-config or language extension dependencies. +data PotentialGoal = PotentialGoal (FlaggedDep QPN) QGoalReason + +-- | Like a PotentialGoal, except that it always introduces a new Goal. +data OpenGoal = + FlagGoal (FN QPN) FInfo (FlaggedDeps QPN) (FlaggedDeps QPN) QGoalReason + | StanzaGoal (SN QPN) (FlaggedDeps QPN) QGoalReason + | PkgGoal QPN QGoalReason + +-- | Closes a goal, i.e., removes all the extraneous information that we +-- need only during the build phase. +close :: OpenGoal -> Goal QPN +close (FlagGoal qfn _ _ _ gr) = Goal (F qfn) gr +close (StanzaGoal qsn _ gr) = Goal (S qsn) gr +close (PkgGoal qpn gr) = Goal (P qpn) gr + +{------------------------------------------------------------------------------- + Auxiliary +-------------------------------------------------------------------------------} + +-- | Pairs each element of a list with the list resulting from removal of that +-- element from the original list. +splits :: [a] -> [(a, [a])] +splits = go id + where + go :: ([a] -> [a]) -> [a] -> [(a, [a])] + go _ [] = [] + go f (x : xs) = (x, f xs) : go (f . (x :)) xs diff --git a/cabal-install/Distribution/Solver/Modular/Dependency.hs b/cabal-install/Distribution/Solver/Modular/Dependency.hs index 66a7a3f573f..6224655f962 100644 --- a/cabal-install/Distribution/Solver/Modular/Dependency.hs +++ b/cabal-install/Distribution/Solver/Modular/Dependency.hs @@ -26,9 +26,6 @@ module Distribution.Solver.Modular.Dependency ( , QualifyOptions(..) , qualifyDeps , unqualifyDeps - -- ** Setting/forgetting components - , forgetCompOpenGoal - , setCompFlaggedDeps -- * Reverse dependency map , RevDepMap -- * Goals @@ -40,9 +37,6 @@ module Distribution.Solver.Modular.Dependency ( , goalVarToConflictSet , varToConflictSet , goalReasonToVars - -- * Open goals - , OpenGoal(..) - , close ) where import Prelude hiding (pi) @@ -124,24 +118,14 @@ merge (Constrained rs) (Constrained ss) = Right (Constrained (rs ++ ss)) -- rather than having the dependencies indexed by component, each dependency -- defines what component it is in. -- --- However, top-level goals are also modelled as dependencies, but of course --- these don't actually belong in any component of any package. Therefore, we --- parameterize 'FlaggedDeps' and derived datatypes with a type argument that --- specifies whether or not we have a component: we only ever instantiate this --- type argument with @()@ for top-level goals, or 'Component' for everything --- else (we could express this as a kind at the type-level, but that would --- require a very recent GHC). --- --- Note however, crucially, that independent of the type parameters, the list --- of dependencies underneath a flag choice or stanza choices _always_ uses --- Component as the type argument. This is important: when we pick a value for --- a flag, we _must_ know what component the new dependencies belong to, or --- else we don't be able to construct fine-grained reverse dependencies. -type FlaggedDeps comp qpn = [FlaggedDep comp qpn] +-- Note that each dependency is associated with a Component. We must know what +-- component the dependencies belong to, or else we won't be able to construct +-- fine-grained reverse dependencies. +type FlaggedDeps qpn = [FlaggedDep qpn] -- | Flagged dependencies can either be plain dependency constraints, -- or flag-dependent dependency trees. -data FlaggedDep comp qpn = +data FlaggedDep qpn = -- | Dependencies which are conditional on a flag choice. Flagged (FN qpn) FInfo (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn) -- | Dependencies which are conditional on whether or not a stanza @@ -149,22 +133,22 @@ data FlaggedDep comp qpn = | Stanza (SN qpn) (TrueFlaggedDeps qpn) -- | Dependencies for which are always enabled, for the component -- 'comp' (or requested for the user, if comp is @()@). - | Simple (Dep qpn) comp + | Simple (Dep qpn) Component deriving (Eq, Show) -- | Conversatively flatten out flagged dependencies -- -- NOTE: We do not filter out duplicates. -flattenFlaggedDeps :: FlaggedDeps Component qpn -> [(Dep qpn, Component)] +flattenFlaggedDeps :: FlaggedDeps qpn -> [(Dep qpn, Component)] flattenFlaggedDeps = concatMap aux where - aux :: FlaggedDep Component qpn -> [(Dep qpn, Component)] + aux :: FlaggedDep qpn -> [(Dep qpn, Component)] aux (Flagged _ _ t f) = flattenFlaggedDeps t ++ flattenFlaggedDeps f aux (Stanza _ t) = flattenFlaggedDeps t aux (Simple d c) = [(d, c)] -type TrueFlaggedDeps qpn = FlaggedDeps Component qpn -type FalseFlaggedDeps qpn = FlaggedDeps Component qpn +type TrueFlaggedDeps qpn = FlaggedDeps qpn +type FalseFlaggedDeps qpn = FlaggedDeps qpn -- | Is this dependency on an executable type IsExe = Bool @@ -176,7 +160,7 @@ type IsExe = Bool -- is used both to record the dependencies as well as who's doing the -- depending; having a 'Functor' instance makes bugs where we don't distinguish -- these two far too likely. (By rights 'Dep' ought to have two type variables.) -data Dep qpn = Dep IsExe qpn (CI qpn) -- ^ dependency on a package (possibly for executable +data Dep qpn = Dep IsExe qpn (CI qpn) -- ^ dependency on a package (possibly for executable) | Ext Extension -- ^ dependency on a language extension | Lang Language -- ^ dependency on a language version | Pkg PkgconfigName VR -- ^ dependency on a pkg-config package @@ -220,13 +204,13 @@ data QualifyOptions = QO { -- -- NOTE: It's the _dependencies_ of a package that may or may not be independent -- from the package itself. Package flag choices must of course be consistent. -qualifyDeps :: QualifyOptions -> QPN -> FlaggedDeps Component PN -> FlaggedDeps Component QPN +qualifyDeps :: QualifyOptions -> QPN -> FlaggedDeps PN -> FlaggedDeps QPN qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go where - go :: FlaggedDeps Component PN -> FlaggedDeps Component QPN + go :: FlaggedDeps PN -> FlaggedDeps QPN go = map go1 - go1 :: FlaggedDep Component PN -> FlaggedDep Component QPN + go1 :: FlaggedDep PN -> FlaggedDep QPN go1 (Flagged fn nfo t f) = Flagged (fmap (Q pp) fn) nfo (go t) (go f) go1 (Stanza sn t) = Stanza (fmap (Q pp) sn) (go t) go1 (Simple dep comp) = Simple (goD dep comp) comp @@ -278,13 +262,13 @@ qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go -- what to link these dependencies to, we need to requalify @Q.B@ to become -- @Q'.B@; we do this by first removing all qualifiers and then calling -- 'qualifyDeps' again. -unqualifyDeps :: FlaggedDeps comp QPN -> FlaggedDeps comp PN +unqualifyDeps :: FlaggedDeps QPN -> FlaggedDeps PN unqualifyDeps = go where - go :: FlaggedDeps comp QPN -> FlaggedDeps comp PN + go :: FlaggedDeps QPN -> FlaggedDeps PN go = map go1 - go1 :: FlaggedDep comp QPN -> FlaggedDep comp PN + go1 :: FlaggedDep QPN -> FlaggedDep PN go1 (Flagged fn nfo t f) = Flagged (fmap unq fn) nfo (go t) (go f) go1 (Stanza sn t) = Stanza (fmap unq sn) (go t) go1 (Simple dep comp) = Simple (goD dep) comp @@ -298,35 +282,6 @@ unqualifyDeps = go unq :: QPN -> PN unq (Q _ pn) = pn -{------------------------------------------------------------------------------- - Setting/forgetting the Component --------------------------------------------------------------------------------} - -forgetCompOpenGoal :: OpenGoal Component -> OpenGoal () -forgetCompOpenGoal = mapCompOpenGoal $ const () - -setCompFlaggedDeps :: Component -> FlaggedDeps () qpn -> FlaggedDeps Component qpn -setCompFlaggedDeps = mapCompFlaggedDeps . const - -{------------------------------------------------------------------------------- - Auxiliary: Mapping over the Component goal - - We don't export these, because the only type instantiations for 'a' and 'b' - here should be () or Component. (We could express this at the type level - if we relied on newer versions of GHC.) --------------------------------------------------------------------------------} - -mapCompOpenGoal :: (a -> b) -> OpenGoal a -> OpenGoal b -mapCompOpenGoal g (OpenGoal d gr) = OpenGoal (mapCompFlaggedDep g d) gr - -mapCompFlaggedDeps :: (a -> b) -> FlaggedDeps a qpn -> FlaggedDeps b qpn -mapCompFlaggedDeps = L.map . mapCompFlaggedDep - -mapCompFlaggedDep :: (a -> b) -> FlaggedDep a qpn -> FlaggedDep b qpn -mapCompFlaggedDep _ (Flagged fn nfo t f) = Flagged fn nfo t f -mapCompFlaggedDep _ (Stanza sn t ) = Stanza sn t -mapCompFlaggedDep g (Simple pn a ) = Simple pn (g a) - {------------------------------------------------------------------------------- Reverse dependency map -------------------------------------------------------------------------------} @@ -397,28 +352,6 @@ goalReasonToVars (PDependency (PI qpn _)) = [P qpn] goalReasonToVars (FDependency qfn _) = [F qfn] goalReasonToVars (SDependency qsn) = [S qsn] -{------------------------------------------------------------------------------- - Open goals --------------------------------------------------------------------------------} - --- | For open goals as they occur during the build phase, we need to store --- additional information about flags. -data OpenGoal comp = OpenGoal (FlaggedDep comp QPN) QGoalReason - deriving (Eq, Show) - --- | Closes a goal, i.e., removes all the extraneous information that we --- need only during the build phase. -close :: OpenGoal comp -> Goal QPN -close (OpenGoal (Simple (Dep _ qpn _) _) gr) = Goal (P qpn) gr -close (OpenGoal (Simple (Ext _) _) _ ) = - error "Distribution.Solver.Modular.Dependency.close: called on Ext goal" -close (OpenGoal (Simple (Lang _) _) _ ) = - error "Distribution.Solver.Modular.Dependency.close: called on Lang goal" -close (OpenGoal (Simple (Pkg _ _) _) _ ) = - error "Distribution.Solver.Modular.Dependency.close: called on Pkg goal" -close (OpenGoal (Flagged qfn _ _ _ ) gr) = Goal (F qfn) gr -close (OpenGoal (Stanza qsn _) gr) = Goal (S qsn) gr - {------------------------------------------------------------------------------- Version ranges paired with origins -------------------------------------------------------------------------------} diff --git a/cabal-install/Distribution/Solver/Modular/Index.hs b/cabal-install/Distribution/Solver/Modular/Index.hs index f148d846f6e..bf3faf03247 100644 --- a/cabal-install/Distribution/Solver/Modular/Index.hs +++ b/cabal-install/Distribution/Solver/Modular/Index.hs @@ -14,8 +14,6 @@ import Distribution.Solver.Modular.Flag import Distribution.Solver.Modular.Package import Distribution.Solver.Modular.Tree -import Distribution.Solver.Types.ComponentDeps (Component) - -- | An index contains information about package instances. This is a nested -- dictionary. Package names are mapped to instances, which in turn is mapped -- to info. @@ -27,7 +25,7 @@ type Index = Map PN (Map I PInfo) -- globally, for reasons external to the solver. We currently use this -- for shadowing which essentially is a GHC limitation, and for -- installed packages that are broken. -data PInfo = PInfo (FlaggedDeps Component PN) FlagInfo (Maybe FailReason) +data PInfo = PInfo (FlaggedDeps PN) FlagInfo (Maybe FailReason) deriving (Show) mkIndex :: [(PN, I, PInfo)] -> Index diff --git a/cabal-install/Distribution/Solver/Modular/IndexConversion.hs b/cabal-install/Distribution/Solver/Modular/IndexConversion.hs index 0f59ae566b3..87721eb350b 100644 --- a/cabal-install/Distribution/Solver/Modular/IndexConversion.hs +++ b/cabal-install/Distribution/Solver/Modular/IndexConversion.hs @@ -55,8 +55,8 @@ import Distribution.Solver.Modular.Version -- explicitly requested. convPIs :: OS -> Arch -> CompilerInfo -> ShadowPkgs -> StrongFlags -> SolveExecutables -> SI.InstalledPackageIndex -> CI.PackageIndex (SourcePackage loc) -> Index -convPIs os arch comp sip strfl sexes iidx sidx = - mkIndex (convIPI' sip iidx ++ convSPI' os arch comp strfl sexes sidx) +convPIs os arch comp sip strfl solveExes iidx sidx = + mkIndex (convIPI' sip iidx ++ convSPI' os arch comp strfl solveExes sidx) -- | Convert a Cabal installed package index to the simpler, -- more uniform index format of the solver. @@ -85,15 +85,14 @@ convId ipi = (pn, I ver $ Inst $ IPI.installedUnitId ipi) -- | Convert a single installed package into the solver-specific format. convIP :: SI.InstalledPackageIndex -> InstalledPackageInfo -> (PN, I, PInfo) convIP idx ipi = - case mapM (convIPId pn idx) (IPI.depends ipi) of - Nothing -> (pn, i, PInfo [] M.empty (Just Broken)) - Just fds -> (pn, i, PInfo (setComp fds) M.empty Nothing) + case mapM (convIPId pn comp idx) (IPI.depends ipi) of + Nothing -> (pn, i, PInfo [] M.empty (Just Broken)) + Just fds -> (pn, i, PInfo fds M.empty Nothing) where (pn, i) = convId ipi -- 'sourceLibName' is unreliable, but for now we only really use this for -- primary libs anyways - setComp = setCompFlaggedDeps $ componentNameToComponent - $ libraryComponentName $ sourceLibName ipi + comp = componentNameToComponent $ libraryComponentName $ sourceLibName ipi -- TODO: Installed packages should also store their encapsulations! -- Note [Index conversion with internal libraries] @@ -129,12 +128,12 @@ convIP idx ipi = -- May return Nothing if the package can't be found in the index. That -- indicates that the original package having this dependency is broken -- and should be ignored. -convIPId :: PN -> SI.InstalledPackageIndex -> UnitId -> Maybe (FlaggedDep () PN) -convIPId pn' idx ipid = +convIPId :: PN -> Component -> SI.InstalledPackageIndex -> UnitId -> Maybe (FlaggedDep PN) +convIPId pn' comp idx ipid = case SI.lookupUnitId idx ipid of Nothing -> Nothing Just ipi -> let (pn, i) = convId ipi - in Just (D.Simple (Dep False pn (Fixed i (P pn'))) ()) + in Just (D.Simple (Dep False pn (Fixed i (P pn'))) comp) -- NB: something we pick up from the -- InstalledPackageIndex is NEVER an executable @@ -142,13 +141,13 @@ convIPId pn' idx ipid = -- more uniform index format of the solver. convSPI' :: OS -> Arch -> CompilerInfo -> StrongFlags -> SolveExecutables -> CI.PackageIndex (SourcePackage loc) -> [(PN, I, PInfo)] -convSPI' os arch cinfo strfl sexes = L.map (convSP os arch cinfo strfl sexes) . CI.allPackages +convSPI' os arch cinfo strfl solveExes = L.map (convSP os arch cinfo strfl solveExes) . CI.allPackages -- | Convert a single source package into the solver-specific format. convSP :: OS -> Arch -> CompilerInfo -> StrongFlags -> SolveExecutables -> SourcePackage loc -> (PN, I, PInfo) -convSP os arch cinfo strfl sexes (SourcePackage (PackageIdentifier pn pv) gpd _ _pl) = +convSP os arch cinfo strfl solveExes (SourcePackage (PackageIdentifier pn pv) gpd _ _pl) = let i = I pv InRepo - in (pn, i, convGPD os arch cinfo strfl sexes (PI pn i) gpd) + in (pn, i, convGPD os arch cinfo strfl solveExes (PI pn i) gpd) -- We do not use 'flattenPackageDescription' or 'finalizePD' -- from 'Distribution.PackageDescription.Configuration' here, because we @@ -157,7 +156,7 @@ convSP os arch cinfo strfl sexes (SourcePackage (PackageIdentifier pn pv) gpd _ -- | Convert a generic package description to a solver-specific 'PInfo'. convGPD :: OS -> Arch -> CompilerInfo -> StrongFlags -> SolveExecutables -> PI PN -> GenericPackageDescription -> PInfo -convGPD os arch cinfo strfl sexes pi +convGPD os arch cinfo strfl solveExes pi (GenericPackageDescription pkg flags mlib sub_libs flibs exes tests benchs) = let fds = flagInfo strfl flags @@ -172,8 +171,8 @@ convGPD os arch cinfo strfl sexes pi | (nm, _) <- sub_libs ] conv :: Mon.Monoid a => Component -> (a -> BuildInfo) -> - CondTree ConfVar [Dependency] a -> FlaggedDeps Component PN - conv comp getInfo = convCondTree pkg os arch cinfo pi fds comp getInfo ipns sexes . + CondTree ConfVar [Dependency] a -> FlaggedDeps PN + conv comp getInfo = convCondTree pkg os arch cinfo pi fds comp getInfo ipns solveExes . PDC.addBuildableCondition getInfo flagged_deps @@ -193,8 +192,8 @@ convGPD os arch cinfo strfl sexes pi -- | Create a flagged dependency tree from a list @fds@ of flagged -- dependencies, using @f@ to form the tree node (@f@ will be -- something like @Stanza sn@). -prefix :: (FlaggedDeps comp qpn -> FlaggedDep comp' qpn) - -> [FlaggedDeps comp qpn] -> FlaggedDeps comp' qpn +prefix :: (FlaggedDeps qpn -> FlaggedDep qpn) + -> [FlaggedDeps qpn] -> FlaggedDeps qpn prefix _ [] = [] prefix f fds = [f (concat fds)] @@ -213,7 +212,7 @@ type IPNs = Set PN -- | Convenience function to delete a 'FlaggedDep' if it's -- for a 'PN' that isn't actually real. -filterIPNs :: IPNs -> Dependency -> FlaggedDep Component PN -> FlaggedDeps Component PN +filterIPNs :: IPNs -> Dependency -> FlaggedDep PN -> FlaggedDeps PN filterIPNs ipns (Dependency pn _) fd | S.notMember pn ipns = [fd] | otherwise = [] @@ -226,22 +225,22 @@ convCondTree :: PackageDescription -> OS -> Arch -> CompilerInfo -> PI PN -> Fla (a -> BuildInfo) -> IPNs -> SolveExecutables -> - CondTree ConfVar [Dependency] a -> FlaggedDeps Component PN -convCondTree pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns sexes@(SolveExecutables sexes') (CondNode info ds branches) = + CondTree ConfVar [Dependency] a -> FlaggedDeps PN +convCondTree pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns solveExes@(SolveExecutables solveExes') (CondNode info ds branches) = concatMap (\d -> filterIPNs ipns d (D.Simple (convLibDep pn d) comp)) ds -- unconditional package dependencies ++ L.map (\e -> D.Simple (Ext e) comp) (PD.allExtensions bi) -- unconditional extension dependencies ++ L.map (\l -> D.Simple (Lang l) comp) (PD.allLanguages bi) -- unconditional language dependencies ++ L.map (\(PkgconfigDependency pkn vr) -> D.Simple (Pkg pkn vr) comp) (PD.pkgconfigDepends bi) -- unconditional pkg-config dependencies - ++ concatMap (convBranch pkg os arch cinfo pi fds comp getInfo ipns sexes) branches + ++ concatMap (convBranch pkg os arch cinfo pi fds comp getInfo ipns solveExes) branches -- build-tools dependencies -- NB: Only include these dependencies if SolveExecutables -- is True. It might be false in the legacy solver -- codepath, in which case there won't be any record of -- an executable we need. ++ [ D.Simple (convExeDep pn exeDep) comp - | sexes' + | solveExes' , exeDep <- getAllToolDependencies pkg bi , not $ isInternal pkg exeDep ] @@ -286,13 +285,13 @@ convBranch :: PackageDescription -> OS -> Arch -> CompilerInfo -> IPNs -> SolveExecutables -> CondBranch ConfVar [Dependency] a -> - FlaggedDeps Component PN -convBranch pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns sexes (CondBranch c' t' mf') = - go c' ( convCondTree pkg os arch cinfo pi fds comp getInfo ipns sexes t') - (maybe [] (convCondTree pkg os arch cinfo pi fds comp getInfo ipns sexes) mf') + FlaggedDeps PN +convBranch pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns solveExes (CondBranch c' t' mf') = + go c' ( convCondTree pkg os arch cinfo pi fds comp getInfo ipns solveExes t') + (maybe [] (convCondTree pkg os arch cinfo pi fds comp getInfo ipns solveExes) mf') where go :: Condition ConfVar -> - FlaggedDeps Component PN -> FlaggedDeps Component PN -> FlaggedDeps Component PN + FlaggedDeps PN -> FlaggedDeps PN -> FlaggedDeps PN go (Lit True) t _ = t go (Lit False) _ f = f go (CNot c) t f = go c f t @@ -328,7 +327,7 @@ convBranch pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns sexes (CondBranc -- point have been generated using 'convLibDep'. -- -- WARNING: This is quadratic! - extractCommon :: FlaggedDeps Component PN -> FlaggedDeps Component PN -> FlaggedDeps Component PN + extractCommon :: FlaggedDeps PN -> FlaggedDeps PN -> FlaggedDeps PN extractCommon ps ps' = [ D.Simple (Dep is_exe1 pn1 (Constrained [(vr1 .||. vr2, P pn)])) comp | D.Simple (Dep is_exe1 pn1 (Constrained [(vr1, _)])) _ <- ps , D.Simple (Dep is_exe2 pn2 (Constrained [(vr2, _)])) _ <- ps' @@ -346,6 +345,6 @@ convExeDep :: PN -> ExeDependency -> Dep PN convExeDep pn' (ExeDependency pn _ vr) = Dep True pn (Constrained [(vr, P pn')]) -- | Convert setup dependencies -convSetupBuildInfo :: PI PN -> SetupBuildInfo -> FlaggedDeps Component PN +convSetupBuildInfo :: PI PN -> SetupBuildInfo -> FlaggedDeps PN convSetupBuildInfo (PI pn _i) nfo = L.map (\d -> D.Simple (convLibDep pn d) ComponentSetup) (PD.setupDepends nfo) diff --git a/cabal-install/Distribution/Solver/Modular/Linking.hs b/cabal-install/Distribution/Solver/Modular/Linking.hs index 03f1805c079..5f63eac1cb2 100644 --- a/cabal-install/Distribution/Solver/Modular/Linking.hs +++ b/cabal-install/Distribution/Solver/Modular/Linking.hs @@ -28,7 +28,6 @@ import qualified Distribution.Solver.Modular.WeightedPSQ as W import Distribution.Solver.Types.OptionalStanza import Distribution.Solver.Types.PackagePath -import Distribution.Solver.Types.ComponentDeps (Component) import Distribution.Types.GenericPackageDescription (unFlagName) {------------------------------------------------------------------------------- @@ -149,7 +148,7 @@ conflict = lift' . Left execUpdateState :: UpdateState () -> ValidateState -> Either Conflict ValidateState execUpdateState = execStateT . unUpdateState -pickPOption :: QPN -> POption -> FlaggedDeps Component QPN -> UpdateState () +pickPOption :: QPN -> POption -> FlaggedDeps QPN -> UpdateState () pickPOption qpn (POption i Nothing) _deps = pickConcrete qpn i pickPOption qpn (POption i (Just pp')) deps = pickLink qpn i pp' deps @@ -167,7 +166,7 @@ pickConcrete qpn@(Q pp _) i = do Just lg -> makeCanonical lg qpn i -pickLink :: QPN -> I -> PackagePath -> FlaggedDeps Component QPN -> UpdateState () +pickLink :: QPN -> I -> PackagePath -> FlaggedDeps QPN -> UpdateState () pickLink qpn@(Q _pp pn) i pp' deps = do vs <- get @@ -223,7 +222,7 @@ makeCanonical lg qpn@(Q pp _) i = -- because having the direct dependencies in a link group means that we must -- have already made or will make sooner or later a link choice for one of these -- as well, and cover their dependencies at that point. -linkDeps :: QPN -> [Var QPN] -> FlaggedDeps Component QPN -> UpdateState () +linkDeps :: QPN -> [Var QPN] -> FlaggedDeps QPN -> UpdateState () linkDeps target = \blame deps -> do -- linkDeps is called in two places: when we first link one package to -- another, and when we discover more dependencies of an already linked @@ -233,10 +232,10 @@ linkDeps target = \blame deps -> do rdeps <- requalify deps go blame deps rdeps where - go :: [Var QPN] -> FlaggedDeps Component QPN -> FlaggedDeps Component QPN -> UpdateState () + go :: [Var QPN] -> FlaggedDeps QPN -> FlaggedDeps QPN -> UpdateState () go = zipWithM_ . go1 - go1 :: [Var QPN] -> FlaggedDep Component QPN -> FlaggedDep Component QPN -> UpdateState () + go1 :: [Var QPN] -> FlaggedDep QPN -> FlaggedDep QPN -> UpdateState () go1 blame dep rdep = case (dep, rdep) of (Simple (Dep _ qpn _) _, ~(Simple (Dep _ qpn' _) _)) -> do vs <- get @@ -263,7 +262,7 @@ linkDeps target = \blame deps -> do (Simple (Lang _) _, _) -> return () (Simple (Pkg _ _) _, _) -> return () - requalify :: FlaggedDeps Component QPN -> UpdateState (FlaggedDeps Component QPN) + requalify :: FlaggedDeps QPN -> UpdateState (FlaggedDeps QPN) requalify deps = do vs <- get return $ qualifyDeps (vsQualifyOptions vs) target (unqualifyDeps deps) @@ -298,10 +297,10 @@ linkNewDeps var b = do linkedTo = S.delete pp (lgMembers lg) forM_ (S.toList linkedTo) $ \pp' -> linkDeps (Q pp' pn) (P qpn : parents) newDeps where - findNewDeps :: ValidateState -> FlaggedDeps comp QPN -> ([Var QPN], FlaggedDeps Component QPN) + findNewDeps :: ValidateState -> FlaggedDeps QPN -> ([Var QPN], FlaggedDeps QPN) findNewDeps vs = concatMapUnzip (findNewDeps' vs) - findNewDeps' :: ValidateState -> FlaggedDep comp QPN -> ([Var QPN], FlaggedDeps Component QPN) + findNewDeps' :: ValidateState -> FlaggedDep QPN -> ([Var QPN], FlaggedDeps QPN) findNewDeps' _ (Simple _ _) = ([], []) findNewDeps' vs (Flagged qfn _ t f) = case (F qfn == var, M.lookup qfn (vsFlags vs)) of diff --git a/cabal-install/Distribution/Solver/Modular/PSQ.hs b/cabal-install/Distribution/Solver/Modular/PSQ.hs index e76fcff08d3..d589a49cde3 100644 --- a/cabal-install/Distribution/Solver/Modular/PSQ.hs +++ b/cabal-install/Distribution/Solver/Modular/PSQ.hs @@ -24,7 +24,6 @@ module Distribution.Solver.Modular.PSQ , snoc , sortBy , sortByKeys - , splits , toList , union ) where @@ -78,13 +77,6 @@ casePSQ (PSQ xs) n c = [] -> n (k, v) : ys -> c k v (PSQ ys) -splits :: PSQ k a -> PSQ k (a, PSQ k a) -splits = go id - where - go f xs = casePSQ xs - (PSQ []) - (\ k v ys -> cons k (v, f ys) (go (f . cons k v) ys)) - sortBy :: (a -> a -> Ordering) -> PSQ k a -> PSQ k a sortBy cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` snd) xs) diff --git a/cabal-install/Distribution/Solver/Modular/Tree.hs b/cabal-install/Distribution/Solver/Modular/Tree.hs index 535127d84a1..ddd2029a12c 100644 --- a/cabal-install/Distribution/Solver/Modular/Tree.hs +++ b/cabal-install/Distribution/Solver/Modular/Tree.hs @@ -72,7 +72,6 @@ data Tree d c = -- | We failed to find a solution in this path through the tree | Fail ConflictSet FailReason - deriving (Eq, Show) -- | A package option is a package instance with an optional linking annotation -- diff --git a/cabal-install/Distribution/Solver/Modular/Validate.hs b/cabal-install/Distribution/Solver/Modular/Validate.hs index ac215958881..963937edff4 100644 --- a/cabal-install/Distribution/Solver/Modular/Validate.hs +++ b/cabal-install/Distribution/Solver/Modular/Validate.hs @@ -29,8 +29,6 @@ import Distribution.Solver.Modular.Tree import Distribution.Solver.Modular.Version (VR) import qualified Distribution.Solver.Modular.WeightedPSQ as W -import Distribution.Solver.Types.ComponentDeps (Component) - import Distribution.Solver.Types.PackagePath import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb, pkgConfigPkgIsPresent) @@ -90,7 +88,7 @@ data ValidateState = VS { supportedLang :: Language -> Bool, presentPkgs :: PkgconfigName -> VR -> Bool, index :: Index, - saved :: Map QPN (FlaggedDeps Component QPN), -- saved, scoped, dependencies + saved :: Map QPN (FlaggedDeps QPN), -- saved, scoped, dependencies pa :: PreAssignment, qualifyOptions :: QualifyOptions } @@ -221,7 +219,7 @@ validate = cata go -- | We try to extract as many concrete dependencies from the given flagged -- dependencies as possible. We make use of all the flag knowledge we have -- already acquired. -extractDeps :: FAssignment -> SAssignment -> FlaggedDeps comp QPN -> [Dep QPN] +extractDeps :: FAssignment -> SAssignment -> FlaggedDeps QPN -> [Dep QPN] extractDeps fa sa deps = do d <- deps case d of @@ -238,10 +236,10 @@ extractDeps fa sa deps = do -- | We try to find new dependencies that become available due to the given -- flag or stanza choice. We therefore look for the choice in question, and then call -- 'extractDeps' for everything underneath. -extractNewDeps :: Var QPN -> Bool -> FAssignment -> SAssignment -> FlaggedDeps comp QPN -> [Dep QPN] +extractNewDeps :: Var QPN -> Bool -> FAssignment -> SAssignment -> FlaggedDeps QPN -> [Dep QPN] extractNewDeps v b fa sa = go where - go :: FlaggedDeps comp QPN -> [Dep QPN] -- Type annotation necessary (polymorphic recursion) + go :: FlaggedDeps QPN -> [Dep QPN] go deps = do d <- deps case d of diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index 7dac8302861..c499a164bdb 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -466,7 +466,7 @@ Test-Suite unit-tests UnitTests.Distribution.Client.JobControl UnitTests.Distribution.Client.IndexUtils.Timestamp UnitTests.Distribution.Client.InstallPlan - UnitTests.Distribution.Solver.Modular.PSQ + UnitTests.Distribution.Solver.Modular.Builder UnitTests.Distribution.Solver.Modular.RetryLog UnitTests.Distribution.Solver.Modular.Solver UnitTests.Distribution.Solver.Modular.DSL diff --git a/cabal-install/tests/UnitTests.hs b/cabal-install/tests/UnitTests.hs index b7a7837ece5..a0d3b12fd96 100644 --- a/cabal-install/tests/UnitTests.hs +++ b/cabal-install/tests/UnitTests.hs @@ -10,7 +10,7 @@ import Distribution.Verbosity import Distribution.Compat.Time -import qualified UnitTests.Distribution.Solver.Modular.PSQ +import qualified UnitTests.Distribution.Solver.Modular.Builder import qualified UnitTests.Distribution.Solver.Modular.WeightedPSQ import qualified UnitTests.Distribution.Solver.Modular.Solver import qualified UnitTests.Distribution.Solver.Modular.RetryLog @@ -38,8 +38,8 @@ tests mtimeChangeCalibrated = else mtimeChangeCalibrated in testGroup "Unit Tests" - [ testGroup "UnitTests.Distribution.Solver.Modular.PSQ" - UnitTests.Distribution.Solver.Modular.PSQ.tests + [ testGroup "UnitTests.Distribution.Solver.Modular.Builder" + UnitTests.Distribution.Solver.Modular.Builder.tests , testGroup "UnitTests.Distribution.Solver.Modular.WeightedPSQ" UnitTests.Distribution.Solver.Modular.WeightedPSQ.tests , testGroup "UnitTests.Distribution.Solver.Modular.Solver" diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Builder.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Builder.hs new file mode 100644 index 00000000000..b8509834a2b --- /dev/null +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Builder.hs @@ -0,0 +1,20 @@ +module UnitTests.Distribution.Solver.Modular.Builder ( + tests + ) where + +import Distribution.Solver.Modular.Builder + +import Test.Tasty +import Test.Tasty.QuickCheck + +tests :: [TestTree] +tests = [ testProperty "splitsAltImplementation" splitsTest + ] + +-- | Simpler splits implementation +splits' :: [a] -> [(a, [a])] +splits' [] = [] +splits' (x : xs) = (x, xs) : map (\ (y, ys) -> (y, x : ys)) (splits' xs) + +splitsTest :: [Int] -> Property +splitsTest xs = splits' xs === splits xs diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/PSQ.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/PSQ.hs deleted file mode 100644 index 851c6dee37a..00000000000 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/PSQ.hs +++ /dev/null @@ -1,22 +0,0 @@ -module UnitTests.Distribution.Solver.Modular.PSQ ( - tests - ) where - -import Distribution.Solver.Modular.PSQ - -import Test.Tasty -import Test.Tasty.QuickCheck - -tests :: [TestTree] -tests = [ testProperty "splitsAltImplementation" splitsTest - ] - --- | Original splits implementation -splits' :: PSQ k a -> PSQ k (a, PSQ k a) -splits' xs = - casePSQ xs - (PSQ []) - (\ k v ys -> cons k (v, ys) (fmap (\ (w, zs) -> (w, cons k v zs)) (splits' ys))) - -splitsTest :: [(Int, Int)] -> Bool -splitsTest psq = splits' (PSQ psq) == splits (PSQ psq)