packages feed

halive-0.1.2: exec/HaliveMain.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif

import Banner
import System.Environment
import Control.Exception
import Control.Monad
import Control.Concurrent
import Data.IORef
import Control.Concurrent.STM
import Halive.SubHalive
import Halive.Recompiler
import System.FilePath


separateArgs :: [String] -> ([String], [String])
separateArgs args = (haliveArgs, drop 1 targetArgs)
    where (haliveArgs, targetArgs) = break (== "--") args

main :: IO ()
main = do
    (args, targetArgs) <- separateArgs <$> getArgs
    case args of
        [] -> putStrLn "Usage: halive <main.hs> <include dir> [-- <args to myapp>]"
        (mainFileName:includeDirs) -> do
            let mainFilePath = dropFileName mainFileName
            setEnv "Halive Active" "Yes"
            putStrLn banner
            withArgs targetArgs $ startRecompiler mainFileName (mainFilePath:includeDirs)

printBanner :: String -> IO ()
printBanner title = putStrLn $ ribbon ++ " " ++ title ++ " " ++ ribbon
    where ribbon = replicate 25 '*'

startRecompiler :: FilePath -> [FilePath] -> IO b
startRecompiler mainFileName includeDirs = do
    ghc <- startGHC
        (defaultGHCSessionConfig
            { gscImportPaths = includeDirs
            -- , gscCompilationMode = Compiled
            , gscCompilationMode = Interpreted
            })

    let fileTypes = ["hs", "pd", "frag", "vert"]

    recompiler <- recompilerWithConfig ghc RecompilerConfig
        { rccWatchAll = Just (".", fileTypes)
        , rccExpression = "main"
        , rccFilePath = mainFileName
        , rccCompileImmediately = True
        }

    mainThreadId <- myThreadId

    newCodeTChan <- newTChanIO
    isMainRunning <- newIORef False
    _ <- forkIO $ forever $ do
        result <- atomically $ readTChan (recResultTChan recompiler)
        case result of
            Left errors -> do
                printBanner "Compilation Errors, Waiting...     "
                putStrLn (concat errors)
            Right newCode -> do
                printBanner "Compilation Success, Relaunching..."
                atomically $ writeTChan newCodeTChan newCode
                mainIsRunning <- readIORef isMainRunning
                when mainIsRunning $ killThread mainThreadId

    forever $ do
        newCode <- atomically $ readTChan newCodeTChan
        case getCompiledValue newCode of
            Just (mainFunc :: IO ()) -> do
                writeIORef isMainRunning True
                mainFunc `catch` (\x ->
                    putStrLn ("App killed: " ++ show (x :: SomeException)))
                writeIORef isMainRunning False
            Nothing -> do
                putStrLn "main was not of type IO ()"