    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Distribution.Simple.Build.Macros
    -- Copyright   :  Simon Marlow 2008
    --
    -- Maintainer  :  cabal-devel@haskell.org
    -- Portability :  portable
    --
    -- Generate cabal_macros.h - CPP macros for package version testing
    --
    -- When using CPP you get
    --
    -- > VERSION_<package>
    -- > MIN_VERSION_<package>(A,B,C)
    --
    -- for each /package/ in @build-depends@, which is true if the version of
    -- /package/ in use is @>= A.B.C@, using the normal ordering on version
    -- numbers.
    --
    -- TODO Figure out what to do about backpack and internal libraries. It is very
    -- suspecious that this stuff works with munged package identifiers
    module Distribution.Simple.Build.Macros (
        generateCabalMacrosHeader,
        generatePackageVersionMacros,
      ) where

    import Prelude ()
    import Distribution.Compat.Prelude

    import Distribution.Version
    import Distribution.PackageDescription
    import Distribution.Simple.LocalBuildInfo
    import Distribution.Simple.Program.Db
    import Distribution.Simple.Program.Types
    import Distribution.Types.MungedPackageId
    import Distribution.Types.MungedPackageName
    import Distribution.Pretty

    import qualified Distribution.Simple.Build.Macros.Z as Z

    -- | The contents of the @cabal_macros.h@ for the given configured package.
    --
    generateCabalMacrosHeader :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> String
    generateCabalMacrosHeader :: PackageDescription
    -> LocalBuildInfo -> ComponentLocalBuildInfo -> String
    generateCabalMacrosHeader PackageDescription
    pkg_descr LocalBuildInfo
    lbi ComponentLocalBuildInfo
    clbi = Z -> String
    Z.render Z :: [ZPackage]
    -> [ZTool]
    -> String
    -> String
    -> Version
    -> (String -> Bool)
    -> (PackageName -> String)
    -> (String -> String)
    -> Z
    Z.Z
        { zPackages :: [ZPackage]
    Z.zPackages        = (PackageId -> ZPackage) -> [PackageId] -> [ZPackage]
    forall a b. (a -> b) -> [a] -> [b]
    map PackageId -> ZPackage
    mkZPackage ([PackageId] -> [ZPackage]) -> [PackageId] -> [ZPackage]
    forall a b. (a -> b) -> a -> b
    $ PackageDescription -> PackageId
    package PackageDescription
    pkg_descr PackageId -> [PackageId] -> [PackageId]
    forall a. a -> [a] -> [a]
    : ((UnitId, MungedPackageId) -> PackageId)
    -> [(UnitId, MungedPackageId)] -> [PackageId]
    forall a b. (a -> b) -> [a] -> [b]
    map (UnitId, MungedPackageId) -> PackageId
    forall {a}. (a, MungedPackageId) -> PackageId
    getPid (ComponentLocalBuildInfo -> [(UnitId, MungedPackageId)]
    componentPackageDeps ComponentLocalBuildInfo
    clbi)
        , zTools :: [ZTool]
    Z.zTools           =
            [ ZTool :: String -> Version -> String -> String -> String -> ZTool
    Z.ZTool
                { ztoolName :: String
    Z.ztoolName    = ConfiguredProgram -> String
    programId ConfiguredProgram
    prog
                , ztoolVersion :: Version
    Z.ztoolVersion = Version
    ver
                , ztoolX :: String
    Z.ztoolX       = String
    major1
                , ztoolY :: String
    Z.ztoolY       = String
    major2
                , ztoolZ :: String
    Z.ztoolZ       = String
    minor
                }
            | ConfiguredProgram
    prog <- ProgramDb -> [ConfiguredProgram]
    configuredPrograms (ProgramDb -> [ConfiguredProgram])
    -> ProgramDb -> [ConfiguredProgram]
    forall a b. (a -> b) -> a -> b
    $ LocalBuildInfo -> ProgramDb
    withPrograms LocalBuildInfo
    lbi
            , Version
    ver <- Maybe Version -> [Version]
    forall a. Maybe a -> [a]
    maybeToList (ConfiguredProgram -> Maybe Version
    programVersion ConfiguredProgram
    prog)
            , let (String
    major1,String
    major2,String
    minor) = Version -> (String, String, String)
    majorMinor Version
    ver
            ]
        , zPackageKey :: String
    Z.zPackageKey      = case ComponentLocalBuildInfo
    clbi of
            LibComponentLocalBuildInfo{} -> ComponentLocalBuildInfo -> String
    componentCompatPackageKey ComponentLocalBuildInfo
    clbi
            ComponentLocalBuildInfo
    _                            -> String
    ""
        , zComponentId :: String
    Z.zComponentId     = ComponentId -> String
    forall a. Pretty a => a -> String
    prettyShow (ComponentLocalBuildInfo -> ComponentId
    componentComponentId ComponentLocalBuildInfo
    clbi)
        , zPackageVersion :: Version
    Z.zPackageVersion  = PackageId -> Version
    pkgVersion (PackageDescription -> PackageId
    package PackageDescription
    pkg_descr)
        , zNotNull :: String -> Bool
    Z.zNotNull         = Bool -> Bool
    not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null
        , zManglePkgName :: PackageName -> String
    Z.zManglePkgName   = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    fixchar (String -> String)
    -> (PackageName -> String) -> PackageName -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PackageName -> String
    unPackageName
        , zMangleStr :: String -> String
    Z.zMangleStr       = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    fixchar
        }
      where
        getPid :: (a, MungedPackageId) -> PackageId
    getPid (a
    _, MungedPackageId (MungedPackageName PackageName
    pn LibraryName
    _) Version
    v) =
           -- NB: Drop the library name! We're just reporting package versions.
           -- This would have to be revisited if you are allowed to depend
           -- on different versions of the same package
            PackageName -> Version -> PackageId
    PackageIdentifier PackageName
    pn Version
    v

    -- | Helper function that generates just the @VERSION_pkg@ and @MIN_VERSION_pkg@
    -- macros for a list of package ids (usually used with the specific deps of
    -- a configured package).
    --
    generatePackageVersionMacros :: Version -> [PackageId] -> String
    generatePackageVersionMacros :: Version -> [PackageId] -> String
    generatePackageVersionMacros Version
    ver [PackageId]
    pkgids = Z -> String
    Z.render Z :: [ZPackage]
    -> [ZTool]
    -> String
    -> String
    -> Version
    -> (String -> Bool)
    -> (PackageName -> String)
    -> (String -> String)
    -> Z
    Z.Z
        { zPackages :: [ZPackage]
    Z.zPackages        = (PackageId -> ZPackage) -> [PackageId] -> [ZPackage]
    forall a b. (a -> b) -> [a] -> [b]
    map PackageId -> ZPackage
    mkZPackage [PackageId]
    pkgids
        , zTools :: [ZTool]
    Z.zTools           = []
        , zPackageKey :: String
    Z.zPackageKey      = String
    ""
        , zComponentId :: String
    Z.zComponentId     = String
    ""
        , zPackageVersion :: Version
    Z.zPackageVersion  = Version
    ver
        , zNotNull :: String -> Bool
    Z.zNotNull         = Bool -> Bool
    not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null
        , zManglePkgName :: PackageName -> String
    Z.zManglePkgName   = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    fixchar (String -> String)
    -> (PackageName -> String) -> PackageName -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PackageName -> String
    unPackageName
        , zMangleStr :: String -> String
    Z.zMangleStr       = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    fixchar
        }

    mkZPackage :: PackageId -> Z.ZPackage
    mkZPackage :: PackageId -> ZPackage
    mkZPackage (PackageIdentifier PackageName
    name Version
    ver) = ZPackage :: PackageName -> Version -> String -> String -> String -> ZPackage
    Z.ZPackage
        { zpkgName :: PackageName
    Z.zpkgName    = PackageName
    name
        , zpkgVersion :: Version
    Z.zpkgVersion = Version
    ver
        , zpkgX :: String
    Z.zpkgX       = String
    major1
        , zpkgY :: String
    Z.zpkgY       = String
    major2
        , zpkgZ :: String
    Z.zpkgZ       = String
    minor
        }
      where
        (String
    major1,String
    major2,String
    minor) = Version -> (String, String, String)
    majorMinor Version
    ver

    majorMinor :: Version -> (String, String, String)
    majorMinor :: Version -> (String, String, String)
    majorMinor Version
    ver = case (Int -> String) -> [Int] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Int -> String
    forall a. Show a => a -> String
    show (Version -> [Int]
    versionNumbers Version
    ver) of
            []        -> (String
    "0", String
    "0", String
    "0")
            [String
    x]       -> (String
    x,   String
    "0", String
    "0")
            [String
    x,String
    y]     -> (String
    x,   String
    y,   String
    "0")
            (String
    x:String
    y:String
    z:[String]
    _) -> (String
    x,   String
    y,   String
    z)

    fixchar :: Char -> Char
    fixchar :: Char -> Char
    fixchar Char
    '-' = Char
    '_'
    fixchar Char
    c   = Char
    c