{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} ----------------------------------------------------------------------------- -- | -- Module : Distribution.Package -- Copyright : Isaac Jones 2003-2004 -- License : BSD3 -- -- Maintainer : cabal-devel@haskell.org -- Portability : portable -- -- Defines a package identifier along with a parser and pretty printer for it. -- 'PackageIdentifier's consist of a name and an exact version. It also defines -- a 'Dependency' data type. A dependency is a package name and a version -- range, like @\"foo >= 1.2 && < 2\"@. module Distribution.Package ( -- * Package ids PackageName(..), PackageIdentifier(..), PackageId, -- * Package keys/installed package IDs (used for linker symbols) ComponentId(..), UnitId(..), mkUnitId, mkLegacyUnitId, getHSLibraryName, InstalledPackageId, -- backwards compat -- * ABI hash AbiHash(..), -- * Package source dependencies Dependency(..), thisPackageVersion, notThisPackageVersion, simplifyDependency, -- * Package classes Package(..), packageName, packageVersion, HasUnitId(..), installedPackageId, PackageInstalled(..), ) where import Distribution.Version ( Version(..), VersionRange, anyVersion, thisVersion , notThisVersion, simplifyVersionRange ) import qualified Distribution.Compat.ReadP as Parse import qualified Text.PrettyPrint as Disp import Distribution.Compat.ReadP import Distribution.Compat.Binary import Distribution.Text import Control.DeepSeq (NFData(..)) import qualified Data.Char as Char ( isDigit, isAlphaNum, ) import Data.Data ( Data ) import Data.List ( intercalate ) import Data.Typeable ( Typeable ) import GHC.Generics (Generic) import Text.PrettyPrint ((<>), (<+>), text) newtype PackageName = PackageName { unPackageName :: String } deriving (Generic, Read, Show, Eq, Ord, Typeable, Data) instance Binary PackageName instance Text PackageName where disp (PackageName n) = Disp.text n parse = do ns <- Parse.sepBy1 component (Parse.char '-') return (PackageName (intercalate "-" ns)) where component = do cs <- Parse.munch1 Char.isAlphaNum if all Char.isDigit cs then Parse.pfail else return cs -- each component must contain an alphabetic character, to avoid -- ambiguity in identifiers like foo-1 (the 1 is the version number). instance NFData PackageName where rnf (PackageName pkg) = rnf pkg -- | Type alias so we can use the shorter name PackageId. type PackageId = PackageIdentifier -- | The name and version of a package. data PackageIdentifier = PackageIdentifier { pkgName :: PackageName, -- ^The name of this package, eg. foo pkgVersion :: Version -- ^the version of this package, eg 1.2 } deriving (Generic, Read, Show, Eq, Ord, Typeable, Data) instance Binary PackageIdentifier instance Text PackageIdentifier where disp (PackageIdentifier n v) = case v of Version [] _ -> disp n -- if no version, don't show version. _ -> disp n <> Disp.char '-' <> disp v parse = do n <- parse v <- (Parse.char '-' >> parse) <++ return (Version [] []) return (PackageIdentifier n v) instance NFData PackageIdentifier where rnf (PackageIdentifier name version) = rnf name `seq` rnf version -- ------------------------------------------------------------ -- * Component Source Hash -- ------------------------------------------------------------ -- | A 'ComponentId' uniquely identifies the transitive source -- code closure of a component. For non-Backpack components, it also -- serves as the basis for install paths, symbols, etc. -- data ComponentId = ComponentId String deriving (Generic, Read, Show, Eq, Ord, Typeable, Data) {-# DEPRECATED InstalledPackageId "Use UnitId instead" #-} type InstalledPackageId = UnitId instance Binary ComponentId instance Text ComponentId where disp (ComponentId str) = text str parse = ComponentId `fmap` Parse.munch1 abi_char where abi_char c = Char.isAlphaNum c || c `elem` "-_." instance NFData ComponentId where rnf (ComponentId pk) = rnf pk -- | Returns library name prefixed with HS, suitable for filenames getHSLibraryName :: UnitId -> String getHSLibraryName (SimpleUnitId (ComponentId s)) = "HS" ++ s -- | For now, there is no distinction between component IDs -- and unit IDs in Cabal. newtype UnitId = SimpleUnitId ComponentId deriving (Generic, Read, Show, Eq, Ord, Typeable, Data, Binary, Text, NFData) -- | Makes a simple-style UnitId from a string. mkUnitId :: String -> UnitId mkUnitId = SimpleUnitId . ComponentId -- | Make an old-style UnitId from a package identifier mkLegacyUnitId :: PackageId -> UnitId mkLegacyUnitId = SimpleUnitId . ComponentId . display -- ------------------------------------------------------------ -- * Package source dependencies -- ------------------------------------------------------------ -- | Describes a dependency on a source package (API) -- data Dependency = Dependency PackageName VersionRange deriving (Generic, Read, Show, Eq, Typeable, Data) instance Binary Dependency instance Text Dependency where disp (Dependency name ver) = disp name <+> disp ver parse = do name <- parse Parse.skipSpaces ver <- parse <++ return anyVersion Parse.skipSpaces return (Dependency name ver) thisPackageVersion :: PackageIdentifier -> Dependency thisPackageVersion (PackageIdentifier n v) = Dependency n (thisVersion v) notThisPackageVersion :: PackageIdentifier -> Dependency notThisPackageVersion (PackageIdentifier n v) = Dependency n (notThisVersion v) -- | Simplify the 'VersionRange' expression in a 'Dependency'. -- See 'simplifyVersionRange'. -- simplifyDependency :: Dependency -> Dependency simplifyDependency (Dependency name range) = Dependency name (simplifyVersionRange range) -- | Class of things that have a 'PackageIdentifier' -- -- Types in this class are all notions of a package. This allows us to have -- different types for the different phases that packages go though, from -- simple name\/id, package description, configured or installed packages. -- -- Not all kinds of packages can be uniquely identified by a -- 'PackageIdentifier'. In particular, installed packages cannot, there may be -- many installed instances of the same source package. -- class Package pkg where packageId :: pkg -> PackageIdentifier packageName :: Package pkg => pkg -> PackageName packageName = pkgName . packageId packageVersion :: Package pkg => pkg -> Version packageVersion = pkgVersion . packageId instance Package PackageIdentifier where packageId = id -- | Packages that have an installed package ID class Package pkg => HasUnitId pkg where installedUnitId :: pkg -> UnitId {-# DEPRECATED installedPackageId "Use installedUnitId instead" #-} -- | Compatibility wrapper for Cabal pre-1.24. installedPackageId :: HasUnitId pkg => pkg -> UnitId installedPackageId = installedUnitId -- | Class of installed packages. -- -- The primary data type which is an instance of this package is -- 'InstalledPackageInfo', but when we are doing install plans in Cabal install -- we may have other, installed package-like things which contain more metadata. -- Installed packages have exact dependencies 'installedDepends'. class (HasUnitId pkg) => PackageInstalled pkg where installedDepends :: pkg -> [UnitId] -- ----------------------------------------------------------------------------- -- ABI hash newtype AbiHash = AbiHash String deriving (Eq, Show, Read, Generic) instance Binary AbiHash instance Text AbiHash where disp (AbiHash abi) = Disp.text abi parse = fmap AbiHash (Parse.munch Char.isAlphaNum)