-----------------------------------------------------------------------------

-- |
-- 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
-- suspicious that this stuff works with munged package identifiers
module Distribution.Simple.Build.Macros
  ( generateCabalMacrosHeader
  , generatePackageVersionMacros
  ) where

import Distribution.Compat.Prelude
import Prelude ()

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

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 pkg_descr lbi clbi =
  Z.render
    Z.Z
      { Z.zPackages = map mkZPackage $ package pkg_descr : map getPid (componentPackageDeps clbi)
      , Z.zTools =
          [ Z.ZTool
            { Z.ztoolName = programId prog
            , Z.ztoolVersion = ver
            , Z.ztoolX = major1
            , Z.ztoolY = major2
            , Z.ztoolZ = minor
            }
          | prog <- configuredPrograms $ withPrograms lbi
          , ver <- maybeToList (programVersion prog)
          , let (major1, major2, minor) = majorMinor ver
          ]
      , Z.zPackageKey = case clbi of
          LibComponentLocalBuildInfo{componentCompatPackageKey = compatPackageKey} -> compatPackageKey
          _ -> ""
      , Z.zComponentId = prettyShow (componentComponentId clbi)
      , Z.zPackageVersion = pkgVersion (package pkg_descr)
      , Z.zNotNull = not . null
      , Z.zManglePkgName = map fixchar . unPackageName
      , Z.zMangleStr = map fixchar
      }
  where
    getPid (_, MungedPackageId (MungedPackageName pn _) 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
      PackageIdentifier pn 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 ver pkgids =
  Z.render
    Z.Z
      { Z.zPackages = map mkZPackage pkgids
      , Z.zTools = []
      , Z.zPackageKey = ""
      , Z.zComponentId = ""
      , Z.zPackageVersion = ver
      , Z.zNotNull = not . null
      , Z.zManglePkgName = map fixchar . unPackageName
      , Z.zMangleStr = map fixchar
      }

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

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

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