Skip to content

Commit 4f8eed1

Browse files
committed
Resolve #6281: Add foo:bar syntax to mixins
1 parent 9b380e2 commit 4f8eed1

File tree

21 files changed

+218
-53
lines changed

21 files changed

+218
-53
lines changed

Cabal/Cabal-described/src/Distribution/Described.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,9 @@ instance Described LibVersionInfo where
431431
reDigits = reChars ['0'..'9']
432432

433433
instance Described Mixin where
434-
describe _ = RENamed "package-name" (describe (Proxy :: Proxy PackageName)) <>
434+
describe _ =
435+
RENamed "package-name" (describe (Proxy :: Proxy PackageName)) <>
436+
REOpt (reChar ':' <> RENamed "library-name" (describe (Proxy :: Proxy UnqualComponentName))) <>
435437
REOpt (RESpaces1 <> describe (Proxy :: Proxy IncludeRenaming))
436438

437439
instance Described ModuleName where

Cabal/Distribution/Backpack/ConfiguredComponent.hs

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import Distribution.Types.PackageName
3232
import Distribution.Types.Mixin
3333
import Distribution.Types.ComponentName
3434
import Distribution.Types.LibraryName
35-
import Distribution.Types.UnqualComponentName
3635
import Distribution.Types.ComponentInclude
3736
import Distribution.Package
3837
import Distribution.PackageDescription
@@ -48,7 +47,8 @@ import qualified Data.Set as Set
4847
import qualified Distribution.Compat.NonEmptySet as NonEmptySet
4948
import qualified Data.Map as Map
5049
import Distribution.Pretty
51-
import Text.PrettyPrint
50+
import Text.PrettyPrint (Doc, hang, text, vcat, hsep, quotes, ($$))
51+
import qualified Text.PrettyPrint as PP
5252

5353
-- | A configured component, we know exactly what its 'ComponentId' is,
5454
-- and the 'ComponentId's of the things it depends on.
@@ -112,13 +112,12 @@ mkConfiguredComponent
112112
mkConfiguredComponent pkg_descr this_cid lib_deps exe_deps component = do
113113
-- Resolve each @mixins@ into the actual dependency
114114
-- from @lib_deps@.
115-
explicit_includes <- forM (mixins bi) $ \(Mixin name rns) -> do
116-
let keys = fixFakePkgName pkg_descr name
117-
aid <- case Map.lookup keys deps_map of
115+
explicit_includes <- forM (mixins bi) $ \(Mixin pn ln rns) -> do
116+
aid <- case Map.lookup (pn, CLibName ln) deps_map of
118117
Nothing ->
119118
dieProgress $
120-
text "Mix-in refers to non-existent package" <+>
121-
quotes (pretty name) $$
119+
text "Mix-in refers to non-existent library" <+>
120+
quotes (pretty pn <<>> prettyLN ln) $$
122121
text "(did you forget to add the package to build-depends?)"
123122
Just r -> return r
124123
return ComponentInclude {
@@ -150,9 +149,17 @@ mkConfiguredComponent pkg_descr this_cid lib_deps exe_deps component = do
150149
cc_includes = explicit_includes ++ implicit_includes
151150
}
152151
where
152+
bi :: BuildInfo
153153
bi = componentBuildInfo component
154+
155+
prettyLN :: LibraryName -> Doc
156+
prettyLN LMainLibName = PP.empty
157+
prettyLN (LSubLibName n) = PP.colon <<>> pretty n
158+
159+
deps_map :: Map (PackageName, ComponentName) (AnnotatedId ComponentId)
154160
deps_map = Map.fromList [ ((packageName dep, ann_cname dep), dep)
155161
| dep <- lib_deps ]
162+
156163
is_public = componentName component == CLibName LMainLibName
157164

