{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
module Distribution.Types.PackageDescription (
PackageDescription(..),
specVersion,
specVersion',
license,
license',
descCabalVersion,
buildType,
emptyPackageDescription,
hasPublicLib,
hasLibs,
allLibraries,
withLib,
hasExes,
withExe,
hasTests,
withTest,
hasBenchmarks,
withBenchmark,
hasForeignLibs,
withForeignLib,
allBuildInfo,
enabledBuildInfos,
allBuildDepends,
enabledBuildDepends,
updatePackageDescription,
pkgComponents,
pkgBuildableComponents,
enabledComponents,
lookupComponent,
getComponent,
) where
import Prelude ()
import Distribution.Compat.Prelude
import Control.Monad ((<=<))
import qualified Distribution.Types.BuildInfo.Lens as L
import Distribution.Types.Library
import Distribution.Types.TestSuite
import Distribution.Types.Executable
import Distribution.Types.Benchmark
import Distribution.Types.ForeignLib
import Distribution.Types.Component
import Distribution.Types.ComponentRequestedSpec
import Distribution.Types.Dependency
import Distribution.Types.PackageId
import Distribution.Types.ComponentName
import Distribution.Types.PackageName
import Distribution.Types.UnqualComponentName
import Distribution.Types.SetupBuildInfo
import Distribution.Types.BuildInfo
import Distribution.Types.BuildType
import Distribution.Types.SourceRepo
import Distribution.Types.HookedBuildInfo
import Distribution.Compiler
import Distribution.License
import Distribution.Package
import Distribution.Version
import qualified Distribution.SPDX as SPDX
data PackageDescription
= PackageDescription {
specVersionRaw :: Either Version VersionRange,
package :: PackageIdentifier,
licenseRaw :: Either SPDX.License License,
licenseFiles :: [FilePath],
copyright :: String,
maintainer :: String,
author :: String,
stability :: String,
testedWith :: [(CompilerFlavor,VersionRange)],
homepage :: String,
pkgUrl :: String,
bugReports :: String,
sourceRepos :: [SourceRepo],
synopsis :: String,
description :: String,
category :: String,
customFieldsPD :: [(String,String)],
buildTypeRaw :: Maybe BuildType,
setupBuildInfo :: Maybe SetupBuildInfo,
library :: Maybe Library,
subLibraries :: [Library],
executables :: [Executable],
foreignLibs :: [ForeignLib],
testSuites :: [TestSuite],
benchmarks :: [Benchmark],
dataFiles :: [FilePath],
dataDir :: FilePath,
extraSrcFiles :: [FilePath],
extraTmpFiles :: [FilePath],
extraDocFiles :: [FilePath]
}
deriving (Generic, Show, Read, Eq, Typeable, Data)
instance Binary PackageDescription
instance NFData PackageDescription where rnf = genericRnf
instance Package PackageDescription where
packageId = package
specVersion :: PackageDescription -> Version
specVersion = specVersion' . specVersionRaw
specVersion' :: Either Version VersionRange -> Version
specVersion' (Left version) = version
specVersion' (Right versionRange) = case asVersionIntervals versionRange of
[] -> mkVersion [0]
((LowerBound version _, _):_) -> version
license :: PackageDescription -> SPDX.License
license = license' . licenseRaw
license' :: Either SPDX.License License -> SPDX.License
license' = either id licenseToSPDX
descCabalVersion :: PackageDescription -> VersionRange
descCabalVersion pkg = case specVersionRaw pkg of
Left version -> orLaterVersion version
Right versionRange -> versionRange
{-# DEPRECATED descCabalVersion "Use specVersion instead. This symbol will be removed in Cabal-3.0 (est. Mar 2019)." #-}
buildType :: PackageDescription -> BuildType
buildType pkg
| specVersion pkg >= mkVersion [2,1]
= fromMaybe newDefault (buildTypeRaw pkg)
| otherwise
= fromMaybe Custom (buildTypeRaw pkg)
where
newDefault | isNothing (setupBuildInfo pkg) = Simple
| otherwise = Custom
emptyPackageDescription :: PackageDescription
emptyPackageDescription
= PackageDescription {
package = PackageIdentifier (mkPackageName "")
nullVersion,
licenseRaw = Right UnspecifiedLicense,
licenseFiles = [],
specVersionRaw = Right anyVersion,
buildTypeRaw = Nothing,
copyright = "",
maintainer = "",
author = "",
stability = "",
testedWith = [],
homepage = "",
pkgUrl = "",
bugReports = "",
sourceRepos = [],
synopsis = "",
description = "",
category = "",
customFieldsPD = [],
setupBuildInfo = Nothing,
library = Nothing,
subLibraries = [],
foreignLibs = [],
executables = [],
testSuites = [],
benchmarks = [],
dataFiles = [],
dataDir = "",
extraSrcFiles = [],
extraTmpFiles = [],
extraDocFiles = []
}
hasPublicLib :: PackageDescription -> Bool
hasPublicLib p =
case library p of
Just lib -> buildable (libBuildInfo lib)
Nothing -> False
hasLibs :: PackageDescription -> Bool
hasLibs p = any (buildable . libBuildInfo) (allLibraries p)
allLibraries :: PackageDescription -> [Library]
allLibraries p = maybeToList (library p) ++ subLibraries p
withLib :: PackageDescription -> (Library -> IO ()) -> IO ()
withLib pkg_descr f =
sequence_ [f lib | lib <- allLibraries pkg_descr, buildable (libBuildInfo lib)]
hasExes :: PackageDescription -> Bool
hasExes p = any (buildable . buildInfo) (executables p)
withExe :: PackageDescription -> (Executable -> IO ()) -> IO ()
withExe pkg_descr f =
sequence_ [f exe | exe <- executables pkg_descr, buildable (buildInfo exe)]
hasTests :: PackageDescription -> Bool
hasTests = any (buildable . testBuildInfo) . testSuites
withTest :: PackageDescription -> (TestSuite -> IO ()) -> IO ()
withTest pkg_descr f =
sequence_ [ f test | test <- testSuites pkg_descr, buildable (testBuildInfo test) ]
hasBenchmarks :: PackageDescription -> Bool
hasBenchmarks = any (buildable . benchmarkBuildInfo) . benchmarks
withBenchmark :: PackageDescription -> (Benchmark -> IO ()) -> IO ()
withBenchmark pkg_descr f =
sequence_ [f bench | bench <- benchmarks pkg_descr, buildable (benchmarkBuildInfo bench)]
hasForeignLibs :: PackageDescription -> Bool
hasForeignLibs p = any (buildable . foreignLibBuildInfo) (foreignLibs p)
withForeignLib :: PackageDescription -> (ForeignLib -> IO ()) -> IO ()
withForeignLib pkg_descr f =
sequence_ [ f flib
| flib <- foreignLibs pkg_descr
, buildable (foreignLibBuildInfo flib)
]
allBuildInfo :: PackageDescription -> [BuildInfo]
allBuildInfo pkg_descr = [ bi | lib <- allLibraries pkg_descr
, let bi = libBuildInfo lib ]
++ [ bi | flib <- foreignLibs pkg_descr
, let bi = foreignLibBuildInfo flib ]
++ [ bi | exe <- executables pkg_descr
, let bi = buildInfo exe ]
++ [ bi | tst <- testSuites pkg_descr
, let bi = testBuildInfo tst ]
++ [ bi | tst <- benchmarks pkg_descr
, let bi = benchmarkBuildInfo tst ]
enabledBuildInfos :: PackageDescription -> ComponentRequestedSpec -> [BuildInfo]
enabledBuildInfos pkg enabled =
[ componentBuildInfo comp
| comp <- enabledComponents pkg enabled ]
allBuildDepends :: PackageDescription -> [Dependency]
allBuildDepends = targetBuildDepends <=< allBuildInfo
enabledBuildDepends :: PackageDescription -> ComponentRequestedSpec -> [Dependency]
enabledBuildDepends spec pd = targetBuildDepends =<< enabledBuildInfos spec pd
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) }
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 :: [(UnqualComponentName, BuildInfo)]
-> [Executable]
-> [Executable]
updateExecutables exe_bi' executables' = foldr updateExecutable executables' exe_bi'
updateExecutable :: (UnqualComponentName, BuildInfo)
-> [Executable]
-> [Executable]
updateExecutable _ [] = []
updateExecutable exe_bi'@(name,bi) (exe:exes)
| exeName exe == name = exe{buildInfo = bi `mappend` buildInfo exe} : exes
| otherwise = exe : updateExecutable exe_bi' exes
pkgComponents :: PackageDescription -> [Component]
pkgComponents pkg =
[ CLib lib | lib <- allLibraries pkg ]
++ [ CFLib flib | flib <- foreignLibs pkg ]
++ [ CExe exe | exe <- executables pkg ]
++ [ CTest tst | tst <- testSuites pkg ]
++ [ CBench bm | bm <- benchmarks pkg ]
pkgBuildableComponents :: PackageDescription -> [Component]
pkgBuildableComponents = filter componentBuildable . pkgComponents
enabledComponents :: PackageDescription -> ComponentRequestedSpec -> [Component]
enabledComponents pkg enabled = filter (componentEnabled enabled) $ pkgBuildableComponents pkg
lookupComponent :: PackageDescription -> ComponentName -> Maybe Component
lookupComponent pkg CLibName = fmap CLib (library pkg)
lookupComponent pkg (CSubLibName name) =
fmap CLib $ find ((Just name ==) . libName) (subLibraries pkg)
lookupComponent pkg (CFLibName name) =
fmap CFLib $ find ((name ==) . foreignLibName) (foreignLibs pkg)
lookupComponent pkg (CExeName name) =
fmap CExe $ find ((name ==) . exeName) (executables pkg)
lookupComponent pkg (CTestName name) =
fmap CTest $ find ((name ==) . testName) (testSuites pkg)
lookupComponent pkg (CBenchName name) =
fmap CBench $ find ((name ==) . benchmarkName) (benchmarks pkg)
getComponent :: PackageDescription -> ComponentName -> Component
getComponent pkg cname =
case lookupComponent pkg cname of
Just cpnt -> cpnt
Nothing -> missingComponent
where
missingComponent =
error $ "internal error: the package description contains no "
++ "component corresponding to " ++ show cname
instance L.HasBuildInfos PackageDescription where
traverseBuildInfos f (PackageDescription a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19
x1 x2 x3 x4 x5 x6
a20 a21 a22 a23 a24) =
PackageDescription a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19
<$> (traverse . L.buildInfo) f x1
<*> (traverse . L.buildInfo) f x2
<*> (traverse . L.buildInfo) f x3
<*> (traverse . L.buildInfo) f x4
<*> (traverse . L.buildInfo) f x5
<*> (traverse . L.buildInfo) f x6
<*> pure a20
<*> pure a21
<*> pure a22
<*> pure a23
<*> pure a24