forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProject.hs
More file actions
218 lines (203 loc) · 7.61 KB
/
Project.hs
File metadata and controls
218 lines (203 loc) · 7.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-unused-top-binds -Wno-identities #-}
-- | Work with SQLite database used for caches across a single project.
module Stack.Storage.Project
( initProjectStorage
, ConfigCacheKey
, configCacheKey
, loadConfigCache
, saveConfigCache
, deactiveConfigCache
) where
import qualified Data.ByteString as S
import qualified Data.Set as Set
import Database.Persist.Sqlite
import Database.Persist.TH
import qualified Pantry.Internal as SQLite
import Path
import Stack.Prelude hiding (MigrationFailure)
import Stack.Storage.Util
import Stack.Types.Build
import Stack.Types.Cache
import Stack.Types.Config (HasBuildConfig, buildConfigL, bcProjectStorage, ProjectStorage (..))
import Stack.Types.GhcPkgId
share [ mkPersist sqlSettings
, mkDeleteCascade sqlSettings
, mkMigrate "migrateAll"
]
[persistLowerCase|
ConfigCacheParent sql="config_cache"
directory FilePath "default=(hex(randomblob(16)))"
type ConfigCacheType
pkgSrc CachePkgSrc
active Bool
pathEnvVar Text
haddock Bool default=0
UniqueConfigCacheParent directory type sql="unique_config_cache"
deriving Show
ConfigCacheDirOption
parent ConfigCacheParentId sql="config_cache_id"
index Int
value String sql="option"
UniqueConfigCacheDirOption parent index
deriving Show
ConfigCacheNoDirOption
parent ConfigCacheParentId sql="config_cache_id"
index Int
value String sql="option"
UniqueConfigCacheNoDirOption parent index
deriving Show
ConfigCacheDep
parent ConfigCacheParentId sql="config_cache_id"
value GhcPkgId sql="ghc_pkg_id"
UniqueConfigCacheDep parent value
deriving Show
ConfigCacheComponent
parent ConfigCacheParentId sql="config_cache_id"
value S.ByteString sql="component"
UniqueConfigCacheComponent parent value
deriving Show
|]
-- | Initialize the database.
initProjectStorage ::
HasLogFunc env
=> Path Abs File -- ^ storage file
-> (ProjectStorage -> RIO env a)
-> RIO env a
initProjectStorage fp f = SQLite.initStorage "Stack" migrateAll fp $ f . ProjectStorage
-- | Run an action in a database transaction
withProjectStorage ::
(HasBuildConfig env, HasLogFunc env)
=> ReaderT SqlBackend (RIO env) a
-> RIO env a
withProjectStorage inner =
flip SQLite.withStorage_ inner =<< view (buildConfigL . to bcProjectStorage . to unProjectStorage)
-- | Key used to retrieve configuration or flag cache
type ConfigCacheKey = Unique ConfigCacheParent
-- | Build key used to retrieve configuration or flag cache
configCacheKey :: Path Abs Dir -> ConfigCacheType -> ConfigCacheKey
configCacheKey dir = UniqueConfigCacheParent (toFilePath dir)
-- | Internal helper to read the 'ConfigCache'
readConfigCache ::
(HasBuildConfig env, HasLogFunc env)
=> Entity ConfigCacheParent
-> ReaderT SqlBackend (RIO env) ConfigCache
readConfigCache (Entity parentId ConfigCacheParent {..}) = do
let configCachePkgSrc = configCacheParentPkgSrc
coDirs <-
map (configCacheDirOptionValue . entityVal) <$>
selectList
[ConfigCacheDirOptionParent ==. parentId]
[Asc ConfigCacheDirOptionIndex]
coNoDirs <-
map (configCacheNoDirOptionValue . entityVal) <$>
selectList
[ConfigCacheNoDirOptionParent ==. parentId]
[Asc ConfigCacheNoDirOptionIndex]
let configCacheOpts = ConfigureOpts {..}
configCacheDeps <-
Set.fromList . map (configCacheDepValue . entityVal) <$>
selectList [ConfigCacheDepParent ==. parentId] []
configCacheComponents <-
Set.fromList . map (configCacheComponentValue . entityVal) <$>
selectList [ConfigCacheComponentParent ==. parentId] []
let configCachePathEnvVar = configCacheParentPathEnvVar
let configCacheHaddock = configCacheParentHaddock
return ConfigCache {..}
-- | Load 'ConfigCache' from the database.
loadConfigCache ::
(HasBuildConfig env, HasLogFunc env)
=> ConfigCacheKey
-> RIO env (Maybe ConfigCache)
loadConfigCache key =
withProjectStorage $ do
mparent <- getBy key
case mparent of
Nothing -> return Nothing
Just parentEntity@(Entity _ ConfigCacheParent {..})
| configCacheParentActive ->
Just <$> readConfigCache parentEntity
| otherwise -> return Nothing
-- | Insert or update 'ConfigCache' to the database.
saveConfigCache ::
(HasBuildConfig env, HasLogFunc env)
=> ConfigCacheKey
-> ConfigCache
-> RIO env ()
saveConfigCache key@(UniqueConfigCacheParent dir type_) new =
withProjectStorage $ do
mparent <- getBy key
(parentId, mold) <-
case mparent of
Nothing ->
(, Nothing) <$>
insert
ConfigCacheParent
{ configCacheParentDirectory = dir
, configCacheParentType = type_
, configCacheParentPkgSrc = configCachePkgSrc new
, configCacheParentActive = True
, configCacheParentPathEnvVar = configCachePathEnvVar new
, configCacheParentHaddock = configCacheHaddock new
}
Just parentEntity@(Entity parentId _) -> do
old <- readConfigCache parentEntity
update
parentId
[ ConfigCacheParentPkgSrc =. configCachePkgSrc new
, ConfigCacheParentActive =. True
, ConfigCacheParentPathEnvVar =. configCachePathEnvVar new
]
return (parentId, Just old)
updateList
ConfigCacheDirOption
ConfigCacheDirOptionParent
parentId
ConfigCacheDirOptionIndex
(maybe [] (coDirs . configCacheOpts) mold)
(coDirs $ configCacheOpts new)
updateList
ConfigCacheNoDirOption
ConfigCacheNoDirOptionParent
parentId
ConfigCacheNoDirOptionIndex
(maybe [] (coNoDirs . configCacheOpts) mold)
(coNoDirs $ configCacheOpts new)
updateSet
ConfigCacheDep
ConfigCacheDepParent
parentId
ConfigCacheDepValue
(maybe Set.empty configCacheDeps mold)
(configCacheDeps new)
updateSet
ConfigCacheComponent
ConfigCacheComponentParent
parentId
ConfigCacheComponentValue
(maybe Set.empty configCacheComponents mold)
(configCacheComponents new)
-- | Mark 'ConfigCache' as inactive in the database.
-- We use a flag instead of deleting the records since, in most cases, the same
-- cache will be written again within in a few seconds (after
-- `cabal configure`), so this avoids unnecessary database churn.
deactiveConfigCache :: HasBuildConfig env => ConfigCacheKey -> RIO env ()
deactiveConfigCache (UniqueConfigCacheParent dir type_) =
withProjectStorage $
updateWhere
[ConfigCacheParentDirectory ==. dir, ConfigCacheParentType ==. type_]
[ConfigCacheParentActive =. False]