happstack-util-0.3.1: src/Happstack/Util/AutoBuild.hs
-------------------------------------------------------------------------------
-- |
-- Module : Happstack.Util.AutoBuild
-- Copyright : Happstack.com 2009
-- License : BSD3
--
-- Maintainer : Matthew Elder
-- Stability : provisional
-- Portability : linux/windows
--
-------------------------------------------------------------------------------
module Happstack.Util.AutoBuild (
autoBuild
) where
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
import Control.Exception (bracket)
import System.Directory (getModificationTime)
import System.Exit (ExitCode(..), exitFailure)
import System.Process
import System.Time (ClockTime)
import System.IO
-- | Functionality for the autoBuild tool.
-- Inspired by searchpath.
autoBuild :: String -- ^ Build command
-> String -- ^ Path to binary
-> [String] -- ^ Arguments to use when running binary
-> IO ()
autoBuild buildCmd binPath binArgs = do
putStrLn "Performing the initial build. . ."
buildSuccess <- buildBin buildCmd -- initial build
if buildSuccess
then do
mph <- newEmptyMVar
newMod <- getModificationTime binPath
forkIO (builder mph buildCmd binPath newMod)
runner mph binPath binArgs
else do
putStrLn "Initial build failed, see 'build.out.log' and 'build.err.log'."
exitFailure
-- builds the program
builder :: MVar ProcessHandle -> String -> FilePath -> ClockTime -> IO ()
builder mph buildCmd binPath lastMod = do
-- add a delay between build attempts (5 seconds)
threadDelay 5000000
buildSuccess <- buildBin buildCmd
newMod <- getModificationTime binPath
-- if the build yielded a new binary, terminate the process
if buildSuccess && (newMod /= lastMod)
then do
putStrLn "A new binary has been built, killing the existing one. . ."
terminateProcess =<< takeMVar mph
else return ()
-- continue loop
builder mph buildCmd binPath newMod
-- runs the program
runner :: MVar ProcessHandle -> FilePath -> [String] -> IO ()
runner mph binPath binArgs = do
bracket
(runBin binPath binArgs)
(terminateProcess)
(\ph -> putMVar mph ph >> waitForProcess ph)
-- continue loop
runner mph binPath binArgs
-- does not block, returns ph
runBin :: String -> [String] -> IO ProcessHandle
runBin binPath binArgs = do
putStrLn $ "Running binary: " ++ (showCmd binPath binArgs)
ph <- runProcess binPath binArgs Nothing Nothing Nothing Nothing Nothing
return ph
where showCmd bp [] = bp
showCmd bp ba = bp ++ " " ++ unwords ba
-- blocks until built, returns True if build was a success
buildBin :: String -> IO Bool
buildBin buildCmd = do
(_inp,out,err,ph) <- runInteractiveCommand buildCmd
appendFile "build.out.log" =<< hGetContents out
appendFile "build.err.log" =<< hGetContents err
waitForProcess ph
exitCode <- getProcessExitCode ph
return (exitCode == Just ExitSuccess)