1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveTraversable #-}
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE NoImplicitPrelude #-}
7{-# LANGUAGE OverloadedStrings #-}
8{-# LANGUAGE RecordWildCards #-}
9{-# LANGUAGE TypeFamilies #-}
10-- | Manipulate @GenericPackageDescription@ from Cabal into something more
12module Stackage.PackageDescription
20import Control.Monad.Writer.Strict (MonadWriter, execWriterT,
23import qualified Data.Map as Map
24import Distribution.Compiler (CompilerFlavor)
25import Distribution.Package (Dependency (..))
26import Distribution.PackageDescription
27import Distribution.System (Arch, OS)
28import Stackage.Prelude
30data Component = CompLibrary
34 deriving (Show, Read, Eq, Ord, Enum, Bounded)
36compToText :: Component -> Text
37compToText CompLibrary = "library"
38compToText CompExecutable = "executable"
39compToText CompTestSuite = "test-suite"
40compToText CompBenchmark = "benchmark"
42instance ToJSON Component where
43 toJSON = toJSON . compToText
44instance FromJSON Component where
45 parseJSON = withText "Component" $ \t -> maybe
46 (fail $ "Invalid component: " ++ unpack t)
50 comps = asHashMap $ mapFromList $ map (compToText &&& id) [minBound..maxBound]
53 { diComponents :: Set Component
54 , diRange :: VersionRange
58instance Semigroup DepInfo where
59 DepInfo a x <> DepInfo b y = DepInfo
61 (intersectVersionRanges x y)
62instance ToJSON DepInfo where
63 toJSON DepInfo {..} = object
64 [ "components" .= diComponents
65 , "range" .= display diRange
67instance FromJSON DepInfo where
68 parseJSON = withObject "DepInfo" $ \o -> do
69 diComponents <- o .: "components"
70 diRange <- o .: "range" >>= either (fail . show) return . simpleParse
73-- | A simplified package description that tracks:
75-- * Package dependencies
77-- * Build tool dependencies
79-- * Provided executables
81-- It has fully resolved all conditionals
82data SimpleDesc = SimpleDesc
83 { sdPackages :: Map PackageName DepInfo
84 , sdTools :: Map ExeName DepInfo
85 , sdProvidedExes :: Set ExeName
86 , sdModules :: Set Text
87 -- ^ modules exported by the library
90instance Monoid SimpleDesc where
91 mempty = SimpleDesc mempty mempty mempty mempty
92 mappend (SimpleDesc a b c d) (SimpleDesc w x y z) = SimpleDesc
97instance ToJSON SimpleDesc where
98 toJSON SimpleDesc {..} = object
99 [ "packages" .= Map.mapKeysWith const unPackageName sdPackages
100 , "tools" .= Map.mapKeysWith const unExeName sdTools
101 , "provided-exes" .= sdProvidedExes
102 , "modules" .= sdModules
104instance FromJSON SimpleDesc where
105 parseJSON = withObject "SimpleDesc" $ \o -> do
106 sdPackages <- Map.mapKeysWith const mkPackageName <$> (o .: "packages")
107 sdTools <- Map.mapKeysWith const ExeName <$> (o .: "tools")
108 sdProvidedExes <- o .: "provided-exes"
109 sdModules <- o .: "modules"
110 return SimpleDesc {..}
112-- | Convert a 'GenericPackageDescription' into a 'SimpleDesc' by following the
113-- constraints in the provided 'CheckCond'.
114toSimpleDesc :: MonadThrow m
116 -> GenericPackageDescription
118toSimpleDesc cc gpd = execWriterT $ do
119 forM_ (condLibrary gpd) $ tellTree cc CompLibrary libBuildInfo getModules
120 forM_ (condExecutables gpd) $ tellTree cc CompExecutable buildInfo noModules . snd
121 tell mempty { sdProvidedExes = setFromList
122 $ map (fromString . fst)
123 $ condExecutables gpd
125 when (ccIncludeTests cc) $ forM_ (condTestSuites gpd)
126 $ tellTree cc CompTestSuite testBuildInfo noModules . snd
127 when (ccIncludeBenchmarks cc) $ forM_ (condBenchmarks gpd)
128 $ tellTree cc CompBenchmark benchmarkBuildInfo noModules . snd
130 noModules = const mempty
131 getModules = setFromList . map display . exposedModules
133-- | Convert a single CondTree to a 'SimpleDesc'.
134tellTree :: (MonadWriter SimpleDesc m, MonadThrow m)
138 -> (a -> Set Text) -- ^ get module names
139 -> CondTree ConfVar [Dependency] a
141tellTree cc component getBI getModules =
144 loop (CondNode dat deps comps) = do
146 { sdPackages = unionsWith (<>) $ flip map deps
147 $ \(Dependency x y) -> singletonMap x DepInfo
148 { diComponents = singletonSet component
149 , diRange = simplifyVersionRange y
151 , sdTools = unionsWith (<>) $ flip map (buildTools $ getBI dat)
152 $ \(Dependency name range) -> singletonMap
153 -- In practice, cabal files refer to the exe name, not the
155 (ExeName $ unPackageName name)
157 { diComponents = singletonSet component
158 , diRange = simplifyVersionRange range
160 , sdModules = getModules dat
162 forM_ comps $ \(cond, ontrue, onfalse) -> do
163 b <- checkCond cc cond
166 else maybe (return ()) loop onfalse
168-- | Resolve a condition to a boolean based on the provided 'CheckCond'.
169checkCond :: MonadThrow m => CheckCond -> Condition ConfVar -> m Bool
170checkCond CheckCond {..} cond0 =
173 go (Var (OS os)) = return $ os == ccOS
174 go (Var (Arch arch)) = return $ arch == ccArch
175 go (Var (Flag flag)) =
176 case lookup flag ccFlags of
177 Nothing -> throwM $ FlagNotDefined ccPackageName flag cond0
179 go (Var (Impl flavor range)) = return
180 $ flavor == ccCompilerFlavor
181 && ccCompilerVersion `withinRange` range
182 go (Lit b) = return b
183 go (CNot c) = not `liftM` go c
184 go (CAnd x y) = (&&) `liftM` go x `ap` go y
185 go (COr x y) = (||) `liftM` go x `ap` go y
187data CheckCondException = FlagNotDefined PackageName FlagName (Condition ConfVar)
188 deriving (Show, Typeable)
189instance Exception CheckCondException
191data CheckCond = CheckCond
192 { ccPackageName :: PackageName -- for debugging only
195 , ccFlags :: Map FlagName Bool
196 , ccCompilerFlavor :: CompilerFlavor
197 , ccCompilerVersion :: Version
198 , ccIncludeTests :: Bool
199 , ccIncludeBenchmarks :: Bool
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list