Skip to content

Commit b83e0c9

Browse files
authored
Merge pull request #6836 from fgaz/pr-6047-3
Solver: Support dependencies on sub-libraries (issue #6039) (3rd iteration)
2 parents 50d59cf + cbc2e53 commit b83e0c9

File tree

19 files changed

+468
-245
lines changed

19 files changed

+468
-245
lines changed

Cabal/Distribution/Simple/Compiler.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ module Distribution.Simple.Compiler (
6363
backpackSupported,
6464
arResponseFilesSupported,
6565
libraryDynDirSupported,
66+
libraryVisibilitySupported,
6667

6768
-- * Support for profiling detail levels
6869
ProfDetailLevel(..),
@@ -380,6 +381,15 @@ profilingSupported comp =
380381
GHCJS -> True
381382
_ -> False
382383

384+
-- | Does this compiler support a package database entry with:
385+
-- "visibility"?
386+
libraryVisibilitySupported :: Compiler -> Bool
387+
libraryVisibilitySupported comp = case compilerFlavor comp of
388+
GHC -> v >= mkVersion [8,8]
389+
_ -> False
390+
where
391+
v = compilerVersion comp
392+
383393
-- | Utility function for GHC only features
384394
ghcSupported :: String -> Compiler -> Bool
385395
ghcSupported key comp =

Cabal/Distribution/Types/CondTree.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ traverseCondBranchC f (CondBranch cnd t me) = CondBranch cnd
140140

141141
-- | Extract the condition matched by the given predicate from a cond tree.
142142
--
143-
-- We use this mainly for extracting buildable conditions (see the Note above),
144-
-- but the function is in fact more general.
143+
-- We use this mainly for extracting buildable conditions (see the Note in
144+
-- Distribution.PackageDescription.Configuration), but the function is in fact
145+
-- more general.
145146
extractCondition :: Eq v => (a -> Bool) -> CondTree v c a -> Condition v
146147
extractCondition p = go
147148
where

cabal-install/Distribution/Client/ProjectPlanning.hs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3412,9 +3412,7 @@ setupHsConfigureFlags (ReadyPackage elab@ElaboratedConfiguredPackage{..})
34123412
configUserInstall = mempty -- don't rely on defaults
34133413
configPrograms_ = mempty -- never use, shouldn't exist
34143414
configUseResponseFiles = mempty
3415-
-- TODO set to true when the solver can prevent private-library-deps by itself
3416-
-- (issue #6039)
3417-
configAllowDependingOnPrivateLibs = mempty
3415+
configAllowDependingOnPrivateLibs = Flag $ not $ libraryVisibilitySupported pkgConfigCompiler
34183416

34193417
setupHsConfigureArgs :: ElaboratedConfiguredPackage
34203418
-> [String]

cabal-install/Distribution/Client/Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ filterConfigureFlags flags cabalLibVersion
512512
convertToLegacyInternalDep (GivenComponent pn LMainLibName cid) =
513513
Just $ GivenComponent pn LMainLibName cid
514514
in catMaybes $ convertToLegacyInternalDep <$> configDependencies flags
515+
-- Cabal < 2.5 doesn't know about '--allow-depending-on-private-libs'.
516+
, configAllowDependingOnPrivateLibs = NoFlag
515517
-- Cabal < 2.5 doesn't know about '--enable/disable-executable-static'.
516518
, configFullyStaticExe = NoFlag
517519
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import qualified Distribution.Solver.Modular.ConflictSet as CS
5555

5656
import Distribution.Solver.Types.ComponentDeps (Component(..))
5757
import Distribution.Solver.Types.PackagePath
58+
import Distribution.Types.LibraryName
5859
import Distribution.Types.PkgconfigVersionRange
5960
import Distribution.Types.UnqualComponentName
6061

@@ -131,7 +132,9 @@ data PkgComponent qpn = PkgComponent qpn ExposedComponent
131132

132133
-- | A component that can be depended upon by another package, i.e., a library
133134
-- or an executable.
134-
data ExposedComponent = ExposedLib | ExposedExe UnqualComponentName
135+
data ExposedComponent =
136+
ExposedLib LibraryName
137+
| ExposedExe UnqualComponentName
135138
deriving (Eq, Ord, Show)
136139

137140
-- | The reason that a dependency is active. It identifies the package and any
@@ -185,7 +188,7 @@ qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go
185188
-- Suppose package B has a setup dependency on package A.
186189
-- This will be recorded as something like
187190
--
188-
-- > LDep (DependencyReason "B") (Dep (PkgComponent "A" ExposedLib) (Constrained AnyVersion))
191+
-- > LDep (DependencyReason "B") (Dep (PkgComponent "A" (ExposedLib LMainLibName)) (Constrained AnyVersion))
189192
--
190193
-- Observe that when we qualify this dependency, we need to turn that
191194
-- @"A"@ into @"B-setup.A"@, but we should not apply that same qualifier
@@ -199,7 +202,7 @@ qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go
199202
goD (Pkg pkn vr) _ = Pkg pkn vr
200203
goD (Dep dep@(PkgComponent qpn (ExposedExe _)) ci) _ =
201204
Dep (Q (PackagePath ns (QualExe pn qpn)) <$> dep) ci
202-
goD (Dep dep@(PkgComponent qpn ExposedLib) ci) comp
205+
goD (Dep dep@(PkgComponent qpn (ExposedLib _)) ci) comp
203206
| qBase qpn = Dep (Q (PackagePath ns (QualBase pn)) <$> dep) ci
204207
| qSetup comp = Dep (Q (PackagePath ns (QualSetup pn)) <$> dep) ci
205208
| otherwise = Dep (Q (PackagePath ns inheritedQ ) <$> dep) ci

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module Distribution.Solver.Modular.Index
22
( Index
33
, PInfo(..)
4+
, ComponentInfo(..)
5+
, IsVisible(..)
46
, IsBuildable(..)
57
, defaultQualifyOptions
68
, mkIndex
@@ -30,10 +32,24 @@ type Index = Map PN (Map I PInfo)
3032
-- globally, for reasons external to the solver. We currently use this
3133
-- for shadowing which essentially is a GHC limitation, and for
3234
-- installed packages that are broken.
33-
data PInfo = PInfo (FlaggedDeps PN) (Map ExposedComponent IsBuildable) FlagInfo (Maybe FailReason)
35+
data PInfo = PInfo (FlaggedDeps PN)
36+
(Map ExposedComponent ComponentInfo)
37+
FlagInfo
38+
(Maybe FailReason)
39+
40+
-- | Info associated with each library and executable in a package instance.
41+
data ComponentInfo = ComponentInfo {
42+
compIsVisible :: IsVisible
43+
, compIsBuildable :: IsBuildable
44+
}
45+
46+
-- | Whether a component is visible in the current environment.
47+
newtype IsVisible = IsVisible Bool
48+
deriving Eq
3449

3550
-- | Whether a component is made unbuildable by a "buildable: False" field.
3651
newtype IsBuildable = IsBuildable Bool
52+
deriving Eq
3753

3854
mkIndex :: [(PN, I, PInfo)] -> Index
3955
mkIndex xs = M.map M.fromList (groupMap (L.map (\ (pn, i, pi) -> (pn, (i, pi))) xs))

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

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Prelude ()
77

88
import qualified Data.List as L
99
import qualified Data.Map.Strict as M
10+
import qualified Distribution.Compat.NonEmptySet as NonEmptySet
1011
import qualified Data.Set as S
1112

1213
import qualified Distribution.InstalledPackageInfo as IPI
@@ -25,6 +26,7 @@ import Distribution.PackageDescription.Configuration
2526
import qualified Distribution.Simple.PackageIndex as SI
2627
import Distribution.System
2728
import Distribution.Types.ForeignLib
29+
import Distribution.Types.LibraryVisibility
2830

2931
import Distribution.Solver.Types.ComponentDeps
3032
( Component(..), componentNameToComponent )
@@ -92,11 +94,18 @@ convIP :: SI.InstalledPackageIndex -> IPI.InstalledPackageInfo -> (PN, I, PInfo)
9294
convIP idx ipi =
9395
case traverse (convIPId (DependencyReason pn M.empty S.empty) comp idx) (IPI.depends ipi) of
9496
Nothing -> (pn, i, PInfo [] M.empty M.empty (Just Broken))
95-
Just fds -> ( pn
96-
, i
97-
, PInfo fds (M.singleton ExposedLib (IsBuildable True)) M.empty Nothing)
97+
Just fds -> ( pn, i, PInfo fds components M.empty Nothing)
9898
where
99+
-- TODO: Handle sub-libraries and visibility.
100+
components =
101+
M.singleton (ExposedLib LMainLibName)
102+
ComponentInfo {
103+
compIsVisible = IsVisible True
104+
, compIsBuildable = IsBuildable True
105+
}
106+
99107
(pn, i) = convId ipi
108+
100109
-- 'sourceLibName' is unreliable, but for now we only really use this for
101110
-- primary libs anyways
102111
comp = componentNameToComponent $ CLibName $ IPI.sourceLibName ipi
@@ -140,7 +149,8 @@ convIPId dr comp idx ipid =
140149
case SI.lookupUnitId idx ipid of
141150
Nothing -> Nothing
142151
Just ipi -> let (pn, i) = convId ipi
143-
in Just (D.Simple (LDep dr (Dep (PkgComponent pn ExposedLib) (Fixed i))) comp)
152+
name = ExposedLib LMainLibName -- TODO: Handle sub-libraries.
153+
in Just (D.Simple (LDep dr (Dep (PkgComponent pn name) (Fixed i))) comp)
144154
-- NB: something we pick up from the
145155
-- InstalledPackageIndex is NEVER an executable
146156

@@ -213,34 +223,52 @@ convGPD os arch cinfo constraints strfl solveExes pn
213223
Just ver -> Just (UnsupportedSpecVer ver)
214224
Nothing -> Nothing
215225

216-
components :: Map ExposedComponent IsBuildable
217-
components = M.fromList $ libComps ++ exeComps
226+
components :: Map ExposedComponent ComponentInfo
227+
components = M.fromList $ libComps ++ subLibComps ++ exeComps
218228
where
219-
libComps = [ (ExposedLib, IsBuildable $ isBuildable libBuildInfo lib)
229+
libComps = [ (ExposedLib LMainLibName, libToComponentInfo lib)
220230
| lib <- maybeToList mlib ]
221-
exeComps = [ (ExposedExe name, IsBuildable $ isBuildable buildInfo exe)
231+
subLibComps = [ (ExposedLib (LSubLibName name), libToComponentInfo lib)
232+
| (name, lib) <- sub_libs ]
233+
exeComps = [ ( ExposedExe name
234+
, ComponentInfo {
235+
compIsVisible = IsVisible True
236+
, compIsBuildable = IsBuildable $ testCondition (buildable . buildInfo) exe /= Just False
237+
}
238+
)
222239
| (name, exe) <- exes ]
223-
isBuildable = isBuildableComponent os arch cinfo constraints
240+
241+
libToComponentInfo lib =
242+
ComponentInfo {
243+
compIsVisible = IsVisible $ testCondition (isPrivate . libVisibility) lib /= Just True
244+
, compIsBuildable = IsBuildable $ testCondition (buildable . libBuildInfo) lib /= Just False
245+
}
246+
247+
testCondition = testConditionForComponent os arch cinfo constraints
248+
249+
isPrivate LibraryVisibilityPrivate = True
250+
isPrivate LibraryVisibilityPublic = False
224251

225252
in PInfo flagged_deps components fds fr
226253

227-
-- | Returns true if the component is buildable in the given environment.
228-
-- This function can give false-positives. For example, it only considers flags
229-
-- that are set by unqualified flag constraints, and it doesn't check whether
230-
-- the intra-package dependencies of a component are buildable. It is also
231-
-- possible for the solver to later assign a value to an automatic flag that
232-
-- makes the component unbuildable.
233-
isBuildableComponent :: OS
234-
-> Arch
235-
-> CompilerInfo
236-
-> [LabeledPackageConstraint]
237-
-> (a -> BuildInfo)
238-
-> CondTree ConfVar [Dependency] a
239-
-> Bool
240-
isBuildableComponent os arch cinfo constraints getInfo tree =
241-
case simplifyCondition $ extractCondition (buildable . getInfo) tree of
242-
Lit False -> False
243-
_ -> True
254+
-- | Applies the given predicate (for example, testing buildability or
255+
-- visibility) to the given component and environment. Values are combined with
256+
-- AND. This function returns 'Nothing' when the result cannot be determined
257+
-- before dependency solving. Additionally, this function only considers flags
258+
-- that are set by unqualified flag constraints, and it doesn't check the
259+
-- intra-package dependencies of a component.
260+
testConditionForComponent :: OS
261+
-> Arch
262+
-> CompilerInfo
263+
-> [LabeledPackageConstraint]
264+
-> (a -> Bool)
265+
-> CondTree ConfVar [Dependency] a
266+
-> Maybe Bool
267+
testConditionForComponent os arch cinfo constraints p tree =
268+
case simplifyCondition $ extractCondition p tree of
269+
Lit True -> Just True
270+
Lit False -> Just False
271+
_ -> Nothing
244272
where
245273
flagAssignment :: [(FlagName, Bool)]
246274
flagAssignment =
@@ -332,8 +360,10 @@ convCondTree flags dr pkg os arch cinfo pn fds comp getInfo ipns solveExes@(Solv
332360
-- duplicates could grow exponentially from the leaves to the root
333361
-- of the tree.
334362
mergeSimpleDeps $
335-
L.map (\d -> D.Simple (convLibDep dr d) comp)
336-
(mapMaybe (filterIPNs ipns) ds) -- unconditional package dependencies
363+
[ D.Simple singleDep comp
364+
| dep <- mapMaybe (filterIPNs ipns) ds
365+
, singleDep <- convLibDeps dr dep ] -- unconditional package dependencies
366+
337367
++ L.map (\e -> D.Simple (LDep dr (Ext e)) comp) (allExtensions bi) -- unconditional extension dependencies
338368
++ L.map (\l -> D.Simple (LDep dr (Lang l)) comp) (allLanguages bi) -- unconditional language dependencies
339369
++ L.map (\(PkgconfigDependency pkn vr) -> D.Simple (LDep dr (Pkg pkn vr)) comp) (pkgconfigDepends bi) -- unconditional pkg-config dependencies
@@ -537,9 +567,12 @@ unionDRs :: DependencyReason pn -> DependencyReason pn -> DependencyReason pn
537567
unionDRs (DependencyReason pn' fs1 ss1) (DependencyReason _ fs2 ss2) =
538568
DependencyReason pn' (M.union fs1 fs2) (S.union ss1 ss2)
539569

540-
-- | Convert a Cabal dependency on a library to a solver-specific dependency.
541-
convLibDep :: DependencyReason PN -> Dependency -> LDep PN
542-
convLibDep dr (Dependency pn vr _) = LDep dr $ Dep (PkgComponent pn ExposedLib) (Constrained vr)
570+
-- | Convert a Cabal dependency on a set of library components (from a single
571+
-- package) to solver-specific dependencies.
572+
convLibDeps :: DependencyReason PN -> Dependency -> [LDep PN]
573+
convLibDeps dr (Dependency pn vr libs) =
574+
[ LDep dr $ Dep (PkgComponent pn (ExposedLib lib)) (Constrained vr)
575+
| lib <- NonEmptySet.toList libs ]
543576

544577
-- | Convert a Cabal dependency on an executable (build-tools) to a solver-specific dependency.
545578
convExeDep :: DependencyReason PN -> ExeDependency -> LDep PN
@@ -548,5 +581,6 @@ convExeDep dr (ExeDependency pn exe vr) = LDep dr $ Dep (PkgComponent pn (Expose
548581
-- | Convert setup dependencies
549582
convSetupBuildInfo :: PN -> SetupBuildInfo -> FlaggedDeps PN
550583
convSetupBuildInfo pn nfo =
551-
L.map (\d -> D.Simple (convLibDep (DependencyReason pn M.empty S.empty) d) ComponentSetup)
552-
(setupDepends nfo)
584+
[ D.Simple singleDep ComponentSetup
585+
| dep <- setupDepends nfo
586+
, singleDep <- convLibDeps (DependencyReason pn M.empty S.empty) dep ]

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import Distribution.Solver.Modular.Version
2525
import Distribution.Solver.Types.ConstraintSource
2626
import Distribution.Solver.Types.PackagePath
2727
import Distribution.Solver.Types.Progress
28+
import Distribution.Types.LibraryName
2829
import Distribution.Types.UnqualComponentName
2930

3031
data Message =
@@ -220,8 +221,10 @@ showFR _ (MissingPkgconfigPackage pn vr) = " (conflict: pkg-config package " ++
220221
showFR _ (NewPackageDoesNotMatchExistingConstraint d) = " (conflict: " ++ showConflictingDep d ++ ")"
221222
showFR _ (ConflictingConstraints d1 d2) = " (conflict: " ++ L.intercalate ", " (L.map showConflictingDep [d1, d2]) ++ ")"
222223
showFR _ (NewPackageIsMissingRequiredComponent comp dr) = " (does not contain " ++ showExposedComponent comp ++ ", which is required by " ++ showDependencyReason dr ++ ")"
224+
showFR _ (NewPackageHasPrivateRequiredComponent comp dr) = " (" ++ showExposedComponent comp ++ " is private, but it is required by " ++ showDependencyReason dr ++ ")"
223225
showFR _ (NewPackageHasUnbuildableRequiredComponent comp dr) = " (" ++ showExposedComponent comp ++ " is not buildable in the current environment, but it is required by " ++ showDependencyReason dr ++ ")"
224226
showFR _ (PackageRequiresMissingComponent qpn comp) = " (requires " ++ showExposedComponent comp ++ " from " ++ showQPN qpn ++ ", but the component does not exist)"
227+
showFR _ (PackageRequiresPrivateComponent qpn comp) = " (requires " ++ showExposedComponent comp ++ " from " ++ showQPN qpn ++ ", but the component is private)"
225228
showFR _ (PackageRequiresUnbuildableComponent qpn comp) = " (requires " ++ showExposedComponent comp ++ " from " ++ showQPN qpn ++ ", but the component is not buildable in the current environment)"
226229
showFR _ CannotInstall = " (only already installed instances can be used)"
227230
showFR _ CannotReinstall = " (avoiding to reinstall a package with same version but new dependencies)"
@@ -247,8 +250,9 @@ showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA
247250
showFR _ EmptyGoalChoice = " (INTERNAL ERROR: EMPTY GOAL CHOICE)"
248251

249252
showExposedComponent :: ExposedComponent -> String
250-
showExposedComponent ExposedLib = "library"
251-
showExposedComponent (ExposedExe name) = "executable '" ++ unUnqualComponentName name ++ "'"
253+
showExposedComponent (ExposedLib LMainLibName) = "library"
254+
showExposedComponent (ExposedLib (LSubLibName name)) = "library '" ++ unUnqualComponentName name ++ "'"
255+
showExposedComponent (ExposedExe name) = "executable '" ++ unUnqualComponentName name ++ "'"
252256

253257
constraintSource :: ConstraintSource -> String
254258
constraintSource src = "constraint from " ++ showConstraintSource src
@@ -257,8 +261,9 @@ showConflictingDep :: ConflictingDep -> String
257261
showConflictingDep (ConflictingDep dr (PkgComponent qpn comp) ci) =
258262
let DependencyReason qpn' _ _ = dr
259263
componentStr = case comp of
260-
ExposedExe exe -> " (exe " ++ unUnqualComponentName exe ++ ")"
261-
ExposedLib -> ""
264+
ExposedExe exe -> " (exe " ++ unUnqualComponentName exe ++ ")"
265+
ExposedLib LMainLibName -> ""
266+
ExposedLib (LSubLibName lib) -> " (lib " ++ unUnqualComponentName lib ++ ")"
262267
in case ci of
263268
Fixed i -> (if qpn /= qpn' then showDependencyReason dr ++ " => " else "") ++
264269
showQPN qpn ++ componentStr ++ "==" ++ showI i

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,10 @@ data FailReason = UnsupportedExtension Extension
102102
| NewPackageDoesNotMatchExistingConstraint ConflictingDep
103103
| ConflictingConstraints ConflictingDep ConflictingDep
104104
| NewPackageIsMissingRequiredComponent ExposedComponent (DependencyReason QPN)
105+
| NewPackageHasPrivateRequiredComponent ExposedComponent (DependencyReason QPN)
105106
| NewPackageHasUnbuildableRequiredComponent ExposedComponent (DependencyReason QPN)
106107
| PackageRequiresMissingComponent QPN ExposedComponent
108+
| PackageRequiresPrivateComponent QPN ExposedComponent
107109
| PackageRequiresUnbuildableComponent QPN ExposedComponent
108110
| CannotInstall
109111
| CannotReinstall

0 commit comments

Comments
 (0)