Parse.hs 2.39 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
-----------------------------------------------------------------------------
-- |
-- Module     : Hadrian.Haskell.Cabal.Parse
-- Copyright  : (c) Andrey Mokhov 2014-2017
-- License    : MIT (see the file LICENSE)
-- Maintainer : andrey.mokhov@gmail.com
-- Stability  : experimental
--
-- Extracting Haskell package metadata stored in @.cabal@ files.
-----------------------------------------------------------------------------
module Hadrian.Haskell.Cabal.Parse (Cabal (..), parseCabal) where

import Data.List.Extra
import Development.Shake
import Development.Shake.Classes
import qualified Distribution.Package                  as C
import qualified Distribution.PackageDescription       as C
import qualified Distribution.PackageDescription.Parse as C
import qualified Distribution.Text                     as C
import qualified Distribution.Types.CondTree           as C
import qualified Distribution.Verbosity                as C

import Hadrian.Haskell.Package

25
-- TODO: Use fine-grain tracking instead of tracking the whole @.cabal@ file.
26 27
-- | Haskell package metadata extracted from a @.cabal@ file.
data Cabal = Cabal
28 29
    { dependencies :: [PackageName]
    , name         :: PackageName
30
    , synopsis     :: String
31 32 33 34 35 36 37 38 39 40 41
    , version      :: String
    } deriving (Eq, Read, Show, Typeable)

instance Binary Cabal where
    put = put . show
    get = fmap read get

instance Hashable Cabal where
    hashWithSalt salt = hashWithSalt salt . show

instance NFData Cabal where
42
    rnf (Cabal a b c d) = a `seq` b `seq` c `seq` d `seq` ()
43 44 45 46 47

-- | Parse a @.cabal@ file.
parseCabal :: FilePath -> IO Cabal
parseCabal file = do
    gpd <- liftIO $ C.readGenericPackageDescription C.silent file
48 49
    let pd      = C.packageDescription gpd
        pkgId   = C.package pd
Andrey Mokhov's avatar
Andrey Mokhov committed
50 51
        name    = C.unPackageName (C.pkgName pkgId)
        version = C.display (C.pkgVersion pkgId)
52 53 54 55
        libDeps = collectDeps (C.condLibrary gpd)
        exeDeps = map (collectDeps . Just . snd) (C.condExecutables gpd)
        allDeps = concat (libDeps : exeDeps)
        sorted  = sort [ C.unPackageName p | C.Dependency p _ <- allDeps ]
Andrey Mokhov's avatar
Andrey Mokhov committed
56
        deps    = nubOrd sorted \\ [name]
57
    return $ Cabal deps name (C.synopsis pd) version
58 59 60 61 62 63

collectDeps :: Maybe (C.CondTree v [C.Dependency] a) -> [C.Dependency]
collectDeps Nothing = []
collectDeps (Just (C.CondNode _ deps ifs)) = deps ++ concatMap f ifs
  where
    f (C.CondBranch _ t mt) = collectDeps (Just t) ++ collectDeps mt