{-| Module : Devel.Compile Description : For building and running your WAI application. Copyright : (c) 2015 Njagi Mwaniki License : MIT Maintainer : njagi@urbanslug.com Stability : experimental Portability : POSIX Does the actual building of your code. Most of the work wai-devel does is coordinated from here. -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} module Devel.Build ( build ) where import IdeSession import qualified Data.ByteString.Char8 as S8 import Data.Text (unpack) import GHC.Conc (newTVarIO) import Control.Concurrent (forkIO, killThread, ThreadId) -- Rebuild import Control.Monad (unless) # if __GLASGOW_HASKELL__ < 710 import Data.Monoid (mempty) #endif import Devel.Compile import Devel.ReverseProxy (startReverseProxy) import Devel.Watch import Data.IORef -- | Compiles and calls run on your WAI application. build :: FilePath -> String -> [String] -> Bool -> SessionConfig -> (Int, Int) -> Maybe IdeSession -> Bool -> IORef [String] -> IO () build buildFile runFunction watchDirectories isReverseProxy sessionConfig (fromProxyPort, toProxyPort) mSession isRebuild iStrLst = do (initialSession, extensionList, includeTargets, additionalWatch) <- initCompile watchDirectories sessionConfig mSession -- Do this if isRebuild is False. unless isRebuild $ if isReverseProxy then do _ <- forkIO $ startReverseProxy (fromProxyPort, toProxyPort) iStrLst putStrLn $ "Starting devel application at http://localhost:" ++ show fromProxyPort else putStrLn $ "Starting app without reverse proxying at http://localhost:" ++ show fromProxyPort (updatedSession, update) <- if isRebuild then return (initialSession, mempty) else compile initialSession buildFile extensionList includeTargets eitherSession <- finishCompile (updatedSession, update) iStrLst let -- clearLog :: IORef [String] -> IO () clearLog = do _ <- readIORef iStrLst writeIORef iStrLst ([] :: [String]) case eitherSession of Left _ -> do -- Listen for changes in the current working directory. isDirty <- newTVarIO False _ <- forkIO $ watch isDirty includeTargets additionalWatch -- Block until relevant change is made then carry on with program execution. _ <- checkForChange isDirty -- Stop the current app. putStrLn "\n\nRebuilding...\n\n" _ <- shutdownSession updatedSession _ <- clearLog -- Coming to fix. build buildFile runFunction watchDirectories False sessionConfig (fromProxyPort, toProxyPort) Nothing False iStrLst Right session -> do -- run the session (runActionsRunResult, threadId) <- run session buildFile runFunction -- Start watching for file changes. isDirty <- newTVarIO False -- Watch for changes in the current working directory. watchId <- forkIO $ watch isDirty includeTargets additionalWatch -- Block until relevant change is made then carry on with program execution. _ <- checkForChange isDirty killThread watchId _ <- clearLog -- Stop the current app. _ <- stopApp runActionsRunResult threadId putStrLn "\n\nRebuilding...\n\n" build buildFile runFunction watchDirectories isReverseProxy sessionConfig (fromProxyPort, toProxyPort) (Just session) True iStrLst run :: IdeSession -> FilePath -> String -> IO (RunActions RunResult, ThreadId) run session buildFile runFunction = do -- Get the module name from the file path mapFunction <- getFileMap session buildModule <- case mapFunction buildFile of Nothing -> fail $ "The file's module name for: " ++ show buildFile ++" couldn't be found" Just moduleId -> return $ unpack $ moduleName moduleId -- Run the given ide-backend session. runActionsRunResult <- runStmt session buildModule runFunction threadId <- forkIO $ loop runActionsRunResult return (runActionsRunResult, threadId) -- | Stop the currently running WAI application. stopApp :: RunActions RunResult -> ThreadId -> IO () stopApp runResult threadId = do interrupt runResult killThread threadId -- | Run for as long as we need to. loop :: RunActions RunResult -> IO () loop res = do runAction <- runWait res case runAction of Left bs -> S8.putStr bs >> loop res Right result -> putStrLn $ "Run result: " ++ show result