Skip to content

Convenience libraries #3022

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

Merged
merged 30 commits into from
Mar 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3c03ccc
Add a custom setup to cabal-install.
ezyang Jan 3, 2016
2040c1c
Implement "convenience libraries", fixes #269.
ezyang Jan 3, 2016
90e908b
Per-component cabal_macros.h (#1893) and install paths
ezyang Jan 11, 2016
8633574
Do not install/register internal libraries when unnecessary.
ezyang Jan 12, 2016
ca93c47
Refactor ComponentLocalBuildInfo, fixing rpaths with internal libraries.
ezyang Jan 12, 2016
5239f89
Make register print the IPI when verbose (useful!)
ezyang Jan 12, 2016
516abff
Unconditionally turn on package name munging for components.
ezyang Jan 12, 2016
36eb65e
Tests for internal libraries (#269) and separate cabal_macros.h (#1893)
ezyang Jan 12, 2016
42177a4
Remove redundant Tasty import.
ezyang Mar 5, 2016
9011946
Don't double-encode the component name string.
ezyang Jan 12, 2016
5d2697e
Don't match dist-test directories.
ezyang Jan 13, 2016
2d0b9af
Run sdist using bootstrapped cabal-install.
ezyang Jan 13, 2016
3e78150
Undo backwards incompatible change to install paths.
ezyang Jan 13, 2016
6cf5078
Warning about compatibility requirements on Setup.hs in cabal-install.
ezyang Jan 13, 2016
9fd776e
Always generate CURRENT_COMPONENT_ID macros.
ezyang Jan 13, 2016
5beab86
Minor renaming to avoid conflicts.
ezyang Jan 13, 2016
4260a38
Warn if user attempts to define a package with a reserved name.
ezyang Jan 13, 2016
49fca14
Minor refactoring of compatibility keys, streamline library keys.
ezyang Jan 13, 2016
4021ce4
Fix HookedBuildInfo parsing to support old-style format (needs test.)
ezyang Jan 14, 2016
8d05c58
Test case for the Configure build-type.
ezyang Mar 16, 2016
d490618
Enforce unique naming for internal libraries as well.
ezyang Jan 14, 2016
8e3c2d7
Take advantage of unique section naming to simplify some paths.
ezyang Jan 14, 2016
baf36ad
Fix test-case after simplified component IDs.
ezyang Jan 14, 2016
d55a9ef
Add NFData instance to ModuleName.
ezyang Jan 13, 2016
5e7531a
Ignore bak files.
ezyang Jan 13, 2016
f88f502
Accept components to copy in ./Setup copy, fixes #2780.
ezyang Jan 19, 2016
cec1a61
Disable tests involving shared libraries on Windows.
ezyang Mar 5, 2016
8f3902b
Make GHC flag comment less cryptic.
ezyang Mar 6, 2016
af7bb53
Generalize HookedBuildInfo to work with any type of component.
ezyang Mar 6, 2016
40d6f0a
Use ADT for MultiInstance boolean argument.
ezyang Mar 6, 2016
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dist-*
.#*
*~
.*.swp
*.bak

# GHC build

Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ script:
- ./dist/setup/setup test unit-tests --show-details=streaming --test-option=--hide-successes
- ./dist/setup/setup test integration-tests --show-details=streaming --test-option=--hide-successes
- cabal check
- cabal sdist
- ./dist/setup/setup sdist
- install_from_tarball

# Check what we got
Expand Down
28 changes: 28 additions & 0 deletions Cabal/Cabal.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ extra-source-files:
tests/PackageTests/CMain/Bar.hs
tests/PackageTests/CMain/foo.c
tests/PackageTests/CMain/my.cabal
tests/PackageTests/Configure/A.hs
tests/PackageTests/Configure/Setup.hs
tests/PackageTests/Configure/X11.cabal
tests/PackageTests/CopyComponent/Exe/Main.hs
tests/PackageTests/CopyComponent/Exe/Main2.hs
tests/PackageTests/CopyComponent/Exe/myprog.cabal
tests/PackageTests/CopyComponent/Lib/Main.hs
tests/PackageTests/CopyComponent/Lib/p.cabal
tests/PackageTests/CopyComponent/Lib/src/P.hs
tests/PackageTests/DeterministicAr/Lib.hs
tests/PackageTests/DeterministicAr/my.cabal
tests/PackageTests/DuplicateModuleName/DuplicateModuleName.cabal
Expand Down Expand Up @@ -119,6 +128,25 @@ extra-source-files:
tests/PackageTests/HaddockNewline/A.hs
tests/PackageTests/HaddockNewline/HaddockNewline.cabal
tests/PackageTests/HaddockNewline/Setup.hs
tests/PackageTests/InternalLibraries/Executable/exe/Main.hs
tests/PackageTests/InternalLibraries/Executable/foo.cabal
tests/PackageTests/InternalLibraries/Executable/src/Foo.hs
tests/PackageTests/InternalLibraries/Library/fooexe/Main.hs
tests/PackageTests/InternalLibraries/Library/fooexe/fooexe.cabal
tests/PackageTests/InternalLibraries/Library/foolib/Foo.hs
tests/PackageTests/InternalLibraries/Library/foolib/foolib.cabal
tests/PackageTests/InternalLibraries/Library/foolib/private/Internal.hs
tests/PackageTests/InternalLibraries/p/Foo.hs
tests/PackageTests/InternalLibraries/p/p.cabal
tests/PackageTests/InternalLibraries/p/p/P.hs
tests/PackageTests/InternalLibraries/p/q/Q.hs
tests/PackageTests/InternalLibraries/q/Q.hs
tests/PackageTests/InternalLibraries/q/q.cabal
tests/PackageTests/Macros/A.hs
tests/PackageTests/Macros/B.hs
tests/PackageTests/Macros/Main.hs
tests/PackageTests/Macros/macros.cabal
tests/PackageTests/Macros/src/C.hs
tests/PackageTests/Options.hs
tests/PackageTests/OrderFlags/Foo.hs
tests/PackageTests/OrderFlags/my.cabal
Expand Down
4 changes: 4 additions & 0 deletions Cabal/Distribution/ModuleName.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import qualified Distribution.Compat.ReadP as Parse

import qualified Data.Char as Char
( isAlphaNum, isUpper )
import Control.DeepSeq
import Data.Data (Data)
import Data.Typeable (Typeable)
import qualified Text.PrettyPrint as Disp
Expand All @@ -43,6 +44,9 @@ newtype ModuleName = ModuleName [String]

instance Binary ModuleName

instance NFData ModuleName where
rnf (ModuleName ms) = rnf ms

instance Text ModuleName where
disp (ModuleName ms) =
Disp.hcat (intersperse (Disp.char '.') (map Disp.text ms))
Expand Down
108 changes: 70 additions & 38 deletions Cabal/Distribution/PackageDescription.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ module Distribution.PackageDescription (
ModuleReexport(..),
emptyLibrary,
withLib,
hasPublicLib,
hasLibs,
libModules,

Expand Down Expand Up @@ -89,6 +90,8 @@ module Distribution.PackageDescription (
hcSharedOptions,

-- ** Supplementary build information
ComponentName(..),
defaultLibName,
HookedBuildInfo,
emptyHookedBuildInfo,
updatePackageDescription,
Expand Down Expand Up @@ -124,7 +127,6 @@ import Distribution.Text
import Language.Haskell.Extension

import Data.Data (Data)
import Data.Foldable (traverse_)
import Data.List (nub, intercalate)
import Data.Maybe (fromMaybe, maybeToList)
import Data.Foldable as Fold (Foldable(foldMap))
Expand Down Expand Up @@ -189,7 +191,7 @@ data PackageDescription
buildType :: Maybe BuildType,
setupBuildInfo :: Maybe SetupBuildInfo,
-- components
library :: Maybe Library,
libraries :: [Library],
executables :: [Executable],
testSuites :: [TestSuite],
benchmarks :: [Benchmark],
Expand Down Expand Up @@ -256,7 +258,7 @@ emptyPackageDescription
category = "",
customFieldsPD = [],
setupBuildInfo = Nothing,
library = Nothing,
libraries = [],
executables = [],
testSuites = [],
benchmarks = [],
Expand Down Expand Up @@ -387,6 +389,7 @@ instance Text ModuleRenaming where
-- The Library type

data Library = Library {
libName :: String,
exposedModules :: [ModuleName],
reexportedModules :: [ModuleReexport],
requiredSignatures:: [ModuleName], -- ^ What sigs need implementations?
Expand All @@ -400,6 +403,7 @@ instance Binary Library

instance Monoid Library where
mempty = Library {
libName = mempty,
exposedModules = mempty,
reexportedModules = mempty,
requiredSignatures = mempty,
Expand All @@ -411,6 +415,7 @@ instance Monoid Library where

instance Semigroup Library where
a <> b = Library {
libName = combine' libName,
exposedModules = combine exposedModules,
reexportedModules = combine reexportedModules,
requiredSignatures = combine requiredSignatures,
Expand All @@ -419,26 +424,31 @@ instance Semigroup Library where
libBuildInfo = combine libBuildInfo
}
where combine field = field a `mappend` field b
combine' field = case (field a, field b) of
("","") -> ""
("", x) -> x
(x, "") -> x
(x, y) -> error $ "Ambiguous values for library field: '"
++ x ++ "' and '" ++ y ++ "'"

emptyLibrary :: Library
emptyLibrary = mempty

-- |does this package have any libraries?
hasLibs :: PackageDescription -> Bool
hasLibs p = maybe False (buildable . libBuildInfo) (library p)
-- | Does this package have a PUBLIC library?
hasPublicLib :: PackageDescription -> Bool
hasPublicLib p = any f (libraries p)
where f lib = buildable (libBuildInfo lib) &&
libName lib == display (packageName (package p))

-- |'Maybe' version of 'hasLibs'
maybeHasLibs :: PackageDescription -> Maybe Library
maybeHasLibs p =
library p >>= \lib -> if buildable (libBuildInfo lib)
then Just lib
else Nothing
-- | Does this package have any libraries?
hasLibs :: PackageDescription -> Bool
hasLibs p = any (buildable . libBuildInfo) (libraries p)

-- |If the package description has a library section, call the given
-- function with the library build info as argument.
withLib :: PackageDescription -> (Library -> IO ()) -> IO ()
withLib pkg_descr f =
traverse_ f (maybeHasLibs pkg_descr)
sequence_ [f lib | lib <- libraries pkg_descr, buildable (libBuildInfo lib)]

-- | Get all the module names from the library (exposed and internal modules)
-- which need to be compiled. (This does not include reexports, which
Expand Down Expand Up @@ -915,7 +925,7 @@ emptyBuildInfo = mempty
-- all buildable executables, test suites and benchmarks. Useful for gathering
-- dependencies.
allBuildInfo :: PackageDescription -> [BuildInfo]
allBuildInfo pkg_descr = [ bi | Just lib <- [library pkg_descr]
allBuildInfo pkg_descr = [ bi | lib <- libraries pkg_descr
, let bi = libBuildInfo lib
, buildable bi ]
++ [ bi | exe <- executables pkg_descr
Expand Down Expand Up @@ -950,10 +960,22 @@ usedExtensions :: BuildInfo -> [Extension]
usedExtensions bi = oldExtensions bi
++ defaultExtensions bi

type HookedBuildInfo = (Maybe BuildInfo, [(String, BuildInfo)])
-- Libraries live in a separate namespace, so must distinguish
data ComponentName = CLibName String
| CExeName String
| CTestName String
| CBenchName String
deriving (Eq, Generic, Ord, Read, Show)

instance Binary ComponentName

defaultLibName :: PackageIdentifier -> ComponentName
defaultLibName pid = CLibName (display (pkgName pid))

type HookedBuildInfo = [(ComponentName, BuildInfo)]

emptyHookedBuildInfo :: HookedBuildInfo
emptyHookedBuildInfo = (Nothing, [])
emptyHookedBuildInfo = []

-- |Select options for a particular Haskell compiler.
hcOptions :: CompilerFlavor -> BuildInfo -> [String]
Expand Down Expand Up @@ -1109,28 +1131,38 @@ lowercase = map Char.toLower
-- ------------------------------------------------------------

updatePackageDescription :: HookedBuildInfo -> PackageDescription -> PackageDescription
updatePackageDescription (mb_lib_bi, exe_bi) p
= p{ executables = updateExecutables exe_bi (executables p)
, library = updateLibrary mb_lib_bi (library p)
}
updatePackageDescription hooked_bis p
= p{ executables = updateMany (CExeName . exeName) updateExecutable (executables p)
, libraries = updateMany (CLibName . libName) updateLibrary (libraries p)
, benchmarks = updateMany (CBenchName . benchmarkName) updateBenchmark (benchmarks p)
, testSuites = updateMany (CTestName . testName) updateTestSuite (testSuites p)
}
where
updateLibrary :: Maybe BuildInfo -> Maybe Library -> Maybe Library
updateLibrary (Just bi) (Just lib) = Just (lib{libBuildInfo = bi `mappend` libBuildInfo lib})
updateLibrary Nothing mb_lib = mb_lib
updateLibrary (Just _) Nothing = Nothing

updateExecutables :: [(String, BuildInfo)] -- ^[(exeName, new buildinfo)]
-> [Executable] -- ^list of executables to update
-> [Executable] -- ^list with exeNames updated
updateExecutables exe_bi' executables' = foldr updateExecutable executables' exe_bi'

updateExecutable :: (String, BuildInfo) -- ^(exeName, new buildinfo)
-> [Executable] -- ^list of executables to update
-> [Executable] -- ^list with exeName updated
updateExecutable _ [] = []
updateExecutable exe_bi'@(name,bi) (exe:exes)
| exeName exe == name = exe{buildInfo = bi `mappend` buildInfo exe} : exes
| otherwise = exe : updateExecutable exe_bi' exes
updateMany :: (a -> ComponentName) -- ^ get 'ComponentName' from @a@
-> (BuildInfo -> a -> a) -- ^ @updateExecutable@, @updateLibrary@, etc
-> [a] -- ^list of components to update
-> [a] -- ^list with updated components
updateMany name update cs' = foldr (updateOne name update) cs' hooked_bis

updateOne :: (a -> ComponentName) -- ^ get 'ComponentName' from @a@
-> (BuildInfo -> a -> a) -- ^ @updateExecutable@, @updateLibrary@, etc
-> (ComponentName, BuildInfo) -- ^(name, new buildinfo)
-> [a] -- ^list of components to update
-> [a] -- ^list with name component updated
updateOne _ _ _ [] = []
updateOne name_sel update hooked_bi'@(name,bi) (c:cs)
| name_sel c == name ||
-- Special case: an empty name means "please update the BuildInfo for
-- the public library, i.e. the one with the same name as the
-- package." See 'parseHookedBuildInfo'.
name == CLibName "" && name_sel c == defaultLibName (package p)
= update bi c : cs
| otherwise = c : updateOne name_sel update hooked_bi' cs

updateExecutable bi exe = exe{buildInfo = bi `mappend` buildInfo exe}
updateLibrary bi lib = lib{libBuildInfo = bi `mappend` libBuildInfo lib}
updateBenchmark bi ben = ben{benchmarkBuildInfo = bi `mappend` benchmarkBuildInfo ben}
updateTestSuite bi test = test{testBuildInfo = bi `mappend` testBuildInfo test}

-- ---------------------------------------------------------------------------
-- The GenericPackageDescription type
Expand All @@ -1139,7 +1171,7 @@ data GenericPackageDescription =
GenericPackageDescription {
packageDescription :: PackageDescription,
genPackageFlags :: [Flag],
condLibrary :: Maybe (CondTree ConfVar [Dependency] Library),
condLibraries :: [(String, CondTree ConfVar [Dependency] Library)],
condExecutables :: [(String, CondTree ConfVar [Dependency] Executable)],
condTestSuites :: [(String, CondTree ConfVar [Dependency] TestSuite)],
condBenchmarks :: [(String, CondTree ConfVar [Dependency] Benchmark)]
Expand Down
39 changes: 29 additions & 10 deletions Cabal/Distribution/PackageDescription/Check.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import Distribution.Text
import Language.Haskell.Extension

import Data.Maybe
( isNothing, isJust, catMaybes, mapMaybe, maybeToList, fromMaybe )
( isNothing, isJust, catMaybes, mapMaybe, fromMaybe )
import Data.List (sort, group, isPrefixOf, nub, find)
import Control.Monad
( filterM, liftM )
Expand Down Expand Up @@ -173,19 +173,19 @@ checkSanity pkg =
, check (all ($ pkg) [ null . executables
, null . testSuites
, null . benchmarks
, isNothing . library ]) $
, null . libraries ]) $
PackageBuildImpossible
"No executables, libraries, tests, or benchmarks found. Nothing to do."

, check (not (null duplicateNames)) $
PackageBuildImpossible $ "Duplicate sections: " ++ commaSep duplicateNames
++ ". The name of every executable, test suite, and benchmark section in"
++ ". The name of every library, executable, test suite, and benchmark section in"
++ " the package must be unique."
]
--TODO: check for name clashes case insensitively: windows file systems cannot
--cope.

++ maybe [] (checkLibrary pkg) (library pkg)
++ concatMap (checkLibrary pkg) (libraries pkg)
++ concatMap (checkExecutable pkg) (executables pkg)
++ concatMap (checkTestSuite pkg) (testSuites pkg)
++ concatMap (checkBenchmark pkg) (benchmarks pkg)
Expand All @@ -199,10 +199,15 @@ checkSanity pkg =
++ "tool only supports up to version " ++ display cabalVersion ++ "."
]
where
-- The public library gets special dispensation, because it
-- is common practice to export a library and name the executable
-- the same as the package. We always put the public library
-- in the top-level directory in dist, so no conflicts either.
libNames = filter (/= unPackageName (packageName pkg)) . map libName $ libraries pkg
exeNames = map exeName $ executables pkg
testNames = map testName $ testSuites pkg
bmNames = map benchmarkName $ benchmarks pkg
duplicateNames = dups $ exeNames ++ testNames ++ bmNames
duplicateNames = dups $ libNames ++ exeNames ++ testNames ++ bmNames

checkLibrary :: PackageDescription -> Library -> [PackageCheck]
checkLibrary pkg lib =
Expand Down Expand Up @@ -359,6 +364,11 @@ checkFields pkg =
++ "need to convert package names to file names so using this name "
++ "would cause problems."

, check ((isPrefixOf "z-") . display . packageName $ pkg) $
PackageDistInexcusable $
"Package names with the prefix 'z-' are reserved by Cabal and "
++ "cannot be used."

, check (isNothing (buildType pkg)) $
PackageBuildWarning $
"No 'build-type' specified. If you do not need a custom Setup.hs or "
Expand Down Expand Up @@ -681,7 +691,7 @@ checkGhcOptions pkg =

where
all_ghc_options = concatMap get_ghc_options (allBuildInfo pkg)
lib_ghc_options = maybe [] (get_ghc_options . libBuildInfo) (library pkg)
lib_ghc_options = concatMap (get_ghc_options . libBuildInfo) (libraries pkg)
get_ghc_options bi = hcOptions GHC bi ++ hcProfOptions GHC bi
++ hcSharedOptions GHC bi

Expand Down Expand Up @@ -904,9 +914,18 @@ checkCabalVersion pkg =
++ "different modules then list the other ones in the "
++ "'other-languages' field."

, checkVersion [1,23]
(case libraries pkg of
[lib] -> libName lib /= unPackageName (packageName pkg)
[] -> False
_ -> True) $
PackageDistInexcusable $
"To use multiple 'library' sections or a named library section "
++ "the package needs to specify at least 'cabal-version >= 1.23'."

-- check use of reexported-modules sections
, checkVersion [1,21]
(maybe False (not.null.reexportedModules) (library pkg)) $
(any (not.null.reexportedModules) (libraries pkg)) $
PackageDistInexcusable $
"To use the 'reexported-module' field the package needs to specify "
++ "at least 'cabal-version: >= 1.21'."
Expand Down Expand Up @@ -1312,7 +1331,7 @@ checkConditionals pkg =
unknownOSs = [ os | OS (OtherOS os) <- conditions ]
unknownArches = [ arch | Arch (OtherArch arch) <- conditions ]
unknownImpls = [ impl | Impl (OtherCompiler impl) _ <- conditions ]
conditions = maybe [] fvs (condLibrary pkg)
conditions = concatMap (fvs . snd) (condLibraries pkg)
++ concatMap (fvs . snd) (condExecutables pkg)
fvs (CondNode _ _ ifs) = concatMap compfv ifs -- free variables
compfv (c, ct, mct) = condfv c ++ fvs ct ++ maybe [] fvs mct
Expand Down Expand Up @@ -1416,8 +1435,8 @@ checkDevelopmentOnlyFlags pkg =

allConditionalBuildInfo :: [([Condition ConfVar], BuildInfo)]
allConditionalBuildInfo =
concatMap (collectCondTreePaths libBuildInfo)
(maybeToList (condLibrary pkg))
concatMap (collectCondTreePaths libBuildInfo . snd)
(condLibraries pkg)

++ concatMap (collectCondTreePaths buildInfo . snd)
(condExecutables pkg)
Expand Down
Loading