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