{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable, ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns, RecordWildCards, FlexibleInstances, TypeFamilies #-}

module Development.Shake.Internal.Rules.File(
    need, needHasChanged, needBS, needed, neededBS, want,
    trackRead, trackWrite, trackAllow,
    defaultRuleFile,
    (%>), (|%>), (?>), phony, (~>), phonys,
    resultHasChanged,
    -- * Internal only
    FileQ(..), FileA, fileStoredValue, fileEqualValue, EqualCost(..), fileForward
    ) where

import Control.Applicative
import Control.Monad.Extra
import Control.Monad.IO.Class
import Data.Typeable
import Data.List
import Data.Maybe
import qualified Data.ByteString.Char8 as BS
import qualified Data.HashSet as Set
import Foreign.Storable
import Data.Word
import Data.Monoid
import General.Binary
import General.Extra

import Development.Shake.Internal.Core.Types
import Development.Shake.Internal.Core.Rules
import Development.Shake.Internal.Core.Run
import Development.Shake.Internal.Core.Action hiding (trackAllow)
import qualified Development.Shake.Internal.Core.Action as S
import Development.Shake.Internal.FileName
import Development.Shake.Internal.Rules.Rerun
import Development.Shake.Classes
import Development.Shake.FilePath(toStandard)
import Development.Shake.Internal.FilePattern
import Development.Shake.Internal.FileInfo
import Development.Shake.Internal.Options
import Development.Shake.Internal.Errors

import System.FilePath(takeDirectory) -- important that this is the system local filepath, or wrong slashes go wrong
import System.IO.Unsafe(unsafeInterleaveIO)

import Prelude


infix 1 %>, ?>, |%>, ~>

---------------------------------------------------------------------
-- TYPES

type instance RuleResult FileQ = FileR

-- | The unique key we use to index File rules, to avoid name clashes.
newtype FileQ = FileQ {FileQ -> FileName
fromFileQ :: FileName}
    deriving (Typeable,FileQ -> FileQ -> Bool
(FileQ -> FileQ -> Bool) -> (FileQ -> FileQ -> Bool) -> Eq FileQ
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FileQ -> FileQ -> Bool
$c/= :: FileQ -> FileQ -> Bool
== :: FileQ -> FileQ -> Bool
$c== :: FileQ -> FileQ -> Bool
Eq,Int -> FileQ -> Int
FileQ -> Int
(Int -> FileQ -> Int) -> (FileQ -> Int) -> Hashable FileQ
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
hash :: FileQ -> Int
$chash :: FileQ -> Int
hashWithSalt :: Int -> FileQ -> Int
$chashWithSalt :: Int -> FileQ -> Int
Hashable,Get FileQ
[FileQ] -> Put
FileQ -> Put
(FileQ -> Put) -> Get FileQ -> ([FileQ] -> Put) -> Binary FileQ
forall t. (t -> Put) -> Get t -> ([t] -> Put) -> Binary t
putList :: [FileQ] -> Put
$cputList :: [FileQ] -> Put
get :: Get FileQ
$cget :: Get FileQ
put :: FileQ -> Put
$cput :: FileQ -> Put
Binary,ByteString -> FileQ
FileQ -> Builder
(FileQ -> Builder) -> (ByteString -> FileQ) -> BinaryEx FileQ
forall a. (a -> Builder) -> (ByteString -> a) -> BinaryEx a
getEx :: ByteString -> FileQ
$cgetEx :: ByteString -> FileQ
putEx :: FileQ -> Builder
$cputEx :: FileQ -> Builder
BinaryEx,FileQ -> ()
(FileQ -> ()) -> NFData FileQ
forall a. (a -> ()) -> NFData a
rnf :: FileQ -> ()
$crnf :: FileQ -> ()
NFData)

-- | Raw information about a file.
data FileA = FileA {-# UNPACK #-} !ModTime {-# UNPACK #-} !FileSize FileHash
    deriving (Typeable)

-- | Result of a File rule, may contain raw file information and whether the rule did run this build
data FileR = FileR { FileR -> Maybe FileA
result :: Maybe FileA -- ^ Raw information about the file built by this rule.
                                           --   Set to 'Nothing' to prevent linting some times.
                   , FileR -> Bool
hasChanged :: Bool    -- ^ Whether the file changed this build. Transient
                                           --   information, that doesn't get serialized.
                   }
    deriving (Typeable)

-- | The types of file rule that occur.
data Mode
    = ModePhony (Action ()) -- ^ An action with no file value
    | ModeDirect (Action ()) -- ^ An action that produces this file
    | ModeForward (Action (Maybe FileA)) -- ^ An action that looks up a file someone else produced

-- | The results of the various 'Mode' rules.
data Result
    = ResultPhony
    | ResultDirect FileA
    | ResultForward FileA

-- | The use rules we use.
newtype FileRule = FileRule (FilePath -> Maybe Mode)
    deriving Typeable


---------------------------------------------------------------------
-- INSTANCES

instance Show FileQ where show :: FileQ -> String
show (FileQ x :: FileName
x) = FileName -> String
fileNameToString FileName
x

instance BinaryEx [FileQ] where
    putEx :: [FileQ] -> Builder
putEx = [FileName] -> Builder
forall a. BinaryEx a => a -> Builder
putEx ([FileName] -> Builder)
-> ([FileQ] -> [FileName]) -> [FileQ] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FileQ -> FileName) -> [FileQ] -> [FileName]
forall a b. (a -> b) -> [a] -> [b]
map FileQ -> FileName
fromFileQ
    getEx :: ByteString -> [FileQ]
getEx = (FileName -> FileQ) -> [FileName] -> [FileQ]
forall a b. (a -> b) -> [a] -> [b]
map FileName -> FileQ
FileQ ([FileName] -> [FileQ])
-> (ByteString -> [FileName]) -> ByteString -> [FileQ]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [FileName]
forall a. BinaryEx a => ByteString -> a
getEx

instance NFData FileA where
    rnf :: FileA -> ()
rnf (FileA a :: ModTime
a b :: FileSize
b c :: FileHash
c) = ModTime -> ()
forall a. NFData a => a -> ()
rnf ModTime
a () -> () -> ()
forall a b. a -> b -> b
`seq` FileSize -> ()
forall a. NFData a => a -> ()
rnf FileSize
b () -> () -> ()
forall a b. a -> b -> b
`seq` FileHash -> ()
forall a. NFData a => a -> ()
rnf FileHash
c

instance NFData FileR where
    rnf :: FileR -> ()
rnf (FileR f :: Maybe FileA
f b :: Bool
b) = Maybe FileA -> ()
forall a. NFData a => a -> ()
rnf Maybe FileA
f () -> () -> ()
forall a b. a -> b -> b
`seq` Bool -> ()
forall a. NFData a => a -> ()
rnf Bool
b

instance Show FileA where
    show :: FileA -> String
show (FileA m :: ModTime
m s :: FileSize
s h :: FileHash
h) = "File {mod=" String -> ShowS
forall a. [a] -> [a] -> [a]
++ ModTime -> String
forall a. Show a => a -> String
show ModTime
m String -> ShowS
forall a. [a] -> [a] -> [a]
++ ",size=" String -> ShowS
forall a. [a] -> [a] -> [a]
++ FileSize -> String
forall a. Show a => a -> String
show FileSize
s String -> ShowS
forall a. [a] -> [a] -> [a]
++ ",digest=" String -> ShowS
forall a. [a] -> [a] -> [a]
++ FileHash -> String
forall a. Show a => a -> String
show FileHash
h String -> ShowS
forall a. [a] -> [a] -> [a]
++ "}"

instance Show FileR where
    show :: FileR -> String
show FileR{..} = Maybe FileA -> String
forall a. Show a => a -> String
show Maybe FileA
result String -> ShowS
forall a. [a] -> [a] -> [a]
++ if Bool
hasChanged then " recomputed" else " not recomputed"

instance Storable FileA where
    sizeOf :: FileA -> Int
sizeOf _ = 4 Int -> Int -> Int
forall a. Num a => a -> a -> a
* 3 -- 4 Word32's
    alignment :: FileA -> Int
alignment _ = ModTime -> Int
forall a. Storable a => a -> Int
alignment (ModTime
forall a. HasCallStack => a
undefined :: ModTime)
    peekByteOff :: Ptr b -> Int -> IO FileA
peekByteOff p :: Ptr b
p i :: Int
i = ModTime -> FileSize -> FileHash -> FileA
FileA (ModTime -> FileSize -> FileHash -> FileA)
-> IO ModTime -> IO (FileSize -> FileHash -> FileA)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr b -> Int -> IO ModTime
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr b
p Int
i IO (FileSize -> FileHash -> FileA)
-> IO FileSize -> IO (FileHash -> FileA)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Ptr b -> Int -> IO FileSize
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr b
p (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+4) IO (FileHash -> FileA) -> IO FileHash -> IO FileA
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Ptr b -> Int -> IO FileHash
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr b
p (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+8)
    pokeByteOff :: Ptr b -> Int -> FileA -> IO ()
pokeByteOff p :: Ptr b
p i :: Int
i (FileA a :: ModTime
a b :: FileSize
b c :: FileHash
c) = Ptr b -> Int -> ModTime -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr b
p Int
i ModTime
a IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr b -> Int -> FileSize -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr b
p (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+4) FileSize
b IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr b -> Int -> FileHash -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr b
p (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+8) FileHash
c

instance BinaryEx FileA where
    putEx :: FileA -> Builder
putEx = FileA -> Builder
forall a. Storable a => a -> Builder
putExStorable
    getEx :: ByteString -> FileA
getEx = ByteString -> FileA
forall a. Storable a => ByteString -> a
getExStorable

instance BinaryEx [FileA] where
    putEx :: [FileA] -> Builder
putEx = [FileA] -> Builder
forall a. Storable a => [a] -> Builder
putExStorableList
    getEx :: ByteString -> [FileA]
getEx = ByteString -> [FileA]
forall a. Storable a => ByteString -> [a]
getExStorableList

fromResult :: Result -> Maybe FileA
fromResult :: Result -> Maybe FileA
fromResult ResultPhony = Maybe FileA
forall a. Maybe a
Nothing
fromResult (ResultDirect x :: FileA
x) = FileA -> Maybe FileA
forall a. a -> Maybe a
Just FileA
x
fromResult (ResultForward x :: FileA
x) = FileA -> Maybe FileA
forall a. a -> Maybe a
Just FileA
x

instance BinaryEx Result where
    putEx :: Result -> Builder
putEx ResultPhony = Builder
forall a. Monoid a => a
mempty
    putEx (ResultDirect x :: FileA
x) = FileA -> Builder
forall a. BinaryEx a => a -> Builder
putEx FileA
x
    putEx (ResultForward x :: FileA
x) = Word8 -> Builder
forall a. BinaryEx a => a -> Builder
putEx (0 :: Word8) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> FileA -> Builder
forall a. BinaryEx a => a -> Builder
putEx FileA
x

    getEx :: ByteString -> Result
getEx x :: ByteString
x = case ByteString -> Int
BS.length ByteString
x of
        0 -> Result
ResultPhony
        12 -> FileA -> Result
ResultDirect (FileA -> Result) -> FileA -> Result
forall a b. (a -> b) -> a -> b
$ ByteString -> FileA
forall a. BinaryEx a => ByteString -> a
getEx ByteString
x
        13 -> FileA -> Result
ResultForward (FileA -> Result) -> FileA -> Result
forall a b. (a -> b) -> a -> b
$ ByteString -> FileA
forall a. BinaryEx a => ByteString -> a
getEx (ByteString -> FileA) -> ByteString -> FileA
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BS.tail ByteString
x


---------------------------------------------------------------------
-- FILE CHECK QUERIES

-- | An equality check and a cost.
data EqualCost
    = EqualCheap -- ^ The equality check was cheap.
    | EqualExpensive -- ^ The equality check was expensive, as the results are not trivially equal.
    | NotEqual -- ^ The values are not equal.
      deriving (EqualCost -> EqualCost -> Bool
(EqualCost -> EqualCost -> Bool)
-> (EqualCost -> EqualCost -> Bool) -> Eq EqualCost
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: EqualCost -> EqualCost -> Bool
$c/= :: EqualCost -> EqualCost -> Bool
== :: EqualCost -> EqualCost -> Bool
$c== :: EqualCost -> EqualCost -> Bool
Eq,Eq EqualCost
Eq EqualCost =>
(EqualCost -> EqualCost -> Ordering)
-> (EqualCost -> EqualCost -> Bool)
-> (EqualCost -> EqualCost -> Bool)
-> (EqualCost -> EqualCost -> Bool)
-> (EqualCost -> EqualCost -> Bool)
-> (EqualCost -> EqualCost -> EqualCost)
-> (EqualCost -> EqualCost -> EqualCost)
-> Ord EqualCost
EqualCost -> EqualCost -> Bool
EqualCost -> EqualCost -> Ordering
EqualCost -> EqualCost -> EqualCost
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: EqualCost -> EqualCost -> EqualCost
$cmin :: EqualCost -> EqualCost -> EqualCost
max :: EqualCost -> EqualCost -> EqualCost
$cmax :: EqualCost -> EqualCost -> EqualCost
>= :: EqualCost -> EqualCost -> Bool
$c>= :: EqualCost -> EqualCost -> Bool
> :: EqualCost -> EqualCost -> Bool
$c> :: EqualCost -> EqualCost -> Bool
<= :: EqualCost -> EqualCost -> Bool
$c<= :: EqualCost -> EqualCost -> Bool
< :: EqualCost -> EqualCost -> Bool
$c< :: EqualCost -> EqualCost -> Bool
compare :: EqualCost -> EqualCost -> Ordering
$ccompare :: EqualCost -> EqualCost -> Ordering
$cp1Ord :: Eq EqualCost
Ord,Int -> EqualCost -> ShowS
[EqualCost] -> ShowS
EqualCost -> String
(Int -> EqualCost -> ShowS)
-> (EqualCost -> String)
-> ([EqualCost] -> ShowS)
-> Show EqualCost
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [EqualCost] -> ShowS
$cshowList :: [EqualCost] -> ShowS
show :: EqualCost -> String
$cshow :: EqualCost -> String
showsPrec :: Int -> EqualCost -> ShowS
$cshowsPrec :: Int -> EqualCost -> ShowS
Show,ReadPrec [EqualCost]
ReadPrec EqualCost
Int -> ReadS EqualCost
ReadS [EqualCost]
(Int -> ReadS EqualCost)
-> ReadS [EqualCost]
-> ReadPrec EqualCost
-> ReadPrec [EqualCost]
-> Read EqualCost
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [EqualCost]
$creadListPrec :: ReadPrec [EqualCost]
readPrec :: ReadPrec EqualCost
$creadPrec :: ReadPrec EqualCost
readList :: ReadS [EqualCost]
$creadList :: ReadS [EqualCost]
readsPrec :: Int -> ReadS EqualCost
$creadsPrec :: Int -> ReadS EqualCost
Read,Typeable,Int -> EqualCost
EqualCost -> Int
EqualCost -> [EqualCost]
EqualCost -> EqualCost
EqualCost -> EqualCost -> [EqualCost]
EqualCost -> EqualCost -> EqualCost -> [EqualCost]
(EqualCost -> EqualCost)
-> (EqualCost -> EqualCost)
-> (Int -> EqualCost)
-> (EqualCost -> Int)
-> (EqualCost -> [EqualCost])
-> (EqualCost -> EqualCost -> [EqualCost])
-> (EqualCost -> EqualCost -> [EqualCost])
-> (EqualCost -> EqualCost -> EqualCost -> [EqualCost])
-> Enum EqualCost
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: EqualCost -> EqualCost -> EqualCost -> [EqualCost]
$cenumFromThenTo :: EqualCost -> EqualCost -> EqualCost -> [EqualCost]
enumFromTo :: EqualCost -> EqualCost -> [EqualCost]
$cenumFromTo :: EqualCost -> EqualCost -> [EqualCost]
enumFromThen :: EqualCost -> EqualCost -> [EqualCost]
$cenumFromThen :: EqualCost -> EqualCost -> [EqualCost]
enumFrom :: EqualCost -> [EqualCost]
$cenumFrom :: EqualCost -> [EqualCost]
fromEnum :: EqualCost -> Int
$cfromEnum :: EqualCost -> Int
toEnum :: Int -> EqualCost
$ctoEnum :: Int -> EqualCost
pred :: EqualCost -> EqualCost
$cpred :: EqualCost -> EqualCost
succ :: EqualCost -> EqualCost
$csucc :: EqualCost -> EqualCost
Enum,EqualCost
EqualCost -> EqualCost -> Bounded EqualCost
forall a. a -> a -> Bounded a
maxBound :: EqualCost
$cmaxBound :: EqualCost
minBound :: EqualCost
$cminBound :: EqualCost
Bounded)

fileStoredValue :: ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue :: ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions{shakeChange :: ShakeOptions -> Change
shakeChange=Change
c} (FileQ x :: FileName
x) = do
    Maybe (ModTime, FileSize)
res <- FileName -> IO (Maybe (ModTime, FileSize))
getFileInfo FileName
x
    case Maybe (ModTime, FileSize)
res of
        Nothing -> Maybe FileA -> IO (Maybe FileA)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe FileA
forall a. Maybe a
Nothing
        Just (time :: ModTime
time,size :: FileSize
size) | Change
c Change -> Change -> Bool
forall a. Eq a => a -> a -> Bool
== Change
ChangeModtime -> Maybe FileA -> IO (Maybe FileA)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe FileA -> IO (Maybe FileA))
-> Maybe FileA -> IO (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ FileA -> Maybe FileA
forall a. a -> Maybe a
Just (FileA -> Maybe FileA) -> FileA -> Maybe FileA
forall a b. (a -> b) -> a -> b
$ ModTime -> FileSize -> FileHash -> FileA
FileA ModTime
time FileSize
size FileHash
fileInfoNoHash
        Just (time :: ModTime
time,size :: FileSize
size) -> do
            FileHash
hash <- IO FileHash -> IO FileHash
forall a. IO a -> IO a
unsafeInterleaveIO (IO FileHash -> IO FileHash) -> IO FileHash -> IO FileHash
forall a b. (a -> b) -> a -> b
$ FileName -> IO FileHash
getFileHash FileName
x
            Maybe FileA -> IO (Maybe FileA)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe FileA -> IO (Maybe FileA))
