Skip to content

Commit ad09611

Browse files
committed
Use a per-version cache file for the index state
cabal-install will now use a version suffixed cache file for the index state. If you are regularly changing between cabal-install versions, this will be less annoying as you won't have to regenerate the cache each time you switch project. There is one tricky part of the implementation. If you update the index with a newer cabal-install, then the old-style cabal-install caches are invalidated by replacing them with an empty file. This is because in cabal-install (until this commit), the freshness of the cache was now checked by `readIndexCache`. If you update with an older `cabal-install` then the freshness check will see the cache for your new cabal-install is older than the index, and update it. Fixes #7502
1 parent d4d92e9 commit ad09611

File tree

2 files changed

+76
-16
lines changed

2 files changed

+76
-16
lines changed

cabal-install/src/Distribution/Client/CmdUpdate.hs

+8-4
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import Distribution.Client.HttpUtils
2323
)
2424
import Distribution.Client.IndexUtils
2525
( Index (..)
26+
, IndexFileType(..)
2627
, currentIndexTimestamp
27-
, indexBaseName
28+
, indexFilePath
2829
, updatePackageIndexCacheFile
30+
, clearPackageIndexCacheFiles
2931
, updateRepoIndexCache
3032
, writeIndexTimestamp
3133
)
@@ -93,7 +95,7 @@ import Distribution.Simple.Command
9395
( CommandUI (..)
9496
, usageAlternatives
9597
)
96-
import System.FilePath (dropExtension, (<.>))
98+
import System.FilePath (dropExtension)
9799

98100
import Distribution.Client.Errors
99101
import Distribution.Client.IndexUtils.Timestamp (Timestamp (NoTimestamp))
@@ -245,11 +247,12 @@ updateRepo verbosity _updateFlags repoCtxt (repo, indexState) = do
245247
repoLocalDir
246248
case downloadResult of
247249
FileAlreadyInCache ->
248-
setModificationTime (indexBaseName repo <.> "tar")
250+
setModificationTime (indexFilePath repo IndexTar)
249251
=<< getCurrentTime
250252
FileDownloaded indexPath -> do
251253
writeFileAtomic (dropExtension indexPath) . maybeDecompress
252254
=<< BS.readFile indexPath
255+
clearPackageIndexCacheFiles verbosity (RepoIndex repoCtxt repo)
253256
updateRepoIndexCache verbosity (RepoIndex repoCtxt repo)
254257
RepoSecure{} -> repoContextWithSecureRepo repoCtxt repo $ \repoSecure -> do
255258
let index = RepoIndex repoCtxt repo
@@ -273,12 +276,13 @@ updateRepo verbosity _updateFlags repoCtxt (repo, indexState) = do
273276
case updated of
274277
Sec.NoUpdates -> do
275278
now <- getCurrentTime
276-
setModificationTime (indexBaseName repo <.> "tar") now
279+
setModificationTime (indexFilePath repo IndexTar) now
277280
`catchIO` \e ->
278281
warn verbosity $ "Could not set modification time of index tarball -- " ++ displayException e
279282
noticeNoWrap verbosity $
280283
"Package list of " ++ prettyShow rname ++ " is up to date."
281284
Sec.HasUpdates -> do
285+
clearPackageIndexCacheFiles verbosity index
282286
updateRepoIndexCache verbosity index
283287
noticeNoWrap verbosity $
284288
"Package list of " ++ prettyShow rname ++ " has been updated."

cabal-install/src/Distribution/Client/IndexUtils.hs