158165
type ConfiguredComponentMap =
@@ -179,10 +186,7 @@ toConfiguredComponent pkg_descr this_cid lib_dep_map exe_dep_map component = do
179186
-- Return all library components
180187
forM (NonEmptySet.toList sublibs) $ \lib ->
181188
let comp = CLibName lib in
182-
case Map.lookup (CLibName $ LSubLibName $
183-
packageNameToUnqualComponentName name) pkg
184-
<|> Map.lookup comp pkg
185-
of
189+
case Map.lookup comp pkg of
186190
Nothing ->
187191
dieProgress $
188192
text "Dependency on unbuildable" <+>
@@ -302,19 +306,3 @@ newPackageDepsBehaviourMinVersion = CabalSpecV1_8
302306
newPackageDepsBehaviour :: PackageDescription -> Bool
303307
newPackageDepsBehaviour pkg =
304308
specVersion pkg >= newPackageDepsBehaviourMinVersion
305-
306-
-- | 'build-depends:' stanzas are currently ambiguous as the external packages
307-
-- and internal libraries are specified the same. For now, we assume internal
308-
-- libraries shadow, and this function disambiguates accordingly, but soon the
309-
-- underlying ambiguity will be addressed.
310-
-- Multiple public libraries (cabal 3.0) added an unambiguous way of specifying
311-
-- sublibraries, but we still have to support the old syntax for bc reasons.
312-
fixFakePkgName :: PackageDescription -> PackageName -> (PackageName, ComponentName)
313-
fixFakePkgName pkg_descr pn =
314-
if subLibName `elem` internalLibraries
315-
then (packageName pkg_descr, CLibName (LSubLibName subLibName))
316-
else (pn, CLibName LMainLibName )
317-
where
318-
subLibName = packageNameToUnqualComponentName pn
319-
internalLibraries = mapMaybe (libraryNameString . libName)
320-
(allLibraries pkg_descr)

Cabal/Distribution/PackageDescription/Parsec.hs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import Distribution.Fields.LexerMonad (LexWarning, toPWarnings)
4747
import Distribution.Fields.Parser
4848
import Distribution.Fields.ParseResult
4949
import Distribution.PackageDescription
50-
import Distribution.PackageDescription.Configuration (freeVars, transformAllBuildDependsN)
50+
import Distribution.PackageDescription.Configuration (freeVars, transformAllBuildInfos)
5151
import Distribution.PackageDescription.FieldGrammar
5252
import Distribution.PackageDescription.Quirks (patchQuirks)
5353
import Distribution.Parsec (parsec, simpleParsecBS)
@@ -56,6 +56,7 @@ import Distribution.Parsec.Position (Position (..), zeroPos)
5656
import Distribution.Parsec.Warning (PWarnType (..))
5757
import Distribution.Pretty (prettyShow)
5858
import Distribution.Simple.Utils (fromUTF8BS, toUTF8BS)
59+
import Distribution.Types.Mixin (Mixin (..), mkMixin)
5960
import Distribution.Utils.Generic (breakMaybe, unfoldrM, validateUTF8)
6061
import Distribution.Verbosity (Verbosity)
6162
import Distribution.Version (Version, mkVersion, versionNumbers)
@@ -71,6 +72,7 @@ import qualified Distribution.Types.Executable.Lens as L
7172
import qualified Distribution.Types.ForeignLib.Lens as L
7273
import qualified Distribution.Types.GenericPackageDescription.Lens as L
7374
import qualified Distribution.Types.PackageDescription.Lens as L
75+
import qualified Distribution.Types.SetupBuildInfo.Lens as L
7476
import qualified Text.Parsec as P
7577

7678
-- ---------------------------------------------------------------
@@ -727,14 +729,25 @@ checkForUndefinedFlags gpd = do
727729
-- i.e. what you write is what you get;
728730
-- For pre-3.4 we post-process the file.
729731
--
732+
-- Similarly, we process mixins.
733+
-- See https://github.com/haskell/cabal/issues/6281
734+
--
730735