-> Maybe FileA -> IO (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ FileA -> Maybe FileA
forall a. a -> Maybe a
Just (FileA -> Maybe FileA) -> FileA -> Maybe FileA
forall a b. (a -> b) -> a -> b
$ ModTime -> FileSize -> FileHash -> FileA
FileA ModTime
time FileSize
size FileHash
hash


fileEqualValue :: ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue :: ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue ShakeOptions{shakeChange :: ShakeOptions -> Change
shakeChange=Change
c} (FileA x1 :: ModTime
x1 x2 :: FileSize
x2 x3 :: FileHash
x3) (FileA y1 :: ModTime
y1 y2 :: FileSize
y2 y3 :: FileHash
y3) = case Change
c of
    ChangeModtime -> Bool -> EqualCost
bool (Bool -> EqualCost) -> Bool -> EqualCost
forall a b. (a -> b) -> a -> b
$ ModTime
x1 ModTime -> ModTime -> Bool
forall a. Eq a => a -> a -> Bool
== ModTime
y1
    ChangeDigest -> Bool -> EqualCost
bool (Bool -> EqualCost) -> Bool -> EqualCost
forall a b. (a -> b) -> a -> b
$ FileSize
x2 FileSize -> FileSize -> Bool
forall a. Eq a => a -> a -> Bool
== FileSize
y2 Bool -> Bool -> Bool
&& FileHash
x3 FileHash -> FileHash -> Bool
forall a. Eq a => a -> a -> Bool
== FileHash
y3
    ChangeModtimeOrDigest -> Bool -> EqualCost
bool (Bool -> EqualCost) -> Bool -> EqualCost
forall a b. (a -> b) -> a -> b
$ ModTime
x1 ModTime -> ModTime -> Bool
forall a. Eq a => a -> a -> Bool
== ModTime
y1 Bool -> Bool -> Bool
&& FileSize
x2 FileSize -> FileSize -> Bool
forall a. Eq a => a -> a -> Bool
== FileSize
y2 Bool -> Bool -> Bool
&& FileHash
x3 FileHash -> FileHash -> Bool
forall a. Eq a => a -> a -> Bool
== FileHash
y3
    _ | ModTime
x1 ModTime -> ModTime -> Bool
forall a. Eq a => a -> a -> Bool
== ModTime
y1 -> EqualCost
EqualCheap
      | FileSize
x2 FileSize -> FileSize -> Bool
forall a. Eq a => a -> a -> Bool
== FileSize
y2 Bool -> Bool -> Bool
&& FileHash
x3 FileHash -> FileHash -> Bool
forall a. Eq a => a -> a -> Bool
== FileHash
y3 -> EqualCost
EqualExpensive
      | Bool
otherwise -> EqualCost
NotEqual
    where bool :: Bool -> EqualCost
bool b :: Bool
b = if Bool
b then EqualCost
EqualCheap else EqualCost
NotEqual


-- | Arguments: options; is the file an input; a message for failure if the file does not exist; filename
storedValueError :: ShakeOptions -> Bool -> String -> FileQ -> IO (Maybe FileA)
{-
storedValueError opts False msg x | False && not (shakeOutputCheck opts) = do
    when (shakeCreationCheck opts) $ do
        whenM (isNothing <$> (storedValue opts x :: IO (Maybe FileA))) $ error $ msg ++ "\n  " ++ unpackU (fromFileQ x)
    return $ FileA fileInfoEq fileInfoEq fileInfoEq
-}
storedValueError :: ShakeOptions -> Bool -> String -> FileQ -> IO (Maybe FileA)
storedValueError opts :: ShakeOptions
opts input :: Bool
input msg :: String
msg x :: FileQ
x = Maybe FileA -> (FileA -> Maybe FileA) -> Maybe FileA -> Maybe FileA
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Maybe FileA
def FileA -> Maybe FileA
forall a. a -> Maybe a
Just (Maybe FileA -> Maybe FileA)
-> IO (Maybe FileA) -> IO (Maybe FileA)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions
opts2 FileQ
x
    where def :: Maybe FileA
def = if ShakeOptions -> Bool
shakeCreationCheck ShakeOptions
opts Bool -> Bool -> Bool
|| Bool
input then String -> Maybe FileA
forall a. HasCallStack => String -> a
error String
err else Maybe FileA
forall a. Maybe a
Nothing
          err :: String
err = String
msg String -> ShowS
forall a. [a] -> [a] -> [a]
++ "\n  " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FileName -> String
fileNameToString (FileQ -> FileName
fromFileQ FileQ
x)
          opts2 :: ShakeOptions
opts2 = if Bool -> Bool
not Bool
input Bool -> Bool -> Bool
&& ShakeOptions -> Change
shakeChange ShakeOptions
opts Change -> Change -> Bool
forall a. Eq a => a -> a -> Bool
== Change
ChangeModtimeAndDigestInput then ShakeOptions
opts{shakeChange :: Change
shakeChange=Change
ChangeModtime} else ShakeOptions
opts


---------------------------------------------------------------------
-- THE DEFAULT RULE

defaultRuleFile :: Rules ()
defaultRuleFile :: Rules ()
defaultRuleFile = do
    opts :: ShakeOptions
opts@ShakeOptions{..} <- Rules ShakeOptions
getShakeOptionsRules
    -- A rule from FileQ to (Maybe FileA). The result value is only useful for linting.
    BuiltinLint FileQ FileR -> BuiltinRun FileQ FileR -> Rules ()
forall key value.
(RuleResult key ~ value, ShakeValue key, BinaryEx key,
 Typeable value, NFData value, Show value) =>
BuiltinLint key value -> BuiltinRun key value -> Rules ()
addBuiltinRuleEx (ShakeOptions -> BuiltinLint FileQ FileR
ruleLint ShakeOptions
opts) (ShakeOptions -> (String -> Rebuild) -> BuiltinRun FileQ FileR
ruleRun ShakeOptions
opts ((String -> Rebuild) -> BuiltinRun FileQ FileR)
-> (String -> Rebuild) -> BuiltinRun FileQ FileR
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> String -> Rebuild
shakeRebuildApply ShakeOptions
opts)

ruleLint :: ShakeOptions -> BuiltinLint FileQ FileR
ruleLint :: ShakeOptions -> BuiltinLint FileQ FileR
ruleLint opts :: ShakeOptions
opts k :: FileQ
k (FileR Nothing _) = Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
ruleLint opts :: ShakeOptions
opts k :: FileQ
k (FileR (Just v :: FileA
v) _) = do
    Maybe FileA
now <- ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions
opts FileQ
k
    Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ case Maybe FileA
now of
        Nothing -> String -> Maybe String
forall a. a -> Maybe a
Just "<missing>"
        Just now :: FileA
now | ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue ShakeOptions
opts FileA
v FileA
now EqualCost -> EqualCost -> Bool
forall a. Eq a => a -> a -> Bool
== EqualCost
EqualCheap -> Maybe String
forall a. Maybe a
Nothing
                 | Bool
otherwise -> String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ FileA -> String
forall a. Show a => a -> String
show FileA
now

ruleRun :: ShakeOptions -> (FilePath -> Rebuild) -> BuiltinRun FileQ FileR
ruleRun :: ShakeOptions -> (String -> Rebuild) -> BuiltinRun FileQ FileR
ruleRun opts :: ShakeOptions
opts@ShakeOptions{..} rebuildFlags :: String -> Rebuild
rebuildFlags o :: FileQ
o@(FileQ x :: FileName
x) oldBin :: Maybe ByteString
oldBin@((ByteString -> Result) -> Maybe ByteString -> Maybe Result
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Result
forall a. BinaryEx a => ByteString -> a
getEx -> Maybe Result
old) dirtyChildren :: Bool
dirtyChildren = do
    -- for One, rebuild makes perfect sense
    -- for Forward, we expect the child will have already rebuilt - Rebuild just lets us deal with code changes
    -- for Phony, it doesn't make that much sense, but probably isn't harmful?
    let r :: Rebuild
r = String -> Rebuild
rebuildFlags (String -> Rebuild) -> String -> Rebuild
forall a b. (a -> b) -> a -> b
$ FileName -> String
fileNameToString FileName
x
    case Maybe Result
old of
        _ | Rebuild
r Rebuild -> Rebuild -> Bool
forall a. Eq a => a -> a -> Bool
== Rebuild
RebuildNow -> Action (RunResult FileR)
rebuild
        _ | Rebuild
r Rebuild -> Rebuild -> Bool
forall a. Eq a => a -> a -> Bool
== Rebuild
RebuildLater -> case Maybe Result
old of
            Just old :: Result
old ->
                -- ignoring the currently stored value, which may trigger lint has changed
                -- so disable lint on this file
                RunResult FileR -> RunResult FileR
unLint (RunResult FileR -> RunResult FileR)
-> Action (RunResult FileR) -> Action (RunResult FileR)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> RunChanged -> Action (RunResult FileR)
retOld RunChanged
ChangedNothing
            Nothing -> do
                -- i don't have a previous value, so assume this is a source node, and mark rebuild in future
                Maybe FileA
now <- IO (Maybe FileA) -> Action (Maybe FileA)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe FileA) -> Action (Maybe FileA))
-> IO (Maybe FileA) -> Action (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions
opts FileQ
o
                case Maybe FileA
now of
                    Nothing -> Action (RunResult FileR)
rebuild
                    Just now :: FileA
now -> do Action ()
alwaysRerun; RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
ChangedStore (Result -> Action (RunResult FileR))
-> Result -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ FileA -> Result
ResultDirect FileA
now
        {-
        _ | r == RebuildNever -> do
            now <- liftIO $ fileStoredValue opts o
            case now of
                Nothing -> rebuild
                Just now -> do
                    let diff | Just (ResultDirect old) <- old, fileEqualValue opts old now /= NotEqual = ChangedRecomputeSame
                                | otherwise = ChangedRecomputeDiff
                    retNew diff $ ResultDirect now
        -}
        Just (ResultDirect old :: FileA
old) | Bool -> Bool
not Bool
dirtyChildren -> do
            Maybe FileA
now <- IO (Maybe FileA) -> Action (Maybe FileA)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe FileA) -> Action (Maybe FileA))
-> IO (Maybe FileA) -> Action (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions
opts FileQ
o
            case Maybe FileA
now of
                Nothing -> Action (RunResult FileR)
rebuild
                Just now :: FileA
now -> case ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue ShakeOptions
opts FileA
old FileA
now of
                    EqualCheap -> RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
ChangedNothing (Result -> Action (RunResult FileR))
-> Result -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ FileA -> Result
ResultDirect FileA
now
                    EqualExpensive -> RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
ChangedStore (Result -> Action (RunResult FileR))
-> Result -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ FileA -> Result
ResultDirect FileA
now
                    NotEqual -> Action (RunResult FileR)
rebuild
        Just (ResultForward old :: FileA
old) | Bool -> Bool
not Bool
dirtyChildren -> RunChanged -> Action (RunResult FileR)
retOld RunChanged
ChangedNothing
        _ -> Action (RunResult FileR)
rebuild
    where
        -- no need to lint check forward files
        -- but more than that, it goes wrong if you do, see #427
        asLint :: Result -> Maybe FileA
asLint (ResultDirect x :: FileA
x) = FileA -> Maybe FileA
forall a. a -> Maybe a
Just FileA
x
        asLint x :: Result
x = Maybe FileA
forall a. Maybe a
Nothing
        unLint :: RunResult FileR -> RunResult FileR
unLint (RunResult a :: RunChanged
a b :: ByteString
b (FileR _ c :: Bool
c)) = RunChanged -> ByteString -> FileR -> RunResult FileR
forall value. RunChanged -> ByteString -> value -> RunResult value
RunResult RunChanged
a ByteString
b (FileR -> RunResult FileR) -> FileR -> RunResult FileR
forall a b. (a -> b) -> a -> b
$ Maybe FileA -> Bool -> FileR
FileR Maybe FileA
forall a. Maybe a
Nothing Bool
c

        retNew :: RunChanged -> Result -> Action (RunResult FileR)
        retNew :: RunChanged -> Result -> Action (RunResult FileR)
retNew c :: RunChanged
c v :: Result
v = RunResult FileR -> Action (RunResult FileR)
forall (m :: * -> *) a. Monad m => a -> m a
return (RunResult FileR -> Action (RunResult FileR))
-> RunResult FileR -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ RunChanged -> ByteString -> FileR -> RunResult FileR
forall value. RunChanged -> ByteString -> value -> RunResult value
RunResult RunChanged
c (Builder -> ByteString
runBuilder (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ Result -> Builder
forall a. BinaryEx a => a -> Builder
putEx Result
v) (Maybe FileA -> Bool -> FileR
FileR (Result -> Maybe FileA
asLint Result
v) (RunChanged
c RunChanged -> RunChanged -> Bool
forall a. Eq a => a -> a -> Bool
== RunChanged
ChangedRecomputeDiff))

        retOld :: RunChanged -> Action (RunResult FileR)
        retOld :: RunChanged -> Action (RunResult FileR)
retOld c :: RunChanged
c = RunResult FileR -> Action (RunResult FileR)
forall (m :: * -> *) a. Monad m => a -> m a
return (RunResult FileR -> Action (RunResult FileR))
-> RunResult FileR -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ RunChanged -> ByteString -> FileR -> RunResult FileR
forall value. RunChanged -> ByteString -> value -> RunResult value
RunResult RunChanged
c (Maybe ByteString -> ByteString
forall a. HasCallStack => Maybe a -> a
fromJust Maybe ByteString
oldBin) (FileR -> RunResult FileR) -> FileR -> RunResult FileR
forall a b. (a -> b) -> a -> b
$ Maybe FileA -> Bool -> FileR
FileR (Result -> Maybe FileA
asLint (Result -> Maybe FileA) -> Result -> Maybe FileA
forall a b. (a -> b) -> a -> b
$ Maybe Result -> Result
forall a. HasCallStack => Maybe a -> a
fromJust Maybe Result
old) Bool
False

        -- actually run the rebuild
        rebuild :: Action (RunResult FileR)
rebuild = do
            Verbosity -> String -> Action ()
putWhen Verbosity
Chatty (String -> Action ()) -> String -> Action ()
forall a b. (a -> b) -> a -> b
$ "# " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FileQ -> String
forall a. Show a => a -> String
show FileQ
o
            String
x <- String -> Action String
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Action String) -> String -> Action String
forall a b. (a -> b) -> a -> b
$ FileName -> String
fileNameToString FileName
x
            UserRule FileRule
rules <- Action (UserRule FileRule)
forall a. Typeable a => Action (UserRule a)
getUserRules
            Maybe Mode
act <- case UserRule FileRule -> (FileRule -> Maybe Mode) -> [Mode]
forall a b. UserRule a -> (a -> Maybe b) -> [b]
userRuleMatch UserRule FileRule
rules ((FileRule -> Maybe Mode) -> [Mode])
-> (FileRule -> Maybe Mode) -> [Mode]
forall a b. (a -> b) -> a -> b
$ \(FileRule f :: String -> Maybe Mode
f) -> String -> Maybe Mode
f String
x of
                [] -> Maybe Mode -> Action (Maybe Mode)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Mode
forall a. Maybe a
Nothing
                [r :: Mode
r] -> Maybe Mode -> Action (Maybe Mode)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Mode -> Action (Maybe Mode))
-> Maybe Mode -> Action (Maybe Mode)
forall a b. (a -> b) -> a -> b
$ Mode -> Maybe Mode
forall a. a -> Maybe a
Just Mode
r
                rs :: [Mode]
