leksah-server-0.8.0.1: src/IDE/Utils/GHCUtils.hs
{-# OPTIONS_GHC -XCPP -fno-warn-orphans #-}
-----------------------------------------------------------------------------
--
-- Module : IDE.Utils.GHCUtils
-- Copyright : 2007-2010 Juergen Nicklisch-Franken, Hamish Mackenzie
-- License : GPL
--
-- Maintainer : Jutaro <jutaro@leksah.org>
-- Stability : provisional
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------
module IDE.Utils.GHCUtils (
inGhcIO
, getInstalledPackageInfos
, findFittingPackages
, myParseModule
, myParseHeader
, findAndReadIface2
) where
#if MIN_VERSION_Cabal(1,8,0)
#else
import UniqFM (eltsUFM)
#endif
import Distribution.Simple (withinRange,PackageIdentifier(..),Dependency(..))
#if MIN_VERSION_Cabal(1,8,0)
import qualified Distribution.InstalledPackageInfo as IPI (sourcePackageId)
#else
import qualified Distribution.InstalledPackageInfo as IPI (package)
#endif
import GHC hiding (SuccessFlag, Failed, Succeeded)
import DriverPipeline(preprocess)
import StringBuffer (StringBuffer(..),hGetStringBuffer)
import FastString (mkFastString)
import Lexer (mkPState,ParseResult(..),getMessages,unP)
import Outputable (showSDoc, text, ppr)
import ErrUtils
(Message, dumpIfSet_dyn, printErrorsAndWarnings, mkPlainErrMsg,
showPass, ErrMsg(..))
import PackageConfig (PackageConfig)
import Data.Foldable (maximumBy)
import qualified Parser as P (parseModule,parseHeader)
import HscStats (ppSourceStats)
import HscTypes
(mi_module, FindResult(..), ModIface(..), HscEnv(..))
import IDE.Utils.FileUtils (getSysLibDir)
import DynFlags (dopt_set)
import System.Log.Logger(warningM, debugM)
import qualified GHC as Module (ModuleName)
import TcRnMonad (TcRnIf)
import Finder (findHomeModule)
import System.Directory (canonicalizePath)
import LoadIface (readIface)
import Maybes (MaybeErr(..), MaybeErr)
import MonadUtils (MonadIO(..))
---- this should not be repeated here, why is it necessary?
--instance MonadIO Ghc where
-- liftIO ioA = Ghc $ \_ -> ioA
inGhcIO :: [String] -> [DynFlag] -> (DynFlags -> Ghc a) -> IO a
inGhcIO flags' udynFlags ghcAct = do
debugM "leksah-server" $ "inGhcIO called with: " ++ show flags'
libDir <- getSysLibDir
-- (restFlags, _) <- parseStaticFlags (map noLoc flags')
runGhc (Just libDir) $ do
dynflags <- getSessionDynFlags
let dynflags' = foldl (\ flags'' flag' -> dopt_set flags'' flag') dynflags udynFlags
let dynflags'' = dynflags' {
hscTarget = HscNothing,
ghcMode = CompManager,
ghcLink = NoLink
}
dynflags''' <- parseGhcFlags dynflags'' (map noLoc flags') flags'
res <- defaultCleanupHandler dynflags''' $ do
setSessionDynFlags dynflags'''
ghcAct dynflags'''
unload
return res
where
parseGhcFlags :: DynFlags -> [Located String]
-> [String] -> Ghc DynFlags
parseGhcFlags dynflags flags_ _origFlags = do
(dynflags', rest, _) <- parseDynamicFlags dynflags flags_
if not (null rest)
then do
liftIO $ warningM "leksah-server" ("No dynamic GHC options: " ++ (unwords (map unLoc rest)))
return dynflags'
else return dynflags'
-- | Unload whatever is currently loaded.
unload :: Ghc ()
unload = do
setTargets []
load LoadAllTargets
return ()
getInstalledPackageInfos :: Ghc [PackageConfig]
getInstalledPackageInfos = do
dflags1 <- getSessionDynFlags
setSessionDynFlags dflags1{flags = Opt_ReadUserPackageConf : (flags dflags1)}
pkgInfos <- case pkgDatabase dflags1 of
Nothing -> return []
#if MIN_VERSION_Cabal(1,8,0)
Just fm -> return fm
#else
Just fm -> return (eltsUFM fm)
#endif
return pkgInfos
findFittingPackages :: [Dependency] -> Ghc [PackageIdentifier]
findFittingPackages dependencyList = do
knownPackages <- getInstalledPackageInfos
#if MIN_VERSION_Cabal(1,8,0)
let packages = map IPI.sourcePackageId knownPackages
#else
let packages = map IPI.package knownPackages
#endif
return (concatMap (fittingKnown packages) dependencyList)
where
fittingKnown packages (Dependency dname versionRange) =
let filtered = filter (\ (PackageIdentifier name version) ->
name == dname && withinRange version versionRange)
packages
in if length filtered > 1
then [maximumBy (\a b -> compare (pkgVersion a) (pkgVersion b)) filtered]
else filtered
---------------------------------------------------------------------
-- | Reading an interface file
findAndReadIface2 :: HscEnv -> Module.ModuleName -> Module -> TcRnIf gbl lcl (MaybeErr Message (ModIface, FilePath))
findAndReadIface2 session modName = do
mb_found <- liftIO $ findHomeModule session modName
case mb_found of
Found loc mod' -> do
let file_path = ml_hi_file loc
-- filePath <- liftIO $ canonicalizePath file_path
read_result <- readIface mod' file_path False
case read_result of
Failed mess -> return (Failed (text $ "can't read iface " ++
moduleNameString modName ++ " at " ++ file_path ++ " " ++ showSDoc mess))
Succeeded iface
| mi_module iface /= mod'
-> return (Failed (text $ "read but not equal" ++ moduleNameString modName))
| otherwise
-> return (Succeeded (iface, file_path))
_ -> return (Failed (text $ "can't locate " ++ moduleNameString modName))
---------------------------------------------------------------------
-- | Parser function copied here, because it is not exported
myParseModule :: DynFlags -> FilePath -> Maybe StringBuffer
-> IO (Either ErrMsg (Located (HsModule RdrName)))
myParseModule dflags src_filename maybe_src_buf
= -------------------------- Parser ----------------
showPass dflags "Parser" >>
{-# SCC "Parser" #-} do
-- sometimes we already have the buffer in memory, perhaps
-- because we needed to parse the imports out of it, or get the
-- module name.
buf' <- case maybe_src_buf of
Just b -> return b
Nothing -> hGetStringBuffer src_filename
let loc = mkSrcLoc (mkFastString src_filename) 1 0
case unP P.parseModule (mkPState buf' loc dflags) of {
PFailed span' err -> return (Left (mkPlainErrMsg span' err));
POk pst rdr_module -> do {
let {ms = getMessages pst};
printErrorsAndWarnings dflags ms;
-- when (errorsFound dflags ms) $ exitWith (ExitFailure 1);
dumpIfSet_dyn dflags Opt_D_dump_parsed "Parser" (ppr rdr_module) ;
dumpIfSet_dyn dflags Opt_D_source_stats "Source Statistics"
(ppSourceStats False rdr_module) ;
return (Right rdr_module)
-- ToDo: free the string buffer later.
}}
myParseHeader :: FilePath -> String -> [String] -> IO (Either String (HsModule RdrName))
myParseHeader fp _str opts = inGhcIO opts [Opt_Cpp] $ \ _dynFlags -> do
session <- getSession
(dynFlags',fp') <- preprocess session (fp,Nothing)
liftIO $ do
stringBuffer <- hGetStringBuffer fp'
parseResult <- myParseModuleHeader dynFlags' fp (Just stringBuffer)
case parseResult of
Right (L _ mod') -> return (Right mod')
Left errMsg -> do
let str = "Failed to parse " ++ show errMsg
return (Left str)
---------------------------------------------------------------------
-- | Parser function copied here, because it is not exported
myParseModuleHeader :: DynFlags -> FilePath -> Maybe StringBuffer
-> IO (Either ErrMsg (Located (HsModule RdrName)))
myParseModuleHeader dflags src_filename maybe_src_buf
= -------------------------- Parser ----------------
showPass dflags "Parser" >>
{-# SCC "Parser" #-} do
-- sometimes we already have the buffer in memory, perhaps
-- because we needed to parse the imports out of it, or get the
-- module name.
buf' <- case maybe_src_buf of
Just b -> return b
Nothing -> hGetStringBuffer src_filename
let loc = mkSrcLoc (mkFastString src_filename) 1 0
case unP P.parseHeader (mkPState buf' loc dflags) of {
PFailed span' err -> return (Left (mkPlainErrMsg span' err));
POk pst rdr_module -> do {
let {ms = getMessages pst};
printErrorsAndWarnings dflags ms;
-- when (errorsFound dflags ms) $ exitWith (ExitFailure 1);
dumpIfSet_dyn dflags Opt_D_dump_parsed "Parser" (ppr rdr_module) ;
dumpIfSet_dyn dflags Opt_D_source_stats "Source Statistics"
(ppSourceStats False rdr_module) ;
return (Right rdr_module)
-- ToDo: free the string buffer later.
}}