court-0.1.0.0: src/Court/Builder.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Court.Builder
( builder
, buildNext
) where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.List
import Data.Maybe
import Data.Time
import System.Directory
import System.FilePath
import System.IO
import System.Locale
import System.Process
import Court.Job
import Court.Options
import Court.Queue
import Court.Result
import Court.Utils
builder :: Options -> TVar Queue -> IO ()
builder opts queueTVar = builder' []
where
builder' :: [MVar ()] -> IO ()
builder' mvars = do
mJob <- takeNextJob queueTVar
mvars' <- case mJob of
Nothing -> do
threadDelay $ 1000 * 1000
return mvars
Just job -> builder'' job mvars
builder' mvars'
builder'' :: Job -> [MVar ()] -> IO [MVar ()]
builder'' job mvars = do
mvars' <- cleanMVars mvars
if length mvars' >= optThreads opts
then do
threadDelay $ 5 * 1000 * 1000
builder'' job mvars'
else do
mvar' <- newEmptyMVar
_ <- forkIO $ buildNext opts job mvar'
return $ mvar' : mvars'
cleanMVars :: [MVar ()] -> IO [MVar ()]
cleanMVars mvars = do
mMVars <- forM mvars $ \mvar -> do
mres <- tryTakeMVar mvar
return $ maybe (Just mvar) (const Nothing) mres
return $ catMaybes mMVars
buildNext :: Options -> Job -> MVar () -> IO ()
buildNext opts job mvar = handle errorHandler $ do
hPutStrLn stderr $ "Building " ++ show job ++ " ..."
(buildPath, outputPath, processHandle) <- spawnBuild job
exitCode <- waitForProcess processHandle
output <- readFile outputPath
let result = Result
{ resultExitCode = exitCode
, resultOutput = output
, resultPath = buildPath
}
modifyGlobalResults opts $ changeResults result
modifyLocalResults job $ changeResults result
cleanBuilds 20 $ jobProjectPath job
putMVar mvar ()
where
changeResults :: Result -> Results -> Results
changeResults result (Results results) = Results $ take 20 $ result : results
errorHandler :: SomeException -> IO ()
errorHandler e = do
hPutStrLn stderr $ "ERROR: " ++ show e
putMVar mvar ()
spawnBuild :: Job -> IO (FilePath, FilePath, ProcessHandle)
spawnBuild job = do
now <- getCurrentTime
let buildDir = "build." ++ formatTime defaultTimeLocale "%Y%m%d%H%M%S" now
executablePath = jobProjectPath job </> "build"
buildPath = jobProjectPath job </> buildDir
outputPath = buildPath </> "build.out"
createDirectory buildPath
(inR, inW) <- createPipeHandles
hClose inW
stdout' <- openFile outputPath ReadWriteMode
processHandle <- runProcess
executablePath (jobArguments job) (Just buildPath) Nothing
(Just inR) (Just stdout') Nothing
return (buildPath, outputPath, processHandle)
cleanBuilds :: Int -> FilePath -> IO ()
cleanBuilds n path = do
items <- getDirectoryContents path
mapM_ (removeDirectoryRecursive . (path </>))
. drop n
. sortBy (\a b -> b `compare` a)
. filter ("build." `isPrefixOf`)
$ items