rs  -> IO (Maybe Mode) -> Action (Maybe Mode)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe Mode) -> Action (Maybe Mode))
-> IO (Maybe Mode) -> Action (Maybe Mode)
forall a b. (a -> b) -> a -> b
$ TypeRep -> String -> Int -> IO (Maybe Mode)
forall a. TypeRep -> String -> Int -> IO a
errorMultipleRulesMatch (FileQ -> TypeRep
forall a. Typeable a => a -> TypeRep
typeOf FileQ
o) (FileQ -> String
forall a. Show a => a -> String
show FileQ
o) ([Mode] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Mode]
rs)
            let answer :: (FileA -> Result) -> FileA -> Action (RunResult FileR)
answer ctor :: FileA -> Result
ctor new :: FileA
new = do
                    let b :: RunChanged
b = case () of
                                _ | Just old :: Result
old <- Maybe Result
old
                                    , Just old :: FileA
old <- Result -> Maybe FileA
fromResult Result
old
                                    , ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue ShakeOptions
opts FileA
old FileA
new EqualCost -> EqualCost -> Bool
forall a. Eq a => a -> a -> Bool
/= EqualCost
NotEqual -> RunChanged
ChangedRecomputeSame
                                _ -> RunChanged
ChangedRecomputeDiff
                    RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
