IndexUtils.hs 21.6 KB
Newer Older
1
2
-----------------------------------------------------------------------------
-- |
3
-- Module      :  Distribution.Client.IndexUtils
4
5
6
-- Copyright   :  (c) Duncan Coutts 2008
-- License     :  BSD-like
--
Duncan Coutts's avatar
Duncan Coutts committed
7
-- Maintainer  :  duncan@community.haskell.org
8
9
10
11
12
-- Stability   :  provisional
-- Portability :  portable
--
-- Extra utils related to the package indexes.
-----------------------------------------------------------------------------
13
module Distribution.Client.IndexUtils (
14
  getInstalledPackages,
15
  getSourcePackages,
16
  getSourcePackagesStrict,
17
  convert,
18
19

  readPackageIndexFile,
20
21
22
  parsePackageIndex,
  readRepoIndex,
  updateRepoIndexCache,
23
24
  ) where

Duncan Coutts's avatar
Duncan Coutts committed
25
import qualified Distribution.Client.Tar as Tar
26
import Distribution.Client.Types
27

28
import Distribution.Package
29
         ( PackageId, PackageIdentifier(..), PackageName(..)
30
         , Package(..), packageVersion, packageName
31
32
         , Dependency(Dependency), InstalledPackageId(..) )
import Distribution.Client.PackageIndex (PackageIndex)
refold's avatar
refold committed
33
34
35
import qualified Distribution.Client.PackageIndex      as PackageIndex
import qualified Distribution.Simple.PackageIndex      as InstalledPackageIndex
import qualified Distribution.InstalledPackageInfo     as InstalledPackageInfo
36
import qualified Distribution.PackageDescription.Parse as PackageDesc.Parse
37
38
import Distribution.PackageDescription
         ( GenericPackageDescription )
39
import Distribution.PackageDescription.Parse
40
         ( parsePackageDescription )
41
42
43
44
45
46
import Distribution.Simple.Compiler
         ( Compiler, PackageDBStack )
import Distribution.Simple.Program
         ( ProgramConfiguration )
import qualified Distribution.Simple.Configure as Configure
         ( getInstalledPackages )
47
48
import Distribution.ParseUtils
         ( ParseResult(..) )
49
import Distribution.Version
50
         ( Version(Version), intersectVersionRanges )
51
import Distribution.Text
52
         ( display, simpleParse )
53
import Distribution.Verbosity
54
         ( Verbosity, normal, lessVerbose )
55
import Distribution.Simple.Utils
56
         ( die, warn, info, fromUTF8, findPackageDesc )
57

58
import Data.Char   (isAlphaNum)
EyalLotem's avatar
EyalLotem committed
59
import Data.Maybe  (mapMaybe, fromMaybe)
60
import Data.List   (isPrefixOf)
61
import Data.Monoid (Monoid(..))
62
import qualified Data.Map as Map
63
import Control.Monad (MonadPlus(mplus), when, unless, liftM)
64
import Control.Exception (evaluate)
65
66
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString.Lazy.Char8 as BS.Char8
67
import qualified Data.ByteString.Char8 as BSS
68
import Data.ByteString.Lazy (ByteString)
69
import Distribution.Client.GZipUtils (maybeDecompress)
70
import Distribution.Client.Utils (byteStringToFilePath)
71
import System.FilePath ((</>), takeExtension, splitDirectories, normalise)
72
73
import System.FilePath.Posix as FilePath.Posix
         ( takeFileName )
74
import System.IO
75
import System.IO.Unsafe (unsafeInterleaveIO)
76
import System.IO.Error (isDoesNotExistError)
refold's avatar
refold committed
77
import Distribution.Compat.Exception (catchIO)
78
import System.Directory
79
         ( getModificationTime, doesFileExist )
80
import Distribution.Compat.Time
81

82

83
84
getInstalledPackages :: Verbosity -> Compiler
                     -> PackageDBStack -> ProgramConfiguration
85
                     -> IO InstalledPackageIndex.PackageIndex
86
getInstalledPackages verbosity comp packageDbs conf =
87
    Configure.getInstalledPackages verbosity' comp packageDbs conf
88
  where
89
90
91
    --FIXME: make getInstalledPackages use sensible verbosity in the first place
    verbosity'  = lessVerbose verbosity

