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
actionBoom :: Bool -> Action a -> IO b -> Action a
actionBoom runOnSuccess act clean = do
cleanup <- Action $ getsRO globalCleanup
undo <- liftIO $ addCleanup cleanup $ 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 $ getsRO globalOptions
getProgress :: Action Progress
getProgress = do
res <- Action $ getsRO globalProgress
liftIO res
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 $ getsRW localVerbosity
withVerbosity :: Verbosity -> Action a -> Action a
withVerbosity new = Action . unmodifyRW f . fromAction
where f 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 = Action . unmodifyRW f . fromAction
where f s0 = (s0{localBlockApply=reason}, \s -> s{localBlockApply=localBlockApply s0})
traced :: String -> IO a -> Action a
traced msg act = do
Global{..} <- Action getRO
stack <- Action $ getsRW localStack
start <- liftIO globalTimestamp
putNormal $ "# " ++ msg ++ " (for " ++ showTopStack stack ++ ")"
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)