shake-0.4: Development/Shake/Oracle.hs
{-# LANGUAGE MultiParamTypeClasses, GeneralizedNewtypeDeriving, DeriveDataTypeable, ScopedTypeVariables #-}
module Development.Shake.Oracle(
addOracle, addOracles, askOracle
) where
import Control.DeepSeq
import Data.Binary
import Data.Hashable
import Data.List
import Data.Typeable
import Development.Shake.Core
newtype Question = Question [String]
deriving (Typeable,Eq,Hashable,Binary,NFData)
newtype Answer = Answer [String]
deriving (Show,Typeable,Eq,Hashable,Binary,NFData)
instance Show Question where
show (Question xs) = "Oracle " ++ unwords xs
instance Rule Question Answer where
storedValue _ = return Nothing
-- | Add extra information which your build should depend on. For example:
--
-- > addOracle ["ghc"] $ return ["7.2.1"]
-- > addOracle ["ghc-pkg","shake"] $ return ["1.0"]
--
-- If a rule depends on the GHC version, it can then use @'askOracle' [\"ghc\"]@, and
-- if the GHC version changes, the rule will rebuild. It is common for the value returned
-- by 'askOracle' to be ignored.
--
-- The Oracle maps questions of @[String]@ to answers of @[String]@. This type is a
-- compromise. Questions will often be the singleton list, but allowing a list of strings
-- allows hierarchical schemes such as @ghc-pkg shake@, @ghc-pkg base@ etc.
-- The answers are often singleton lists, but sometimes are used as sets - for example
-- the list of packages returned by @ghc-pkg@.
--
-- Actions passed to 'addOracle' will be run in every Shake execution they are required,
-- their value will not be kept between runs. To get a similar behaviour using files, see
-- 'Development.Shake.alwaysRerun'.
addOracle :: [String] -> Action [String] -> Rules ()
addOracle question act = rule $ \(Question q) ->
if q == question then Just $ fmap Answer act else Nothing
-- | Add a function to generate an oracle, matching a prefix.
--
-- > addOracles ["reverse"] $ \xs -> return $ reverse xs
addOracles :: [String] -> ([String] -> Action [String]) -> Rules ()
addOracles pre act = rule $ \(Question q) ->
fmap (fmap Answer . act) $ stripPrefix pre q
-- | Get information previously added with 'addOracle'.
askOracle :: [String] -> Action [String]
askOracle question = do Answer answer <- apply1 $ Question question; return answer