{-# LANGUAGE CPP #-}

module SimpleCabal (
  findCabalFile,
  readFinalPackageDescription,
  finalPackageDescription,
#if MIN_VERSION_Cabal(2,2,0)
  parseFinalPackageDescription,
#endif
  makeFinalPackageDescription,
  getPackageId,
--  dependencies,
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,4,0)
  buildDepends,
#endif
  buildDependencies,
  setupDependencies,
  testsuiteDependencies,

  allBuildInfo, -- deprecated by allLibraries et al
  allLibraries,
  BuildInfo (..),
  Library(..),
  depPkgName, exeDepName, pkgcfgDepName,
  FlagName, mkFlagName,
  hasExes, hasLibs,
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(1,20,0)
#else
  licenseFiles,
#endif
  PackageDescription (..),
  PackageIdentifier (..),
  PackageName, mkPackageName, unPackageName,
  packageName, packageVersion,
  readGenericPackageDescription,
  showPkgId,
  showVersion,
  simpleParse,
  tryFindPackageDesc
  ) where

#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0))
#else
import Control.Applicative ((<$>))
#endif

#if MIN_VERSION_Cabal(2,2,0)
import qualified Data.ByteString.Char8 as B
#endif
#if !MIN_VERSION_Cabal(2,0,0)
import Data.Maybe (maybeToList)
#endif
import Data.List (delete, nub)

import Distribution.Compiler
import Distribution.Package  (
                              packageName,
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(1,22,0)
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,0,0)
                              depPkgName,
                              mkPackageName,
                              unPackageName,
                              unPkgconfigName,
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,4,0)
                              Dependency,
#endif
#else
#endif
#endif
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,0,0)
#else
                              Dependency (..),
#endif
#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,0,0)
                              PackageName,
#else
                              PackageName (..),
#endif
                              PackageIdentifier (..),
                                       )

import Distribution.PackageDescription (
  PackageDescription (..),
  allBuildInfo,
#if MIN_VERSION_Cabal(2,0,0)
  allLibraries,
#endif
  BuildInfo (..),
--  buildToolDepends,
#if MIN_VERSION_Cabal(2,4,0)
  enabledBuildDepends,
#endif
  extraLibs,
#if MIN_VERSION_Cabal(2,0,0)
  FlagName,
  mkFlagName,
#else
  FlagName (..),
#endif
  GenericPackageDescription(packageDescription),
  hasExes, hasLibs,
  Library(..),
#if MIN_VERSION_Cabal(2,2,0)
  mkFlagAssignment,
#endif
  pkgconfigDepends,
#if MIN_VERSION_Cabal(1,24,0)
  setupDepends,
#endif
  targetBuildDepends,
  TestSuite (..)
  )
#if MIN_VERSION_Cabal(2,0,0)
import Distribution.PackageDescription.Configuration (finalizePD)
import Distribution.Types.ComponentRequestedSpec (defaultComponentRequestedSpec)
import Distribution.Types.LegacyExeDependency (LegacyExeDependency (..))
import Distribution.Types.PkgconfigDependency (PkgconfigDependency (..))
#else
import Distribution.PackageDescription.Configuration (finalizePackageDescription)
#endif
#if MIN_VERSION_Cabal(2,2,0)
import Distribution.PackageDescription.Parsec
       (
#if !MIN_VERSION_Cabal(3,8,0)
         readGenericPackageDescription,
#endif
         parseGenericPackageDescriptionMaybe)
#elif MIN_VERSION_Cabal(2,0,0)
import Distribution.PackageDescription.Parse (readGenericPackageDescription)
#else
import Distribution.PackageDescription.Parse (readPackageDescription)
#endif

import Distribution.Simple.Compiler (
#if MIN_VERSION_Cabal(1,22,0)
    compilerInfo
#else
    Compiler (..)
#endif
    )
import Distribution.Simple.Configure (
#if MIN_VERSION_Cabal(1,18,0)
    configCompilerEx
#else
    configCompiler
#endif
    )
