stack-0.1.3.0: src/Stack/Ghci.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
-- | Run a GHCi configured with the user's project(s).
module Stack.Ghci where
import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Monad.Logger
import Control.Monad.Reader
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.List
import qualified Data.Map.Strict as M
import Data.Maybe
import Data.Monoid
import Data.Set (Set)
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
import Data.Typeable
import Distribution.ModuleName (ModuleName)
import Distribution.Text (display)
import Network.HTTP.Client.Conduit
import Path
import Path.IO
import Stack.Build.Source
import Stack.Constants
import Stack.Exec
import Stack.Package
import Stack.Types
data GhciOpts = GhciOpts
{ghciTargets :: [Text]
,ghciArgs :: [String]
,ghciGhcCommand :: FilePath
,ghciNoLoadModules :: Bool
,ghciAdditionalPackages :: [String]
} deriving (Show,Eq)
-- | Launch a GHCi session for the given local project targets with the
-- given options and configure it with the load paths and extensions
-- of those targets.
ghci :: (HasConfig r, HasBuildConfig r, HasHttpManager r, HasEnvConfig r, MonadReader r m, MonadIO m, MonadThrow m, MonadLogger m, MonadCatch m, MonadBaseControl IO m)
=> GhciOpts
-> m ()
ghci GhciOpts{..} = do
pkgs <- ghciSetup ghciTargets
config <- asks getBuildConfig
let pkgopts = concatMap ghciPkgOpts pkgs
srcfiles
| ghciNoLoadModules = []
| otherwise = concatMap (map display . S.toList . ghciPkgModules) pkgs
odir = ["-odir=" <> toFilePath (objectInterfaceDir config)
,"-hidir=" <> toFilePath (objectInterfaceDir config)]
$logInfo
("Configuring GHCi with the following packages: " <>
T.intercalate ", " (map (packageNameText . ghciPkgName) pkgs))
exec
defaultEnvSettings
ghciGhcCommand
("--interactive" : odir <> pkgopts <> srcfiles <> ghciArgs)
data GhciPkgInfo = GhciPkgInfo
{ ghciPkgName :: PackageName
, ghciPkgOpts :: [String]
, ghciPkgDir :: Path Abs Dir
, ghciPkgModules :: Set ModuleName
}
ghciSetup
:: (HasConfig r, HasHttpManager r, HasBuildConfig r, HasEnvConfig r, MonadReader r m, MonadIO m, MonadThrow m, MonadLogger m, MonadCatch m, MonadBaseControl IO m)
=> [Text] -> m [GhciPkgInfo]
ghciSetup targets = do
econfig <- asks getEnvConfig
bconfig <- asks getBuildConfig
pwd <- getWorkingDir
(_,_,_,sourceMap) <- loadSourceMap defaultBuildOpts
locals <-
liftM catMaybes $
forM (M.toList (envConfigPackages econfig)) $
\(dir,validWanted) ->
do cabalfp <- getCabalFileName dir
name <- parsePackageNameFromFilePath cabalfp
if validWanted && wanted pwd cabalfp name
then return (Just (name, cabalfp))
else return Nothing
let findTarget x = find ((x ==) . packageNameText . fst) locals
unmetTargets = filter (isNothing . findTarget) targets
when (not (null unmetTargets)) $ throwM (TargetsNotFound unmetTargets)
forM locals $
\(name,cabalfp) ->
do let config =
PackageConfig
{ packageConfigEnableTests = True
, packageConfigEnableBenchmarks = True
, packageConfigFlags = localFlags mempty bconfig name
, packageConfigGhcVersion = envConfigGhcVersion econfig
, packageConfigPlatform = configPlatform
(getConfig bconfig)
}
pkg <- readPackage config cabalfp
pkgOpts <-
getPackageOpts (packageOpts pkg) sourceMap (map fst locals) cabalfp
modules <- getPackageModules (packageModules pkg) cabalfp
return
GhciPkgInfo
{ ghciPkgName = packageName pkg
, ghciPkgOpts = filter (not . badForGhci) pkgOpts
, ghciPkgDir = parent cabalfp
, ghciPkgModules = modules
}
where
wanted pwd cabalfp name = isInWantedList || targetsEmptyAndInDir
where
isInWantedList = elem (packageNameText name) targets
targetsEmptyAndInDir = null targets || isParentOf (parent cabalfp) pwd
badForGhci :: String -> Bool
badForGhci x =
isPrefixOf "-O" x || elem x (words "-debug -threaded -ticky")
data GhciSetupException =
TargetsNotFound [Text]
deriving Typeable
instance Exception GhciSetupException
instance Show GhciSetupException where
show (TargetsNotFound targets) = unlines
[ "Couldn't find targets: " ++ T.unpack (T.unwords targets)
, "(expecting package names)"
]