Fix performance bug: do not call ghc-cabal to determine package targets
[hadrian.git] / src / Hadrian / Haskell / Cabal / Parse.hs
1 -----------------------------------------------------------------------------
2 -- |
3 -- Module : Hadrian.Haskell.Cabal.Parse
4 -- Copyright : (c) Andrey Mokhov 2014-2017
5 -- License : MIT (see the file LICENSE)
6 -- Maintainer : andrey.mokhov@gmail.com
7 -- Stability : experimental
8 --
9 -- Extracting Haskell package metadata stored in @.cabal@ files.
10 -----------------------------------------------------------------------------
11 module Hadrian.Haskell.Cabal.Parse (Cabal (..), parseCabal) where
12
13 import Data.List.Extra
14 import Development.Shake
15 import Development.Shake.Classes
16 import qualified Distribution.Package as C
17 import qualified Distribution.PackageDescription as C
18 import qualified Distribution.PackageDescription.Parse as C
19 import qualified Distribution.Text as C
20 import qualified Distribution.Types.CondTree as C
21 import qualified Distribution.Verbosity as C
22
23 import Hadrian.Haskell.Package
24
25 -- TODO: Use fine-grain tracking instead of tracking the whole @.cabal@ file.
26 -- | Haskell package metadata extracted from a @.cabal@ file.
27 data Cabal = Cabal
28 { dependencies :: [PackageName]
29 , name :: PackageName
30 , version :: String
31 } deriving (Eq, Read, Show, Typeable)
32
33 instance Binary Cabal where
34 put = put . show
35 get = fmap read get
36
37 instance Hashable Cabal where
38 hashWithSalt salt = hashWithSalt salt . show
39
40 instance NFData Cabal where
41 rnf (Cabal a b c) = a `seq` b `seq` c `seq` ()
42
43 -- | Parse a @.cabal@ file.
44 parseCabal :: FilePath -> IO Cabal
45 parseCabal file = do
46 gpd <- liftIO $ C.readGenericPackageDescription C.silent file
47 let pkgId = C.package (C.packageDescription gpd)
48 name = C.unPackageName (C.pkgName pkgId)
49 version = C.display (C.pkgVersion pkgId)
50 libDeps = collectDeps (C.condLibrary gpd)
51 exeDeps = map (collectDeps . Just . snd) (C.condExecutables gpd)
52 allDeps = concat (libDeps : exeDeps)
53 sorted = sort [ C.unPackageName p | C.Dependency p _ <- allDeps ]
54 deps = nubOrd sorted \\ [name]
55 return $ Cabal deps name version
56
57 collectDeps :: Maybe (C.CondTree v [C.Dependency] a) -> [C.Dependency]
58 collectDeps Nothing = []
59 collectDeps (Just (C.CondNode _ deps ifs)) = deps ++ concatMap f ifs
60 where
61 f (C.CondBranch _ t mt) = collectDeps (Just t) ++ collectDeps mt