#if MIN_VERSION_Cabal(3,8,0)
import Distribution.Simple.PackageDescription (readGenericPackageDescription)
#endif
#if MIN_VERSION_Cabal(2,0,0)
--import Distribution.Simple.BuildToolDepends (getAllToolDependencies)
import Distribution.Simple.Program   (defaultProgramDb)
#else
import Distribution.Simple.Program   (defaultProgramConfiguration)
#endif
import qualified Distribution.Simple.Utils as DSU (
#if MIN_VERSION_Cabal(1,20,0)
    tryFindPackageDesc
#else
    findPackageDesc
#endif
    )

import Distribution.System (Platform (..), buildArch, buildOS)

import Distribution.Text (simpleParse)

import Distribution.Verbosity (normal,
#if !MIN_VERSION_Cabal(2,0,0)
                               Verbosity
#endif
                              )

#if MIN_VERSION_Cabal(2,2,0)
import Distribution.Pretty (prettyShow)
#else
#if MIN_VERSION_Cabal(2,0,0)
import Distribution.Version (showVersion)
#else
import Data.Version (showVersion)
#endif
#endif

#if MIN_VERSION_Cabal(2,2,0)
import qualified Distribution.Version (Version)
#endif

import System.Directory (getDirectoryContents)
import System.FilePath (takeExtension)

-- | Find the .cabal file in the current directory.
--
-- Errors if more than one or no file found.
--
-- @since 0.0.0.1
findCabalFile :: IO FilePath
findCabalFile :: IO String
findCabalFile = do
  [String]
allCabals <- String -> String -> IO [String]
filesWithExtension String
"." String
".cabal"
  case [String]
allCabals of
    [String
file] -> forall (m :: * -> *) a. Monad m => a -> m a
return String
file
    [] -> forall a. HasCallStack => String -> a
error String
"No .cabal file found"
    [String]
_ -> forall a. HasCallStack => String -> a
error String
"More than one .cabal file found!"
  where
    filesWithExtension :: FilePath -> String -> IO [FilePath]
    filesWithExtension :: String -> String -> IO [String]
filesWithExtension String
dir String
ext =
      forall a. (a -> Bool) -> [a] -> [a]
filter (\ String
f -> String -> String
takeExtension String
f forall a. Eq a => a -> a -> Bool
== String
ext Bool -> Bool -> Bool
&& forall a. [a] -> a
head String
f forall a. Eq a => a -> a -> Bool
/= Char
'.')
      forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO [String]
getDirectoryContents String
dir

-- | Get the package name-version from the .cabal file in the current directory.
--
-- @since 0.0.0.1
getPackageId :: IO PackageIdentifier
getPackageId :: IO PackageIdentifier
getPackageId = do
  GenericPackageDescription
gpd <- IO String
findCabalFile forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Verbosity -> String -> IO GenericPackageDescription
readGenericPackageDescription Verbosity
normal
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ PackageDescription -> PackageIdentifier
package forall a b. (a -> b) -> a -> b
$ GenericPackageDescription -> PackageDescription
packageDescription GenericPackageDescription
gpd

#if !MIN_VERSION_Cabal(2,0,0)
readGenericPackageDescription :: Verbosity
                              -> FilePath -> IO GenericPackageDescription
readGenericPackageDescription = readPackageDescription
#endif

#if MIN_VERSION_Cabal(2,2,0)
-- | only available with Cabal-2.2+
--
-- @since 0.1.2
parseFinalPackageDescription :: [(FlagName, Bool)] -> B.ByteString
                          -> IO (Maybe PackageDescription)
parseFinalPackageDescription :: [(FlagName, Bool)] -> ByteString -> IO (Maybe PackageDescription)
parseFinalPackageDescription [(FlagName, Bool)]
flags ByteString
cabalfile = do
  let mgenPkgDesc :: Maybe GenericPackageDescription
