stack-3.9.1: src/Stack/Types/Cache.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Stack.Types.Cache
License : BSD-3-Clause
-}
module Stack.Types.Cache
( FileCache
, BuildFileCache (..)
, FileCacheInfo (..)
, ConfigCache (..)
, CachePkgSrc (..)
, PrecompiledCache (..)
, ConfigCacheType (..)
, Action (..)
) where
import Data.Aeson
( ToJSON (..), FromJSON (..), (.=), (.:), object, withObject
)
import qualified Data.ByteString as S
import qualified Data.Text as T
import Database.Persist.Sql
( PersistField (..), PersistFieldSql (..), PersistValue (..)
, SqlType (..)
)
import Stack.Prelude
import Stack.Types.ConfigureOpts ( ConfigureOpts )
import Stack.Types.GhcPkgId
( GhcPkgId, ghcPkgIdToText, parseGhcPkgId )
-- | Type representing types of cache in the Stack project SQLite database.
data ConfigCacheType
= ConfigCacheTypeConfig
-- ^ Cabal configuration cache.
| ConfigCacheTypeFlagLibrary GhcPkgId
-- ^ Library Cabal flag cache.
| ConfigCacheTypeFlagExecutable PackageIdentifier
-- ^ Executable Cabal flag cache.
deriving (Eq, Show)
instance PersistField ConfigCacheType where
toPersistValue ConfigCacheTypeConfig = PersistText "config"
toPersistValue (ConfigCacheTypeFlagLibrary v) =
PersistText $ "lib:" <> ghcPkgIdToText v
toPersistValue (ConfigCacheTypeFlagExecutable v) =
PersistText $ "exe:" <> T.pack (packageIdentifierString v)
fromPersistValue (PersistText t) =
fromMaybe (Left $ "Unexpected ConfigCacheType value: " <> t) $
config <|> fmap lib (T.stripPrefix "lib:" t) <|>
fmap exe (T.stripPrefix "exe:" t)
where
config
| t == "config" = Just (Right ConfigCacheTypeConfig)
| otherwise = Nothing
lib v = do
ghcPkgId <- mapLeft tshow (parseGhcPkgId v)
Right $ ConfigCacheTypeFlagLibrary ghcPkgId
exe v = do
pkgId <-
maybe (Left $ "Unexpected ConfigCacheType value: " <> t) Right $
parsePackageIdentifier (T.unpack v)
Right $ ConfigCacheTypeFlagExecutable pkgId
fromPersistValue _ = Left "Unexpected ConfigCacheType type"
instance PersistFieldSql ConfigCacheType where
sqlType _ = SqlString
-- | Type representing actions for which the last time the action was performed
-- should be cached.
data Action
= UpgradeCheck
deriving (Eq, Ord, Show)
instance PersistField Action where
toPersistValue UpgradeCheck = PersistInt64 1
fromPersistValue (PersistInt64 1) = Right UpgradeCheck
fromPersistValue x = Left $ T.pack $ "Invalid Action: " ++ show x
instance PersistFieldSql Action where
sqlType _ = SqlInt64
-- | Type synonym representing caches of files and information about them
-- sufficient to identify if they have changed subsequently.
type FileCache = Map FilePath FileCacheInfo
-- | Type representing caches of information about files sufficient to identify
-- if they have changed subsequently. Stored on disk.
newtype BuildFileCache = BuildFileCache
{ fileCache :: FileCache
}
deriving (Eq, FromJSON, Generic, Show, ToJSON)
instance NFData BuildFileCache
-- | Type representing information about a file sufficient to identify if
-- it has changed subsequently.
newtype FileCacheInfo = FileCacheInfo
{ hash :: SHA256
-- ^ SHA-256 hash of file contents.
}
deriving (Eq, Generic, Show)
instance NFData FileCacheInfo
-- Provided for storing the t'BuildFileCache' values in a file. But maybe
-- JSON/YAML isn't the right choice here, worth considering.
instance ToJSON FileCacheInfo where
toJSON fileCacheInfo = object
[ "hash" .= fileCacheInfo.hash
]
instance FromJSON FileCacheInfo where
parseJSON = withObject "FileCacheInfo" $ \o -> FileCacheInfo
<$> o .: "hash"
-- | Stored in the project's SQLite database to know whether the Cabal
-- configuration has changed or libarary or executable Cabal flags have changed.
data ConfigCache = ConfigCache
{ configureOpts :: !ConfigureOpts
-- ^ All Cabal configure options used for this package.
, deps :: !(Set GhcPkgId)
-- ^ The GhcPkgIds of all of the dependencies. Since Cabal doesn't take
-- the complete GhcPkgId (only a PackageIdentifier) in the configure
-- options, just using the previous value is insufficient to know if
-- dependencies have changed.
, components :: !(Set S.ByteString)
-- ^ The components to be built. It's a bit of a hack to include this in
-- here, as it's not a configure option (just a build option), but this
-- is a convenient way to force compilation when the components change.
, buildHaddocks :: !Bool
-- ^ Is Haddock documentation to be built?
, pkgSrc :: !CachePkgSrc
-- ^ The origin of the package's source code.
, pathEnvVar :: !Text
-- ^ Value of the PATH environment variable. See
-- <https://github.com/commercialhaskell/stack/issues/3138>
}
deriving (Data, Eq, Generic, Show)
instance NFData ConfigCache
data CachePkgSrc
= CacheSrcUpstream
| CacheSrcLocal FilePath
deriving (Data, Eq, Generic, Read, Show)
instance NFData CachePkgSrc
instance PersistField CachePkgSrc where
toPersistValue CacheSrcUpstream = PersistText "upstream"
toPersistValue (CacheSrcLocal fp) = PersistText ("local:" <> T.pack fp)
fromPersistValue (PersistText t) =
if t == "upstream"
then Right CacheSrcUpstream
else case T.stripPrefix "local:" t of
Just fp -> Right $ CacheSrcLocal (T.unpack fp)
Nothing -> Left $ "Unexpected CachePkgSrc value: " <> t
fromPersistValue _ = Left "Unexpected CachePkgSrc type"
instance PersistFieldSql CachePkgSrc where
sqlType _ = SqlString
-- | Information on a compiled package: the library .conf file (if relevant),
-- the sub-libraries (if present) and all of the executable paths.
data PrecompiledCache base = PrecompiledCache
{ library :: !(Maybe (Path base File))
-- ^ .conf file inside the package database
, subLibs :: ![Path base File]
-- ^ .conf file inside the package database, for each of the sub-libraries
, exes :: ![Path base File]
-- ^ Full paths to executables
}
deriving (Eq, Generic, Show)
instance NFData (PrecompiledCache Abs)
instance NFData (PrecompiledCache Rel)