b (Result -> Action (RunResult FileR))
-> Result -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ FileA -> Result
ctor FileA
new
            case Maybe Mode
act of
                Nothing -> do
                    Maybe FileA
new <- IO (Maybe FileA) -> Action (Maybe FileA)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe FileA) -> Action (Maybe FileA))
-> IO (Maybe FileA) -> Action (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> Bool -> String -> FileQ -> IO (Maybe FileA)
storedValueError ShakeOptions
opts Bool
True "Error, file does not exist and no rule available:" FileQ
o
                    (FileA -> Result) -> FileA -> Action (RunResult FileR)
answer FileA -> Result
ResultDirect (FileA -> Action (RunResult FileR))
-> FileA -> Action (RunResult FileR)
forall a b. (a -> b) -> a -> b
$ Maybe FileA -> FileA
forall a. HasCallStack => Maybe a -> a
fromJust Maybe FileA
new
                Just (ModeForward act :: Action (Maybe FileA)
act) -> do
                    Maybe FileA
new <- Action (Maybe FileA)
act
                    case Maybe FileA
new of
                        Nothing -> do
                            Action ()
alwaysRerun
                            RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
ChangedRecomputeDiff Result
ResultPhony
                        Just new :: FileA
new -> (FileA -> Result) -> FileA -> Action (RunResult FileR)
answer FileA -> Result
ResultForward FileA
new
                Just (ModeDirect act :: Action ()
act) -> do
                    Action ()
act
                    Maybe FileA
new <- IO (Maybe FileA) -> Action (Maybe FileA)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe FileA) -> Action (Maybe FileA))
-> IO (Maybe FileA) -> Action (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> Bool -> String -> FileQ -> IO (Maybe FileA)
storedValueError ShakeOptions
opts Bool
False "Error, rule finished running but did not produce file:" FileQ
o
                    case Maybe FileA
new of
                        Nothing -> RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
ChangedRecomputeDiff Result
ResultPhony
                        Just new :: FileA
new -> (FileA -> Result) -> FileA -> Action (RunResult FileR)
answer FileA -> Result
ResultDirect FileA
new
                Just (ModePhony act :: Action ()
act) -> do
                    -- See #523 and #524
                    -- Shake runs the dependencies first, but stops when one has changed.
                    -- We don't want to run the existing deps first if someone changes the build system,
                    -- so insert a fake dependency that cuts the process dead.
                    Action ()
alwaysRerun
                    Action ()
act
                    RunChanged -> Result -> Action (RunResult FileR)
retNew RunChanged
ChangedRecomputeDiff Result
ResultPhony


apply_ :: (a -> FileName) -> [a] -> Action [FileR]
apply_ :: (a -> FileName) -> [a] -> Action [FileR]
apply_ f :: a -> FileName
f = [FileQ] -> Action [FileR]
forall key value.
(RuleResult key ~ value, ShakeValue key, Typeable value) =>
[key] -> Action [value]
apply ([FileQ] -> Action [FileR])
-> ([a] -> [FileQ]) -> [a] -> Action [FileR]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> FileQ) -> [a] -> [FileQ]
forall a b. (a -> b) -> [a] -> [b]
map (FileName -> FileQ
FileQ (FileName -> FileQ) -> (a -> FileName) -> a -> FileQ
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> FileName
f)


