{-# LANGUAGE CPP #-}
module Language.Haskell.GhcMod.Monad (
runGmOutT
, runGmOutT'
, runGhcModT
, runGhcModT'
, hoistGhcModT
, runGmlT
, runGmlT'
, runGmlTWith
, runGmPkgGhc
, withGhcModEnv
, withGhcModEnv'
, module Language.Haskell.GhcMod.Monad.Types
) where
import Language.Haskell.GhcMod.Types
import Language.Haskell.GhcMod.Monad.Types
import Language.Haskell.GhcMod.Error
import Language.Haskell.GhcMod.Logging
import Language.Haskell.GhcMod.Cradle
import Language.Haskell.GhcMod.Target
import Language.Haskell.GhcMod.Output
import Control.Arrow (first)
import Control.Applicative
import Control.Concurrent
import Control.Monad.Reader (runReaderT)
import Control.Monad.State.Strict (runStateT)
import Control.Monad.Trans.Journal (runJournalT)
import Exception
import System.Directory
import System.IO.Unsafe
import Prelude
withGhcModEnv :: (IOish m, GmOut m) => FilePath -> Options -> ((GhcModEnv, GhcModLog) -> m a) -> m a
withGhcModEnv dir opts f = withGhcModEnv' withCradle dir opts f
where
withCradle dir' =
gbracket
(runJournalT $ do
gmSetLogLevel $ ooptLogLevel $ optOutput opts
findCradle' (optPrograms opts) dir')
(liftIO . cleanupCradle . fst)
cwdLock :: MVar ThreadId
cwdLock = unsafePerformIO $ newEmptyMVar
{-# NOINLINE cwdLock #-}
withGhcModEnv' :: (IOish m, GmOut m) => (FilePath -> ((Cradle, GhcModLog) -> m a) -> m a) -> FilePath -> Options -> ((GhcModEnv, GhcModLog) -> m a) -> m a
withGhcModEnv' withCradle dir opts f =
withCradle dir $ \(crdl,lg) ->
withCradleRootDir crdl $
f (GhcModEnv opts crdl, lg)
where
swapCurrentDirectory ndir = do
odir <- canonicalizePath =<< getCurrentDirectory
setCurrentDirectory ndir
return odir
withCradleRootDir (cradleRootDir -> projdir) a = do
success <- liftIO $ tryPutMVar cwdLock =<< myThreadId
if not success
then error "withGhcModEnv': using ghc-mod from multiple threads is not supported!"
else gbracket setup teardown (const a)
where
setup = liftIO $ swapCurrentDirectory projdir
teardown odir = liftIO $ do
setCurrentDirectory odir
void $ takeMVar cwdLock
runGmOutT :: IOish m => Options -> GmOutT m a -> m a
runGmOutT opts ma = do
gmo@GhcModOut{..} <- GhcModOut (optOutput opts) <$> liftIO newChan
let action = runGmOutT' gmo ma
case ooptLinePrefix $ optOutput opts of
Nothing -> action
Just pfxs ->
gbracket_ (liftIO $ forkIO $ stdoutGateway pfxs gmoChan)
(const $ liftIO $ flushStdoutGateway gmoChan)
action
runGmOutT' :: GhcModOut -> GmOutT m a -> m a
runGmOutT' gmo ma = flip runReaderT gmo $ unGmOutT ma
runGhcModT :: IOish m
=> Options
-> GhcModT m a
-> m (Either GhcModError a, GhcModLog)
runGhcModT opt action = liftIO (getCurrentDirectory >>= canonicalizePath) >>= \dir' -> do
runGmOutT opt $
withGhcModEnv dir' opt $ \(env,lg) ->
first (fst <$>) <$> runGhcModT' env defaultGhcModState (do
gmSetLogLevel (ooptLogLevel $ optOutput opt)
gmAppendLogQuiet lg
action)
hoistGhcModT :: IOish m
=> (Either GhcModError a, GhcModLog)
-> GhcModT m a
hoistGhcModT (r,l) = do
gmlJournal l >> case r of
Left e -> throwError e
Right a -> return a
runGhcModT' :: IOish m
=> GhcModEnv
-> GhcModState
-> GhcModT m a
-> GmOutT m (Either GhcModError (a, GhcModState), GhcModLog)
runGhcModT' r s a = do
flip runReaderT r $ runJournalT $ runErrorT $ runStateT (unGmT a) s
gbracket_ :: ExceptionMonad m => m a -> (a -> m b) -> m c -> m c
gbracket_ ma mb mc = gbracket ma mb (const mc)