module HSBencher.Methods
(makeMethod, ghcMethod, cabalMethod,
)
where
import Control.Monad
import Control.Monad.Reader
import Control.Exception (bracket)
import qualified Data.ByteString.Char8 as B
import qualified Data.Map as M
import System.Process
import System.Directory
import System.FilePath
import Text.Printf
import Prelude hiding (log)
import HSBencher.Types
import HSBencher.Logging (log)
import HSBencher.MeasureProcess
import HSBencher.Utils (runLogged)
makeMethod :: BuildMethod
makeMethod = BuildMethod
{ methodName = "make"
, canBuild = (IsExactly "Makefile")
`PredOr`
InDirectoryWithExactlyOne (IsExactly "Makefile")
, concurrentBuild = False
, setThreads = Nothing
, clean = \ pathMap _ target -> do
doMake pathMap target $ \ makePath -> do
_ <- runSuccessful subtag (makePath++" clean")
return ()
, compile = \ pathMap bldid flags target -> do
doMake pathMap target $ \ makePath -> do
absolute <- liftIO getCurrentDirectory
_ <- runSuccessful subtag (makePath++" COMPILE_ARGS='"++ unwords flags ++"'")
log$ tag++"Done building with Make, assuming this benchmark needs to run in-place..."
let runit args =
CommandDescr
{ command = ShellCommand (makePath++" run RUN_ARGS='"++ unwords args ++"'")
, timeout = Just 150
, workingDir = Just absolute
, envVars = []
}
return (RunInPlace runit)
}
where
tag = " [makeMethod] "
subtag = " [make] "
doMake pathMap target action = do
isdir <- liftIO$ doesDirectoryExist target
let dir = if isdir then target
else takeDirectory target
makePath = M.findWithDefault "make" "make" pathMap
inDirectory dir (action makePath)
ghcMethod :: BuildMethod
ghcMethod = BuildMethod
{ methodName = "ghc"
, canBuild = WithExtension ".hs"
, concurrentBuild = True
, setThreads = Just $ \ n -> [ CompileParam "-threaded -rtsopts"
, RuntimeParam ("+RTS -N"++ show n++" -RTS")]
, clean = \ pathMap bldid target -> do
let buildD = "buildoutput_" ++ bldid
liftIO$ do b <- doesDirectoryExist buildD
when b$ removeDirectoryRecursive buildD
return ()
, compile = \ pathMap bldid flags target -> do
let dir = takeDirectory target
file = takeBaseName target
suffix = "_"++bldid
ghcPath = M.findWithDefault "ghc" "ghc" pathMap
log$ tag++" Building target with GHC method: "++show target
inDirectory dir $ do
let buildD = "buildoutput_" ++ bldid
liftIO$ createDirectoryIfMissing True buildD
let dest = buildD </> file ++ suffix
runSuccessful " [ghc] " $
printf "%s %s -outputdir ./%s -o %s %s"
ghcPath file buildD dest (unwords flags)
return (StandAloneBinary$ dir </> dest)
}
where
tag = " [ghcMethod] "
cabalMethod :: BuildMethod
cabalMethod = BuildMethod
{ methodName = "cabal"
, canBuild = dotcab `PredOr`
InDirectoryWithExactlyOne dotcab
, concurrentBuild = True
, setThreads = Just $ \ n -> [ CompileParam "--ghc-option='-threaded' --ghc-option='-rtsopts'"
, RuntimeParam ("+RTS -N"++ show n++" -RTS")]
, clean = \ pathMap _ target -> do
return ()
, compile = \ pathMap bldid flags target -> do
let suffix = "_"++bldid
cabalPath = M.findWithDefault "cabal" "cabal" pathMap
ghcPath = M.findWithDefault "ghc" "ghc" pathMap
dir <- liftIO$ getDir target
inDirectory dir $ do
log$ tag++" Switched to "++dir++", clearing binary target dir... "
_ <- runSuccessful tag "rm -rf ./bin/*"
let extra_args = "--bindir=./bin/ ./ --program-suffix="++suffix
extra_args' = if ghcPath /= "ghc"
then extra_args
else extra_args
let cmd = cabalPath++" install "++ extra_args' ++" "++unwords flags
log$ tag++"Running cabal command: "++cmd
_ <- runSuccessful " [cabal] " cmd
ls <- liftIO$ filesInDir "./bin/"
case ls of
[] -> error$"No binaries were produced from building cabal file! In: "++show dir
[f] -> return (StandAloneBinary$ dir </> "bin" </> f)
_ -> error$"Multiple binaries were produced from building cabal file!:"
++show ls ++" In: "++show dir
}
where
dotcab = WithExtension ".cabal"
tag = " [cabalMethod] "
getDir :: FilePath -> IO FilePath
getDir path = do
b <- doesDirectoryExist path
b2 <- doesFileExist path
if b
then return path
else if b2
then return (takeDirectory path)
else error$ "getDir: benchmark target path does not exist at all: "++path
inDirectory :: (MonadIO m) => FilePath -> m a -> m a
inDirectory dir act = do
orig <- liftIO$ getCurrentDirectory
liftIO$ setCurrentDirectory dir
x <- act
liftIO$ setCurrentDirectory orig
return x
filesInDir :: FilePath -> IO [FilePath]
filesInDir d = do
inDirectory d $ do
ls <- getDirectoryContents "."
filterM doesFileExist ls
runSuccessful :: String -> String -> BenchM [B.ByteString]
runSuccessful tag cmd = do
(res,lines) <- runLogged tag cmd
case res of
ExitError code -> error$ "expected this command to succeed! But it exited with code "++show code++ ":\n "++ cmd
TimeOut {} -> error "Methods.hs/runSuccessful - internal error!"
RunCompleted {} -> return lines