mgenPkgDesc = ByteString -> Maybe GenericPackageDescription
parseGenericPackageDescriptionMaybe ByteString
cabalfile
  case Maybe GenericPackageDescription
mgenPkgDesc of
    Maybe GenericPackageDescription
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
    Just GenericPackageDescription
genPkgDesc -> forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(FlagName, Bool)]
-> GenericPackageDescription -> IO PackageDescription
makeFinalPackageDescription [(FlagName, Bool)]
flags GenericPackageDescription
genPkgDesc
#endif

-- | Generate PackageDescription from the specified .cabal file and flags.
--
-- deprecated in favour of readFinalPackageDescription
--
-- @since 0.0.0.1
finalPackageDescription :: [(FlagName, Bool)] -> FilePath
                          -> IO PackageDescription
finalPackageDescription :: [(FlagName, Bool)] -> String -> IO PackageDescription
finalPackageDescription = [(FlagName, Bool)] -> String -> IO PackageDescription
readFinalPackageDescription

-- | get PackageDescription from a cabal file
--
-- deprecates finalPackageDescription
--
-- @since 0.1.2
readFinalPackageDescription :: [(FlagName, Bool)] -> FilePath
                            -> IO PackageDescription
readFinalPackageDescription :: [(FlagName, Bool)] -> String -> IO PackageDescription
readFinalPackageDescription [(FlagName, Bool)]
flags String
cabalfile =
  Verbosity -> String -> IO GenericPackageDescription
readGenericPackageDescription Verbosity
normal String
cabalfile forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
  [(FlagName, Bool)]
-> GenericPackageDescription -> IO PackageDescription
makeFinalPackageDescription [(FlagName, Bool)]
flags

-- | convert a GenericPackageDescription to a final PackageDescription
--
-- @since 0.1.2
makeFinalPackageDescription :: [(FlagName, Bool)] -> GenericPackageDescription
                            -> IO PackageDescription
makeFinalPackageDescription :: [(FlagName, Bool)]
-> GenericPackageDescription -> IO PackageDescription
makeFinalPackageDescription [(FlagName, Bool)]
flags GenericPackageDescription
genPkgDesc = do
#if !MIN_VERSION_Cabal(2,0,0)
  let defaultProgramDb = defaultProgramConfiguration
#endif
  CompilerInfo
compiler <- do
#if MIN_VERSION_Cabal(1,18,0)
    (Compiler
compiler, Platform
_, ProgramDb
_) <- Maybe CompilerFlavor
-> Maybe String
-> Maybe String
-> ProgramDb
-> Verbosity
-> IO (Compiler, Platform, ProgramDb)
configCompilerEx
#else
    (compiler, _) <- configCompiler
#endif
       (forall a. a -> Maybe a
Just CompilerFlavor
GHC) forall a. Maybe a
Nothing forall a. Maybe a
Nothing ProgramDb
defaultProgramDb Verbosity
normal
#if MIN_VERSION_Cabal(1,22,0)
    forall (m :: * -> *) a. Monad m => a -> m a
return (Compiler -> CompilerInfo
compilerInfo Compiler
compiler)
#else
    return (compilerId compiler)
#endif
#if !MIN_VERSION_Cabal(2,2,0)
  let mkFlagAssignment = id
#endif
#if MIN_VERSION_Cabal(2,0,0)
  let finalizePackageDescription :: FlagAssignment
-> (Dependency -> Bool)
-> Platform
-> CompilerInfo
-> [PackageVersionConstraint]
-> GenericPackageDescription
-> Either [Dependency] (PackageDescription, FlagAssignment)
finalizePackageDescription FlagAssignment
flags' = FlagAssignment
-> ComponentRequestedSpec
-> (Dependency -> Bool)
-> Platform
-> CompilerInfo
-> [PackageVersionConstraint]
-> GenericPackageDescription
-> Either [Dependency] (PackageDescription, FlagAssignment)
finalizePD FlagAssignment
flags' ComponentRequestedSpec
defaultComponentRequestedSpec
#endif
  let final :: Either [Dependency] (PackageDescription, FlagAssignment)
