Distribution/Package.hs

    {-# 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, unPackageName, mkPackageName,
            PackageIdentifier(..),
            PackageId,
            PkgconfigName, unPkgconfigName, mkPkgconfigName,

            -- * Package keys/installed package IDs (used for linker symbols)
            ComponentId, unComponentId, mkComponentId,
            UnitId, unUnitId, mkUnitId,
            DefUnitId,
            unsafeMkDefUnitId,
            unDefUnitId,
            newSimpleUnitId,
            mkLegacyUnitId,
            getHSLibraryName,
            InstalledPackageId, -- backwards compat

            -- * Modules
            Module(..),

            -- * ABI hash
            AbiHash, unAbiHash, mkAbiHash,

            -- * Package classes
            Package(..), packageName, packageVersion,
            HasUnitId(..),
            installedPackageId,
            PackageInstalled(..),
      ) where

    import Prelude ()
    import Distribution.Compat.Prelude
    import Distribution.Utils.ShortText

    import Distribution.Version
             ( Version, nullVersion )

    import qualified Distribution.Compat.ReadP as Parse
    import qualified Text.PrettyPrint as Disp
    import Distribution.Compat.ReadP
    import Distribution.ParseUtils
    import Distribution.Text
    import Distribution.ModuleName

    import Text.PrettyPrint (text)

    -- | A package name.
    --
    -- Use 'mkPackageName' and 'unPackageName' to convert from/to a
    -- 'String'.
    --
    -- This type is opaque since @Cabal-2.0@
    --
    -- @since 2.0
    newtype PackageName = PackageName ShortText
        deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)

    -- | Convert 'PackageName' to 'String'
    unPackageName :: PackageName -> String
    unPackageName (PackageName s) = fromShortText s

    -- | Construct a 'PackageName' from a 'String'
    --
    -- 'mkPackageName' is the inverse to 'unPackageName'
    --
    -- Note: No validations are performed to ensure that the resulting
    -- 'PackageName' is valid
    --
    -- @since 2.0
    mkPackageName :: String -> PackageName
    mkPackageName = PackageName . toShortText

    -- | 'mkPackageName'
    --
    -- @since 2.0
    instance IsString PackageName where
      fromString = mkPackageName

    instance Binary PackageName

    instance Text PackageName where
      disp = Disp.text . unPackageName
      parse = mkPackageName <$> parsePackageName

    instance NFData PackageName where
        rnf (PackageName pkg) = rnf pkg

    -- | A pkg-config library name
    --
    -- This is parsed as any valid argument to the pkg-config utility.
    --
    -- @since 2.0
    newtype PkgconfigName = PkgconfigName ShortText
        deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)

    -- | Convert 'PkgconfigName' to 'String'
    --
    -- @since 2.0
    unPkgconfigName :: PkgconfigName -> String
    unPkgconfigName (PkgconfigName s) = fromShortText s

    -- | Construct a 'PkgconfigName' from a 'String'
    --
    -- 'mkPkgconfigName' is the inverse to 'unPkgconfigName'
    --
    -- Note: No validations are performed to ensure that the resulting
    -- 'PkgconfigName' is valid
    --
    -- @since 2.0
    mkPkgconfigName :: String -> PkgconfigName
    mkPkgconfigName = PkgconfigName . toShortText

    -- | 'mkPkgconfigName'
    --
    -- @since 2.0
    instance IsString PkgconfigName where
        fromString = mkPkgconfigName

    instance Binary PkgconfigName

    -- pkg-config allows versions and other letters in package names, eg
    -- "gtk+-2.0" is a valid pkg-config package _name_.  It then has a package
    -- version number like 2.10.13
    instance Text PkgconfigName where
      disp = Disp.text . unPkgconfigName
      parse = mkPkgconfigName
              <$> munch1 (\c -> isAlphaNum c || c `elem` "+-._")

    instance NFData PkgconfigName where
        rnf (PkgconfigName 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)
        | v == nullVersion = disp n -- if no version, don't show version.
        | otherwise        = disp n <<>> Disp.char '-' <<>> disp v

      parse = do
        n <- parse
        v <- (Parse.char '-' >> parse) <++ return nullVersion
        return (PackageIdentifier n v)

    instance NFData PackageIdentifier where
        rnf (PackageIdentifier name version) = rnf name `seq` rnf version

    -- | A module identity uniquely identifies a Haskell module by
    -- qualifying a 'ModuleName' with the 'UnitId' which defined
    -- it.  This type distinguishes between two packages
    -- which provide a module with the same name, or a module
    -- from the same package compiled with different dependencies.
    -- There are a few cases where Cabal needs to know about
    -- module identities, e.g., when writing out reexported modules in
    -- the 'InstalledPackageInfo'.
    data Module =
          Module DefUnitId ModuleName
        deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)

    instance Binary Module

    instance Text Module where
        disp (Module uid mod_name) =
            disp uid <<>> Disp.text ":" <<>> disp mod_name
        parse = do
            uid <- parse
            _ <- Parse.char ':'
            mod_name <- parse
            return (Module uid mod_name)

    instance NFData Module where
        rnf (Module uid mod_name) = rnf uid `seq` rnf mod_name

    -- | A 'ComponentId' uniquely identifies the transitive source
    -- code closure of a component (i.e. libraries, executables).
    --
    -- For non-Backpack components, this corresponds one to one with
    -- the 'UnitId', which serves as the basis for install paths,
    -- linker symbols, etc.
    --
    -- Use 'mkComponentId' and 'unComponentId' to convert from/to a
    -- 'String'.
    --
    -- This type is opaque since @Cabal-2.0@
    --
    -- @since 2.0
    newtype ComponentId = ComponentId ShortText
        deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)

    -- | Construct a 'ComponentId' from a 'String'
    --
    -- 'mkComponentId' is the inverse to 'unComponentId'
    --
    -- Note: No validations are performed to ensure that the resulting
    -- 'ComponentId' is valid
    --
    -- @since 2.0
    mkComponentId :: String -> ComponentId
    mkComponentId = ComponentId . toShortText

    -- | 'mkComponentId'
    --
    -- @since 2.0
    instance IsString ComponentId where
        fromString = mkComponentId

    -- | Convert 'ComponentId' to 'String'
    --
    -- @since 2.0
    unComponentId :: ComponentId -> String
    unComponentId (ComponentId s) = fromShortText s

    {-# DEPRECATED InstalledPackageId "Use UnitId instead" #-}
    type InstalledPackageId = UnitId

    instance Binary ComponentId

    instance Text ComponentId where
      disp = text . unComponentId

      parse = mkComponentId `fmap` Parse.munch1 abi_char
       where abi_char c = isAlphaNum c || c `elem` "-_."

    instance NFData ComponentId where
        rnf = rnf . unComponentId

    -- | Returns library name prefixed with HS, suitable for filenames
    getHSLibraryName :: UnitId -> String
    getHSLibraryName uid = "HS" ++ display uid

    -- | A unit identifier identifies a (possibly instantiated)
    -- package/component that can be installed the installed package
    -- database.  There are several types of components that can be
    -- installed:
    --
    --  * A traditional library with no holes, so that 'unitIdHash'
    --    is @Nothing@.  In the absence of Backpack, 'UnitId'
    --    is the same as a 'ComponentId'.
    --
    --  * An indefinite, Backpack library with holes.  In this case,
    --    'unitIdHash' is still @Nothing@, but in the install,
    --    there are only interfaces, no compiled objects.
    --
    --  * An instantiated Backpack library with all the holes
    --    filled in.  'unitIdHash' is a @Just@ a hash of the
    --    instantiating mapping.
    --
    -- A unit is a component plus the additional information on how the
    -- holes are filled in. Thus there is a one to many relationship: for a
    -- particular component there are many different ways of filling in the
    -- holes, and each different combination is a unit (and has a separate
    -- 'UnitId').
    --
    -- 'UnitId' is distinct from 'OpenUnitId', in that it is always
    -- installed, whereas 'OpenUnitId' are intermediate unit identities
    -- that arise during mixin linking, and don't necessarily correspond
    -- to any actually installed unit.  Since the mapping is not actually
    -- recorded in a 'UnitId', you can't actually substitute over them
    -- (but you can substitute over 'OpenUnitId').  See also
    -- "Distribution.Backpack.FullUnitId" for a mechanism for expanding an
    -- instantiated 'UnitId' to retrieve its mapping.
    --
    -- Backwards compatibility note: if you need to get the string
    -- representation of a UnitId to pass, e.g., as a @-package-id@
    -- flag, use the 'display' function, which will work on all
    -- versions of Cabal.
    --
    newtype UnitId = UnitId ShortText
      deriving (Generic, Read, Show, Eq, Ord, Typeable, Data, NFData)

    instance Binary UnitId

    -- | The textual format for 'UnitId' coincides with the format
    -- GHC accepts for @-package-id@.
    --
    instance Text UnitId where
        disp = text . unUnitId
        parse = mkUnitId <$> Parse.munch1 (\c -> isAlphaNum c || c `elem` "-_.+")

    -- | If you need backwards compatibility, consider using 'display'
    -- instead, which is supported by all versions of Cabal.
    --
    unUnitId :: UnitId -> String
    unUnitId (UnitId s) = fromShortText s

    mkUnitId :: String -> UnitId
    mkUnitId = UnitId . toShortText

    -- | 'mkUnitId'
    --
    -- @since 2.0
    instance IsString UnitId where
        fromString = mkUnitId

    -- | A 'UnitId' for a definite package.  The 'DefUnitId' invariant says
    -- that a 'UnitId' identified this way is definite; i.e., it has no
    -- unfilled holes.
    newtype DefUnitId = DefUnitId { unDefUnitId :: UnitId }
      deriving (Generic, Read, Show, Eq, Ord, Typeable, Data, Binary, NFData, Text)

    -- | Unsafely create a 'DefUnitId' from a 'UnitId'.  Your responsibility
    -- is to ensure that the 'DefUnitId' invariant holds.
    unsafeMkDefUnitId :: UnitId -> DefUnitId
    unsafeMkDefUnitId = DefUnitId

    -- | Create a unit identity with no associated hash directly
    -- from a 'ComponentId'.
    newSimpleUnitId :: ComponentId -> UnitId
    newSimpleUnitId (ComponentId s) = UnitId s

    -- | Make an old-style UnitId from a package identifier
    mkLegacyUnitId :: PackageId -> UnitId
    mkLegacyUnitId = newSimpleUnitId . mkComponentId . display

    -- | 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 unit 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

    -- | ABI Hashes
    --
    -- Use 'mkAbiHash' and 'unAbiHash' to convert from/to a
    -- 'String'.
    --
    -- This type is opaque since @Cabal-2.0@
    --
    -- @since 2.0
    newtype AbiHash = AbiHash ShortText
        deriving (Eq, Show, Read, Generic)

    -- | Construct a 'AbiHash' from a 'String'
    --
    -- 'mkAbiHash' is the inverse to 'unAbiHash'
    --
    -- Note: No validations are performed to ensure that the resulting
    -- 'AbiHash' is valid
    --
    -- @since 2.0
    unAbiHash :: AbiHash -> String
    unAbiHash (AbiHash h) = fromShortText h

    -- | Convert 'AbiHash' to 'String'
    --
    -- @since 2.0
    mkAbiHash :: String -> AbiHash
    mkAbiHash = AbiHash . toShortText

    -- | 'mkAbiHash'
    --
    -- @since 2.0
    instance IsString AbiHash where
        fromString = mkAbiHash

    instance Binary AbiHash

    instance Text AbiHash where
        disp = Disp.text . unAbiHash
        parse = fmap mkAbiHash (Parse.munch isAlphaNum)