-- | Has a file changed. This function will only give the correct answer if called in the rule
--   producing the file, /before/ the rule has modified the file in question.
--   Best avoided, but sometimes necessary in conjunction with 'needHasChanged' to cause rebuilds
--   to happen if the result is deleted or modified.
resultHasChanged :: FilePath -> Action Bool
resultHasChanged :: String -> Action Bool
resultHasChanged file :: String
file = do
    let filename :: FileQ
filename = FileName -> FileQ
FileQ (FileName -> FileQ) -> FileName -> FileQ
forall a b. (a -> b) -> a -> b
$ String -> FileName
fileNameFromString String
file
    Maybe (Either ByteString FileR)
res <- FileQ -> Action (Maybe (Either ByteString FileR))
forall key value.
(RuleResult key ~ value, ShakeValue key, Typeable value) =>
key -> Action (Maybe (Either ByteString value))
getDatabaseValue FileQ
filename
    Maybe FileA
old <- Maybe FileA -> Action (Maybe FileA)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe FileA -> Action (Maybe FileA))
-> Maybe FileA -> Action (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ case Maybe (Either ByteString FileR)
res of
        Nothing -> Maybe FileA
forall a. Maybe a
Nothing
        Just (Left bs :: ByteString
bs) -> Result -> Maybe FileA
fromResult (Result -> Maybe FileA) -> Result -> Maybe FileA
forall a b. (a -> b) -> a -> b
$ ByteString -> Result
forall a. BinaryEx a => ByteString -> a
getEx ByteString
bs
        Just (Right v :: FileR
v) -> FileR -> Maybe FileA
result FileR
v
    case Maybe FileA
old of
        Nothing -> Bool -> Action Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
        Just old :: FileA
old -> do
            ShakeOptions
opts <- Action ShakeOptions
getShakeOptions
            Maybe FileA
new <- IO (Maybe FileA) -> Action (Maybe FileA)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe FileA) -> Action (Maybe FileA))
-> IO (Maybe FileA) -> Action (Maybe FileA)
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions
opts FileQ
filename
            Bool -> Action Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Action Bool) -> Bool -> Action Bool
