Skip to content

Commit 86cba88

Browse files
committed
Implement "convenience libraries", fixes haskell#269.
Convenience libraries are package-private libraries that can be used as part of executables, libraries, etc without being exposed to the external world. Private libraries are signified using the library foo stanza. Within a Cabal package, the name convenience library shadows the conventional meaning of package name in build-depends, so that references to "foo" do not indicate foo in Hackage, but the convenience library defined in the same package. (So, don't shadow Hackage packages!) This commit implements convenience libraries such that they ARE installed the package database (this prevents us from having to special case dynamically linked executables); in GHC 7.10 and later they are installed under the same package name as the package that contained them, but have a distinct "component ID" (one pay off of making the distinction between component IDs and installed package IDs.) There is a "default" library which is identified by the fact that its library name coincides with the package name. There are some new convenience functions to permit referencing this. There are a few latent bugs in this commit which are fixed in later commits in this patchset. (Those bugfixes required a bit of refactoring, so it's clearer if they're not with this patch.) Signed-off-by: Edward Z. Yang <[email protected]>
1 parent ad723cc commit 86cba88

39 files changed

+1865
-357
lines changed

Cabal/Cabal.cabal

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ extra-source-files:
119119
tests/PackageTests/HaddockNewline/A.hs
120120
tests/PackageTests/HaddockNewline/HaddockNewline.cabal
121121
tests/PackageTests/HaddockNewline/Setup.hs
122+
tests/PackageTests/MultipleLibraries/p.cabal
123+
tests/PackageTests/MultipleLibraries/p/P.hs
124+
tests/PackageTests/MultipleLibraries/p/Foo.hs
125+
tests/PackageTests/MultipleLibraries/p/p.cabal
126+
tests/PackageTests/MultipleLibraries/p/p/P.hs
127+
tests/PackageTests/MultipleLibraries/p/q/Q.hs
128+
tests/PackageTests/MultipleLibraries/q/Q.hs
129+
tests/PackageTests/MultipleLibraries/q/q.cabal
122130
tests/PackageTests/Options.hs
123131
tests/PackageTests/OrderFlags/Foo.hs
124132
tests/PackageTests/OrderFlags/my.cabal

Cabal/Distribution/PackageDescription.hs

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ data PackageDescription
189189
buildType :: Maybe BuildType,
190190
setupBuildInfo :: Maybe SetupBuildInfo,
191191
-- components
192-
library :: Maybe Library,
192+
libraries :: [Library],
193193
executables :: [Executable],
194194
testSuites :: [TestSuite],
195195
benchmarks :: [Benchmark],
@@ -256,7 +256,7 @@ emptyPackageDescription
256256
category = "",
257257
customFieldsPD = [],
258258
setupBuildInfo = Nothing,
259-
library = Nothing,
259+
libraries = [],
260260
executables = [],
261261
testSuites = [],
262262
benchmarks = [],
@@ -387,6 +387,7 @@ instance Text ModuleRenaming where
387387
-- The Library type
388388

389389
data Library = Library {
390+
libName :: String,
390391
exposedModules :: [ModuleName],
391392
reexportedModules :: [ModuleReexport],
392393
requiredSignatures:: [ModuleName], -- ^ What sigs need implementations?
@@ -400,6 +401,7 @@ instance Binary Library
400401

401402
instance Monoid Library where
402403
mempty = Library {
404+
libName = mempty,
403405
exposedModules = mempty,
404406
reexportedModules = mempty,
405407
requiredSignatures = mempty,
@@ -411,6 +413,7 @@ instance Monoid Library where
411413

412414
instance Semigroup Library where
413415
a <> b = Library {
416+
libName = combine' libName,
414417
exposedModules = combine exposedModules,
415418
reexportedModules = combine reexportedModules,
416419
requiredSignatures = combine requiredSignatures,
@@ -419,20 +422,26 @@ instance Semigroup Library where
419422
libBuildInfo = combine libBuildInfo
420423
}
421424
where combine field = field a `mappend` field b
425+
combine' field = case (field a, field b) of
426+
("","") -> ""
427+
("", x) -> x
428+
(x, "") -> x
429+
(x, y) -> error $ "Ambiguous values for library field: '"
430+
++ x ++ "' and '" ++ y ++ "'"
422431

423432
emptyLibrary :: Library
424433
emptyLibrary = mempty
425434

426435
-- |does this package have any libraries?
427436
hasLibs :: PackageDescription -> Bool
428-
hasLibs p = maybe False (buildable . libBuildInfo) (library p)
437+
hasLibs p = any (buildable . libBuildInfo) (libraries p)
429438

430439
-- |'Maybe' version of 'hasLibs'
431-
maybeHasLibs :: PackageDescription -> Maybe Library
440+
maybeHasLibs :: PackageDescription -> [Library]
432441
maybeHasLibs p =
433-
library p >>= \lib -> if buildable (libBuildInfo lib)
434-
then Just lib
435-
else Nothing
442+
libraries p >>= \lib -> if buildable (libBuildInfo lib)
443+
then return lib
444+
else []
436445

437446
-- |If the package description has a library section, call the given
438447
-- function with the library build info as argument.
@@ -915,7 +924,7 @@ emptyBuildInfo = mempty
915924
-- all buildable executables, test suites and benchmarks. Useful for gathering
916925
-- dependencies.
917926
allBuildInfo :: PackageDescription -> [BuildInfo]
918-
allBuildInfo pkg_descr = [ bi | Just lib <- [library pkg_descr]
927+
allBuildInfo pkg_descr = [ bi | lib <- libraries pkg_descr
919928
, let bi = libBuildInfo lib
920929
, buildable bi ]
921930
++ [ bi | exe <- executables pkg_descr
@@ -950,10 +959,10 @@ usedExtensions :: BuildInfo -> [Extension]
950959
usedExtensions bi = oldExtensions bi
951960
++ defaultExtensions bi
952961

953-
type HookedBuildInfo = (Maybe BuildInfo, [(String, BuildInfo)])
962+
type HookedBuildInfo = ([(String, BuildInfo)], [(String, BuildInfo)])
954963

955964
emptyHookedBuildInfo :: HookedBuildInfo
956-
emptyHookedBuildInfo = (Nothing, [])
965+
emptyHookedBuildInfo = ([], [])
957966

958967
-- |Select options for a particular Haskell compiler.
959968
hcOptions :: CompilerFlavor -> BuildInfo -> [String]
@@ -1109,28 +1118,30 @@ lowercase = map Char.toLower
11091118
-- ------------------------------------------------------------
11101119

11111120
updatePackageDescription :: HookedBuildInfo -> PackageDescription -> PackageDescription
1112-
updatePackageDescription (mb_lib_bi, exe_bi) p
1113-
= p{ executables = updateExecutables exe_bi (executables p)
1114-
, library = updateLibrary mb_lib_bi (library p)
1121+
updatePackageDescription (lib_bi, exe_bi) p
1122+
= p{ executables = updateMany exeName updateExecutable exe_bi (executables p)
1123+
, libraries = updateMany libName updateLibrary lib_bi (libraries p)
11151124
}
11161125
where
1117-
updateLibrary :: Maybe BuildInfo -> Maybe Library -> Maybe Library
1118-
updateLibrary (Just bi) (Just lib) = Just (lib{libBuildInfo = bi `mappend` libBuildInfo lib})
1119-
updateLibrary Nothing mb_lib = mb_lib
1120-
updateLibrary (Just _) Nothing = Nothing
1121-
1122-
updateExecutables :: [(String, BuildInfo)] -- ^[(exeName, new buildinfo)]
1123-
-> [Executable] -- ^list of executables to update
1124-
-> [Executable] -- ^list with exeNames updated
1125-
updateExecutables exe_bi' executables' = foldr updateExecutable executables' exe_bi'
1126-
1127-
updateExecutable :: (String, BuildInfo) -- ^(exeName, new buildinfo)
1128-
-> [Executable] -- ^list of executables to update
1129-
-> [Executable] -- ^list with exeName updated
1130-
updateExecutable _ [] = []
1131-
updateExecutable exe_bi'@(name,bi) (exe:exes)
1132-
| exeName exe == name = exe{buildInfo = bi `mappend` buildInfo exe} : exes
1133-
| otherwise = exe : updateExecutable exe_bi' exes
1126+
updateMany :: (a -> String) -- ^ @exeName@ or @libName@
1127+
-> (BuildInfo -> a -> a) -- ^ @updateExecutable@ or @updateLibrary@
1128+
-> [(String, BuildInfo)] -- ^[(name, new buildinfo)]
1129+
-> [a] -- ^list of components to update
1130+
-> [a] -- ^list with updated components
1131+
updateMany name update hooked_bi' cs' = foldr (updateOne name update) cs' hooked_bi'
1132+
1133+
updateOne :: (a -> String) -- ^ @exeName@ or @libName@
1134+
-> (BuildInfo -> a -> a) -- ^ @updateExecutable@ or @updateLibrary@
1135+
-> (String, BuildInfo) -- ^(name, new buildinfo)
1136+
-> [a] -- ^list of compnoents to update
1137+
-> [a] -- ^list with name component updated
1138+
updateOne _ _ _ [] = []
1139+
updateOne name_sel update hooked_bi'@(name,bi) (c:cs)
1140+
| name_sel c == name = update bi c : cs
1141+
| otherwise = c : updateOne name_sel update hooked_bi' cs
1142+
1143+
updateExecutable bi exe = exe{buildInfo = bi `mappend` buildInfo exe}
1144+
updateLibrary bi lib = lib{libBuildInfo = bi `mappend` libBuildInfo lib}
11341145

11351146
-- ---------------------------------------------------------------------------
11361147
-- The GenericPackageDescription type
@@ -1139,7 +1150,7 @@ data GenericPackageDescription =
11391150
GenericPackageDescription {
11401151
packageDescription :: PackageDescription,
11411152
genPackageFlags :: [Flag],
1142-
condLibrary :: Maybe (CondTree ConfVar [Dependency] Library),
1153+
condLibraries :: [(String, CondTree ConfVar [Dependency] Library)],
11431154
condExecutables :: [(String, CondTree ConfVar [Dependency] Executable)],
11441155
condTestSuites :: [(String, CondTree ConfVar [Dependency] TestSuite)],
11451156
condBenchmarks :: [(String, CondTree ConfVar [Dependency] Benchmark)]

Cabal/Distribution/PackageDescription/Check.hs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import Distribution.Text
4646
import Language.Haskell.Extension
4747

4848
import Data.Maybe
49-
( isNothing, isJust, catMaybes, mapMaybe, maybeToList, fromMaybe )
49+
( isNothing, isJust, catMaybes, mapMaybe, fromMaybe )
5050
import Data.List (sort, group, isPrefixOf, nub, find)
5151
import Control.Monad
5252
( filterM, liftM )
@@ -173,7 +173,7 @@ checkSanity pkg =
173173
, check (all ($ pkg) [ null . executables
174174
, null . testSuites
175175
, null . benchmarks
176-
, isNothing . library ]) $
176+
, null . libraries ]) $
177177
PackageBuildImpossible
178178
"No executables, libraries, tests, or benchmarks found. Nothing to do."
179179

@@ -185,7 +185,7 @@ checkSanity pkg =
185185
--TODO: check for name clashes case insensitively: windows file systems cannot
186186
--cope.
187187

188-
++ maybe [] (checkLibrary pkg) (library pkg)
188+
++ concatMap (checkLibrary pkg) (libraries pkg)
189189
++ concatMap (checkExecutable pkg) (executables pkg)
190190
++ concatMap (checkTestSuite pkg) (testSuites pkg)
191191
++ concatMap (checkBenchmark pkg) (benchmarks pkg)
@@ -681,7 +681,7 @@ checkGhcOptions pkg =
681681

682682
where
683683
all_ghc_options = concatMap get_ghc_options (allBuildInfo pkg)
684-
lib_ghc_options = maybe [] (get_ghc_options . libBuildInfo) (library pkg)
684+
lib_ghc_options = concatMap (get_ghc_options . libBuildInfo) (libraries pkg)
685685
get_ghc_options bi = hcOptions GHC bi ++ hcProfOptions GHC bi
686686
++ hcSharedOptions GHC bi
687687

@@ -904,9 +904,18 @@ checkCabalVersion pkg =
904904
++ "different modules then list the other ones in the "
905905
++ "'other-languages' field."
906906

907+
, checkVersion [1,23]
908+
(case libraries pkg of
909+
[lib] -> libName lib /= unPackageName (packageName pkg)
910+
[] -> False
911+
_ -> True) $
912+
PackageDistInexcusable $
913+
"To use multiple 'library' sections or a named library section "
914+
++ "the package needs to specify at least 'cabal-version >= 1.23'."
915+
907916
-- check use of reexported-modules sections
908917
, checkVersion [1,21]
909-
(maybe False (not.null.reexportedModules) (library pkg)) $
918+
(any (not.null.reexportedModules) (libraries pkg)) $
910919
PackageDistInexcusable $
911920
"To use the 'reexported-module' field the package needs to specify "
912921
++ "at least 'cabal-version: >= 1.21'."
@@ -1312,7 +1321,7 @@ checkConditionals pkg =
13121321
unknownOSs = [ os | OS (OtherOS os) <- conditions ]
13131322
unknownArches = [ arch | Arch (OtherArch arch) <- conditions ]
13141323
unknownImpls = [ impl | Impl (OtherCompiler impl) _ <- conditions ]
1315-
conditions = maybe [] fvs (condLibrary pkg)
1324+
conditions = concatMap (fvs . snd) (condLibraries pkg)
13161325
++ concatMap (fvs . snd) (condExecutables pkg)
13171326
fvs (CondNode _ _ ifs) = concatMap compfv ifs -- free variables
13181327
compfv (c, ct, mct) = condfv c ++ fvs ct ++ maybe [] fvs mct
@@ -1416,8 +1425,8 @@ checkDevelopmentOnlyFlags pkg =
14161425

14171426
allConditionalBuildInfo :: [([Condition ConfVar], BuildInfo)]
14181427
allConditionalBuildInfo =
1419-
concatMap (collectCondTreePaths libBuildInfo)
1420-
(maybeToList (condLibrary pkg))
1428+
concatMap (collectCondTreePaths libBuildInfo . snd)
1429+
(condLibraries pkg)
14211430

14221431
++ concatMap (collectCondTreePaths buildInfo . snd)
14231432
(condExecutables pkg)

0 commit comments

Comments
 (0)