Skip to content

Commit f75882d

Browse files
andreabedinigeekosaur
authored andcommitted
feat(cabal-install): Store garbage collection
bla bla - Create backlinks from storedir/gc-root to the dist directory at the same time as we write the plan.json file. - TODO: add top level command that 1. checks the gc-root directory for old roots and removes them (by checking whether the symlink resolves). 2. traverse the dependency graph and do the garbage collection.
1 parent 3727226 commit f75882d

File tree

3 files changed

+80
-2
lines changed

3 files changed

+80
-2
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ data StoreDirLayout = StoreDirLayout
126126
, storePackageDBStack :: Compiler -> [Maybe PackageDBCWD] -> PackageDBStackCWD
127127
, storeIncomingDirectory :: Compiler -> FilePath
128128
, storeIncomingLock :: Compiler -> UnitId -> FilePath
129+
, storeGcRootsDirectory :: FilePath
129130
}
130131

131132
-- TODO: move to another module, e.g. CabalDirLayout?
@@ -300,6 +301,10 @@ defaultStoreDirLayout storeRoot =
300301
storeIncomingLock compiler unitid =
301302
storeIncomingDirectory compiler </> prettyShow unitid <.> "lock"
302303

304+
storeGcRootsDirectory :: FilePath
305+
storeGcRootsDirectory =
306+
storeRoot </> "gc-roots"
307+
303308
defaultCabalDirLayout :: IO CabalDirLayout
304309
defaultCabalDirLayout =
305310
mkCabalDirLayout Nothing Nothing

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

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ module Distribution.Client.ProjectPlanOutput
1515
, createPackageEnvironment
1616
, writePlanGhcEnvironment
1717
, argsEquivalentOfGhcEnvironmentFile
18+
19+
-- * Store garbage collection
20+
, writeGcRoot
1821
) where
1922

2023
import Distribution.Client.DistDirLayout
@@ -36,6 +39,7 @@ import qualified Distribution.Solver.Types.ComponentDeps as ComponentDeps
3639
import qualified Distribution.Compat.Binary as Binary
3740
import Distribution.Compat.Graph (Graph, Node)
3841
import qualified Distribution.Compat.Graph as Graph
42+
import Distribution.Compiler (AbiTag (..), CompilerFlavor (..))
3943
import Distribution.InstalledPackageInfo (InstalledPackageInfo)
4044
import Distribution.Package
4145
import qualified Distribution.PackageDescription as PD
@@ -45,13 +49,28 @@ import Distribution.Simple.BuildPaths
4549
, exeExtension
4650
)
4751
import Distribution.Simple.Compiler
52+
( Compiler (compilerAbiTag)
53+
, CompilerId (..)
54+
, GlobalPackageDB
55+
, PackageDB (..)
56+
, PackageDBCWD
57+
, PackageDBStack
58+
, PackageDBStackCWD
59+
, SpecificPackageDB
60+
, UserPackageDB
61+
, compilerFlavor
62+
, compilerId
63+
, compilerVersion
64+
, showCompilerId
65+
)
4866
import Distribution.Simple.GHC
4967
( GhcEnvironmentFileEntry (..)
5068
, GhcImplInfo (supportsPkgEnvFiles)
5169
, getImplInfo
5270
, simpleGhcEnvironmentFile
5371
, writeGhcEnvironmentFile
5472
)
73+
import Distribution.Simple.Program.GHC (packageDbArgsDb)
5574
import Distribution.Simple.Utils
5675
import Distribution.System
5776
import Distribution.Types.Version
@@ -61,8 +80,10 @@ import Distribution.Utils.Path hiding
6180
( (<.>)
6281
, (</>)
6382
)
83+
import Distribution.Utils.String (encodeStringUtf8)
6484
import Distribution.Verbosity
6585

86+
import Distribution.Client.Compat.Directory (createFileLink)
6687
import Distribution.Client.Compat.Prelude
6788
import Prelude ()
6889

