AutoForms-0.4.0: src/Examples/GhciGui/GhcProcess.hs
{- This module was inspired by:
http://haskell.org/sitewiki/images/5/51/Interactive.hs and
http://haskell.org/haskellwiki/GHC/As_a_library
-}
module GhcProcess where
import qualified Protocol as P
import qualified Options as Opt
import qualified GHC as GHC
import qualified Outputable as GHC
import qualified PackageConfig as GHC
import qualified DynFlags as GHC
import qualified Panic as GHC
import qualified Distribution.InstalledPackageInfo as GHC
import qualified UniqFM as GHC
import Control.Concurrent
import Control.Concurrent.Chan
import Network
import System.IO
import System
import Monad (when)
import System.Time
-- import qualified Packages as GHC
-- import qualified ErrUtils as GHC
-- Should'n we do: main = defaultErrorHandler defaultDynFlags $ do
main :: IO()
main = do clearLog
withSocketsDo startSession
`catch` (\e -> do myLog (show e)
return ()
)
startSession :: IO ()
startSession =
do request <- getArgs
(ghcPath, outPort, inPort) <- readIO $ head request
--
session <- initializeSession ghcPath
logPkgDB session
--
myLog ("Read port numbers: " ++ show (outPort, inPort))
inHandle <- connectTo "localhost" (PortNumber $ fromInteger inPort)
myLog ("Connected to inPort")
outHandle <- connectTo "localhost" (PortNumber $ fromInteger outPort)
myLog ("Connected to outPort")
P.setIsExecuting outHandle False
f <- hGetContents inHandle
let reply :: (Show s) => s -> IO()
reply s = do hPutStrLn inHandle $ show s
hFlush inHandle
insertIO <- channeledIO outHandle
myLog ("Ready to recieve commands")
mapM_ (execCommand session insertIO reply) $ lines f
return ()
myLog :: String -> IO ()
myLog msg = appendFile "log" (msg ++ "\n")
clearLog :: IO ()
clearLog = do t <- getClockTime
writeFile "log" ("Initiating log at " ++ (show t) ++ "\n")
putError :: String -> IO ()
putError msg = hPutStr stderr msg
putMsg :: String -> IO ()
putMsg msg = putStr msg
execCommand :: GHC.Session
-> (IO () -> IO t)
-> (Opt.Options -> IO ())
-> String
-> IO ()
execCommand session insertIO reply cmd =
do cmd' <- readIO cmd
case cmd' of
P.Exec stmt -> do myLog $ show stmt ++ " ... "
insertIO $ evalStmt session stmt
myLog $ "finished.\n"
P.Kill -> do threads <- readMVar GHC.interruptTargetThread
hPutStrLn stderr ("Debug output. Threads: " ++ show threads)
killThread $ head threads
P.GetOptions -> do dynFlags <- GHC.getSessionDynFlags session
reply $ Opt.ghcflagsToOptions dynFlags
P.SetOptions userFlags ->
do dynFlags <- GHC.getSessionDynFlags session
userFlags' <- readIO userFlags
GHC.setSessionDynFlags session $ Opt.setOptions userFlags' dynFlags
return ()
evalStmt :: GHC.Session -> String -> IO ()
evalStmt session stmt | take 4 stmt == ":set"
= do dynFlags <- GHC.getSessionDynFlags session
(newFlags, unknownArgs) <- GHC.parseDynamicFlags dynFlags (words $ drop 4 stmt)
GHC.setSessionDynFlags session newFlags
hPutStrLn stderr $ "Unrecognized flags: " ++ unlines unknownArgs
hFlush stderr
| otherwise = execStmt session stmt
execStmt :: GHC.Session -> String -> IO ()
execStmt session stmt =
do result <- GHC.runStmt session stmt GHC.RunToCompletion
GHC.runStmt session "hFlush stdout >> hFlush stderr" GHC.RunToCompletion
case result of
GHC.RunOk names -> putMsg ("*** Ok: " ++ showNames names ++ "\n")
GHC.RunFailed -> putError "*** Failed\n"
GHC.RunException e -> putError ("*** Exception: " ++ (show e) ++ "\n")
hFlush stdout >> hFlush stderr
channeledIO :: Handle -> IO (IO t -> IO ())
channeledIO outHandle =
do ioChan <- newChan
let readAndExec = do isEmpty <- isEmptyChan ioChan
when isEmpty (P.setIsExecuting outHandle False)
ioAction <- readChan ioChan
P.setIsExecuting outHandle True
ioAction
readAndExec
insertIO io' = do writeChan ioChan io'
forkIO $ readAndExec
return insertIO
-- If GHC.runStmt returns GHC.RunOk names then names is a list
-- of the names of all variables that were bound during evaluation.
-- This function somehow manages to pretty-print such a list.
showNames :: [GHC.Name] -> String
showNames = GHC.showSDoc . GHC.ppr
logPkgDB :: GHC.Session -> IO ()
logPkgDB session =
do flags <- GHC.getSessionDynFlags session
let pkgName pkg = "Package: " ++ (GHC.pkgName $ GHC.package pkg) ++ "\n" ++
"Copyright: " ++ GHC.copyright pkg ++ "\n" ++
"Haddock HTML: " ++ (show $ GHC.haddockHTMLs pkg)
handleUniqFM uniqFM = do myLog $ "Number of elements" ++ show (length $ GHC.ufmToList uniqFM)
mapM_ (myLog . pkgName) $ GHC.eltsUFM uniqFM
maybe (myLog "Pkg DB: Nothing") handleUniqFM (GHC.pkgDatabase flags)
initializeSession :: String -> IO GHC.Session
initializeSession ghcPath =
do session <- GHC.newSession (Just ghcPath)
dynFlags <- GHC.getSessionDynFlags session
GHC.setSessionDynFlags session dynFlags{GHC.hscTarget=GHC.HscInterpreted}
prelude <- GHC.findModule session (GHC.mkModuleName "Prelude") Nothing
systemIO <- GHC.findModule session (GHC.mkModuleName "System.IO") Nothing
GHC.setContext session [] [ prelude, systemIO ]
return session
{- GHC.log_action logs error messages from compilation failure:
let myLogAction _ locSpec style errMsg = hPutStrLn stderr ("Checkpoint charlie: " ++ showMsg) where
showMsg = GHC.showSDoc $ GHC.withPprStyle style $ GHC.mkLocMessage locSpec errMsg
dflags1 = dflags0 { GHC.log_action = myLogAction }
-}
{-
-- Loading modules:
-- 1) For example loading MyPrelude.hs
target <- GHC.guessTarget "MyPrelude.hs" Nothing
GHC.addTarget session target
-- this would unload the standard prelude if it had already been loaded
GHC.load session GHC.LoadAllTargets
-- 2) We need to load the standard prelude (I think), because of
-- "GHC.load seesion..." unloaded the prelude.
let preludeModule = GHC.mkModule (GHC.stringToPackageId "base") (GHC.mkModuleName "Prelude")
systemIOModule <- GHC.findModule session (GHC.mkModuleName "System.IO") Nothing
GHC.setContext session [] [preludeModule, systemIOModule]
-}