Implement better caching for target options

This commit is contained in:
Daniel Gröber 2015-03-28 02:30:51 +01:00
parent 90d9577f8d
commit 7019cbcfa1
7 changed files with 261 additions and 120 deletions

View File

@ -22,6 +22,7 @@ module Language.Haskell.GhcMod.CabalHelper (
import Control.Applicative import Control.Applicative
import Control.Monad import Control.Monad
import Data.Monoid import Data.Monoid
import Data.Version
import Distribution.Helper import Distribution.Helper
import qualified Language.Haskell.GhcMod.Types as T import qualified Language.Haskell.GhcMod.Types as T
import Language.Haskell.GhcMod.Types hiding (ghcProgram, ghcPkgProgram, import Language.Haskell.GhcMod.Types hiding (ghcProgram, ghcPkgProgram,
@ -52,16 +53,31 @@ helperProgs opts = Programs {
-- --
-- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by -- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by
-- 'resolveGmComponents'. -- 'resolveGmComponents'.
getComponents :: (MonadIO m, GmEnv m) => m [GmComponent ChEntrypoint] getComponents :: (MonadIO m, GmEnv m, GmLog m)
getComponents = withCabal $ do => m [GmComponent GMCRaw ChEntrypoint]
Cradle {..} <- cradle getComponents = do
let distdir = cradleRootDir </> "dist" opt <- options
opt <- options Cradle {..} <- cradle
let gmVer = GhcMod.version
chVer = VERSION_cabal_helper
d = (helperProgs opt
, cradleRootDir </> "dist"
, (gmVer, chVer)
)
withCabal $ cached cradleRootDir cabalHelperCache d
runQuery' (helperProgs opt) distdir $ do cabalHelperCache :: MonadIO m => Cached m
q <- liftM4 join4 ghcOptions ghcSrcOptions entrypoints sourceDirs (Programs, FilePath, (Version, String))
return $ flip map q $ \(cn, (opts, (srcOpts, (ep, srcDirs)))) -> [GmComponent GMCRaw ChEntrypoint]
GmComponent cn opts srcOpts ep ep srcDirs mempty cabalHelperCache = Cached {
cacheFile = cabalHelperCacheFile,
cachedAction = \ _ (progs, root, _) ->
runQuery' progs root $ do
q <- liftM4 join4 ghcOptions ghcSrcOptions entrypoints sourceDirs
let cs = flip map q $ \(cn, (opts, (srcOpts, (ep, srcDirs)))) ->
GmComponent cn opts srcOpts ep ep srcDirs mempty
return ([setupConfigPath], cs)
}
where where
join4 a b c = join' a . join' b . join' c join4 a b c = join' a . join' b . join' c
join' :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))] join' :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))]

View File

@ -0,0 +1,83 @@
module Language.Haskell.GhcMod.Caching where
import Control.Monad.Trans.Maybe
import Data.Maybe
import Data.Serialize
import qualified Data.ByteString as BS
import System.FilePath
import Language.Haskell.GhcMod.Monad.Types
import Language.Haskell.GhcMod.Logging
import Utils
data Cached m d a =
Cached { cacheFile :: FilePath,
cachedAction :: TimedCacheFiles -> d -> m ([FilePath], a)
-- ^ The cached action, will only run if
-- * The cache doesn\'t exist yet
-- * The cache exists and 'inputData' changed
-- * any files in 'inputFiles' are older than 'cacheFile'.
}
data TimedCacheFiles =
TimedCacheFiles { tcCacheFile :: Maybe TimedFile,
tcFiles :: [TimedFile]
}
-- | Cache a MonadIO action with proper invalidation.
cached :: forall m a d. (MonadIO m, GmLog m, Serialize a, Eq d, Serialize d)
=> FilePath -- ^ Directory to prepend to 'cacheFile'
-> Cached m d a -- ^ Cache descriptor
-> d
-> m a
cached dir cd d = do
mcc <- readCache
tcfile <- liftIO $ timeMaybe (cacheFile cd)
let defTcf = TimedCacheFiles tcfile []
case mcc of
Nothing -> writeCache defTcf "cache missing"
Just (ifs, d', _) | d /= d' -> do
tcf <- timeCacheInput dir (cacheFile cd) ifs
writeCache tcf "input data changed"
Just (ifs, _, a) -> do
tcf <- timeCacheInput dir (cacheFile cd) ifs
let invifs = invalidatingInputFiles tcf
case invifs of
Nothing -> writeCache tcf "cache missing, existed a sec ago WTF?"
Just [] -> return a
Just _ -> writeCache tcf "input files changed"
where
writeCache tcf cause = do
(ifs', a) <- (cachedAction cd) tcf d
gmLog GmDebug "" $ (text "regenerating cache") <+>: text (cacheFile cd)
<+> parens (text cause)
liftIO $ BS.writeFile (dir </> cacheFile cd) $ encode (ifs', d, a)
return a
readCache :: m (Maybe ([FilePath], d, a))
readCache = runMaybeT $ do
f <- MaybeT $ liftIO $ mightExist $ cacheFile cd
MaybeT $ readCache' f
where
readCache' f = do
gmLog GmDebug "" $ (text "reading cache") <+>: text (cacheFile cd)
cc <- liftIO $ BS.readFile f
return $ either (const Nothing) Just $ decode cc
timeCacheInput :: MonadIO m => FilePath -> FilePath -> [FilePath] -> m TimedCacheFiles
timeCacheInput dir cfile ifs = liftIO $ do
-- TODO: is checking the times this way around race free?
ins <- (timeMaybe . (dir </>)) `mapM` ifs
mtcfile <- timeMaybe cfile
return $ TimedCacheFiles mtcfile (catMaybes ins)
invalidatingInputFiles :: TimedCacheFiles -> Maybe [FilePath]
invalidatingInputFiles tcf =
case tcCacheFile tcf of
Nothing -> Nothing
Just tcfile -> Just $ map tfPath $
-- get input files older than tcfile
filter (tcfile<) $ tcFiles tcf

View File

@ -129,7 +129,7 @@ data GmGhcSession = GmGhcSession {
data GhcModState = GhcModState { data GhcModState = GhcModState {
gmGhcSession :: !(Maybe GmGhcSession) gmGhcSession :: !(Maybe GmGhcSession)
, gmComponents :: !(Map ChComponentName (GmComponent (Set ModulePath))) , gmComponents :: !(Map ChComponentName (GmComponent GMCResolved (Set ModulePath)))
, gmCompilerMode :: !CompilerMode , gmCompilerMode :: !CompilerMode
} }

View File

@ -14,27 +14,26 @@
-- You should have received a copy of the GNU Affero General Public License -- You should have received a copy of the GNU Affero General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>. -- along with this program. If not, see <http://www.gnu.org/licenses/>.
module Language.Haskell.GhcMod.PathsAndFiles where module Language.Haskell.GhcMod.PathsAndFiles (
module Language.Haskell.GhcMod.PathsAndFiles
, module Language.Haskell.GhcMod.Caching
) where
import Config (cProjectVersion) import Config (cProjectVersion)
import Control.Applicative import Control.Applicative
import Control.Monad import Control.Monad
import Control.Monad.Trans.Maybe
import Data.List import Data.List
import Data.Char import Data.Char
import Data.Maybe import Data.Maybe
import Data.Version
import Data.Traversable (traverse) import Data.Traversable (traverse)
import Distribution.Helper
import System.Directory import System.Directory
import System.FilePath import System.FilePath
import System.IO.Unsafe import System.IO.Unsafe
import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Types
import Language.Haskell.GhcMod.Monad.Types
import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Error
import Language.Haskell.GhcMod.Read
import Language.Haskell.GhcMod.Utils hiding (dropWhileEnd) import Language.Haskell.GhcMod.Utils hiding (dropWhileEnd)
import Language.Haskell.GhcMod.Caching
import qualified Language.Haskell.GhcMod.Utils as U import qualified Language.Haskell.GhcMod.Utils as U
-- | Guaranteed to be a path to a directory with no trailing slash. -- | Guaranteed to be a path to a directory with no trailing slash.
@ -43,12 +42,6 @@ type DirPath = FilePath
-- | Guaranteed to be the name of a file only (no slashes). -- | Guaranteed to be the name of a file only (no slashes).
type FileName = String type FileName = String
data Cached d a = Cached {
inputFiles :: [FilePath],
inputData :: d,
cacheFile :: FilePath
}
newtype UnString = UnString { unString :: String } newtype UnString = UnString { unString :: String }
instance Show UnString where instance Show UnString where
@ -57,43 +50,6 @@ instance Show UnString where
instance Read UnString where instance Read UnString where
readsPrec _ = \str -> [(UnString str, "")] readsPrec _ = \str -> [(UnString str, "")]
-- |
--
-- >>> any (Just 3 <) [Just 1, Nothing, Just 2]
-- False
--
-- >>> any (Just 0 <) [Just 1, Nothing, Just 2]
-- True
--
-- >>> any (Just 0 <) [Nothing]
-- False
--
-- >>> any (Just 0 <) []
-- False
cached :: forall a d. (Read a, Show a, Eq d, Read d, Show d)
=> DirPath -> Cached d a -> IO a -> IO a
cached dir Cached {..} ma = do
ins <- (maybeTimeFile . (dir </>)) `mapM` inputFiles
c <- maybeTimeFile (dir </> cacheFile)
if any (c<) ins || isNothing c
then writeCache
else maybe ma return =<< readCache
where
maybeTimeFile :: FilePath -> IO (Maybe TimedFile)
maybeTimeFile f = traverse timeFile =<< mightExist f
writeCache = do
a <- ma
writeFile (dir </> cacheFile) $ unlines [show inputData, show a]
return a
readCache :: IO (Maybe a)
readCache = runMaybeT $ do
hdr:c:_ <- lines <$> liftIO (readFile $ dir </> cacheFile)
if inputData /= read hdr
then liftIO $ writeCache
else MaybeT $ return $ readMaybe c
-- | @findCabalFiles dir@. Searches for a @.cabal@ files in @dir@'s parent -- | @findCabalFiles dir@. Searches for a @.cabal@ files in @dir@'s parent
-- directories. The first parent directory containing more than one cabal file -- directories. The first parent directory containing more than one cabal file
-- is assumed to be the project directory. If only one cabal file exists in this -- is assumed to be the project directory. If only one cabal file exists in this
@ -235,13 +191,6 @@ cabalBuildPlatform = dropWhileEnd isSpace $ unsafePerformIO $
packageCache :: String packageCache :: String
packageCache = "package.cache" packageCache = "package.cache"
cabalHelperCache :: Version -> [String]
-> Cached (Version, [String]) [GmComponent ChEntrypoint]
cabalHelperCache cabalHelperVer cmds = Cached {
inputFiles = [setupConfigPath],
inputData = (cabalHelperVer, cmds),
cacheFile = setupConfigPath <.> "ghc-mod.cabal-helper"
}
-- | Filename of the symbol table cache file. -- | Filename of the symbol table cache file.
symbolCache :: Cradle -> FilePath symbolCache :: Cradle -> FilePath
@ -249,3 +198,9 @@ symbolCache crdl = cradleTempDir crdl </> symbolCacheFile
symbolCacheFile :: String symbolCacheFile :: String
symbolCacheFile = "ghc-mod.symbol-cache" symbolCacheFile = "ghc-mod.symbol-cache"
resolvedComponentsCacheFile :: String
resolvedComponentsCacheFile = setupConfigPath <.> "ghc-mod.resolved-components"
cabalHelperCacheFile :: String
cabalHelperCacheFile = setupConfigPath <.> "ghc-mod.cabal-helper"

View File

@ -32,6 +32,7 @@ import Language.Haskell.GhcMod.DynFlags
import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Monad.Types
import Language.Haskell.GhcMod.CabalHelper import Language.Haskell.GhcMod.CabalHelper
import Language.Haskell.GhcMod.HomeModuleGraph import Language.Haskell.GhcMod.HomeModuleGraph
import Language.Haskell.GhcMod.PathsAndFiles
import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.GhcPkg
import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Error
import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Logging
@ -154,7 +155,7 @@ runGmlTWith efnmns' mdf wrapper action = do
loadTargets (map moduleNameString mns ++ rfns) loadTargets (map moduleNameString mns ++ rfns)
action action
targetGhcOptions :: IOish m targetGhcOptions :: forall m. IOish m
=> Cradle => Cradle
-> Set (Either FilePath ModuleName) -> Set (Either FilePath ModuleName)
-> GhcModT m [GHCOption] -> GhcModT m [GHCOption]
@ -162,12 +163,15 @@ targetGhcOptions crdl sefnmn = do
when (Set.null sefnmn) $ error "targetGhcOptions: no targets given" when (Set.null sefnmn) $ error "targetGhcOptions: no targets given"
case cradleCabalFile crdl of case cradleCabalFile crdl of
Just _ -> cabalOpts Just _ -> cabalOpts crdl
Nothing -> sandboxOpts crdl Nothing -> sandboxOpts crdl
where where
zipMap f l = l `zip` (f `map` l) zipMap f l = l `zip` (f `map` l)
cabalOpts = do
mcs <- resolveGmComponents Nothing =<< getComponents cabalOpts :: Cradle -> GhcModT m [String]
cabalOpts Cradle{..} = do
comps <- mapM (resolveEntrypoint crdl) =<< getComponents
mcs <- cached cradleRootDir resolvedComponentsCache comps
let mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn let mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn
candidates = Set.unions $ map snd mdlcs candidates = Set.unions $ map snd mdlcs
@ -186,7 +190,36 @@ targetGhcOptions crdl sefnmn = do
let cn = pickComponent candidates let cn = pickComponent candidates
return $ gmcGhcOpts $ fromJust $ Map.lookup cn mcs return $ gmcGhcOpts $ fromJust $ Map.lookup cn mcs
moduleComponents :: Map ChComponentName (GmComponent (Set ModulePath)) resolvedComponentsCache :: IOish m => Cached (GhcModT m)
[GmComponent GMCRaw(Set.Set ModulePath)]
(Map.Map ChComponentName (GmComponent GMCResolved (Set.Set ModulePath)))
resolvedComponentsCache = Cached {
cacheFile = resolvedComponentsCacheFile,
cachedAction = \tcfs comps -> do
Cradle {..} <- cradle
let changedFiles =
filter (/= cradleRootDir </> setupConfigPath) $ map tfPath $ tcFiles tcfs
mums = if null changedFiles
then Nothing
else Just $ map Left changedFiles
mcs <- resolveGmComponents mums comps
return (setupConfigPath:flatten mcs , mcs)
}
where
flatten :: Map.Map ChComponentName (GmComponent t (Set.Set ModulePath))
-> [FilePath]
flatten = Map.elems
>>> map (gmcHomeModuleGraph >>> gmgGraph
>>> Map.elems
>>> map (Set.map mpPath)
>>> Set.unions
)
>>> Set.unions
>>> Set.toList
moduleComponents :: Map ChComponentName (GmComponent t (Set ModulePath))
-> Either FilePath ModuleName -> Either FilePath ModuleName
-> Set ChComponentName -> Set ChComponentName
moduleComponents m efnmn = moduleComponents m efnmn =
@ -216,52 +249,57 @@ packageGhcOptions = do
Nothing -> sandboxOpts crdl Nothing -> sandboxOpts crdl
sandboxOpts :: Monad m => Cradle -> m [String] sandboxOpts :: Monad m => Cradle -> m [String]
sandboxOpts crdl = return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts sandboxOpts crdl =
return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"]
where where
pkgOpts = ghcDbStackOpts $ cradlePkgDbStack crdl pkgOpts = ghcDbStackOpts $ cradlePkgDbStack crdl
(wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl) (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl)
resolveGmComponent :: (IOish m, GmLog m, GmEnv m) resolveGmComponent :: (IOish m, GmLog m, GmEnv m)
=> Maybe [Either FilePath ModuleName] -- ^ Updated modules => Maybe [Either FilePath ModuleName] -- ^ Updated modules
-> GmComponent ChEntrypoint -> GmComponent GMCRaw (Set ModulePath)
-> m (GmComponent (Set ModulePath)) -> m (GmComponent GMCResolved (Set ModulePath))
resolveGmComponent mums c@GmComponent {..} = resolveGmComponent mums c@GmComponent {..} = do
withLightHscEnv gmcGhcSrcOpts $ \env -> do withLightHscEnv gmcGhcSrcOpts $ \env -> do
let srcDirs = gmcSourceDirs let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs
mg = gmcHomeModuleGraph let mg = gmcHomeModuleGraph
let simp = gmcEntrypoints
Cradle { cradleRootDir } <- cradle sump <- case mums of
eps <- liftIO $ resolveChEntrypoints cradleRootDir gmcEntrypoints
simp <- liftIO $ resolveEntrypoints env srcDirs eps
sump <- liftIO $ case mums of
Nothing -> return simp Nothing -> return simp
Just ums -> resolveEntrypoints env srcDirs ums Just ums -> Set.fromList . catMaybes <$> mapM (resolveModule env srcDirs) ums
mg' <- updateHomeModuleGraph env mg simp sump mg' <- updateHomeModuleGraph env mg simp sump
return $ c { gmcEntrypoints = simp, gmcHomeModuleGraph = mg' } return $ c { gmcEntrypoints = simp, gmcHomeModuleGraph = mg' }
resolveEntrypoints :: MonadIO m resolveEntrypoint :: IOish m
=> HscEnv -> [FilePath] -> [Either FilePath ModuleName] -> m (Set ModulePath) => Cradle
resolveEntrypoints env srcDirs ms = -> GmComponent GMCRaw ChEntrypoint
liftIO $ Set.fromList . catMaybes <$> resolve `mapM` ms -> m (GmComponent GMCRaw (Set ModulePath))
resolveEntrypoint Cradle {..} c@GmComponent {..} =
withLightHscEnv gmcGhcSrcOpts $ \env -> do
let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs
eps <- liftIO $ resolveChEntrypoints cradleRootDir gmcEntrypoints
rms <- resolveModule env srcDirs `mapM` eps
return c { gmcEntrypoints = Set.fromList $ catMaybes rms }
resolveModule :: MonadIO m =>
HscEnv -> [FilePath] -> Either FilePath ModuleName -> m (Maybe ModulePath)
resolveModule env _srcDirs (Right mn) = liftIO $ findModulePath env mn
resolveModule env srcDirs (Left fn') = liftIO $ do
mfn <- findFile' srcDirs fn'
case mfn of
Nothing -> return Nothing
Just fn'' -> do
let fn = normalise fn''
emn <- fileModuleName env fn
return $ case emn of
Left _ -> Nothing
Right mmn -> Just $
case mmn of
Nothing -> mkMainModulePath fn
Just mn -> ModulePath mn fn
where where
resolve :: Either FilePath ModuleName -> IO (Maybe ModulePath)
resolve (Right mn) = findModulePath env mn
resolve (Left fn') = do
mfn <- findFile' srcDirs fn'
case mfn of
Nothing -> return Nothing
Just fn'' -> do
let fn = normalise fn''
emn <- fileModuleName env fn
return $ case emn of
Left _ -> Nothing
Right mmn -> Just $
case mmn of
Nothing -> mkMainModulePath fn
Just mn -> ModulePath mn fn
findFile' dirs file = findFile' dirs file =
mconcat <$> mapM (mightExist . (</>file)) dirs mconcat <$> mapM (mightExist . (</>file)) dirs
@ -284,11 +322,11 @@ resolveChEntrypoints srcDir ChSetupEntrypoint = do
chModToMod :: ChModuleName -> ModuleName chModToMod :: ChModuleName -> ModuleName
chModToMod (ChModuleName mn) = mkModuleName mn chModToMod (ChModuleName mn) = mkModuleName mn
resolveGmComponents :: (IOish m, GmState m, GmLog m, GmEnv m) resolveGmComponents :: (IOish m, GmState m, GmLog m, GmEnv m) =>
=> Maybe [Either FilePath ModuleName] Maybe [Either FilePath ModuleName]
-- ^ Updated modules -- ^ Updated modules
-> [GmComponent ChEntrypoint] -> [GmComponent GMCRaw (Set ModulePath)]
-> m (Map ChComponentName (GmComponent (Set ModulePath))) -> m (Map ChComponentName (GmComponent GMCResolved (Set ModulePath)))
resolveGmComponents mumns cs = do resolveGmComponents mumns cs = do
s <- gmsGet s <- gmsGet
m' <- foldrM' (gmComponents s) cs $ \c m -> do m' <- foldrM' (gmComponents s) cs $ \c m -> do
@ -307,11 +345,10 @@ resolveGmComponents mumns cs = do
return $ Map.insert (gmcName rc) rc m return $ Map.insert (gmcName rc) rc m
same :: Eq b same :: Eq b
=> (forall a. GmComponent a -> b) => (forall t a. GmComponent t a -> b)
-> GmComponent c -> GmComponent d -> Bool -> GmComponent u c -> GmComponent v d -> Bool
same f a b = (f a) == (f b) same f a b = (f a) == (f b)
-- | Set the files as targets and load them. -- | Set the files as targets and load them.
loadTargets :: IOish m => [String] -> GmlT m () loadTargets :: IOish m => [String] -> GmlT m ()
loadTargets filesOrModules = do loadTargets filesOrModules = do

View File

@ -1,4 +1,5 @@
{-# LANGUAGE DeriveDataTypeable, GADTs, StandaloneDeriving, DataKinds #-} {-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveGeneric, StandaloneDeriving,
DefaultSignatures #-}
{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-} {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-}
module Language.Haskell.GhcMod.Types ( module Language.Haskell.GhcMod.Types (
module Language.Haskell.GhcMod.Types module Language.Haskell.GhcMod.Types
@ -10,18 +11,24 @@ module Language.Haskell.GhcMod.Types (
import Control.Monad.Trans.Control (MonadBaseControl) import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Error (Error(..)) import Control.Monad.Error (Error(..))
import Control.Exception (Exception) import Control.Exception (Exception)
import Control.Applicative
import Control.Arrow
import Data.Serialize
import Data.Version
import Data.List (intercalate) import Data.List (intercalate)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
import Data.Monoid import Data.Monoid
import Data.Maybe
import Data.Typeable (Typeable) import Data.Typeable (Typeable)
import Distribution.Helper import Distribution.Helper
import Exception (ExceptionMonad) import Exception (ExceptionMonad)
import MonadUtils (MonadIO) import MonadUtils (MonadIO)
import GHC (ModuleName, moduleNameString, mkModuleName) import GHC (ModuleName, moduleNameString, mkModuleName)
import PackageConfig (PackageConfig) import PackageConfig (PackageConfig)
import GHC.Generics
-- | A constraint alias (-XConstraintKinds) to make functions dealing with -- | A constraint alias (-XConstraintKinds) to make functions dealing with
-- 'GhcModT' somewhat cleaner. -- 'GhcModT' somewhat cleaner.
@ -158,14 +165,42 @@ data GmModuleGraph = GmModuleGraph {
gmgFileMap :: Map FilePath ModulePath, gmgFileMap :: Map FilePath ModulePath,
gmgModuleMap :: Map ModuleName ModulePath, gmgModuleMap :: Map ModuleName ModulePath,
gmgGraph :: Map ModulePath (Set ModulePath) gmgGraph :: Map ModulePath (Set ModulePath)
} deriving (Eq, Ord, Show, Read, Typeable) } deriving (Eq, Ord, Show, Read, Generic, Typeable)
instance Serialize GmModuleGraph where
put GmModuleGraph {..} = let
mpim :: Map ModulePath Integer
graph :: Map Integer (Set Integer)
mpim = Map.fromList $
(Map.keys gmgGraph) `zip` [0..]
mpToInt :: ModulePath -> Integer
mpToInt mp = fromJust $ Map.lookup mp mpim
graph = Map.map (Set.map mpToInt) $ Map.mapKeys mpToInt gmgGraph
in put (mpim, graph)
get = do
(mpim :: Map ModulePath Integer, graph :: Map Integer (Set Integer)) <- get
let
swapMap = Map.fromList . map swap . Map.toList
swap (a,b) = (b,a)
impm = swapMap mpim
intToMp i = fromJust $ Map.lookup i impm
mpGraph :: Map ModulePath (Set ModulePath)
mpGraph = Map.map (Set.map intToMp) $ Map.mapKeys intToMp graph
mpFm = Map.fromList $ map (mpPath &&& id) $ Map.keys mpim
mpMn = Map.fromList $ map (mpModule &&& id) $ Map.keys mpim
return $ GmModuleGraph mpFm mpMn mpGraph
instance Monoid GmModuleGraph where instance Monoid GmModuleGraph where
mempty = GmModuleGraph mempty mempty mempty mempty = GmModuleGraph mempty mempty mempty
mappend (GmModuleGraph a b c) (GmModuleGraph a' b' c') = mappend (GmModuleGraph a b c) (GmModuleGraph a' b' c') =
GmModuleGraph (a <> a') (b <> b') (Map.unionWith Set.union c c') GmModuleGraph (a <> a') (b <> b') (Map.unionWith Set.union c c')
data GmComponent eps = GmComponent { data GmComponentType = GMCRaw
| GMCResolved
data GmComponent (t :: GmComponentType) eps = GmComponent {
gmcName :: ChComponentName, gmcName :: ChComponentName,
gmcGhcOpts :: [GHCOption], gmcGhcOpts :: [GHCOption],
gmcGhcSrcOpts :: [GHCOption], gmcGhcSrcOpts :: [GHCOption],
@ -173,10 +208,17 @@ data GmComponent eps = GmComponent {
gmcEntrypoints :: eps, gmcEntrypoints :: eps,
gmcSourceDirs :: [FilePath], gmcSourceDirs :: [FilePath],
gmcHomeModuleGraph :: GmModuleGraph gmcHomeModuleGraph :: GmModuleGraph
} deriving (Eq, Ord, Show, Read, Typeable) } deriving (Eq, Ord, Show, Read, Generic, Typeable, Functor)
instance Serialize eps => Serialize (GmComponent t eps)
data ModulePath = ModulePath { mpModule :: ModuleName, mpPath :: FilePath } data ModulePath = ModulePath { mpModule :: ModuleName, mpPath :: FilePath }
deriving (Eq, Ord, Show, Read, Typeable) deriving (Eq, Ord, Show, Read, Generic, Typeable)
instance Serialize ModulePath
instance Serialize ModuleName where
get = mkModuleName <$> get
put mn = put (moduleNameString mn)
instance Show ModuleName where instance Show ModuleName where
show mn = "ModuleName " ++ show (moduleNameString mn) show mn = "ModuleName " ++ show (moduleNameString mn)
@ -235,3 +277,12 @@ data GMConfigStateFileError
| GMConfigStateFileMissing | GMConfigStateFileMissing
-- | GMConfigStateFileBadVersion PackageIdentifier PackageIdentifier (Either ConfigStateFileError LocalBuildInfo) -- | GMConfigStateFileBadVersion PackageIdentifier PackageIdentifier (Either ConfigStateFileError LocalBuildInfo)
deriving (Eq, Show, Read, Typeable) deriving (Eq, Show, Read, Typeable)
deriving instance Generic Version
instance Serialize Version
instance Serialize Programs
instance Serialize ChModuleName
instance Serialize ChComponentName
instance Serialize ChEntrypoint

View File

@ -1,7 +1,6 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
module Utils where module Utils where
import Control.Monad
import Control.Applicative import Control.Applicative
import Data.Traversable import Data.Traversable
import System.Directory import System.Directory
@ -18,7 +17,8 @@ type ModTime = UTCTime
type ModTime = ClockTime type ModTime = ClockTime
#endif #endif
data TimedFile = TimedFile FilePath ModTime deriving (Eq, Show) data TimedFile = TimedFile { tfPath :: FilePath, tfTime :: ModTime }
deriving (Eq, Show)
instance Ord TimedFile where instance Ord TimedFile where
compare (TimedFile _ a) (TimedFile _ b) = compare a b compare (TimedFile _ a) (TimedFile _ b) = compare a b
@ -32,5 +32,4 @@ mightExist f = do
return $ if exists then (Just f) else (Nothing) return $ if exists then (Just f) else (Nothing)
timeMaybe :: FilePath -> IO (Maybe TimedFile) timeMaybe :: FilePath -> IO (Maybe TimedFile)
timeMaybe f = do timeMaybe f = traverse timeFile =<< mightExist f
join $ (timeFile `traverse`) <$> mightExist f