2015-02-07 22:55:57 +00:00
|
|
|
{-# LANGUAGE CPP, RecordWildCards #-}
|
2015-03-03 19:28:34 +00:00
|
|
|
-- ghc-mod: Making Haskell development *more* fun
|
|
|
|
-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>
|
|
|
|
--
|
|
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
|
|
-- it under the terms of the GNU Affero General Public License as published by
|
|
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
|
|
-- (at your option) any later version.
|
|
|
|
--
|
|
|
|
-- This program is distributed in the hope that it will be useful,
|
|
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
-- GNU Affero General Public License for more details.
|
|
|
|
--
|
|
|
|
-- 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/>.
|
2014-05-03 14:08:28 +00:00
|
|
|
module Language.Haskell.GhcMod.Monad (
|
2014-07-17 05:30:42 +00:00
|
|
|
-- * Monad Types
|
2014-07-22 17:45:48 +00:00
|
|
|
GhcModT
|
2014-07-17 05:30:42 +00:00
|
|
|
, IOish
|
2014-07-18 05:13:59 +00:00
|
|
|
-- ** Environment, state and logging
|
2014-07-17 05:30:42 +00:00
|
|
|
, GhcModEnv(..)
|
2014-07-18 05:13:59 +00:00
|
|
|
, newGhcModEnv
|
2014-07-22 17:45:48 +00:00
|
|
|
, GhcModState(..)
|
2014-07-18 05:29:50 +00:00
|
|
|
, defaultState
|
2014-07-22 17:45:48 +00:00
|
|
|
, CompilerMode(..)
|
|
|
|
, GhcModLog
|
2014-08-06 18:40:11 +00:00
|
|
|
, GhcModError(..)
|
2014-07-17 05:30:42 +00:00
|
|
|
-- * Monad utilities
|
|
|
|
, runGhcModT
|
|
|
|
, runGhcModT'
|
2014-08-28 09:54:01 +00:00
|
|
|
, hoistGhcModT
|
2015-02-07 22:55:57 +00:00
|
|
|
-- ** Accessing 'GhcModEnv', 'GhcModState' and 'GhcModLog'
|
2014-08-12 16:22:28 +00:00
|
|
|
, gmsGet
|
|
|
|
, gmsPut
|
2015-02-07 22:55:57 +00:00
|
|
|
, gmLog
|
2014-07-17 05:30:42 +00:00
|
|
|
, options
|
|
|
|
, cradle
|
2014-07-22 17:45:48 +00:00
|
|
|
, getCompilerMode
|
|
|
|
, setCompilerMode
|
2014-07-18 06:31:42 +00:00
|
|
|
, withOptions
|
2014-08-13 17:25:27 +00:00
|
|
|
, withTempSession
|
|
|
|
-- ** Re-exporting convenient stuff
|
|
|
|
, liftIO
|
2014-07-17 05:30:42 +00:00
|
|
|
, module Control.Monad.Reader.Class
|
|
|
|
) where
|
2014-05-03 14:08:28 +00:00
|
|
|
|
2014-07-03 05:22:43 +00:00
|
|
|
import Language.Haskell.GhcMod.Types
|
2015-02-07 22:55:57 +00:00
|
|
|
import Language.Haskell.GhcMod.Monad.Types
|
|
|
|
import Language.Haskell.GhcMod.Logging
|
2014-08-28 09:54:01 +00:00
|
|
|
import Language.Haskell.GhcMod.Error
|
2014-07-12 01:30:06 +00:00
|
|
|
import Language.Haskell.GhcMod.Cradle
|
|
|
|
import Language.Haskell.GhcMod.DynFlags
|
|
|
|
import Language.Haskell.GhcMod.GhcPkg
|
|
|
|
import Language.Haskell.GhcMod.CabalApi
|
2015-02-07 22:55:57 +00:00
|
|
|
import Language.Haskell.GhcMod.CabalConfig
|
2014-07-12 01:30:06 +00:00
|
|
|
import qualified Language.Haskell.GhcMod.Gap as Gap
|
2014-05-03 14:08:28 +00:00
|
|
|
|
|
|
|
import GHC
|
2014-07-12 01:30:06 +00:00
|
|
|
import qualified GHC as G
|
2014-05-03 14:08:28 +00:00
|
|
|
import GHC.Paths (libdir)
|
2014-08-13 17:25:27 +00:00
|
|
|
import GhcMonad hiding (withTempSession)
|
2014-07-03 05:19:36 +00:00
|
|
|
#if __GLASGOW_HASKELL__ <= 702
|
2014-05-08 11:11:29 +00:00
|
|
|
import HscTypes
|
|
|
|
#endif
|
2014-05-09 18:38:35 +00:00
|
|
|
|
2014-07-03 05:26:39 +00:00
|
|
|
-- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.
|
|
|
|
-- RWST does not automatically become an instance of MonadIO.
|
|
|
|
-- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.
|
|
|
|
-- So, RWST automatically becomes an instance of MonadIO.
|
|
|
|
import MonadUtils
|
|
|
|
|
2014-07-22 17:45:48 +00:00
|
|
|
import Control.Arrow (first)
|
2015-02-07 22:55:57 +00:00
|
|
|
import Control.Monad (void)
|
2014-12-22 16:14:58 +00:00
|
|
|
#if !MIN_VERSION_monad_control(1,0,0)
|
|
|
|
import Control.Monad (liftM)
|
|
|
|
#endif
|
2015-02-07 22:55:57 +00:00
|
|
|
import Control.Monad.Base (liftBase)
|
2014-07-11 02:51:11 +00:00
|
|
|
|
2014-07-22 17:45:48 +00:00
|
|
|
import Control.Monad.Reader.Class
|
2014-08-12 16:09:31 +00:00
|
|
|
import Control.Monad.State.Class (MonadState(..))
|
2014-07-22 17:45:48 +00:00
|
|
|
|
2015-02-07 22:55:57 +00:00
|
|
|
import Control.Monad.Error (runErrorT)
|
|
|
|
import Control.Monad.Reader (runReaderT)
|
|
|
|
import Control.Monad.State.Strict (runStateT)
|
|
|
|
import Control.Monad.Trans.Journal (runJournalT)
|
2014-05-03 14:08:28 +00:00
|
|
|
|
2014-11-02 23:30:53 +00:00
|
|
|
import Data.Maybe (isJust)
|
2015-02-07 22:55:57 +00:00
|
|
|
import Data.IORef
|
2014-05-18 01:32:09 +00:00
|
|
|
import System.Directory (getCurrentDirectory)
|
2014-05-10 13:10:34 +00:00
|
|
|
|
2014-07-03 05:22:43 +00:00
|
|
|
----------------------------------------------------------------
|
2014-05-10 13:10:34 +00:00
|
|
|
|
2014-07-12 01:30:06 +00:00
|
|
|
-- | Initialize the 'DynFlags' relating to the compilation of a single
|
|
|
|
-- file or GHC session according to the 'Cradle' and 'Options'
|
|
|
|
-- provided.
|
2015-02-07 22:55:57 +00:00
|
|
|
initializeFlagsWithCradle :: (IOish m, GhcMonad m, GmError m, GmLog m)
|
2014-07-12 01:30:06 +00:00
|
|
|
=> Options
|
|
|
|
-> Cradle
|
2015-02-07 22:55:57 +00:00
|
|
|
-> CabalConfig
|
2014-07-12 01:30:06 +00:00
|
|
|
-> m ()
|
2015-02-07 22:55:57 +00:00
|
|
|
initializeFlagsWithCradle opt c config
|
2014-08-11 22:02:39 +00:00
|
|
|
| cabal = withCabal
|
2014-07-12 01:30:06 +00:00
|
|
|
| otherwise = withSandbox
|
|
|
|
where
|
2014-09-22 02:20:11 +00:00
|
|
|
mCabalFile = cradleCabalFile c
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2014-09-22 02:20:11 +00:00
|
|
|
cabal = isJust mCabalFile
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2014-08-13 16:40:01 +00:00
|
|
|
ghcopts = ghcUserOptions opt
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2014-07-12 01:30:06 +00:00
|
|
|
withCabal = do
|
2014-11-02 23:30:53 +00:00
|
|
|
let Just cabalFile = mCabalFile
|
2015-02-07 22:55:57 +00:00
|
|
|
pkgDesc <- parseCabalFile config cabalFile
|
|
|
|
compOpts <- getCompilerOptions ghcopts c config pkgDesc
|
2014-07-12 01:30:06 +00:00
|
|
|
initSession CabalPkg opt compOpts
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2014-07-12 01:30:06 +00:00
|
|
|
withSandbox = initSession SingleFile opt compOpts
|
|
|
|
where
|
|
|
|
importDirs = [".","..","../..","../../..","../../../..","../../../../.."]
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2014-07-12 01:30:06 +00:00
|
|
|
pkgOpts = ghcDbStackOpts $ cradlePkgDbStack c
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2014-07-12 01:30:06 +00:00
|
|
|
compOpts
|
|
|
|
| null pkgOpts = CompilerOptions ghcopts importDirs []
|
|
|
|
| otherwise = CompilerOptions (ghcopts ++ pkgOpts) [wdir,rdir] []
|
2015-01-16 14:47:56 +00:00
|
|
|
|
|
|
|
(wdir, rdir) = (cradleCurrentDir c, cradleRootDir c)
|
2014-07-12 01:30:06 +00:00
|
|
|
|
|
|
|
initSession :: GhcMonad m
|
|
|
|
=> Build
|
|
|
|
-> Options
|
|
|
|
-> CompilerOptions
|
|
|
|
-> m ()
|
|
|
|
initSession build Options {..} CompilerOptions {..} = do
|
|
|
|
df <- G.getSessionDynFlags
|
2014-07-17 08:16:44 +00:00
|
|
|
void $ G.setSessionDynFlags =<< addCmdOpts ghcOptions
|
|
|
|
( setModeSimple
|
2014-07-18 02:09:11 +00:00
|
|
|
$ Gap.setFlags
|
2014-07-12 01:30:06 +00:00
|
|
|
$ setIncludeDirs includeDirs
|
|
|
|
$ setBuildEnv build
|
|
|
|
$ setEmptyLogger
|
|
|
|
$ Gap.addPackageFlags depPackages df)
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2014-05-18 01:32:09 +00:00
|
|
|
newGhcModEnv :: Options -> FilePath -> IO GhcModEnv
|
|
|
|
newGhcModEnv opt dir = do
|
|
|
|
session <- newIORef (error "empty session")
|
2014-07-11 01:10:37 +00:00
|
|
|
c <- findCradle' dir
|
2014-05-18 01:32:09 +00:00
|
|
|
return GhcModEnv {
|
|
|
|
gmGhcSession = session
|
|
|
|
, gmOptions = opt
|
2014-07-11 01:10:37 +00:00
|
|
|
, gmCradle = c
|
2014-05-18 01:32:09 +00:00
|
|
|
}
|
2014-05-10 13:10:34 +00:00
|
|
|
|
2014-10-14 17:52:58 +00:00
|
|
|
cleanupGhcModEnv :: GhcModEnv -> IO ()
|
|
|
|
cleanupGhcModEnv env = cleanupCradle $ gmCradle env
|
|
|
|
|
2014-07-22 17:45:48 +00:00
|
|
|
-- | Run a @GhcModT m@ computation.
|
|
|
|
runGhcModT :: IOish m
|
|
|
|
=> Options
|
|
|
|
-> GhcModT m a
|
|
|
|
-> m (Either GhcModError a, GhcModLog)
|
2014-12-17 16:52:50 +00:00
|
|
|
runGhcModT opt action = gbracket newEnv delEnv $ \env -> do
|
2014-10-14 17:52:58 +00:00
|
|
|
r <- first (fst <$>) <$> (runGhcModT' env defaultState $ do
|
2014-05-10 13:10:34 +00:00
|
|
|
dflags <- getSessionDynFlags
|
|
|
|
defaultCleanupHandler dflags $ do
|
2015-02-07 22:55:57 +00:00
|
|
|
config <- cabalGetConfig =<< cradle
|
|
|
|
initializeFlagsWithCradle opt (gmCradle env) config
|
|
|
|
action )
|
2014-10-14 17:52:58 +00:00
|
|
|
return r
|
2014-07-14 22:51:22 +00:00
|
|
|
|
2014-12-17 16:52:50 +00:00
|
|
|
where
|
|
|
|
newEnv = liftBase $ newGhcModEnv opt =<< getCurrentDirectory
|
|
|
|
delEnv = liftBase . cleanupGhcModEnv
|
|
|
|
|
2014-08-28 09:54:01 +00:00
|
|
|
-- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT
|
|
|
|
-- computation. Note that if the computation that returned @result@ modified the
|
|
|
|
-- state part of GhcModT this cannot be restored.
|
|
|
|
hoistGhcModT :: IOish m
|
|
|
|
=> (Either GhcModError a, GhcModLog)
|
|
|
|
-> GhcModT m a
|
|
|
|
hoistGhcModT (r,l) = do
|
2015-02-07 22:55:57 +00:00
|
|
|
gmJournal l >> case r of
|
2014-08-28 09:54:01 +00:00
|
|
|
Left e -> throwError e
|
|
|
|
Right a -> return a
|
|
|
|
|
2014-07-14 22:51:22 +00:00
|
|
|
-- | Run a computation inside @GhcModT@ providing the RWST environment and
|
|
|
|
-- initial state. This is a low level function, use it only if you know what to
|
|
|
|
-- do with 'GhcModEnv' and 'GhcModState'.
|
|
|
|
--
|
|
|
|
-- You should probably look at 'runGhcModT' instead.
|
|
|
|
runGhcModT' :: IOish m
|
|
|
|
=> GhcModEnv
|
|
|
|
-> GhcModState
|
|
|
|
-> GhcModT m a
|
2014-07-22 17:45:48 +00:00
|
|
|
-> m (Either GhcModError (a, GhcModState), GhcModLog)
|
2014-07-14 22:51:22 +00:00
|
|
|
runGhcModT' r s a = do
|
2014-07-22 17:45:48 +00:00
|
|
|
(res, w') <-
|
2014-08-14 02:11:02 +00:00
|
|
|
flip runReaderT r $ runJournalT $ runErrorT $
|
|
|
|
runStateT (unGhcModT $ initGhcMonad (Just libdir) >> a) s
|
|
|
|
return (res, w')
|
2014-07-03 05:22:43 +00:00
|
|
|
----------------------------------------------------------------
|
2014-08-13 17:25:27 +00:00
|
|
|
-- | Make a copy of the 'gmGhcSession' IORef, run the action and restore the
|
|
|
|
-- original 'HscEnv'.
|
|
|
|
withTempSession :: IOish m => GhcModT m a -> GhcModT m a
|
|
|
|
withTempSession action = do
|
|
|
|
session <- gmGhcSession <$> ask
|
|
|
|
savedHscEnv <- liftIO $ readIORef session
|
|
|
|
a <- action
|
|
|
|
liftIO $ writeIORef session savedHscEnv
|
|
|
|
return a
|
|
|
|
|
2014-07-03 05:22:43 +00:00
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2014-08-28 09:54:01 +00:00
|
|
|
gmeAsk :: IOish m => GhcModT m GhcModEnv
|
|
|
|
gmeAsk = ask
|
|
|
|
|
2014-08-12 16:09:31 +00:00
|
|
|
gmsGet :: IOish m => GhcModT m GhcModState
|
|
|
|
gmsGet = GhcModT get
|
|
|
|
|
|
|
|
gmsPut :: IOish m => GhcModState -> GhcModT m ()
|
|
|
|
gmsPut = GhcModT . put
|
|
|
|
|
2014-07-12 09:16:16 +00:00
|
|
|
options :: IOish m => GhcModT m Options
|
2014-08-28 09:54:01 +00:00
|
|
|
options = gmOptions <$> gmeAsk
|
2014-05-10 11:51:35 +00:00
|
|
|
|
2014-07-12 09:16:16 +00:00
|
|
|
cradle :: IOish m => GhcModT m Cradle
|
2014-08-28 09:54:01 +00:00
|
|
|
cradle = gmCradle <$> gmeAsk
|
2014-07-11 01:10:37 +00:00
|
|
|
|
2014-08-12 16:09:31 +00:00
|
|
|
getCompilerMode :: IOish m => GhcModT m CompilerMode
|
|
|
|
getCompilerMode = gmCompilerMode <$> gmsGet
|
2014-07-18 05:29:50 +00:00
|
|
|
|
2014-08-12 16:09:31 +00:00
|
|
|
setCompilerMode :: IOish m => CompilerMode -> GhcModT m ()
|
|
|
|
setCompilerMode mode = (\s -> gmsPut s { gmCompilerMode = mode } ) =<< gmsGet
|
2014-07-18 05:29:50 +00:00
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2014-07-18 06:31:42 +00:00
|
|
|
withOptions :: IOish m => (Options -> Options) -> GhcModT m a -> GhcModT m a
|
|
|
|
withOptions changeOpt action = local changeEnv action
|
|
|
|
where
|
|
|
|
changeEnv e = e { gmOptions = changeOpt opt }
|
|
|
|
where
|
|
|
|
opt = gmOptions e
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|