{-# LANGUAGE RecordWildCards, NamedFieldPuns, ScopedTypeVariables, ConstraintKinds #-}
module Development.Shake.Internal.Core.Action(
runAction, actionOnException, actionFinally,
getShakeOptions, getProgress, runAfter,
trackUse, trackChange, trackAllow, trackCheckUsed,
getVerbosity, putWhen, putLoud, putNormal, putQuiet, withVerbosity, quietly,
blockApply, unsafeAllowApply,
traced
) where
import Control.Exception
import Control.Applicative
import Control.Monad.Extra
import Control.Monad.IO.Class
import Control.DeepSeq
import Data.Typeable.Extra
import Data.Function
import Data.Either.Extra
import Data.Maybe
import Data.IORef
import Data.List
import System.IO.Extra
import Development.Shake.Internal.Core.Database
import Development.Shake.Internal.Core.Monad
import Development.Shake.Internal.Core.Types
import Development.Shake.Internal.Value
import Development.Shake.Internal.Options
import Development.Shake.Internal.Errors
import General.Cleanup
import Prelude
runAction :: Global -> Local -> Action a -> Capture (Either SomeException a)
runAction g l (Action x) = runRAW g l x
actionBracket :: (Local -> (Local, Local -> Local)) -> Action a -> Action a
actionBracket f m = Action $ do
s <- getRW
let (s2,undo) = f s
putRW s2
res <- fromAction m
modifyRW undo
return res
actionBoom :: Bool -> Action a -> IO b -> Action a
actionBoom runOnSuccess act clean = do
Global{..} <- Action getRO
undo <- liftIO $ addCleanup globalCleanup $ void clean
res <- Action $ catchRAW (fromAction act) $ \e -> liftIO (mask_ $ undo >> clean) >> throwRAW e
liftIO $ mask_ $ undo >> when runOnSuccess (void clean)
return res
actionOnException :: Action a -> IO b -> Action a
actionOnException = actionBoom False
actionFinally :: Action a -> IO b -> Action a
actionFinally = actionBoom True
getShakeOptions :: Action ShakeOptions
getShakeOptions = Action $ globalOptions <$> getRO
getProgress :: Action Progress
getProgress = do
Global{..} <- Action getRO
liftIO globalProgress
runAfter :: IO () -> Action ()
runAfter op = do
Global{..} <- Action getRO
liftIO $ atomicModifyIORef globalAfter $ \ops -> (op:ops, ())
putWhen :: Verbosity -> String -> Action ()
putWhen v msg = do
Global{..} <- Action getRO
verb <- getVerbosity
when (verb >= v) $
liftIO $ globalOutput v msg
putLoud :: String -> Action ()
putLoud = putWhen Loud
putNormal :: String -> Action ()
putNormal = putWhen Normal
putQuiet :: String -> Action ()
putQuiet = putWhen Quiet
getVerbosity :: Action Verbosity
getVerbosity = Action $ localVerbosity <$> getRW
withVerbosity :: Verbosity -> Action a -> Action a
withVerbosity new = actionBracket $ \s0 ->
(s0{localVerbosity=new}, \s -> s{localVerbosity=localVerbosity s0})
quietly :: Action a -> Action a
quietly = withVerbosity Quiet
unsafeAllowApply :: Action a -> Action a
unsafeAllowApply = applyBlockedBy Nothing
blockApply :: String -> Action a -> Action a
blockApply = applyBlockedBy . Just
applyBlockedBy :: Maybe String -> Action a -> Action a
applyBlockedBy reason = actionBracket $ \s0 ->
(s0{localBlockApply=reason}, \s -> s{localBlockApply=localBlockApply s0})
traced :: String -> IO a -> Action a
traced msg act = do
Global{..} <- Action getRO
Local{localStack} <- Action getRW
start <- liftIO globalTimestamp
putNormal $ "# " ++ msg ++ " (for " ++ showTopStack localStack ++ ")"
res <- liftIO act
stop <- liftIO globalTimestamp
let trace = newTrace msg start stop
liftIO $ evaluate $ rnf trace
Action $ modifyRW $ \s -> s{localTraces = trace : localTraces s}
return res
trackUse :: ShakeValue key => key -> Action ()
trackUse key = do
let k = newKey key
Global{..} <- Action getRO
l@Local{..} <- Action getRW
deps <- liftIO $ concatMapM (listDepends globalDatabase) localDepends
let top = topStack localStack
if top == Just k then
return ()
else if k `elem` deps then
return ()
else if any ($ k) localTrackAllows then
return ()
else
Action $ putRW l{localTrackUsed = k : localTrackUsed}
trackCheckUsed :: Action ()
trackCheckUsed = do
Global{..} <- Action getRO
Local{..} <- Action getRW
liftIO $ do
deps <- concatMapM (listDepends globalDatabase) localDepends
bad <- return $ localTrackUsed \\ deps
unless (null bad) $ do
let n = length bad
errorStructured
("Lint checking error - " ++ (if n == 1 then "value was" else show n ++ " values were") ++ " used but not depended upon")
[("Used", Just $ show x) | x <- bad]
""
bad <- flip filterM localTrackUsed $ \k -> not . null <$> lookupDependencies globalDatabase k
unless (null bad) $ do
let n = length bad
errorStructured
("Lint checking error - " ++ (if n == 1 then "value was" else show n ++ " values were") ++ " depended upon after being used")
[("Used", Just $ show x) | x <- bad]
""
trackChange :: ShakeValue key => key -> Action ()
trackChange key = do
let k = newKey key
Global{..} <- Action getRO
Local{..} <- Action getRW
liftIO $ do
let top = topStack localStack
if top == Just k then
return ()
else if any ($ k) localTrackAllows then
return ()
else
atomicModifyIORef globalTrackAbsent $ \ks -> ((fromMaybe k top, k):ks, ())
trackAllow :: ShakeValue key => (key -> Bool) -> Action ()
trackAllow (test :: key -> Bool) = Action $ modifyRW $ \s -> s{localTrackAllows = f : localTrackAllows s}
where
tk = typeRep (Proxy :: Proxy key)
f k = typeKey k == tk && test (fromKey k)