hsdev-0.1.4.0: src/HsDev/Tools/Ghc/Worker.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
module HsDev.Tools.Ghc.Worker (
-- * Workers
ghcWorker, ghciWorker,
-- * Initializers and actions
withFlags, modifyFlags, addCmdOpts,
importModules, preludeModules,
evaluate,
clearTargets, makeTarget, loadTargets,
-- * Utils
withCurrentDirectory,
Ghc,
module Control.Concurrent.Worker
) where
import Control.Monad
import Control.Monad.Catch
import Control.Monad.Except
import Data.Dynamic
import Data.Time.Clock (getCurrentTime)
import GHC
import GHC.Paths
import Packages
import StringBuffer
import System.Directory (getCurrentDirectory, setCurrentDirectory)
import Control.Concurrent.Worker
-- | Ghc worker. Pass options and initializer action
ghcWorker :: [String] -> Ghc () -> IO (Worker Ghc)
ghcWorker opts initialize = startWorker (runGhc (Just libdir)) ghcInit id where
ghcInit f = do
fs <- getSessionDynFlags
defaultCleanupHandler fs $ do
(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
let fs'' = fs' {
ghcMode = CompManager,
ghcLink = LinkInMemory,
hscTarget = HscInterpreted }
_ <- setSessionDynFlags fs''
_ <- liftIO $ initPackages fs''
initialize
f
-- | Interpreter worker is worker with @preludeModules@ loaded
ghciWorker :: IO (Worker Ghc)
ghciWorker = ghcWorker [] (importModules preludeModules)
-- | Alter @DynFlags@ temporary
withFlags :: Ghc a -> Ghc a
withFlags = gbracket getSessionDynFlags (\fs -> setSessionDynFlags fs >> return ()) . const
-- | Update @DynFlags@
modifyFlags :: (DynFlags -> DynFlags) -> Ghc ()
modifyFlags f = do
fs <- getSessionDynFlags
let
fs' = f fs
_ <- setSessionDynFlags fs'
_ <- liftIO $ initPackages fs'
return ()
addCmdOpts :: [String] -> Ghc DynFlags
addCmdOpts opts = do
fs <- getSessionDynFlags
(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
_ <- setSessionDynFlags fs'
(fs'', _) <- liftIO $ initPackages fs'
return fs''
-- | Import some modules
importModules :: [String] -> Ghc ()
importModules mods = mapM parseImportDecl ["import " ++ m | m <- mods] >>= setContext . map IIDecl
-- | Default interpreter modules
preludeModules :: [String]
preludeModules = ["Prelude", "Data.List", "Control.Monad", "HsDev.Tools.Ghc.Prelude"]
-- | Evaluate expression
evaluate :: String -> Ghc String
evaluate expr = liftM fromDynamic (dynCompileExpr $ "show (" ++ expr ++ ")") >>=
maybe (fail "evaluate fail") return
-- | Clear loaded targets
clearTargets :: Ghc ()
clearTargets = loadTargets []
-- | Make target with its source code optional
makeTarget :: String -> Maybe String -> Ghc Target
makeTarget name Nothing = guessTarget name Nothing
makeTarget name (Just cts) = do
t <- guessTarget name Nothing
tm <- liftIO getCurrentTime
return t { targetContents = Just (stringToStringBuffer cts, tm) }
-- | Load all targets
loadTargets :: [Target] -> Ghc ()
loadTargets ts = setTargets ts >> load LoadAllTargets >> return ()
-- | Set current directory and restore it after action
withCurrentDirectory :: FilePath -> Ghc a -> Ghc a
withCurrentDirectory dir act = gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory) $
const (liftIO (setCurrentDirectory dir) >> act)
-- TODO: Load target by @ModuleLocation@, which may cause updating @DynFlags@
instance MonadThrow Ghc where
throwM = liftIO . throwM
instance MonadCatch Ghc where
catch = gcatch