1module Stackage.LoadDatabase where
3import qualified Codec.Archive.Tar as Tar
4import qualified Codec.Compression.GZip as GZip
5import Control.Monad (guard)
6import qualified Data.ByteString.Lazy as L
7import qualified Data.ByteString.Lazy.Char8 as L8
8import Data.List (stripPrefix)
9import qualified Data.Map as Map
10import Data.Maybe (catMaybes, listToMaybe,
12import Data.Monoid (Monoid (..))
13import Data.Set (member)
14import qualified Data.Set as Set
15import Distribution.Compiler (CompilerFlavor (GHC))
16import Distribution.Package (Dependency (Dependency))
17import Distribution.PackageDescription (Condition (..),
23 buildInfo, buildTools,
31 flagDefault, flagName,
33 homepage, libBuildInfo,
37import Distribution.PackageDescription.Parse (ParseResult (ParseOk),
38 parsePackageDescription)
39import Distribution.System (buildArch, buildOS)
40import Distribution.Version (Version (Version),
45import System.Directory (doesFileExist)
46import System.FilePath ((<.>), (</>))
48-- | Load the raw package database.
50-- We want to put in some restrictions:
52-- * Drop all core packages. We never want to install a new version of
53-- those, nor include them in the package list.
55-- * For packages with a specific version bound, find the maximum matching
58-- * For other packages, select the maximum version number.
59loadPackageDB :: SelectSettings
60 -> Map PackageName Version -- ^ core packages from HP file
61 -> Set PackageName -- ^ all core packages, including extras
62 -> Map PackageName (VersionRange, Maintainer) -- ^ additional deps
64loadPackageDB settings coreMap core deps = do
65 tarName <- getTarballName
66 lbs <- L.readFile tarName
67 addEntries mempty $ Tar.read lbs
69 addEntries _ (Tar.Fail e) = error $ show e
70 addEntries db Tar.Done = return db
71 addEntries db (Tar.Next e es) = addEntry db e >>= flip addEntries es
74 let GhcMajorVersion x y = selectGhcVersion settings
75 in Version [x, y, 2] []
77 addEntry :: PackageDB -> Tar.Entry -> IO PackageDB
79 case getPackageVersion e of
82 | p `member` core -> return pdb
84 case Map.lookup p deps of
85 Just (vrange, _maintainer)
86 | not $ withinRange v vrange -> return pdb
88 let pkgname = packageVersionString (p, v)
89 tarball = selectTarballDir settings </> pkgname <.> "tar.gz"
90 exists <- doesFileExist tarball
93 lbs <- L.readFile tarball
94 findCabalAndAddPackage tarball p v pdb $ Tar.read $ GZip.decompress lbs
96 case Tar.entryContent e of
97 Tar.NormalFile bs _ -> addPackage p v bs pdb
100 skipTests p = p `Set.member` skippedTests settings
102 -- Find the relevant cabal file in the given entries and add its contents
103 -- to the package database
104 findCabalAndAddPackage tarball p v pdb =
107 expectedPath = let PackageName p' = p in concat
108 [ packageVersionString (p, v)
113 loop Tar.Done = error $ concat
114 [ "Missing cabal file "
119 loop (Tar.Fail e) = error $ concat
120 [ "Unable to read tarball "
125 loop (Tar.Next entry rest)
126 | Tar.entryPath entry == expectedPath =
127 case Tar.entryContent entry of
128 Tar.NormalFile bs _ -> addPackage p v bs pdb
134 , " was not a normal file"
136 | otherwise = loop rest
138 addPackage p v lbs pdb = do
139 let (deps', hasTests, buildTools', mgpd, execs, mgithub) = parseDeps p lbs
140 return $ mappend pdb $ PackageDB $ Map.singleton p PackageInfo
143 , piHasTests = hasTests
144 , piBuildTools = buildTools'
147 , piGithubUser = mgithub
151 case parsePackageDescription $ L8.unpack lbs of
152 ParseOk _ gpd -> (mconcat
153 [ maybe mempty (go gpd) $ condLibrary gpd
154 , mconcat $ map (go gpd . snd) $ condExecutables gpd
157 else mconcat $ map (go gpd . snd) $ condTestSuites gpd
158 , mconcat $ map (go gpd . snd) $ condBenchmarks gpd
159 ], not $ null $ condTestSuites gpd
160 , Set.fromList $ map depName $ allBuildInfo gpd
162 , Set.fromList $ map (Executable . fst) $ condExecutables gpd
163 , listToMaybe $ catMaybes
164 $ parseGithubUserHP (homepage $ packageDescription gpd)
165 : map parseGithubUserSR (sourceRepos $ packageDescription gpd)
167 _ -> (mempty, defaultHasTestSuites, Set.empty, Nothing, Set.empty, Nothing)
169 allBuildInfo gpd = concat
170 [ maybe mempty (goBI libBuildInfo) $ condLibrary gpd
171 , concat $ map (goBI buildInfo . snd) $ condExecutables gpd
174 else concat $ map (goBI testBuildInfo . snd) $ condTestSuites gpd
175 , concat $ map (goBI benchmarkBuildInfo . snd) $ condBenchmarks gpd
178 goBI f x = buildTools $ f $ condTreeData x
179 depName (Dependency (PackageName pn) _) = Executable pn
181 = Map.unionsWith unionVersionRanges
182 $ Map.fromList (map (\(Dependency pn vr) -> (pn, vr)) $ condTreeConstraints tree)
183 : map (go gpd) (mapMaybe (checkCond gpd) $ condTreeComponents tree)
185 checkCond gpd (cond, tree, melse)
186 | checkCond' cond = Just tree
189 checkCond' (Var (OS os)) = os == buildOS
190 checkCond' (Var (Arch arch)) = arch == buildArch
192 -- Sigh... the small_base flag on mersenne-random-pure64 is backwards
193 checkCond' (Var (Flag (FlagName "small_base")))
194 | p == PackageName "mersenne-random-pure64" = False
196 checkCond' (Var (Flag flag@(FlagName flag'))) =
197 flag' `Set.notMember` disabledFlags settings &&
199 checkCond' (Var (Impl compiler range)) =
200 compiler == GHC && withinRange ghcVersion' range
201 checkCond' (Lit b) = b
202 checkCond' (CNot c) = not $ checkCond' c
203 checkCond' (COr c1 c2) = checkCond' c1 || checkCond' c2
204 checkCond' (CAnd c1 c2) = checkCond' c1 && checkCond' c2
206 flags' = map flagName (filter flagDefault $ genPackageFlags gpd) ++
207 (map FlagName $ Set.toList $ Stackage.Types.flags settings coreMap)
209-- | Attempt to grab the Github username from a homepage.
210parseGithubUserHP :: String -> Maybe String
211parseGithubUserHP url1 = do
212 url2 <- listToMaybe $ mapMaybe (flip stripPrefix url1)
213 [ "http://github.com/"
214 , "https://github.com/"
216 let x = takeWhile (/= '/') url2
220-- | Attempt to grab the Github username from a source repo.
221parseGithubUserSR :: SourceRepo -> Maybe String
222parseGithubUserSR sr =
223 case (repoType sr, repoLocation sr) of
224 (Just Git, Just s) -> parseGithubUserHP s
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