+68-12
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
module Distribution.Client.IndexUtils
2121
( getIndexFileAge
2222
, getInstalledPackages
23-
, indexBaseName
23+
, indexFilePath
24+
, IndexFileType(..)
2425
, Configure.getInstalledPackagesMonitorFiles
2526
, getSourcePackages
2627
, getSourcePackagesMonitorFiles
@@ -34,6 +35,7 @@ module Distribution.Client.IndexUtils
3435
, parsePackageIndex
3536
, updateRepoIndexCache
3637
, updatePackageIndexCacheFile
38+
, clearPackageIndexCacheFiles
3739
, writeIndexTimestamp
3840
, currentIndexTimestamp
3941
, BuildTreeRefType (..)
@@ -61,6 +63,8 @@ import Distribution.Client.Types
6163
import Distribution.Parsec (simpleParsecBS)
6264
import Distribution.Verbosity
6365

66+
import Distribution.Client.Version
67+
6468
import Distribution.Client.ProjectConfig
6569
( CabalFileParseError
6670
, readSourcePackageCabalFile'
@@ -137,7 +141,7 @@ import Distribution.Compat.Directory (listDirectory)
137141
import Distribution.Compat.Time (getFileAge, getModTime)
138142
import Distribution.Utils.Generic (fstOf3)
139143
import Distribution.Utils.Structured (Structured (..), nominalStructure, structuredDecodeFileOrFail, structuredEncodeFile)
140-
import System.Directory (doesDirectoryExist, doesFileExist)
144+
import System.Directory (doesDirectoryExist, doesFileExist, removeFile)
141145
import System.FilePath
142146
( normalise
143147
, splitDirectories
@@ -168,22 +172,36 @@ getInstalledPackages verbosity comp packageDbs progdb =
168172
where
169173
verbosity' = lessVerbose verbosity
170174

171-
-- | Get filename base (i.e. without file extension) for index-related files
175+
-- | Get filenames for index-related files
172176
--
173177
-- /Secure/ cabal repositories use a new extended & incremental
174178
-- @01-index.tar@. In order to avoid issues resulting from clobbering
175179
-- new/old-style index data, we save them locally to different names.
176180
--
177-
-- Example: Use @indexBaseName repo <.> "tar.gz"@ to compute the 'FilePath' of the
181+
-- Example: Use @indexFilePath repo IndexTarGz@ to compute the 'FilePath' of the
178182
-- @00-index.tar.gz@/@01-index.tar.gz@ file.
179-
indexBaseName :: Repo -> FilePath
180-
indexBaseName repo = repoLocalDir repo </> fn
183+
184+
indexFilePath :: Repo -> IndexFileType -> FilePath
185+
indexFilePath repo idx_file =
186+
case idx_file of
187+
IndexTarGz -> repoLocalDir repo </> fn <.> "tar.gz"
188+
IndexTar -> repoLocalDir repo </> fn <.> "tar"
189+
IndexCache -> repoLocalDir repo </> (fn <.> "cache-" <> prettyShow cabalInstallVersion)
190+
IndexTimestamp -> repoLocalDir repo </> fn <.> "timestamp"
191+
OldIndexCache -> repoLocalDir repo </> fn <.> "cache"
181192
where
182193
fn = case repo of
183194
RepoSecure{} -> "01-index"
184195
RepoRemote{} -> "00-index"
185196
RepoLocalNoIndex{} -> "noindex"
186197

198+
-- | The types of the files which are associated with a particular index.
199+
data IndexFileType = IndexTarGz
200+
| IndexTar
201+
| IndexCache -- ^ The specific cache file, for this version of cabal-install
202+
| IndexTimestamp -- ^ The timestamp file for the index
203+
| OldIndexCache -- ^ The location that old versions (before 3.16) of cabal-install put the index cache
204+
187205
------------------------------------------------------------------------
188206
-- Reading the source package index
189207
--
@@ -495,15 +513,15 @@ readRepoIndex verbosity repoCtxt repo idxState =
495513

496514
-- | Return the age of the index file in days (as a Double).
497515
getIndexFileAge :: Repo -> IO Double
498-
getIndexFileAge repo = getFileAge $ indexBaseName repo <.> "tar"
516+
getIndexFileAge repo = getFileAge $ indexFilePath repo IndexTar
499517

500518
-- | A set of files (or directories) that can be monitored to detect when
501519
-- there might have been a change in the source packages.
502520
getSourcePackagesMonitorFiles :: [Repo] -> [FilePath]
503521
getSourcePackagesMonitorFiles repos =
504522
concat
505-
[ [ indexBaseName repo <.> "cache"
506-
, indexBaseName repo <.> "timestamp"
523+
[ [ indexFilePath repo IndexCache
524+
, indexFilePath repo IndexTimestamp
507525
]
508526
| repo <- repos
509527
]
@@ -752,13 +770,13 @@ data Index
752770
RepoIndex RepoContext Repo
753771

754772
indexFile :: Index -> FilePath
755-
indexFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "tar"
773+
indexFile (RepoIndex _ctxt repo) = indexFilePath repo IndexTar
756774

757775
cacheFile :: Index -> FilePath
758-
cacheFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "cache"
776+
cacheFile (RepoIndex _ctxt repo) = indexFilePath repo IndexCache
759777

760778
timestampFile :: Index -> FilePath
761-
timestampFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "timestamp"
779+
timestampFile (RepoIndex _ctxt repo) = indexFilePath repo IndexTimestamp
762780

763781
-- | Return 'True' if 'Index' uses 01-index format (aka secure repo)
764782
is01Index :: Index -> Bool
@@ -767,6 +785,32 @@ is01Index (RepoIndex _ repo) = case repo of
767785
RepoRemote{} -> False
768786
RepoLocalNoIndex{} -> True
769787

788+
789+
-- | Clear the cache files for old cabal-install versions which have a cache
790+
-- for this index. The cache will be invalid now that we have downloaded a new
791+
-- .tar.gz for the index.
792+
--
793+
-- Note that this invalidation logic only invalidates the old-style caches for
794+
-- cabal-install < 3.16. For never versions, the check in `readIndexCache` that the
795+
-- cache is older than the indexFile is sufficient to update the caches when required.
796+
--
797+
-- If the old version of cabal-install is used again, then this file will be generated
798+
-- lazily.
799+
clearPackageIndexCacheFiles :: Verbosity -> Index -> IO ()
800+
clearPackageIndexCacheFiles verbosity (RepoIndex _ repo) = do
801+
info verbosity ("Deleting caches if they exist for " ++ prettyShow (repoName repo))
802+
let old_cache_path = indexFilePath repo OldIndexCache
803+
-- Delete old-style non-versioned caches, if the file existed then replace
804+
-- it with an empty file. Otherwise old versions of `cabal-install` will complain
805+
-- about a missing package list.
806+
(removeFile old_cache_path
807+
>> writeFile old_cache_path "") `catch` handleExists
808+
809+
where
810+
handleExists e
811+
| isDoesNotExistError e = return ()
812+
| otherwise = throwIO e
813+
770814
updatePackageIndexCacheFile :: Verbosity -> Index -> IO ()
771815
updatePackageIndexCacheFile verbosity index = do
772816
info verbosity ("Updating index cache file " ++ cacheFile index ++ " ...")
@@ -1139,12 +1183,24 @@ packageListFromCache verbosity mkPkg hnd Cache{..} = accum mempty [] mempty cach
11391183

11401184
-- | Read a repository cache from the filesystem
11411185
--
1186+
-- If an out-dated cache is detected, the cache is older than the .tar file corresponding
1187+
-- to the cache, the cache is updated.
1188+
--
11421189
-- If a corrupted index cache is detected this function regenerates
11431190
-- the index cache and then reattempt to read the index once (and
11441191
-- 'dieWithException's if it fails again).
11451192
readIndexCache :: Verbosity -> Index -> IO Cache
11461193
readIndexCache verbosity index = do
1194+
-- 1. Update the cache, if it's out of date.
1195+
-- This covers the case where
1196+
-- - The index .tar.gz is downloaded, but the cache is missing.
1197+
-- - The index .tar.gz is downloaded, but the cache is too old (ie updated by another cabal-install)
1198+
1199+
-- This also fails with a "does not exist" error is the .tar.gz is not downloaded. That's important for
1200+
-- the control flow of functions which call this.
1201+
updateRepoIndexCache verbosity index
11471202
cacheOrFail <- readIndexCache' index
1203+
-- 2. Regenerate the cache if parsing failed.
11481204
case cacheOrFail of
11491205
Left msg -> do
11501206
warn verbosity $

0 commit comments

Comments
 (0)