731736
postProcessInternalDeps :: CabalSpecVersion -> GenericPackageDescription -> GenericPackageDescription
732737
postProcessInternalDeps specVer gpd
733738
| specVer >= CabalSpecV3_4 = gpd
734-
| otherwise = transformAllBuildDependsN (concatMap f) gpd
739+
| otherwise = transformAllBuildInfos transformBI transformSBI gpd
735740
where
736-
f :: Dependency -> [Dependency]
737-
f (Dependency pn vr ln)
741+
transformBI :: BuildInfo -> BuildInfo
742+
transformBI
743+
= over L.targetBuildDepends (concatMap transformD)
744+
. over L.mixins (map transformM)
745+
746+
transformSBI :: SetupBuildInfo -> SetupBuildInfo
747+
transformSBI = over L.setupDepends (concatMap transformD)
748+
749+
transformD :: Dependency -> [Dependency]
750+
transformD (Dependency pn vr ln)
738751
| uqn `Set.member` internalLibs
739752
, LMainLibName `NES.member` ln
740753
= case NES.delete LMainLibName ln of
@@ -744,7 +757,16 @@ postProcessInternalDeps specVer gpd
744757
uqn = packageNameToUnqualComponentName pn
745758
dep = Dependency thisPn vr (NES.singleton (LSubLibName uqn))
746759

747-
f d = [d]
760+
transformD d = [d]
761+
762+
transformM :: Mixin -> Mixin
763+
transformM (Mixin pn LMainLibName incl)
764+
| uqn `Set.member` internalLibs
765+
= mkMixin thisPn (LSubLibName uqn) incl
766+
where
767+
uqn = packageNameToUnqualComponentName pn
768+
769+
transformM m = m
748770

749771
thisPn :: PackageName
750772
thisPn = pkgName (package (packageDescription gpd))