final =
        FlagAssignment
-> (Dependency -> Bool)
-> Platform
-> CompilerInfo
-> [PackageVersionConstraint]
-> GenericPackageDescription
-> Either [Dependency] (PackageDescription, FlagAssignment)
finalizePackageDescription ([(FlagName, Bool)] -> FlagAssignment
mkFlagAssignment [(FlagName, Bool)]
flags)
        (forall a b. a -> b -> a
const Bool
True) (Arch -> OS -> Platform
Platform Arch
buildArch OS
buildOS)
        CompilerInfo
compiler
        [] GenericPackageDescription
genPkgDesc
  case Either [Dependency] (PackageDescription, FlagAssignment)
final of
    Left [Dependency]
e -> forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"finalize failed: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [Dependency]
e
    Right (PackageDescription, FlagAssignment)
res -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a, b) -> a
fst (PackageDescription, FlagAssignment)
res

-- | Return the list of build dependencies of a package, excluding itself
buildDependencies :: PackageDescription -> [PackageName]
buildDependencies :: PackageDescription -> [PackageName]
buildDependencies PackageDescription
pkgDesc =
  let deps :: [PackageName]
deps = forall a. Eq a => [a] -> [a]
nub forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Dependency -> PackageName
depPkgName (PackageDescription -> [Dependency]
buildDepends PackageDescription
pkgDesc)
      self :: PackageName
self = PackageIdentifier -> PackageName
pkgName forall a b. (a -> b) -> a -> b
$ PackageDescription -> PackageIdentifier
package PackageDescription
pkgDesc
  in forall a. Eq a => a -> [a] -> [a]
delete PackageName
self [PackageName]
deps

-- | Return the list of testsuite dependencies of a package, excluding itself
testsuiteDependencies :: PackageDescription -> [PackageName]
testsuiteDependencies :: PackageDescription -> [PackageName]
testsuiteDependencies PackageDescription
pkgDesc =
  let self :: PackageName
self = PackageIdentifier -> PackageName
pkgName forall a b. (a -> b) -> a -> b
$ PackageDescription -> PackageIdentifier
package PackageDescription
pkgDesc in
  forall a. Eq a => a -> [a] -> [a]
delete PackageName
self forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Eq a => [a] -> [a]
nub forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Dependency -> PackageName
depPkgName forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (BuildInfo -> [Dependency]
targetBuildDepends forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestSuite -> BuildInfo
testBuildInfo) (PackageDescription -> [TestSuite]
testSuites PackageDescription
pkgDesc)

-- | version string from PackageIdentifier
packageVersion :: PackageIdentifier -> String
packageVersion :: PackageIdentifier -> String
packageVersion =
#if MIN_VERSION_Cabal(2,2,0)
  forall a. Pretty a => a -> String
prettyShow forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageIdentifier -> Version
pkgVersion
#else
  showVersion . pkgVersion
#endif

-- | convert PackageIdentifier to a displayable string
showPkgId :: PackageIdentifier -> String
showPkgId :: PackageIdentifier -> String
showPkgId PackageIdentifier
pkgid =
#if MIN_VERSION_Cabal(2,2,0)
  forall a. Pretty a => a -> String
prettyShow PackageIdentifier
pkgid
#else
  unPackageName (packageName pkgid) ++ "-" ++ packageVersion pkgid
#endif

#if !MIN_VERSION_Cabal(1,22,0)
unPackageName :: PackageName -> String
unPackageName (PackageName n) = n
#endif

#if MIN_VERSION_Cabal(2,2,0)
-- | render a Version
showVersion :: Distribution.Version.Version -> String
showVersion :: Version -> String
showVersion = forall a. Pretty a => a -> String
prettyShow
#endif

#if !MIN_VERSION_Cabal(2,0,0)
mkFlagName :: String -> FlagName
mkFlagName = FlagName
#endif

