Skip to content

Track static vs. dynamic dependencies. #8461

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

Closed
wants to merge 4 commits into from
Closed
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
15 changes: 13 additions & 2 deletions Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ data InstalledPackageInfo
frameworks :: [String],
haddockInterfaces :: [FilePath],
haddockHTMLs :: [FilePath],
pkgRoot :: Maybe FilePath
pkgRoot :: Maybe FilePath,
-- Artifacts included in this package:
pkgVanillaLib :: Bool,
pkgSharedLib :: Bool,
pkgDynExe :: Bool,
pkgProfLib :: Bool,
pkgProfExe :: Bool
}
deriving (Eq, Generic, Typeable, Read, Show)

Expand Down Expand Up @@ -173,5 +179,10 @@ emptyInstalledPackageInfo
haddockInterfaces = [],
haddockHTMLs = [],
pkgRoot = Nothing,
libVisibility = LibraryVisibilityPrivate
libVisibility = LibraryVisibilityPrivate,
pkgVanillaLib = True,
pkgSharedLib = True,
pkgDynExe = True,
pkgProfLib = True,
pkgProfExe = True
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ ipiFieldGrammar = mkInstalledPackageInfo
<@> monoidalFieldAla "haddock-interfaces" (alaList' FSep FilePathNT) L.haddockInterfaces
<@> monoidalFieldAla "haddock-html" (alaList' FSep FilePathNT) L.haddockHTMLs
<@> optionalFieldAla "pkgroot" FilePathNT L.pkgRoot
<@> booleanFieldDef "pkg-vanilla-lib" L.pkgVanillaLib True
<@> booleanFieldDef "pkg-shared-lib" L.pkgSharedLib True
<@> booleanFieldDef "pkg-dyn-exe" L.pkgDynExe True
<@> booleanFieldDef "pkg-prof-lib" L.pkgProfLib True
<@> booleanFieldDef "pkg-prof-exe" L.pkgProfExe True
where
mkInstalledPackageInfo _ Basic {..} = InstalledPackageInfo
-- _basicPkgName is not used
Expand Down
20 changes: 20 additions & 0 deletions Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,23 @@ libVisibility :: Lens' InstalledPackageInfo LibraryVisibility
libVisibility f s = fmap (\x -> s { T.libVisibility = x }) (f (T.libVisibility s))
{-# INLINE libVisibility #-}

pkgVanillaLib :: Lens' InstalledPackageInfo Bool
pkgVanillaLib f s = fmap (\x -> s { T.pkgVanillaLib = x }) (f (T.pkgVanillaLib s))
{-# INLINE pkgVanillaLib #-}

pkgSharedLib :: Lens' InstalledPackageInfo Bool
pkgSharedLib f s = fmap (\x -> s { T.pkgSharedLib = x }) (f (T.pkgSharedLib s))
{-# INLINE pkgSharedLib #-}

pkgDynExe :: Lens' InstalledPackageInfo Bool
pkgDynExe f s = fmap (\x -> s { T.pkgDynExe = x }) (f (T.pkgDynExe s))
{-# INLINE pkgDynExe #-}

pkgProfLib :: Lens' InstalledPackageInfo Bool
pkgProfLib f s = fmap (\x -> s { T.pkgProfLib = x }) (f (T.pkgProfLib s))
{-# INLINE pkgProfLib #-}

pkgProfExe :: Lens' InstalledPackageInfo Bool
pkgProfExe f s = fmap (\x -> s { T.pkgProfExe = x }) (f (T.pkgProfExe s))
{-# INLINE pkgProfExe #-}

7 changes: 6 additions & 1 deletion Cabal/src/Distribution/Simple/Register.hs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,12 @@ generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDi
IPI.haddockInterfaces = [haddockdir installDirs </> haddockName pkg],
IPI.haddockHTMLs = [htmldir installDirs],
IPI.pkgRoot = Nothing,
IPI.libVisibility = libVisibility lib
IPI.libVisibility = libVisibility lib,
IPI.pkgVanillaLib = withVanillaLib lbi,
IPI.pkgSharedLib = withProfLib lbi,
IPI.pkgDynExe = withSharedLib lbi,
IPI.pkgProfLib = withStaticLib lbi,
IPI.pkgProfExe = withDynExe lbi
}
where
ghc84 = case compilerId $ compiler lbi of
Expand Down
1 change: 1 addition & 0 deletions cabal-install-solver/cabal-install-solver.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ library
Distribution.Solver.Modular.Var
Distribution.Solver.Modular.Version
Distribution.Solver.Modular.WeightedPSQ
Distribution.Solver.Types.ArtifactSelection
Distribution.Solver.Types.ComponentDeps
Distribution.Solver.Types.ConstraintSource
Distribution.Solver.Types.DependencyResolver
Expand Down
71 changes: 42 additions & 29 deletions cabal-install-solver/src/Distribution/Solver/Modular/Builder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ 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.ArtifactSelection
import Distribution.Solver.Types.ComponentDeps
import Distribution.Solver.Types.PackagePath
import Distribution.Solver.Types.Settings
Expand Down Expand Up @@ -61,18 +62,18 @@ type LinkingState = M.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 -> [FlaggedDep QPN] -> BuildState -> BuildState
extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs
extendOpen :: QPN -> [FlaggedDep QPN] -> ArtifactSelection -> BuildState -> BuildState
extendOpen qpn' gs arts s@(BS { rdeps = gs', open = o' }) = go gs' o' gs
where
go :: RevDepMap -> [OpenGoal] -> [FlaggedDep QPN] -> BuildState
go g o [] = s { rdeps = g, open = o }
go g o ((Flagged fn@(FN qpn _) fInfo t f) : ngs) =
go g (FlagGoal fn fInfo t f (flagGR qpn) : o) ngs
go g (FlagGoal fn fInfo t f arts (flagGR qpn) : 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 ((Stanza sn@(SN qpn _) t) : ngs) =
go g (StanzaGoal sn t (flagGR qpn) : o) ngs
go g (StanzaGoal sn t arts (flagGR qpn) : o) ngs
go g o ((Simple (LDep dr (Dep (PkgComponent qpn _) _)) c) : ngs)
| qpn == qpn' =
-- We currently only add a self-dependency to the graph if it is
Expand All @@ -84,7 +85,7 @@ extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs
ComponentSetup -> go (M.adjust (addIfAbsent (ComponentSetup, qpn')) qpn g) o ngs
_ -> go g o ngs
| qpn `M.member` g = go (M.adjust (addIfAbsent (c, qpn')) qpn g) o ngs
| otherwise = go (M.insert qpn [(c, qpn')] g) (PkgGoal qpn (DependencyGoal dr) : o) ngs
| otherwise = go (M.insert qpn [(c, qpn')] g) (PkgGoal qpn arts (DependencyGoal dr) : o) ngs
-- code above is correct; insert/adjust have different arg order
go g o ((Simple (LDep _dr (Ext _ext )) _) : ngs) = go g o ngs
go g o ((Simple (LDep _dr (Lang _lang))_) : ngs) = go g o ngs
Expand All @@ -100,9 +101,9 @@ extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs

-- | 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 -> FlaggedDeps PN -> FlagInfo ->
scopedExtendOpen :: QPN -> FlaggedDeps PN -> FlagInfo -> ArtifactSelection ->
BuildState -> BuildState
scopedExtendOpen qpn fdeps fdefs s = extendOpen qpn gs s
scopedExtendOpen qpn fdeps fdefs arts s = extendOpen qpn gs arts s
where
-- Qualify all package names
qfdeps = qualifyDeps (qualifyOptions s) qpn fdeps
Expand All @@ -117,9 +118,10 @@ scopedExtendOpen qpn 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
| Instance QPN PInfo -- ^ build a tree for a concrete instance
Goals -- ^ build a goal choice node
| OneGoal OpenGoal -- ^ build a node for this goal
| Instance QPN PInfo -- ^ build a tree for a concrete instance
| FailSeed ConflictSet FailReason -- ^ an error occurred while we had access to the package info

build :: Linker BuildState -> Tree () QGoalReason
build = ana go
Expand All @@ -143,22 +145,28 @@ addChildren bs@(BS { rdeps = rdm, open = gs, next = Goals })
--
-- For a package, we look up the instances available in the global info,
-- and then handle each instance in turn.
addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (PkgGoal qpn@(Q _ pn) gr) }) =
addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (PkgGoal qpn@(Q _ pn) requiredArts gr) }) =
case M.lookup pn idx of
Nothing -> FailF
(varToConflictSet (P qpn) `CS.union` goalReasonToConflictSetWithConflict qpn gr)
UnknownPackage
Nothing -> FailF cs UnknownPackage
Just pis -> PChoiceF qpn rdm gr (W.fromList (L.map (\ (i, info) ->
([], POption i Nothing, bs { next = Instance qpn info }))
([], POption i Nothing, infoBs info))
(M.toList pis)))
-- TODO: data structure conversion is rather ugly here
where
infoBs info = bs { next = validateArts (getArts info) $ Instance qpn info }
getArts (PInfo _ _ _ _ arts) = arts
validateArts arts withSuccess
| requiredArts `artsSubsetOf` arts = withSuccess
| otherwise = FailSeed cs (rs arts)
cs = varToConflictSet (P qpn) `CS.union` goalReasonToConflictSetWithConflict qpn gr
rs arts = MissingArtifacts $ requiredArts `artsDifference` arts

-- 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 (FlagGoal qfn@(FN qpn _) (FInfo b m w) t f gr) }) =
addChildren bs@(BS { rdeps = rdm, next = OneGoal (FlagGoal qfn@(FN qpn _) (FInfo b m w) t f arts gr) }) =
FChoiceF qfn rdm gr weak m b (W.fromList
[([if b then 0 else 1], True, (extendOpen qpn t bs) { next = Goals }),
([if b then 1 else 0], False, (extendOpen qpn f bs) { next = Goals })])
[([if b then 0 else 1], True, (extendOpen qpn t arts bs) { next = Goals }),
([if b then 1 else 0], False, (extendOpen qpn f arts bs) { next = Goals })])
where
trivial = L.null t && L.null f
weak = WeakOrTrivial $ unWeakOrTrivial w || trivial
Expand All @@ -168,21 +176,26 @@ addChildren bs@(BS { rdeps = rdm, next = OneGoal (FlagGoal qfn@(FN qpn _) (FInfo
-- 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 (StanzaGoal qsn@(SN qpn _) t gr) }) =
addChildren bs@(BS { rdeps = rdm, next = OneGoal (StanzaGoal qsn@(SN qpn _) t arts gr) }) =
SChoiceF qsn rdm gr trivial (W.fromList
[([0], False, bs { next = Goals }),
([1], True, (extendOpen qpn t bs) { next = Goals })])
([1], True, (extendOpen qpn t arts bs) { next = Goals })])
where
trivial = WeakOrTrivial (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.
addChildren bs@(BS { next = Instance qpn (PInfo fdeps _ fdefs _) }) =
addChildren ((scopedExtendOpen qpn fdeps fdefs bs)
addChildren bs@(BS { next = Instance qpn (PInfo fdeps _ fdefs _ arts) }) =
addChildren ((scopedExtendOpen qpn fdeps fdefs arts bs)
{ next = Goals })

-- While building the tree, we detected a failure from information we had while
-- we were aware of the package info.
addChildren (BS { next = FailSeed cs fr }) =
FailF cs fr

{-------------------------------------------------------------------------------
Add linking
-------------------------------------------------------------------------------}
Expand Down Expand Up @@ -260,7 +273,7 @@ buildTree idx (IndependentGoals ind) igs =
, linkingState = M.empty
}
where
topLevelGoal qpn = PkgGoal qpn UserGoal
topLevelGoal qpn = PkgGoal qpn noOuts UserGoal

qpns | ind = L.map makeIndependent igs
| otherwise = L.map (Q (PackagePath DefaultNamespace QualToplevel)) igs
Expand All @@ -271,16 +284,16 @@ buildTree idx (IndependentGoals ind) igs =

-- | Information needed about a dependency before it is converted into a Goal.
data OpenGoal =
FlagGoal (FN QPN) FInfo (FlaggedDeps QPN) (FlaggedDeps QPN) QGoalReason
| StanzaGoal (SN QPN) (FlaggedDeps QPN) QGoalReason
| PkgGoal QPN QGoalReason
FlagGoal (FN QPN) FInfo (FlaggedDeps QPN) (FlaggedDeps QPN) ArtifactSelection QGoalReason
| StanzaGoal (SN QPN) (FlaggedDeps QPN) ArtifactSelection QGoalReason
| PkgGoal QPN ArtifactSelection 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
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ exploreLog mbj enableBj fineGrainedConflicts (CountConflicts countConflicts) idx
-- to be merged with the previous one.
couldResolveConflicts :: QPN -> POption -> S.Set CS.Conflict -> Maybe ConflictSet
couldResolveConflicts currentQPN@(Q _ pn) (POption i@(I v _) _) conflicts =
let (PInfo deps _ _ _) = idx M.! pn M.! i
let (PInfo deps _ _ _ _) = idx M.! pn M.! i
qdeps = qualifyDeps (defaultQualifyOptions idx) currentQPN deps

couldBeResolved :: CS.Conflict -> Maybe ConflictSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Distribution.Solver.Modular.Dependency
import Distribution.Solver.Modular.Flag
import Distribution.Solver.Modular.Package
import Distribution.Solver.Modular.Tree
import Distribution.Solver.Types.ArtifactSelection

-- | An index contains information about package instances. This is a nested
-- dictionary. Package names are mapped to instances, which in turn is mapped
Expand All @@ -36,6 +37,7 @@ data PInfo = PInfo (FlaggedDeps PN)
(Map ExposedComponent ComponentInfo)
FlagInfo
(Maybe FailReason)
ArtifactSelection -- Which artifacts are available? (sdists have all.)

-- | Info associated with each library and executable in a package instance.
data ComponentInfo = ComponentInfo {
Expand Down Expand Up @@ -64,7 +66,7 @@ defaultQualifyOptions idx = QO {
| -- Find all versions of base ..
Just is <- [M.lookup base idx]
-- .. which are installed ..
, (I _ver (Inst _), PInfo deps _comps _flagNfo _fr) <- M.toList is
, (I _ver (Inst _), PInfo deps _comps _flagNfo _fr _arts) <- M.toList is
-- .. and flatten all their dependencies ..
, (LDep _ (Dep (PkgComponent dep _) _ci), _comp) <- flattenFlaggedDeps deps
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import Distribution.PackageDescription.Configuration
import qualified Distribution.Simple.PackageIndex as SI
import Distribution.System

import Distribution.Solver.Types.ArtifactSelection
( ArtifactSelection(..), allArtifacts, staticOutsOnly, dynOutsOnly )
import Distribution.Solver.Types.ComponentDeps
( Component(..), componentNameToComponent )
import Distribution.Solver.Types.Flag
Expand Down Expand Up @@ -75,8 +77,8 @@ convIPI' (ShadowPkgs sip) idx =
where

-- shadowing is recorded in the package info
shadow (pn, i, PInfo fdeps comps fds _)
| sip = (pn, i, PInfo fdeps comps fds (Just Shadowed))
shadow (pn, i, PInfo fdeps comps fds _ arts)
| sip = (pn, i, PInfo fdeps comps fds (Just Shadowed) arts)
shadow x = x

-- | Extract/recover the package ID from an installed package info, and convert it to a solver's I.
Expand All @@ -90,8 +92,8 @@ convId ipi = (pn, I ver $ Inst $ IPI.installedUnitId ipi)
convIP :: SI.InstalledPackageIndex -> IPI.InstalledPackageInfo -> (PN, I, PInfo)
convIP idx ipi =
case traverse (convIPId (DependencyReason pn M.empty S.empty) comp idx) (IPI.depends ipi) of
Left u -> (pn, i, PInfo [] M.empty M.empty (Just (Broken u)))
Right fds -> (pn, i, PInfo fds components M.empty Nothing)
Left u -> (pn, i, PInfo [] M.empty M.empty (Just (Broken u)) mempty)
Right fds -> (pn, i, PInfo fds components M.empty Nothing (ipiToAS ipi))
where
-- TODO: Handle sub-libraries and visibility.
components =
Expand Down Expand Up @@ -151,6 +153,16 @@ convIPId dr comp idx ipid =
-- NB: something we pick up from the
-- InstalledPackageIndex is NEVER an executable

-- | Extract the ArtifactSelection representing which artifacts are available
-- in this installed package.
ipiToAS :: IPI.InstalledPackageInfo -> ArtifactSelection
ipiToAS ipi = mconcat [statics, dynamics]
where
statics :: ArtifactSelection
statics = if any ($ ipi) [IPI.pkgVanillaLib] then staticOutsOnly else mempty
dynamics :: ArtifactSelection
dynamics = if any ($ ipi) [IPI.pkgSharedLib, IPI.pkgDynExe] then dynOutsOnly else mempty

-- | Convert a cabal-install source package index to the simpler,
-- more uniform index format of the solver.
convSPI' :: OS -> Arch -> CompilerInfo -> Map PN [LabeledPackageConstraint]
Expand Down Expand Up @@ -238,7 +250,7 @@ convGPD os arch cinfo constraints strfl solveExes pn
isPrivate LibraryVisibilityPrivate = True
isPrivate LibraryVisibilityPublic = False

in PInfo flagged_deps components fds fr
in PInfo flagged_deps components fds fr allArtifacts

-- | Applies the given predicate (for example, testing buildability or
-- visibility) to the given component and environment. Values are combined with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ validateLinking index = (`runReader` initVS) . go
goP :: QPN -> POption -> Validate (Tree d c) -> Validate (Tree d c)
goP qpn@(Q _pp pn) opt@(POption i _) r = do
vs <- ask
let PInfo deps _ _ _ = vsIndex vs ! pn ! i
qdeps = qualifyDeps (vsQualifyOptions vs) qpn deps
newSaved = M.insert qpn qdeps (vsSaved vs)
let PInfo deps _ _ _ _ = vsIndex vs ! pn ! i
qdeps = qualifyDeps (vsQualifyOptions vs) qpn deps
newSaved = M.insert qpn qdeps (vsSaved vs)
case execUpdateState (pickPOption qpn opt qdeps) vs of
Left (cs, err) -> return $ Fail cs (DependenciesNotLinked err)
Right vs' -> local (const vs' { vsSaved = newSaved }) r
Expand Down Expand Up @@ -347,7 +347,7 @@ verifyLinkGroup lg =
-- if a constructor is added to the datatype we won't notice it here
Just i -> do
vs <- get
let PInfo _deps _exes finfo _ = vsIndex vs ! lgPackage lg ! i
let PInfo _deps _exes finfo _ _ = vsIndex vs ! lgPackage lg ! i -- TODO: arts can be ignored here, right?
flags = M.keys finfo
stanzas = [TestStanzas, BenchStanzas]
forM_ flags $ \fn -> do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ showFR _ MultipleInstances = " (multiple instances)"
showFR c (DependenciesNotLinked msg) = " (dependencies not linked: " ++ msg ++ "; conflict set: " ++ showConflictSet c ++ ")"
showFR c CyclicDependencies = " (cyclic dependencies; conflict set: " ++ showConflictSet c ++ ")"
showFR _ (UnsupportedSpecVer ver) = " (unsupported spec-version " ++ prettyShow ver ++ ")"
showFR _ (MissingArtifacts arts) = " (missing build artifacts: " ++ prettyShow arts ++ ")"
-- The following are internal failures. They should not occur. In the
-- interest of not crashing unnecessarily, we still just print an error
-- message though.
Expand Down
Loading