Cabal/Distribution/Types/GenericPackageDescription.hs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{-# LANGUAGE DeriveGeneric #-}
33
{-# LANGUAGE FlexibleContexts #-}
44
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+
{-# LANGUAGE ScopedTypeVariables #-}
56

67
module Distribution.Types.GenericPackageDescription (
78
GenericPackageDescription(..),
@@ -74,14 +75,31 @@ emptyGenericPackageDescription = GenericPackageDescription emptyPackageDescripti
7475
-- Traversal Instances
7576

7677
instance L.HasBuildInfos GenericPackageDescription where
77-
traverseBuildInfos f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =
78-
GenericPackageDescription
79-
<$> L.traverseBuildInfos f p
80-
<*> pure v
81-
<*> pure a1
82-
<*> (traverse . traverse . L.buildInfo) f x1
83-
<*> (traverse . L._2 . traverse . L.buildInfo) f x2
84-
<*> (traverse . L._2 . traverse . L.buildInfo) f x3
85-
<*> (traverse . L._2 . traverse . L.buildInfo) f x4
86-
<*> (traverse . L._2 . traverse . L.buildInfo) f x5
87-
<*> (traverse . L._2 . traverse . L.buildInfo) f x6
78+
traverseBuildInfos f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =
79+
GenericPackageDescription
80+
<$> L.traverseBuildInfos f p
81+
<*> pure v
82+
<*> pure a1
83+
<*> (traverse . traverseCondTreeBuildInfo) f x1
84+
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x2
85+
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x3
86+
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x4
87+
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x5
88+
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x6
89+
where
90+
91+
-- We use this traversal to keep [Dependency] field in CondTree up to date.
92+
traverseCondTreeBuildInfo
93+
:: forall f comp v. (Applicative f, L.HasBuildInfo comp)
94+
=> LensLike' f (CondTree v [Dependency] comp) L.BuildInfo
95+
traverseCondTreeBuildInfo g = node where
96+
mkCondNode :: comp -> [CondBranch v [Dependency] comp] -> CondTree v [Dependency] comp
97+
mkCondNode comp branches = CondNode comp (view L.targetBuildDepends comp) branches
98+
99+
node (CondNode comp _ branches) = mkCondNode
100+
<$> L.buildInfo g comp
101+
<*> traverse branch branches
102+
103+
branch (CondBranch v x y) = CondBranch v
104+
<$> node x
105+
<*> traverse node y

Cabal/Distribution/Types/Mixin.hs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,31 @@
33

44
module Distribution.Types.Mixin (
55
Mixin(..),
6+
mkMixin,
67
) where
78

89
import Distribution.Compat.Prelude
910
import Prelude ()
1011

12+
import Distribution.CabalSpecVersion
1113
import Distribution.Parsec
1214
import Distribution.Pretty
1315
import Distribution.Types.IncludeRenaming
16+
import Distribution.Types.LibraryName
1417
import Distribution.Types.PackageName
18+
import Distribution.Types.UnqualComponentName
1519

1620
import qualified Distribution.Compat.CharParsing as P
21+
import qualified Text.PrettyPrint as PP
1722

23+
-- |
24+
--
25+
-- /Invariant:/ if 'mixinLibraryName' is 'LSubLibName', it's not
26+
-- the same as 'mixinPackageName'. In other words,
27+
-- the same invariant as 'Dependency' has.
28+
--
1829
data Mixin = Mixin { mixinPackageName :: PackageName
30+
, mixinLibraryName :: LibraryName
1931
, mixinIncludeRenaming :: IncludeRenaming }
2032
deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
2133

@@ -25,11 +37,50 @@ instance Structured Mixin
2537
instance NFData Mixin where rnf = genericRnf
2638

2739
instance Pretty Mixin where
28-
pretty (Mixin pkg_name incl) = pretty pkg_name <+> pretty incl
40+
pretty (Mixin pn LMainLibName incl) = pretty pn <+> pretty incl
41+
pretty (Mixin pn (LSubLibName ln) incl) = pretty pn <<>> PP.colon <<>> pretty ln <+> pretty incl
2942

43+
-- |
44+
--
45+
-- >>> simpleParsec "mylib" :: Maybe Mixin
46+
-- Just (Mixin {mixinPackageName = PackageName "mylib", mixinLibraryName = LMainLibName, mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})
47+
--
48+
-- >>> simpleParsec "thatlib:sublib" :: Maybe Mixin
49+
-- Just (Mixin {mixinPackageName = PackageName "thatlib", mixinLibraryName = LSubLibName (UnqualComponentName "sublib"), mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})
50+
--
51+
-- >>> simpleParsec "thatlib:thatlib" :: Maybe Mixin
52+
-- Just (Mixin {mixinPackageName = PackageName "thatlib", mixinLibraryName = LMainLibName, mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})
53+
--
54+
-- Sublibrary syntax is accepted since @cabal-version: 3.4@.
55+
--
56+
-- >>> map (`simpleParsec'` "mylib:sub") [CabalSpecV3_0, CabalSpecV3_4] :: [Maybe Mixin]
57+
-- [Nothing,Just (Mixin {mixinPackageName = PackageName "mylib", mixinLibraryName = LSubLibName (UnqualComponentName "sub"), mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})]
58+
--
3059
instance Parsec Mixin where
3160
parsec = do
32-
mod_name <- parsec
61+
pn <- parsec
62+
ln <- P.option LMainLibName $ do
63+
_ <- P.char ':'
64+
versionGuardMultilibs
65+
parsecWarning PWTExperimental "colon specifier is experimental feature (issue #5660)"
66+
LSubLibName <$> parsec
3367
P.spaces
3468
incl <- parsec
35-
return (Mixin mod_name incl)
69+
return (mkMixin pn ln incl)
70+
where
71+
72+
versionGuardMultilibs :: CabalParsing m => m ()
73+
versionGuardMultilibs = do
74+
csv <- askCabalSpecVersion
75+
when (csv < CabalSpecV3_4) $ fail $ unwords
76+
[ "Sublibrary mixin syntax used."
77+
, "To use this syntax the package needs to specify at least 'cabal-version: 3.4'."
78+
]
79+
80+
-- | Smart constructor of 'Mixin', enforces invariant.
81+
mkMixin :: PackageName -> LibraryName -> IncludeRenaming -> Mixin
82+
mkMixin pn (LSubLibName uqn) incl
83+
| packageNameToUnqualComponentName pn == uqn
84+
= Mixin pn LMainLibName incl
85+
mkMixin pn ln incl
86+
= Mixin pn ln incl

Cabal/doc/buildinfo-fields-reference.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ build-depends
197197
* Documentation of :pkg-field:`build-depends`
198198

199199
.. math::
200-
\mathrm{commalist}\left(\mathop{\mathit{pkg\text{-}name}}{\left(\mathop{\mathord{``}\mathtt{\text{:}}\mathord{"}}\left\{ \mathop{\mathit{unqual\text{-}name}}\mid\mathop{\mathord{``}\mathtt{\{}\mathord{"}}\circ{\mathop{\mathit{unqual\text{-}name}}}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\}}\mathord{"}} \right\}\right)}^?{\left(\circ\mathop{\mathit{version\text{-}range}}\right)}^?\right)
200+
\mathrm{commalist}\left(\mathop{\mathit{pkg\text{-}name}}{\left(\mathop{\mathord{``}\mathtt{\text{:}}\mathord{"}}\left\{ \mathop{\mathit{unqual\text{-}name}}\mid\mathop{\mathord{``}\mathtt{\{}\mathord{"}}\circ{\mathop{\mathit{unqual\text{-}name}}}^+_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\}}\mathord{"}} \right\}\right)}^?{\left(\circ\mathop{\mathit{version\text{-}range}}\right)}^?\right)
201201
202202
build-tool-depends
203203
* Monoidal field
@@ -452,7 +452,7 @@ mixins
452452
* Documentation of :pkg-field:`mixins`
453453

454454
.. math::
455-
\mathrm{commalist}\left(\mathop{\mathit{package\text{-}name}}{\left(\bullet\left\{ \mid\mathop{\mathord{``}\mathtt{hiding}\mathord{"}}\circ\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\mathop{\mathit{module\text{-}name}}}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}}\mid\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\left(\mathop{\mathit{module\text{-}name}}{\left(\bullet\mathop{\mathord{``}\mathtt{as}\mathord{"}}\bullet\mathop{\mathit{module\text{-}name}}\right)}^?\right)}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}} \right\}{\left(\circ\mathop{\mathord{``}\mathtt{requires}\mathord{"}}\bullet\left\{ \mid\mathop{\mathord{``}\mathtt{hiding}\mathord{"}}\circ\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\mathop{\mathit{module\text{-}name}}}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}}\mid\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\left(\mathop{\mathit{module\text{-}name}}{\left(\bullet\mathop{\mathord{``}\mathtt{as}\mathord{"}}\bullet\mathop{\mathit{module\text{-}name}}\right)}^?\right)}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}} \right\}\right)}^?\right)}^?\right)
455+
\mathrm{commalist}\left(\mathop{\mathit{package\text{-}name}}{\left(\mathop{\mathord{``}\mathtt{\text{:}}\mathord{"}}\mathop{\mathit{library\text{-}name}}\right)}^?{\left(\bullet\left\{ \mid\mathop{\mathord{``}\mathtt{hiding}\mathord{"}}\circ\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\mathop{\mathit{module\text{-}name}}}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}}\mid\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\left(\mathop{\mathit{module\text{-}name}}{\left(\bullet\mathop{\mathord{``}\mathtt{as}\mathord{"}}\bullet\mathop{\mathit{module\text{-}name}}\right)}^?\right)}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}} \right\}{\left(\circ\mathop{\mathord{``}\mathtt{requires}\mathord{"}}\bullet\left\{ \mid\mathop{\mathord{``}\mathtt{hiding}\mathord{"}}\circ\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\mathop{\mathit{module\text{-}name}}}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}}\mid\mathop{\mathord{``}\mathtt{\text{(}}\mathord{"}}\circ{\left(\mathop{\mathit{module\text{-}name}}{\left(\bullet\mathop{\mathord{``}\mathtt{as}\mathord{"}}\bullet\mathop{\mathit{module\text{-}name}}\right)}^?\right)}^\ast_{\left(\circ\mathop{\mathord{``}\mathtt{\text{,}}\mathord{"}}\circ\right)}\circ\mathop{\mathord{``}\mathtt{\text{)}}\mathord{"}} \right\}\right)}^?\right)}^?\right)
456456
457457
other-extensions
458458
* Monoidal field

Cabal/tests/ParserTests/regressions/mixin-1.expr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ GenericPackageDescription
5858
(ModuleName
5959
"Str.String")],
6060
includeRequiresRn = DefaultRenaming},
61+
mixinLibraryName = LMainLibName,
6162
mixinPackageName = PackageName
6263
"str-string"},
6364
Mixin
@@ -69,6 +70,7 @@ GenericPackageDescription
6970
(ModuleName
7071
"Str.ByteString")],
7172
includeRequiresRn = DefaultRenaming},
73+
mixinLibraryName = LMainLibName,
7274
mixinPackageName = PackageName
7375
"str-bytestring"}],
7476
oldExtensions = [],

0 commit comments

Comments
 (0)