Skip to content

Commit 0874188

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 0874188

File tree

2 files changed

+85
-18
lines changed

2 files changed

+85
-18
lines changed

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import Distribution.Client.HttpUtils
2323
)
2424
import Distribution.Client.IndexUtils
2525
( Index (..)
26+
, IndexFileType (..)
27+
, clearPackageIndexCacheFiles
2628
, currentIndexTimestamp
27-
, indexBaseName
29+
, indexFilePath
2830
, updatePackageIndexCacheFile
2931
, updateRepoIndexCache
3032
, writeIndexTimestamp
@@ -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))
@@ -244,12 +246,14 @@ updateRepo verbosity _updateFlags repoCtxt (repo, indexState) = do
244246
repoRemote
245247
repoLocalDir
246248
case downloadResult of
247-
FileAlreadyInCache ->
248-
setModificationTime (indexBaseName repo <.> "tar")
249-
=<< getCurrentTime
249+
FileAlreadyInCache -> do
250+
t <- getCurrentTime
251+
setModificationTime (indexFilePath repo IndexTar) t
252+
setModificationTime (indexFilePath repo IndexCache) t
250253
FileDownloaded indexPath -> do
251254
writeFileAtomic (dropExtension indexPath) . maybeDecompress
252255
=<< BS.readFile indexPath
256+
clearPackageIndexCacheFiles verbosity (RepoIndex repoCtxt repo)
253257
updateRepoIndexCache verbosity (RepoIndex repoCtxt repo)
254258
RepoSecure{} -> repoContextWithSecureRepo repoCtxt repo $ \repoSecure -> do
255259
let index = RepoIndex repoCtxt repo
@@ -273,12 +277,16 @@ updateRepo verbosity _updateFlags repoCtxt (repo, indexState) = do
273277
case updated of
274278
Sec.NoUpdates -> do
275279
now <- getCurrentTime
276-
setModificationTime (indexBaseName repo <.> "tar") now
280+
setModificationTime (indexFilePath repo IndexTar) now
277281
`catchIO` \e ->
278282
warn verbosity $ "Could not set modification time of index tarball -- " ++ displayException e
283+
setModificationTime (indexFilePath repo IndexCache) now
284+
`catchIO` \e ->
285+
warn verbosity $ "Could not set modification time of cache -- " ++ displayException e
279286
noticeNoWrap verbosity $
280287
"Package list of " ++ prettyShow rname ++ " is up to date."
281288
Sec.HasUpdates -> do
289+
clearPackageIndexCacheFiles verbosity index
282290
updateRepoIndexCache verbosity index
283291
noticeNoWrap verbosity $
284292
"Package list of " ++ prettyShow rname ++ " has been updated."

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

+71-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,39 @@ 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+
indexFilePath :: Repo -> IndexFileType -> FilePath
184+
indexFilePath repo idx_file =
185+
case idx_file of
186+
IndexTarGz -> repoLocalDir repo </> fn <.> "tar.gz"
187+
IndexTar -> repoLocalDir repo </> fn <.> "tar"
188+
IndexCache -> repoLocalDir repo </> (fn <.> "cache-" <> prettyShow cabalInstallVersion)
189+
IndexTimestamp -> repoLocalDir repo </> fn <.> "timestamp"
190+
OldIndexCache -> repoLocalDir repo </> fn <.> "cache"
181191
where
182192
fn = case repo of
183193
RepoSecure{} -> "01-index"
184194
RepoRemote{} -> "00-index"
185195
RepoLocalNoIndex{} -> "noindex"
186196

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

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

500521
-- | A set of files (or directories) that can be monitored to detect when
501522
-- there might have been a change in the source packages.
502523
getSourcePackagesMonitorFiles :: [Repo] -> [FilePath]
503524
getSourcePackagesMonitorFiles repos =
504525
concat
505-
[ [ indexBaseName repo <.> "cache"
506-
, indexBaseName repo <.> "timestamp"
526+
[ [ indexFilePath repo IndexCache
527+
, indexFilePath repo IndexTimestamp
507528
]
508529
| repo <- repos
509530
]
@@ -752,13 +773,13 @@ data Index
752773
RepoIndex RepoContext Repo
753774

754775
indexFile :: Index -> FilePath
755-
indexFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "tar"
776+
indexFile (RepoIndex _ctxt repo) = indexFilePath repo IndexTar
756777

757778
cacheFile :: Index -> FilePath
758-
cacheFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "cache"
779+
cacheFile (RepoIndex _ctxt repo) = indexFilePath repo IndexCache
759780

760781
timestampFile :: Index -> FilePath
761-
timestampFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "timestamp"
782+
timestampFile (RepoIndex _ctxt repo) = indexFilePath repo IndexTimestamp
762783

763784
-- | Return 'True' if 'Index' uses 01-index format (aka secure repo)
764785
is01Index :: Index -> Bool
@@ -767,6 +788,32 @@ is01Index (RepoIndex _ repo) = case repo of
767788
RepoRemote{} -> False
768789
RepoLocalNoIndex{} -> True
769790

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

11401187
-- | Read a repository cache from the filesystem
11411188
--
1189+
-- If an out-dated cache is detected, the cache is older than the .tar file corresponding
1190+
-- to the cache, the cache is updated.
1191+
--
11421192
-- If a corrupted index cache is detected this function regenerates
11431193
-- the index cache and then reattempt to read the index once (and
11441194
-- 'dieWithException's if it fails again).
11451195
readIndexCache :: Verbosity -> Index -> IO Cache
11461196
readIndexCache verbosity index = do
1197+
-- 1. Update the cache, if it's out of date.
1198+
-- This covers the case where
1199+
-- - The index .tar.gz is downloaded, but the cache is missing.
1200+
-- - The index .tar.gz is downloaded, but the cache is too old (ie updated by another cabal-install)
1201+
1202+
-- This also fails with a "does not exist" error is the .tar.gz is not downloaded. That's important for
1203+
-- the control flow of functions which call this.
1204+
updateRepoIndexCache verbosity index
11471205
cacheOrFail <- readIndexCache' index
1206+
-- 2. Regenerate the cache if parsing failed.
11481207
case cacheOrFail of
11491208
Left msg -> do
11501209
warn verbosity $

0 commit comments

Comments
 (0)