92
convert :: InstalledPackageIndex.PackageIndex -> PackageIndex InstalledPackage
93
convert index' = PackageIndex.fromList
94
95
96
97
98
99
100
101
    -- There can be multiple installed instances of each package version,
    -- like when the same package is installed in the global & user dbs.
    -- InstalledPackageIndex.allPackagesBySourcePackageId gives us the
    -- installed packages with the most preferred instances first, so by
    -- picking the first we should get the user one. This is almost but not
    -- quite the same as what ghc does.
    [ InstalledPackage ipkg (sourceDeps index' ipkg)
    | (_,ipkg:_) <- InstalledPackageIndex.allPackagesBySourcePackageId index' ]
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  where
    -- The InstalledPackageInfo only lists dependencies by the
    -- InstalledPackageId, which means we do not directly know the corresponding
    -- source dependency. The only way to find out is to lookup the
    -- InstalledPackageId to get the InstalledPackageInfo and look at its
    -- source PackageId. But if the package is broken because it depends on
    -- other packages that do not exist then we have a problem we cannot find
    -- the original source package id. Instead we make up a bogus package id.
    -- This should have the same effect since it should be a dependency on a
    -- non-existant package.
    sourceDeps index ipkg =
      [ maybe (brokenPackageId depid) packageId mdep
      | let depids = InstalledPackageInfo.depends ipkg
            getpkg = InstalledPackageIndex.lookupInstalledPackageId index
      , (depid, mdep) <- zip depids (map getpkg depids) ]

    brokenPackageId (InstalledPackageId str) =
      PackageIdentifier (PackageName (str ++ "-broken")) (Version [] [])

121
122
123
------------------------------------------------------------------------
-- Reading the source package index
--
124

125
126
127
-- | Read a repository index from disk, from the local files specified by
-- a list of 'Repo's.
--
128
-- All the 'SourcePackage's are marked as having come from the appropriate
129
130
131
132
-- 'Repo'.
--
-- This is a higher level wrapper used internally in cabal-install.
--
133
getSourcePackages :: Verbosity -> [Repo] -> IO SourcePackageDb
134
135
136
137
138
139
140
141
142
143
144
145
146
147
getSourcePackages verbosity repos = getSourcePackages' verbosity repos
                                    ReadPackageIndexLazyIO

-- | Like 'getSourcePackages', but reads the package index strictly. Useful if
-- you want to write to the package index after having read it.
getSourcePackagesStrict :: Verbosity -> [Repo] -> IO SourcePackageDb
getSourcePackagesStrict verbosity repos = getSourcePackages' verbosity repos
                                          ReadPackageIndexStrict

-- | Common implementation used by getSourcePackages and
-- getSourcePackagesStrict.
getSourcePackages' :: Verbosity -> [Repo] -> ReadPackageIndexMode
                      -> IO SourcePackageDb
getSourcePackages' verbosity [] _mode = do
148
149
  warn verbosity $ "No remote package servers have been specified. Usually "
                ++ "you would have one specified in the config file."
150
  return SourcePackageDb {
151
152
153
    packageIndex       = mempty,
    packagePreferences = mempty
  }
154
getSourcePackages' verbosity repos mode = do
155
  info verbosity "Reading available packages..."
156
  pkgss <- mapM (\r -> readRepoIndex verbosity r mode) repos
157
  let (pkgs, prefs) = mconcat pkgss
Duncan Coutts's avatar
Duncan Coutts committed
158
      prefs' = Map.fromListWith intersectVersionRanges
159
                 [ (name, range) | Dependency name range <- prefs ]
160
161
  _ <- evaluate pkgs
  _ <- evaluate prefs'
162
  return SourcePackageDb {
163
164
165
    packageIndex       = pkgs,
    packagePreferences = prefs'
  }
166

167
168
169
-- | Read a repository index from disk, from the local file specified by
-- the 'Repo'.
--
170
-- All the 'SourcePackage's are marked as having come from the given 'Repo'.
171
172
173
--
-- This is a higher level wrapper used internally in cabal-install.
--
174
readRepoIndex :: Verbosity -> Repo -> ReadPackageIndexMode
175
              -> IO (PackageIndex SourcePackage, [Dependency])
176
readRepoIndex verbosity repo mode =
177
  let indexFile = repoLocalDir repo </> "00-index.tar"
178
179
180
181
      cacheFile = repoLocalDir repo </> "00-index.cache"
  in handleNotFound $ do
    warnIfIndexIsOld indexFile
    whenCacheOutOfDate indexFile cacheFile $ do
EyalLotem's avatar
EyalLotem committed
182
      info verbosity "Updating the index cache file..."
183
      updatePackageIndexCacheFile indexFile cacheFile
184
    readPackageIndexCacheFile mkAvailablePackage indexFile cacheFile mode
Duncan Coutts's avatar
Duncan Coutts committed
185

186
  where
187
    mkAvailablePackage pkgEntry =
188
      SourcePackage {
189
        packageInfoId      = pkgid,
190
191
        packageDescription = packageDesc pkgEntry,
        packageSource      = case pkgEntry of
192
193
194
195
196
          NormalPackage _ _ _ _    -> RepoTarballPackage repo pkgid Nothing
          BuildTreeRef  _ _ path _ -> LocalUnpackedPackage path,
        packageDescrOverride = case pkgEntry of
          NormalPackage _ _ pkgtxt _ -> Just pkgtxt
          _                          -> Nothing
197
      }
198
199
      where
        pkgid = packageId pkgEntry
200

refold's avatar
refold committed
201
    handleNotFound action = catchIO action $ \e -> if isDoesNotExistError e
202
203
204
205
206
207
208
209
210
211
212
      then do
        case repoKind repo of
          Left  remoteRepo -> warn verbosity $
               "The package list for '" ++ remoteRepoName remoteRepo
            ++ "' does not exist. Run 'cabal update' to download it."
          Right _localRepo -> warn verbosity $
               "The package list for the local repo '" ++ repoLocalDir repo
            ++ "' is missing. The repo is invalid."
        return mempty
      else ioError e

213
214
    isOldThreshold = 15 --days
    warnIfIndexIsOld indexFile = do
215
216
      dt <- getFileAge indexFile
      when (dt >= isOldThreshold) $ case repoKind repo of
217
218
        Left  remoteRepo -> warn verbosity $
             "The package list for '" ++ remoteRepoName remoteRepo
219
          ++ "' is " ++ show dt ++ " days old.\nRun "
220
221
222
          ++ "'cabal update' to get the latest list of available packages."
        Right _localRepo -> return ()

223
224
225
226
227
228
-- | It is not necessary to call this, as the cache will be updated when the
-- index is read normally. However you can do the work earlier if you like.
--
updateRepoIndexCache :: Verbosity -> Repo -> IO ()
updateRepoIndexCache verbosity repo =
    whenCacheOutOfDate indexFile cacheFile $ do
EyalLotem's avatar
EyalLotem committed
229
      info verbosity "Updating the index cache file..."
230
231
232
233
234
235
236
237
238
239
240
241
242
      updatePackageIndexCacheFile indexFile cacheFile
  where
    indexFile = repoLocalDir repo </> "00-index.tar"
    cacheFile = repoLocalDir repo </> "00-index.cache"

whenCacheOutOfDate :: FilePath-> FilePath -> IO () -> IO ()
whenCacheOutOfDate origFile cacheFile action = do
  exists <- doesFileExist cacheFile
  if not exists
    then action
    else do
      origTime  <- getModificationTime origFile
      cacheTime <- getModificationTime cacheFile
243
      unless (cacheTime >= origTime) action
244
245
246
247
248


------------------------------------------------------------------------
-- Reading the index file
--
249

250
-- | An index entry is either a normal package, or a local build tree reference.
refold's avatar
refold committed
251
252
253
data PackageEntry =
  NormalPackage  PackageId GenericPackageDescription ByteString BlockNo
  | BuildTreeRef PackageId GenericPackageDescription FilePath   BlockNo
254
255
256
257

type MkPackageEntry = IO PackageEntry

instance Package PackageEntry where
258
259
  packageId (NormalPackage pkgid _ _ _) = pkgid
  packageId (BuildTreeRef  pkgid _ _ _) = pkgid
260
261

packageDesc :: PackageEntry -> GenericPackageDescription
262
263
packageDesc (NormalPackage _ descr _ _) = descr
packageDesc (BuildTreeRef  _ descr _ _) = descr
264

265
266
267
268
269
270
271
272
273
274
-- | Read a compressed \"00-index.tar.gz\" file into a 'PackageIndex'.
--
-- This is supposed to be an \"all in one\" way to easily get at the info in
-- the hackage package index.
--
-- It takes a function to map a 'GenericPackageDescription' into any more
-- specific instance of 'Package' that you might want to use. In the simple
-- case you can just use @\_ p -> p@ here.
--
readPackageIndexFile :: Package pkg
275
                     => (PackageEntry -> pkg)
276
277
                     -> FilePath
                     -> IO (PackageIndex pkg, [Dependency])
278
readPackageIndexFile mkPkg indexFile = do
279
280
281
282
  (mkPkgs, prefs) <- either fail return
                     . parsePackageIndex
                     . maybeDecompress
                     =<< BS.readFile indexFile
283

284
285
286
  pkgEntries  <- sequence mkPkgs
  pkgs <- evaluate $ PackageIndex.fromList (map mkPkg pkgEntries)
  return (pkgs, prefs)
287
288
289
290

-- | Parse an uncompressed \"00-index.tar\" repository index file represented
-- as a 'ByteString'.
--
291
parsePackageIndex :: ByteString
292
                  -> Either String ([MkPackageEntry], [Dependency])
293
294
295
296
297
298
299
300
parsePackageIndex = accum 0 [] [] . Tar.read
  where
    accum blockNo pkgs prefs es = case es of
      Tar.Fail err   -> Left  err
      Tar.Done       -> Right (reverse pkgs, reverse prefs)
      Tar.Next e es' -> accum blockNo' pkgs' prefs' es'
        where
          (pkgs', prefs') = extract blockNo pkgs prefs e
301
          blockNo'        = blockNo + Tar.entrySizeInBlocks e
302
303
304
305
306
307
308

    extract blockNo pkgs prefs entry =
       fromMaybe (pkgs, prefs) $
                 tryExtractPkg
         `mplus` tryExtractPrefs
      where
        tryExtractPkg = do
309
310
          mkPkgEntry <- extractPkg entry blockNo
          return (mkPkgEntry:pkgs, prefs)
311
312
313
314
315

        tryExtractPrefs = do
          prefs' <- extractPrefs entry
          return (pkgs, prefs'++prefs)

316
317
extractPkg :: Tar.Entry -> BlockNo -> Maybe MkPackageEntry
extractPkg entry blockNo = case Tar.entryContent entry of
318
319
320
321
  Tar.NormalFile content _
     | takeExtension fileName == ".cabal"
    -> case splitDirectories (normalise fileName) of
        [pkgname,vers,_] -> case simpleParse vers of
322
          Just ver -> Just $ return (NormalPackage pkgid descr content blockNo)
323
324
325
326
327
328
329
330
331
            where
              pkgid  = PackageIdentifier (PackageName pkgname) ver
              parsed = parsePackageDescription . fromUTF8 . BS.Char8.unpack
                                               $ content
              descr  = case parsed of
                ParseOk _ d -> d
                _           -> error $ "Couldn't read cabal file "
                                    ++ show fileName
          _ -> Nothing
332
        _ -> Nothing
333
334

  Tar.OtherEntryType typeCode content _
335
336
337
    | typeCode == Tar.buildTreeRefTypeCode ->
      Just $ do
        let path   = byteStringToFilePath content
338
        cabalFile <- findPackageDesc path
339
        descr     <- PackageDesc.Parse.readPackageDescription normal cabalFile
340
        return $ BuildTreeRef (packageId descr) descr path blockNo
341

342
  _ -> Nothing
343

344
  where
345
    fileName = Tar.entryPath entry
346

347
348
349
350
351
352
353
354
355
extractPrefs :: Tar.Entry -> Maybe [Dependency]
extractPrefs entry = case Tar.entryContent entry of
  Tar.NormalFile content _
     | takeFileName (Tar.entryPath entry) == "preferred-versions"
    -> Just . parsePreferredVersions
     . BS.Char8.unpack $ content
  _ -> Nothing

parsePreferredVersions :: String -> [Dependency]
EyalLotem's avatar
EyalLotem committed
356
parsePreferredVersions = mapMaybe simpleParse
357
358
359
360
361
362
363
364
365
                       . filter (not . isPrefixOf "--")
                       . lines

------------------------------------------------------------------------
-- Reading and updating the index cache
--

updatePackageIndexCacheFile :: FilePath -> FilePath -> IO ()
updatePackageIndexCacheFile indexFile cacheFile = do
366
367
368
369
370
371
    (mkPkgs, prefs) <- either fail return
                       . parsePackageIndex
                       . maybeDecompress
                       =<< BS.readFile indexFile
    pkgEntries <- sequence mkPkgs
    let cache = mkCache pkgEntries prefs
372
373
374
    writeFile cacheFile (showIndexCache cache)
  where
    mkCache pkgs prefs =
refold's avatar
refold committed
375
        [ CachePreference pref          | pref <- prefs ]
376
     ++ [ CachePackageId pkgid blockNo
377
        | (NormalPackage pkgid _ _ blockNo) <- pkgs ]
378
     ++ [ CacheBuildTreeRef blockNo
379
        | (BuildTreeRef _ _ _ blockNo) <- pkgs]
380

381
382
383
data ReadPackageIndexMode = ReadPackageIndexStrict
                          | ReadPackageIndexLazyIO

384
readPackageIndexCacheFile :: Package pkg
385
                          => (PackageEntry -> pkg)
386
387
                          -> FilePath
                          -> FilePath
388
                          -> ReadPackageIndexMode
389
                          -> IO (PackageIndex pkg, [Dependency])
390
readPackageIndexCacheFile mkPkg indexFile cacheFile mode = do
391
  cache    <- liftM readIndexCache (BSS.readFile cacheFile)
392
393
394
395
396
397
398
  myWithFile indexFile ReadMode $ \indexHnd ->
    packageIndexFromCache mkPkg indexHnd cache mode
  where
    myWithFile f m act = case mode of
      ReadPackageIndexStrict -> withFile f m act
      ReadPackageIndexLazyIO -> do indexHnd <- openFile f m
                                   act indexHnd
399
400
401


packageIndexFromCache :: Package pkg
402
                      => (PackageEntry -> pkg)
403
404
                      -> Handle
                      -> [IndexCacheEntry]
405
                      -> ReadPackageIndexMode
406
                      -> IO (PackageIndex pkg, [Dependency])
407
packageIndexFromCache mkPkg hnd entrs mode = accum mempty [] entrs
408
409
410
411
412
413
414
415
416
417
418
419
420
  where
    accum srcpkgs prefs [] = do
      -- Have to reverse entries, since in a tar file, later entries mask
      -- earlier ones, and PackageIndex.fromList does the same, but we
      -- accumulate the list of entries in reverse order, so need to reverse.
      pkgIndex <- evaluate $ PackageIndex.fromList (reverse srcpkgs)
      return (pkgIndex, prefs)

    accum srcpkgs prefs (CachePackageId pkgid blockno : entries) = do
      -- Given the cache entry, make a package index entry.
      -- The magic here is that we use lazy IO to read the .cabal file
      -- from the index tarball if it turns out that we need it.
      -- Most of the time we only need the package id.
421
422
423
424
      ~(pkg, pkgtxt) <- unsafeInterleaveIO $ do
        pkgtxt <- getEntryContent blockno
        pkg    <- readPackageDescription pkgtxt
        return (pkg, pkgtxt)
425
426
427
428
429
430
      let srcpkg = case mode of
            ReadPackageIndexLazyIO ->
              mkPkg (NormalPackage pkgid pkg pkgtxt blockno)
            ReadPackageIndexStrict ->
              pkg `seq` pkgtxt `seq` mkPkg (NormalPackage pkgid pkg
                                            pkgtxt blockno)
431
432
433
434
435
436
      accum (srcpkg:srcpkgs) prefs entries

    accum srcpkgs prefs (CacheBuildTreeRef blockno : entries) = do
      -- We have to read the .cabal file eagerly here because we can't cache the
      -- package id for build tree references - the user might edit the .cabal
      -- file after the reference was added to the index.
437
      path <- liftM byteStringToFilePath . getEntryContent $ blockno
438
439
      pkg  <- do cabalFile <- findPackageDesc path
                 PackageDesc.Parse.readPackageDescription normal cabalFile
440
      let srcpkg = mkPkg (BuildTreeRef (packageId pkg) pkg path blockno)
441
442
      accum (srcpkg:srcpkgs) prefs entries

refold's avatar
refold committed
443
    accum srcpkgs prefs (CachePreference pref : entries) =
444
445
      accum srcpkgs (pref:prefs) entries

446
447
    getEntryContent :: BlockNo -> IO ByteString
    getEntryContent blockno = do
448
449
450
      hSeek hnd AbsoluteSeek (fromIntegral (blockno * 512))
      header  <- BS.hGet hnd 512
      size    <- getEntrySize header
451
      BS.hGet hnd (fromIntegral size)
452

453
    getEntrySize :: ByteString -> IO Tar.FileSize
454
455
456
457
458
    getEntrySize header =
      case Tar.read header of
        Tar.Next e _ ->
          case Tar.entryContent e of
            Tar.NormalFile _ size -> return size
459
            Tar.OtherEntryType typecode _ size
460
              | typecode == Tar.buildTreeRefTypeCode
461
                                  -> return size
462
463
464
            _                     -> interror "unexpected tar entry type"
        _ -> interror "could not read tar file entry"

465
    readPackageDescription :: ByteString -> IO GenericPackageDescription
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
    readPackageDescription content =
      case parsePackageDescription . fromUTF8 . BS.Char8.unpack $ content of
        ParseOk _ d -> return d
        _           -> interror "failed to parse .cabal file"

    interror msg = die $ "internal error when reading package index: " ++ msg
                      ++ "The package index or index cache is probably "
                      ++ "corrupt. Running cabal update might fix it."

------------------------------------------------------------------------
-- Index cache data structure
--

-- | Tar files are block structured with 512 byte blocks. Every header and file
-- content starts on a block boundary.
--
type BlockNo = Int

data IndexCacheEntry = CachePackageId PackageId BlockNo
485
                     | CacheBuildTreeRef BlockNo
refold's avatar
refold committed
486
                     | CachePreference Dependency
487
488
489
490
491
492
493
  deriving (Eq, Show)

readIndexCacheEntry :: BSS.ByteString -> Maybe IndexCacheEntry
readIndexCacheEntry = \line ->
  case BSS.words line of
    [key, pkgnamestr, pkgverstr, sep, blocknostr]
      | key == packageKey && sep == blocknoKey ->
refold's avatar
refold committed
494
495
      case (parseName pkgnamestr, parseVer pkgverstr [],
            parseBlockNo blocknostr) of
496
497
498
        (Just pkgname, Just pkgver, Just blockno)
          -> Just (CachePackageId (PackageIdentifier pkgname pkgver) blockno)
        _ -> Nothing
499
500
501
502
    [key, blocknostr] | key == buildTreeRefKey ->
      case parseBlockNo blocknostr of
        Just blockno -> Just (CacheBuildTreeRef blockno)
        _            -> Nothing
503
    (key: remainder) | key == preferredVersionKey ->
refold's avatar
refold committed
504
      fmap CachePreference (simpleParse (BSS.unpack (BSS.unwords remainder)))
505
    _  -> Nothing
506
  where
507
508
    packageKey = BSS.pack "pkg:"
    blocknoKey = BSS.pack "b#"
509
    buildTreeRefKey     = BSS.pack "build-tree-ref:"
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
    preferredVersionKey = BSS.pack "pref-ver:"

    parseName str
      | BSS.all (\c -> isAlphaNum c || c == '-') str
                  = Just (PackageName (BSS.unpack str))
      | otherwise = Nothing

    parseVer str vs =
      case BSS.readInt str of
        Nothing        -> Nothing
        Just (v, str') -> case BSS.uncons str' of
          Just ('.', str'') -> parseVer str'' (v:vs)
          Just _            -> Nothing
          Nothing           -> Just (Version (reverse (v:vs)) [])

    parseBlockNo str =
      case BSS.readInt str of
        Just (blockno, remainder) | BSS.null remainder -> Just blockno
        _                                              -> Nothing

showIndexCacheEntry :: IndexCacheEntry -> String
showIndexCacheEntry entry = case entry of
   CachePackageId pkgid b -> "pkg: " ++ display (packageName pkgid)
                                  ++ " " ++ display (packageVersion pkgid)
                          ++ " b# " ++ show b
535
536
   CacheBuildTreeRef b    -> "build-tree-ref: " ++ show b
   CachePreference dep    -> "pref-ver: " ++ display dep
537
538

readIndexCache :: BSS.ByteString -> [IndexCacheEntry]
EyalLotem's avatar
EyalLotem committed
539
readIndexCache = mapMaybe readIndexCacheEntry . BSS.lines
540
541
542

showIndexCache :: [IndexCacheEntry] -> String
showIndexCache = unlines . map showIndexCacheEntry