-- | Find cabal file
tryFindPackageDesc :: FilePath -> IO FilePath
tryFindPackageDesc :: String -> IO String
tryFindPackageDesc =
#if MIN_VERSION_Cabal(1,20,0)
  Verbosity -> String -> IO String
DSU.tryFindPackageDesc
#if MIN_VERSION_Cabal(3,0,0)
    Verbosity
normal
#endif
#else
  DSU.findPackageDesc
#endif

#if !MIN_VERSION_Cabal(1,20,0)
-- | singleton list of license file
licenseFiles :: PackageDescription -> [FilePath]
licenseFiles pkgDesc =
  [licenseFile pkgDesc | licenseFile pkgDesc /= ""]
#endif

#if MIN_VERSION_Cabal(2,4,0)
-- | List build dependencies
buildDepends :: PackageDescription -> [Dependency]
buildDepends :: PackageDescription -> [Dependency]
buildDepends = forall a b c. (a -> b -> c) -> b -> a -> c
flip PackageDescription -> ComponentRequestedSpec -> [Dependency]
enabledBuildDepends ComponentRequestedSpec
defaultComponentRequestedSpec
#endif

#if MIN_VERSION_Cabal(2,0,0)
-- | name of legacy exe dep
exeDepName :: LegacyExeDependency -> String
exeDepName :: LegacyExeDependency -> String
exeDepName (LegacyExeDependency String
n VersionRange
_) = String
n

-- | pkgconfig dep name
pkgcfgDepName :: PkgconfigDependency -> String
pkgcfgDepName :: PkgconfigDependency -> String
pkgcfgDepName (PkgconfigDependency PkgconfigName
n PkgconfigVersionRange
_) = PkgconfigName -> String
unPkgconfigName PkgconfigName
n
#else
-- | PackageName of dependency
depPkgName :: Dependency -> PackageName
depPkgName (Dependency pn _) = pn

-- | name of dependency
exeDepName :: Dependency -> String
exeDepName = unPackageName . depPkgName

-- | name of dependency
pkgcfgDepName :: Dependency -> String
pkgcfgDepName = unPackageName . depPkgName
#endif

#if !MIN_VERSION_Cabal(2,0,0)
mkPackageName :: String -> PackageName
mkPackageName = PackageName
#endif

-- | List of setup dependencies
setupDependencies :: PackageDescription  -- ^pkg description
                  -> [PackageName]         -- ^depends
#if MIN_VERSION_Cabal(1,24,0)
setupDependencies :: PackageDescription -> [PackageName]
setupDependencies PackageDescription
pkgDesc =
  forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (forall a b. (a -> b) -> [a] -> [b]
map Dependency -> PackageName
depPkgName forall b c a. (b -> c) -> (a -> b) -> a -> c
. SetupBuildInfo -> [Dependency]
setupDepends) (PackageDescription -> Maybe SetupBuildInfo
setupBuildInfo PackageDescription
pkgDesc)
#else
setupDependencies _pkgDesc = []
#endif

-- dependencies :: PackageDescription  -- ^pkg description
--                 -> ([PackageName], [PackageName], [ExeDependency], [String], [PkgconfigDependency])
--                 -- ^depends, setup, tools, c-libs, pkgcfg
-- dependencies pkgDesc =
--     let --self = pkgName $ package pkgDesc
--         deps = buildDependencies pkgDesc
--         setup = setupDependencies pkgDesc
--         buildinfo = allBuildInfo pkgDesc
--         tools =  nub $ concatMap buildToolDepends buildinfo
--         clibs = nub $ concatMap extraLibs buildinfo
--         pkgcfgs = nub $ concatMap pkgconfigDepends buildinfo
--     in (deps, setup, tools, clibs, pkgcfgs)

#if !MIN_VERSION_Cabal(2,0,0)
allLibraries :: PackageDescription -> [Library]
allLibraries = maybeToList . library
#endif