forall a b. (a -> b) -> a -> b
$ case Maybe FileA
new of
                Nothing -> Bool
True
                Just new :: FileA
new -> ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue ShakeOptions
opts FileA
old FileA
new EqualCost -> EqualCost -> Bool
forall a. Eq a => a -> a -> Bool
== EqualCost
NotEqual


---------------------------------------------------------------------
-- OPTIONS ON TOP

-- | Internal method for adding forwarding actions
fileForward :: (FilePath -> Maybe (Action (Maybe FileA))) -> Rules ()
fileForward :: (String -> Maybe (Action (Maybe FileA))) -> Rules ()
fileForward act :: String -> Maybe (Action (Maybe FileA))
act = FileRule -> Rules ()
forall a. Typeable a => a -> Rules ()
addUserRule (FileRule -> Rules ()) -> FileRule -> Rules ()
forall a b. (a -> b) -> a -> b
$ (String -> Maybe Mode) -> FileRule
FileRule ((String -> Maybe Mode) -> FileRule)
-> (String -> Maybe Mode) -> FileRule
forall a b. (a -> b) -> a -> b
$ (Action (Maybe FileA) -> Mode)
-> Maybe (Action (Maybe FileA)) -> Maybe Mode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Action (Maybe FileA) -> Mode
ModeForward (Maybe (Action (Maybe FileA)) -> Maybe Mode)
-> (String -> Maybe (Action (Maybe FileA))) -> String -> Maybe Mode
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe (Action (Maybe FileA))
act


-- | Add a dependency on the file arguments, ensuring they are built before continuing.
--   The file arguments may be built in parallel, in any order. This function is particularly
--   necessary when calling 'Development.Shake.cmd' or 'Development.Shake.command'. As an example:
--
-- @
-- \"\/\/*.rot13\" '%>' \\out -> do
--     let src = 'Development.Shake.FilePath.dropExtension' out
--     'need' [src]
--     'Development.Shake.cmd' \"rot13\" [src] \"-o\" [out]
-- @
--
--   Usually @need [foo,bar]@ is preferable to @need [foo] >> need [bar]@ as the former allows greater
--   parallelism, while the latter requires @foo@ to finish building before starting to build @bar@.
--
--   This function should not be called with wildcards (e.g. @*.txt@ - use 'getDirectoryFiles' to expand them),
--   environment variables (e.g. @$HOME@ - use 'getEnv' to expand them) or directories (directories cannot be
--   tracked directly - track files within the directory instead).
need :: [FilePath] -> Action ()
need :: [String] -> Action ()
need = Action [FileR] -> Action ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Action [FileR] -> Action ())
-> ([String] -> Action [FileR]) -> [String] -> Action ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> FileName) -> [String] -> Action [FileR]
forall a. (a -> FileName) -> [a] -> Action [FileR]
apply_ String -> FileName
fileNameFromString


-- | Like 'need' but returns a list of rebuild dependencies this build.
--
--   The following example writes a list of changed dependencies to a file as its action.
--
-- @
-- \"target\" '%>' \\out -> do
--       let sourceList = [\"source1\", \"source2\"]
--       rebuildList <- 'needHasChanged' sourceList
--       'Development.Shake.writeFileLines' out rebuildList
-- @
--
--   This function can be used to alter the action depending on which dependency needed
--   to be rebuild.
--
--   Note that a rule can be run even if no dependency has changed, for example
--   because of 'shakeRebuild' or because the target has changed or been deleted.
--   To detect the latter case you may wish to use 'resultHasChanged'.
needHasChanged :: [FilePath] -> Action [FilePath]
needHasChanged :: [String] -> Action [String]
needHasChanged paths :: [String]
paths = do
    [FileR]
res <- (String -> FileName) -> [String] -> Action [FileR]
forall a. (a -> FileName) -> [a] -> Action [FileR]
apply_ String -> FileName
fileNameFromString [String]
paths
    [String] -> Action [String]
forall (m :: * -> *) a. Monad m => a -> m a
return [String
a | (a :: String
a,b :: FileR
b) <- [String] -> [FileR] -> [(String, FileR)]
forall a b. [a] -> [b] -> [(a, b)]
zip [String]
paths [FileR]
res, FileR -> Bool
hasChanged FileR
b]

needBS :: [BS.ByteString] -> Action ()
needBS :: [ByteString] -> Action ()
needBS = Action [FileR] -> Action ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Action [FileR] -> Action ())
-> ([ByteString] -> Action [FileR]) -> [ByteString] -> Action ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> FileName) -> [ByteString] -> Action [FileR]
forall a. (a -> FileName) -> [a] -> Action [FileR]
apply_ ByteString -> FileName
fileNameFromByteString

-- | Like 'need', but if 'shakeLint' is set, check that the file does not rebuild.
--   Used for adding dependencies on files that have already been used in this rule.
needed :: [FilePath] -> Action ()
needed :: [String] -> Action ()
needed xs :: [String]
xs = do
    ShakeOptions
opts <- Action ShakeOptions
getShakeOptions
    if Maybe Lint -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe Lint -> Bool) -> Maybe Lint -> Bool
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> Maybe Lint
shakeLint ShakeOptions
opts then [String] -> Action ()
need [String]
xs else [FileName] -> Action ()
neededCheck ([FileName] -> Action ()) -> [FileName] -> Action ()
forall a b. (a -> b) -> a -> b
$ (String -> FileName) -> [String] -> [FileName]
forall a b. (a -> b) -> [a] -> [b]
map String -> FileName
fileNameFromString [String]
xs


neededBS :: [BS.ByteString] -> Action ()
neededBS :: [ByteString] -> Action ()
neededBS xs :: [ByteString]
xs = do
    ShakeOptions
