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

-- |
-- Module      :  Distribution.Simple.Build.Macros
-- Copyright   :  Isaac Jones 2003-2005,
--                Ross Paterson 2006,
--                Duncan Coutts 2007-2008
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- Generating the Paths_pkgname module.
--
-- This is a module that Cabal generates for the benefit of packages. It
-- enables them to find their version number and find any installed data files
-- at runtime. This code should probably be split off into another module.
module Distribution.Simple.Build.PathsModule
  ( generatePathsModule
  , pkgPathEnvVar
  ) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Package
import Distribution.PackageDescription
import Distribution.Simple.Compiler
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Utils (shortRelativePath)
import Distribution.System
import Distribution.Version

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

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

-- * Building Paths_<pkg>.hs

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

generatePathsModule :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> String
generatePathsModule pkg_descr lbi clbi =
  Z.render
    Z.Z
      { Z.zPackageName = packageName pkg_descr
      , Z.zVersionDigits = show $ versionNumbers $ packageVersion pkg_descr
      , Z.zSupportsCpp = supports_cpp
      , Z.zSupportsNoRebindableSyntax = supports_rebindable_syntax
      , Z.zAbsolute = absolute
      , Z.zRelocatable = relocatable lbi
      , Z.zIsWindows = isWindows
      , Z.zIsI386 = buildArch == I386
      , Z.zIsX8664 = buildArch == X86_64
      , Z.zIsAArch64 = buildArch == AArch64
      , Z.zNot = not
      , Z.zManglePkgName = showPkgName
      , Z.zPrefix = show flat_prefix
      , Z.zBindir = zBindir
      , Z.zLibdir = zLibdir
      , Z.zDynlibdir = zDynlibdir
      , Z.zDatadir = zDatadir
      , Z.zLibexecdir = zLibexecdir
      , Z.zSysconfdir = zSysconfdir
      }
  where
    supports_cpp = supports_language_pragma
    supports_rebindable_syntax = ghc_newer_than (mkVersion [7, 0, 1])
    supports_language_pragma = ghc_newer_than (mkVersion [6, 6, 1])

    ghc_newer_than minVersion =
      case compilerCompatVersion GHC (compiler lbi) of
        Nothing -> False
        Just version -> version `withinRange` orLaterVersion minVersion

    -- In several cases we cannot make relocatable installations
    absolute =
      hasLibs pkg_descr -- we can only make progs relocatable
        || isNothing flat_bindirrel -- if the bin dir is an absolute path
        || not (supportsRelocatableProgs (compilerFlavor (compiler lbi)))

    -- TODO: Here, and with zIsI386 & zIs8664 we should use TARGET platform
    isWindows = case buildOS of
      Windows -> True
      _ -> False

    supportsRelocatableProgs GHC = isWindows
    supportsRelocatableProgs GHCJS = isWindows
    supportsRelocatableProgs _ = False

    cid = componentUnitId clbi

    InstallDirs
      { bindir = flat_bindir
      , libdir = flat_libdir
      , dynlibdir = flat_dynlibdir
      , datadir = flat_datadir
      , libexecdir = flat_libexecdir
      , sysconfdir = flat_sysconfdir
      , prefix = flat_prefix
      } = absoluteInstallCommandDirs pkg_descr lbi cid NoCopyDest

    InstallDirs
      { bindir = flat_bindirrel
      , libdir = flat_libdirrel
      , dynlibdir = flat_dynlibdirrel
      , datadir = flat_datadirrel
      , libexecdir = flat_libexecdirrel
      , sysconfdir = flat_sysconfdirrel
      } = prefixRelativeComponentInstallDirs (packageId pkg_descr) lbi cid

    zBindir, zLibdir, zDynlibdir, zDatadir, zLibexecdir, zSysconfdir :: String
    (zBindir, zLibdir, zDynlibdir, zDatadir, zLibexecdir, zSysconfdir)
      | relocatable lbi =
          ( show flat_bindir_reloc
          , show flat_libdir_reloc
          , show flat_dynlibdir_reloc
          , show flat_datadir_reloc
          , show flat_libexecdir_reloc
          , show flat_sysconfdir_reloc
          )
      | absolute =
          ( show flat_bindir
          , show flat_libdir
          , show flat_dynlibdir
          , show flat_datadir
          , show flat_libexecdir
          , show flat_sysconfdir
          )
      | isWindows =
          ( "maybe (error \"PathsModule.generate\") id (" ++ show flat_bindirrel ++ ")"
          , mkGetDir flat_libdir flat_libdirrel
          , mkGetDir flat_dynlibdir flat_dynlibdirrel
          , mkGetDir flat_datadir flat_datadirrel
          , mkGetDir flat_libexecdir flat_libexecdirrel
          , mkGetDir flat_sysconfdir flat_sysconfdirrel
          )
      | otherwise =
          error "panic! generatePathsModule: should never happen"

    mkGetDir :: FilePath -> Maybe FilePath -> String
    mkGetDir _ (Just dirrel) = "getPrefixDirRel " ++ show dirrel
    mkGetDir dir Nothing = "return " ++ show dir

    flat_bindir_reloc = shortRelativePath flat_prefix flat_bindir
    flat_libdir_reloc = shortRelativePath flat_prefix flat_libdir
    flat_dynlibdir_reloc = shortRelativePath flat_prefix flat_dynlibdir
    flat_datadir_reloc = shortRelativePath flat_prefix flat_datadir
    flat_libexecdir_reloc = shortRelativePath flat_prefix flat_libexecdir
    flat_sysconfdir_reloc = shortRelativePath flat_prefix flat_sysconfdir

-- | Generates the name of the environment variable controlling the path
-- component of interest.
--
-- Note: The format of these strings is part of Cabal's public API;
-- changing this function constitutes a *backwards-compatibility* break.
pkgPathEnvVar
  :: PackageDescription
  -> String
  -- ^ path component; one of \"bindir\", \"libdir\", -- \"datadir\", \"libexecdir\", or \"sysconfdir\"
  -> String
  -- ^ environment variable name
pkgPathEnvVar pkg_descr var =
  showPkgName (packageName pkg_descr) ++ "_" ++ var

showPkgName :: PackageName -> String
showPkgName = map fixchar . unPackageName

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