@@ -71,10 +92,12 @@ import qualified Data.ByteString.Lazy as BS
7192
import qualified Data.Map as Map
7293
import qualified Data.Set as Set
7394

95+
import Control.Exception (handleJust)
96+
import Data.Containers.ListUtils (nubOrd)
97+
import System.Directory (removeFile)
7498
import System.FilePath
7599
import System.IO
76-
77-
import Distribution.Simple.Program.GHC (packageDbArgsDb)
100+
import System.IO.Error (isDoesNotExistError)
78101

79102
-----------------------------------------------------------------------------
80103
-- Writing plan.json files
@@ -1016,3 +1039,50 @@ relativePackageDBPath relroot pkgdb =
10161039
SpecificPackageDB path -> SpecificPackageDB relpath
10171040
where
10181041
relpath = makeRelative (normalise relroot) path
1042+
1043+
-- | Establish backlinks for garbage collection of the store
1044+
writeGcRoot
1045+
:: Verbosity
1046+
-> StoreDirLayout
1047+
-> DistDirLayout
1048+
-> ElaboratedSharedConfig
1049+
-> ElaboratedInstallPlan
1050+
-> IO ()
1051+
writeGcRoot verbosity storeDirLayout distDirLayout elaboratedSharedConfig elaboratedInstallPlan = do
1052+
-- NOTE: this needs some thinking
1053+
-- We need to establish backlinks for the store so that we can collect garbage later on.
1054+
-- We have the whole build graph here so, to be pragmatic we are going to list all the
1055+
-- non-inplace units in the plan, irrespectively of whether they are direct or transitive
1056+
-- dependencies.
1057+
let refsUnitIds =
1058+
[ elabUnitId elab
1059+
| InstallPlan.Configured elab <- InstallPlan.toList elaboratedInstallPlan
1060+
, not (isInplaceBuildStyle (elabBuildStyle elab))
1061+
]
1062+
writeFile referencesFile $ unlines $ map unUnitId $ nubOrd refsUnitIds
1063+
1064+
-- Write the gc root
1065+
createDirectoryIfMissingVerbose verbosity True storeGcRootsDir
1066+
1067+
-- To avoid collision we name the link with the hash of the dist directory.
1068+
let gcRootPath = storeGcRootsDir </> showHashValue (hashValue (encodePath distDir))
1069+
1070+
handleJust (\e -> if isDoesNotExistError e then Just () else Nothing) mempty $
1071+
removeFile gcRootPath
1072+
1073+
createFileLink distDir gcRootPath
1074+
where
1075+
storeGcRootsDir = storeGcRootsDirectory storeDirLayout
1076+
distDir = distDirectory distDirLayout
1077+
referencesFile = distProjectCacheFile distDirLayout "store-refs-" <> compilerTag
1078+
compiler = pkgConfigCompiler elaboratedSharedConfig
1079+
-- NOTE: It would be a good idea to expose this in StoreDirLayoyt
1080+
compilerTag = case compilerAbiTag compiler of
1081+
NoAbiTag -> prettyShow (compilerId compiler)
1082+
AbiTag tag -> prettyShow (compilerId compiler) <> "-" <> tag
1083+
1084+
-- NOTE: A FilePath should never represented as a String as we should never
1085+
-- have to do this. Nevetheless we do not need this to be stable as changes
1086+
-- will only mean a new root is created in place of the old one. Two roots
1087+
-- pointing to the same directory should never be a problem.
1088+
encodePath = BS.pack . encodeStringUtf8

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,9 @@ rebuildInstallPlan
861861
elaboratedPlan
862862
elaboratedShared
863863

864+
debug verbosity "Creating store garbage-collection root"
865+
writeGcRoot verbosity cabalStoreDirLayout distDirLayout elaboratedShared elaboratedPlan
866+
864867
-- Improve the elaborated install plan. The elaborated plan consists
865868
-- mostly of source packages (with full nix-style hashed ids). Where
866869
-- corresponding installed packages already exist in the store, replace

0 commit comments

Comments
 (0)