opts <- Action ShakeOptions
getShakeOptions
    if Maybe Lint -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe Lint -> Bool) -> Maybe Lint -> Bool
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> Maybe Lint
shakeLint ShakeOptions
opts then [ByteString] -> Action ()
needBS [ByteString]
xs else [FileName] -> Action ()
neededCheck ([FileName] -> Action ()) -> [FileName] -> Action ()
forall a b. (a -> b) -> a -> b
$ (ByteString -> FileName) -> [ByteString] -> [FileName]
forall a b. (a -> b) -> [a] -> [b]
map ByteString -> FileName
fileNameFromByteString [ByteString]
xs


neededCheck :: [FileName] -> Action ()
neededCheck :: [FileName] -> Action ()
neededCheck xs :: [FileName]
xs = do
    ShakeOptions
opts <- Action ShakeOptions
getShakeOptions
    [Maybe FileA]
pre <- IO [Maybe FileA] -> Action [Maybe FileA]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [Maybe FileA] -> Action [Maybe FileA])
-> IO [Maybe FileA] -> Action [Maybe FileA]
forall a b. (a -> b) -> a -> b
$ (FileName -> IO (Maybe FileA)) -> [FileName] -> IO [Maybe FileA]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (ShakeOptions -> FileQ -> IO (Maybe FileA)
fileStoredValue ShakeOptions
opts (FileQ -> IO (Maybe FileA))
-> (FileName -> FileQ) -> FileName -> IO (Maybe FileA)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FileName -> FileQ
FileQ) [FileName]
xs
    [FileR]
post <- (FileName -> FileName) -> [FileName] -> Action [FileR]
forall a. (a -> FileName) -> [a] -> Action [FileR]
apply_ FileName -> FileName
forall a. a -> a
id [FileName]
xs
    let bad :: [(FileName, String)]
bad = [ (FileName
x, if Maybe FileA -> Bool
forall a. Maybe a -> Bool
isJust Maybe FileA
a then "File change" else "File created")
              | (x :: FileName
x, a :: Maybe FileA
a, FileR (Just b :: FileA
b) _) <- [FileName]
-> [Maybe FileA] -> [FileR] -> [(FileName, Maybe FileA, FileR)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [FileName]
xs [Maybe FileA]
pre [FileR]
post, EqualCost -> (FileA -> EqualCost) -> Maybe FileA -> EqualCost
forall b a. b -> (a -> b) -> Maybe a -> b
maybe EqualCost
NotEqual (\a :: FileA
a -> ShakeOptions -> FileA -> FileA -> EqualCost
fileEqualValue ShakeOptions
opts FileA
a FileA
b) Maybe FileA
a EqualCost -> EqualCost -> Bool
forall a. Eq a => a -> a -> Bool
== EqualCost
NotEqual]
    case [(FileName, String)]
bad of
        [] -> () -> Action ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        (file :: FileName
file,msg :: String
msg):_ -> IO () -> Action ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Action ()) -> IO () -> Action ()
forall a b. (a -> b) -> a -> b
$ String -> [(String, Maybe String)] -> String -> IO ()
forall a. String -> [(String, Maybe String)] -> String -> IO a
errorStructured
            "Lint checking error - 'needed' file required rebuilding"
            [("File", String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ FileName -> String
fileNameToString FileName
file)
            ,("Error",String -> Maybe String
forall a. a -> Maybe a
Just String
msg)]
            ""


-- | Track that a file was read by the action preceeding it. If 'shakeLint' is activated
--   then these files must be dependencies of this rule. Calls to 'trackRead' are
--   automatically inserted in 'LintFSATrace' mode.
trackRead :: [FilePath] -> Action ()
trackRead :: [String] -> Action ()
trackRead = (String -> Action ()) -> [String] -> Action ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (FileQ -> Action ()
forall key. ShakeValue key => key -> Action ()
trackUse (FileQ -> Action ()) -> (String -> FileQ) -> String -> Action ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FileName -> FileQ
FileQ (FileName -> FileQ) -> (String -> FileName) -> String -> FileQ
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> FileName
fileNameFromString)

-- | Track that a file was written by the action preceeding it. If 'shakeLint' is activated
--   then these files must either be the target of this rule, or never referred to by the build system.
--   Calls to 'trackWrite' are automatically inserted in 'LintFSATrace' mode.
trackWrite :: [FilePath] -> Action ()
trackWrite :: [String] -> Action ()
trackWrite = (String -> Action ()) -> [String] -> Action ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (FileQ -> Action ()
forall key. ShakeValue key => key -> Action ()
trackChange (FileQ -> Action ()) -> (String -> FileQ) -> String -> Action ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FileName -> FileQ
FileQ (FileName -> FileQ) -> (String -> FileName) -> String -> FileQ
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> FileName
fileNameFromString)

-- | Allow accessing a file in this rule, ignoring any 'trackRead' \/ 'trackWrite' calls matching
--   the pattern.
trackAllow :: [FilePattern] -> Action ()
trackAllow :: [String] -> Action ()
trackAllow ps :: [String]
ps = do
    ShakeOptions
opts <- Action ShakeOptions
getShakeOptions
    Bool -> Action () -> Action ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe Lint -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Lint -> Bool) -> Maybe Lint -> Bool
forall a b. (a -> b) -> a -> b
$ ShakeOptions -> Maybe Lint
shakeLint ShakeOptions
opts) (Action () -> Action ()) -> Action () -> Action ()
forall a b. (a -> b) -> a -> b
$
        (FileQ -> Bool) -> Action ()
forall key. ShakeValue key => (key -> Bool) -> Action ()
S.trackAllow ((FileQ -> Bool) -> Action ()) -> (FileQ -> Bool) -> Action ()
forall a b. (a -> b) -> a -> b
$ \(FileQ x :: FileName
x) -> (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> String -> Bool
?== FileName -> String
fileNameToString FileName
x) [String]
ps


-- | Require that the argument files are built by the rules, used to specify the target.
--
-- @
-- main = 'Development.Shake.shake' 'shakeOptions' $ do
--    'want' [\"Main.exe\"]
--    ...
-- @
--
--   This program will build @Main.exe@, given sufficient rules. All arguments to all 'want' calls
--   may be built in parallel, in any order.
--
--   This function is defined in terms of 'action' and 'need', use 'action' if you need more complex
--   targets than 'want' allows.
want :: [FilePath] -> Rules ()
want :: [String] -> Rules ()
want [] = () -> Rules ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
want xs :: [String]
xs = Action () -> Rules ()
forall a. Action a -> Rules ()
action (Action () -> Rules ()) -> Action () -> Rules ()
forall a b. (a -> b) -> a -> b
$ [String] -> Action ()
need [String]
xs


root :: String -> (FilePath -> Bool) -> (FilePath -> Action ()) -> Rules ()
root :: String -> (String -> Bool) -> (String -> Action ()) -> Rules ()
root help :: String
help test :: String -> Bool
test act :: String -> Action ()
act = FileRule -> Rules ()
forall a. Typeable a => a -> Rules ()
addUserRule (FileRule -> Rules ()) -> FileRule -> Rules ()
forall a b. (a -> b) -> a -> b
$ (String -> Maybe Mode) -> FileRule
FileRule ((String -> Maybe Mode) -> FileRule)
-> (String -> Maybe Mode) -> FileRule
forall a b. (a -> b) -> a -> b
$ \x :: String
x -> if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
test String
x then Maybe Mode
forall a. Maybe a
Nothing else Mode -> Maybe Mode
forall a. a -> Maybe a
Just (Mode -> Maybe Mode) -> Mode -> Maybe Mode
forall a b. (a -> b) -> a -> b
$ Action () -> Mode
ModeDirect (Action () -> Mode) -> Action () -> Mode
forall a b. (a -> b) -> a -> b
$ do
    IO () -> Action ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Action ()) -> IO () -> Action ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
createDirectoryRecursive (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ ShowS
takeDirectory String
x
    String -> Action ()
act String
x


-- | Declare a Make-style phony action.  A phony target does not name
--   a file (despite living in the same namespace as file rules);
--   rather, it names some action to be executed when explicitly
--   requested.  You can demand 'phony' rules using 'want'. (And 'need',
--   although that's not recommended.)
--
--   Phony actions are intended to define recipes that can be executed
--   by the user. If you 'need' a phony action in a rule then every
--   execution where that rule is required will rerun both the rule and
--   the phony action.  However, note that phony actions are never
--   executed more than once in a single build run.
--
--   In make, the @.PHONY@ attribute on non-file-producing rules has a
--   similar effect.  However, while in make it is acceptable to omit
--   the @.PHONY@ attribute as long as you don't create the file in
--   question, a Shake rule which behaves this way will fail lint.
--   Use a phony rule!  For file-producing rules which should be
--   rerun every execution of Shake, see 'Development.Shake.alwaysRerun'.
phony :: String -> Action () -> Rules ()
phony :: String -> Action () -> Rules ()
phony (ShowS
toStandard -> String
name) act :: Action ()
act = (String -> Maybe (Action ())) -> Rules ()
phonys ((String -> Maybe (Action ())) -> Rules ())
-> (String -> Maybe (Action ())) -> Rules ()
forall a b. (a -> b) -> a -> b
$ \s :: String
s -> if String
s String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
name then Action () -> Maybe (Action ())
forall a. a -> Maybe a
Just Action ()
act else Maybe (Action ())
forall a. Maybe a
Nothing

-- | A predicate version of 'phony', return 'Just' with the 'Action' for the matching rules.
phonys :: (String -> Maybe (Action ())) -> Rules ()
phonys :: (String -> Maybe (Action ())) -> Rules ()
phonys act :: String -> Maybe (Action ())
act = FileRule -> Rules ()
forall a. Typeable a => a -> Rules ()
addUserRule (FileRule -> Rules ()) -> FileRule -> Rules ()
forall a b. (a -> b) -> a -> b
$ (String -> Maybe Mode) -> FileRule
FileRule ((String -> Maybe Mode) -> FileRule)
-> (String -> Maybe Mode) -> FileRule
forall a b. (a -> b) -> a -> b
$ (Action () -> Mode) -> Maybe (Action ()) -> Maybe Mode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Action () -> Mode
ModePhony (Maybe (Action ()) -> Maybe Mode)
-> (String -> Maybe (Action ())) -> String -> Maybe Mode
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe (Action ())
act

-- | Infix operator alias for 'phony', for sake of consistency with normal
--   rules.
(~>) :: String -> Action () -> Rules ()
~> :: String -> Action () -> Rules ()
(~>) = String -> Action () -> Rules ()
phony


-- | Define a rule to build files. If the first argument returns 'True' for a given file,
--   the second argument will be used to build it. Usually '%>' is sufficient, but '?>' gives
--   additional power. For any file used by the build system, only one rule should return 'True'.
--   This function will create the directory for the result file, if necessary.
--
-- @
-- (all isUpper . 'Development.Shake.FilePath.takeBaseName') '?>' \\out -> do
--     let src = 'Development.Shake.FilePath.replaceBaseName' out $ map toLower $ takeBaseName out
--     'Development.Shake.writeFile'' out . map toUpper =<< 'Development.Shake.readFile'' src
-- @
--
--   If the 'Action' completes successfully the file is considered up-to-date, even if the file
--   has not changed.
(?>) :: (FilePath -> Bool) -> (FilePath -> Action ()) -> Rules ()
?> :: (String -> Bool) -> (String -> Action ()) -> Rules ()
(?>) test :: String -> Bool
test act :: String -> Action ()
act = Double -> Rules () -> Rules ()
priority 0.5 (Rules () -> Rules ()) -> Rules () -> Rules ()
forall a b. (a -> b) -> a -> b
$ String -> (String -> Bool) -> (String -> Action ()) -> Rules ()
root "with ?>" String -> Bool
test String -> Action ()
act


-- | Define a set of patterns, and if any of them match, run the associated rule. Defined in terms of '%>'.
--   Think of it as the OR (@||@) equivalent of '%>'.
(|%>) :: [FilePattern] -> (FilePath -> Action ()) -> Rules ()
|%> :: [String] -> (String -> Action ()) -> Rules ()
(|%>) pats :: [String]
pats act :: String -> Action ()
act = do
    let (simp :: [String]
simp,other :: [String]
other) = (String -> Bool) -> [String] -> ([String], [String])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition String -> Bool
simple [String]
pats
    case [String]
simp of
        [] -> () -> Rules ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        [p :: String
p] -> let pp :: String
pp = ShowS
toStandard String
p in String -> (String -> Bool) -> (String -> Action ()) -> Rules ()
root "with |%>" (\x :: String
x -> ShowS
toStandard String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
pp) String -> Action ()
act
        ps :: [String]
ps -> let ps :: HashSet String
ps = [String] -> HashSet String
forall a. (Eq a, Hashable a) => [a] -> HashSet a
Set.fromList ([String] -> HashSet String) -> [String] -> HashSet String
forall a b. (a -> b) -> a -> b
$ ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ShowS
toStandard [String]
pats in String -> (String -> Bool) -> (String -> Action ()) -> Rules ()
root "with |%>" ((String -> HashSet String -> Bool)
-> HashSet String -> String -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> HashSet String -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
Set.member HashSet String
ps (String -> Bool) -> ShowS -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
toStandard) String -> Action ()
act
    Bool -> Rules () -> Rules ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
other) (Rules () -> Rules ()) -> Rules () -> Rules ()
forall a b. (a -> b) -> a -> b
$
        let ps :: [String -> Bool]
ps = (String -> String -> Bool) -> [String] -> [String -> Bool]
forall a b. (a -> b) -> [a] -> [b]
map String -> String -> Bool
(?==) [String]
other in Double -> Rules () -> Rules ()
priority 0.5 (Rules () -> Rules ()) -> Rules () -> Rules ()
forall a b. (a -> b) -> a -> b
$ String -> (String -> Bool) -> (String -> Action ()) -> Rules ()
root "with |%>" (\x :: String
x -> ((String -> Bool) -> Bool) -> [String -> Bool] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ String
x) [String -> Bool]
ps) String -> Action ()
act

-- | Define a rule that matches a 'FilePattern', see '?==' for the pattern rules.
--   Patterns with no wildcards have higher priority than those with wildcards, and no file
--   required by the system may be matched by more than one pattern at the same priority
--   (see 'priority' and 'alternatives' to modify this behaviour).
--   This function will create the directory for the result file, if necessary.
--
-- @
-- \"*.asm.o\" '%>' \\out -> do
--     let src = 'Development.Shake.FilePath.dropExtension' out
--     'need' [src]
--     'Development.Shake.cmd' \"as\" [src] \"-o\" [out]
-- @
--
--   To define a build system for multiple compiled languages, we recommend using @.asm.o@,
--   @.cpp.o@, @.hs.o@, to indicate which language produces an object file.
--   I.e., the file @foo.cpp@ produces object file @foo.cpp.o@.
--
--   Note that matching is case-sensitive, even on Windows.
--
--   If the 'Action' completes successfully the file is considered up-to-date, even if the file
--   has not changed.
(%>) :: FilePattern -> (FilePath -> Action ()) -> Rules ()
%> :: String -> (String -> Action ()) -> Rules ()
(%>) test :: String
test act :: String -> Action ()
act = (if String -> Bool
simple String
test then Rules () -> Rules ()
forall a. a -> a
id else Double -> Rules () -> Rules ()
priority 0.5) (Rules () -> Rules ()) -> Rules () -> Rules ()
forall a b. (a -> b) -> a -> b
$ String -> (String -> Bool) -> (String -> Action ()) -> Rules ()
root (ShowS
forall a. Show a => a -> String
show String
test) (String
test String -> String -> Bool
?==) String -> Action ()
act