shake 0.3.4 → 0.3.5
raw patch · 7 files changed
+124/−58 lines, 7 files
Files
- Development/Shake/Core.hs +10/−8
- Development/Shake/FilePattern.hs +57/−29
- Examples/Test/Basic.hs +0/−8
- Examples/Test/FilePattern.hs +51/−0
- Examples/Test/Files.hs +0/−10
- Main.hs +4/−2
- shake.cabal +2/−1
Development/Shake/Core.hs view
@@ -45,7 +45,7 @@ ,shakeStaunch :: Bool -- ^ Operate in staunch mode, where building continues even after errors (defaults to 'False'). ,shakeReport :: Maybe FilePath -- ^ Produce an HTML profiling report (defaults to 'Nothing'). ,shakeLint :: Bool -- ^ Perform basic sanity checks after building (defaults to 'False').- ,shakeDeterministic :: Bool -- ^ Build files in a detereminstic order, as far as possbile+ ,shakeDeterministic :: Bool -- ^ Build files in a deterministic order, as far as possible } deriving (Show,Eq,Ord,Typeable,Data) @@ -54,7 +54,7 @@ shakeOptions = ShakeOptions ".shake" 1 1 Normal False Nothing False False --- | All forseen exception conditions thrown by Shake, such problems with the rules or errors when executing+-- | All foreseen exception conditions thrown by Shake, such problems with the rules or errors when executing -- rules, will be raised using this exception type. data ShakeException = ShakeException [String] -- Entries on the stack, starting at the top of the stack.@@ -170,18 +170,20 @@ -- | Like 'rule', but lower priority, if no 'rule' exists then 'defaultRule' is checked. -- All default rules must be disjoint. defaultRule :: Rule key value => (key -> Maybe (Action value)) -> Rules ()-defaultRule = ruleWith 0+defaultRule = rulePriority 0 -- | Add a rule to build a key, returning an appropriate 'Action'. All rules must be disjoint. -- To define lower priority rules use 'defaultRule'. rule :: Rule key value => (key -> Maybe (Action value)) -> Rules ()-rule = ruleWith 1+rule = rulePriority 1 --- | Add a rule at a given priority.-ruleWith :: Rule key value => Int -> (key -> Maybe (Action value)) -> Rules ()-ruleWith i r = mempty{rules = Map.singleton k (k, v, [(i,ARule r)])}+-- | Add a rule at a given priority, higher numbers correspond to higher-priority rules.+-- The function 'defaultRule' is priority 0 and 'rule' is priority 1. All rules of the same+-- priority must be disjoint.+rulePriority :: Rule key value => Int -> (key -> Maybe (Action value)) -> Rules ()+rulePriority i r = mempty{rules = Map.singleton k (k, v, [(i,ARule r)])} where k = typeOf $ ruleKey r; v = typeOf $ ruleValue r @@ -217,7 +219,7 @@ deriving (Functor, Monad, MonadIO) --- | This function is not actually exported, but Haddock is buggy. Please ignore.+-- | Internal main function (not exported publicly) run :: ShakeOptions -> Rules () -> IO () run opts@ShakeOptions{..} rs = do start <- startTime
Development/Shake/FilePattern.hs view
@@ -1,11 +1,10 @@-{-# LANGUAGE MultiParamTypeClasses, GeneralizedNewtypeDeriving, DeriveDataTypeable #-} module Development.Shake.FilePattern( FilePattern, (?==), compatible, extract, substitute ) where -import Data.List+import System.FilePath(pathSeparators) -- | A type synonym for file patterns, containing @\/\/@ and @*@. For the syntax@@ -13,11 +12,57 @@ type FilePattern = String +data Lexeme = Star | SlashSlash | Char Char deriving (Show, Eq)++isChar (Char _) = True; isChar _ = False++data Regex = Lit [Char] | Not [Char] | Any+ | Start | End+ | Bracket Regex+ | Or Regex Regex | Concat Regex Regex+ | Repeat Regex | Empty+ deriving Show++type SString = (Bool, String) -- fst is True if at the start of the string+++lexer :: FilePattern -> [Lexeme]+lexer ('*':xs) = Star : lexer xs+lexer ('/':'/':xs) = SlashSlash : lexer xs+lexer (x:xs) = Char x : lexer xs+lexer [] = []+++pattern :: [Lexeme] -> Regex+pattern = Concat Start . foldr Concat End . map f+ where+ f Star = Bracket $ Repeat $ Not pathSeparators+ f SlashSlash = let s = Start `Or` End `Or` Lit pathSeparators in Bracket $ s `Concat` Repeat Any `Concat` s+ f (Char x) = Lit $ if x == '/' then pathSeparators else [x]+++-- | Return is (brackets, matched, rest)+match :: Regex -> SString -> [([String], String, SString)]+match (Lit l) (_, x:xs) | x `elem` l = [([], [x], (False, xs))]+match (Not l) (_, x:xs) | x `notElem` l = [([], [x], (False, xs))]+match Any (_, x:xs) = [([], [x], (False, xs))]+match Start (True, xs) = [([], [], (True, xs))]+match End (s, []) = [([], [], (s, []))]+match (Bracket r) xs = [(a ++ [b], b, c) | (a,b,c) <- match r xs]+match (Or r1 r2) xs = match r1 xs ++ match r2 xs+match (Concat r1 r2) xs = [(a1++a2,b1++b2,c2) | (a1,b1,c1) <- match r1 xs, (a2,b2,c2) <- match r2 c1]+match (Repeat r) xs = match (Empty `Or` Concat r (Repeat r)) xs+match Empty xs = [([], "", xs)]+match _ _ = []++++ -- | Match a 'FilePattern' against a 'FilePath', There are only two special forms: -- -- * @*@ matches an entire path component, excluding any separators. ----- * @\/\/@ matches an arbitrary number of path componenets.+-- * @\/\/@ matches an arbitrary number of path components. -- -- Some examples that match: --@@ -32,46 +77,29 @@ -- > "*/*.c" ?== "foo/bar/baz.c" -- (?==) :: FilePattern -> FilePath -> Bool-(?==) ('/':'/':p) x = any (p ?==) $ x : [i | '/':i <- tails x]-(?==) ('*':p) x = any (p ?==) $ a ++ take 1 b- where (a,b) = break ("/" `isPrefixOf`) $ tails x-(?==) (p:ps) (x:xs) | p == x = ps ?== xs-(?==) [] [] = True-(?==) _ _ = False+(?==) p x = not $ null $ match (pattern $ lexer p) (True, x) -- | Do they have the same * and // counts in the same order compatible :: [FilePattern] -> Bool compatible [] = True compatible (x:xs) = all ((==) (f x) . f) xs- where- f ('*':xs) = '*':f xs- f ('/':'/':xs) = '/':f xs- f (x:xs) = f xs- f [] = []+ where f = filter (not . isChar) . lexer -- | Extract the items that match the wildcards. The pair must match with '?=='. extract :: FilePattern -> FilePath -> [String]-extract p x = head $ f p x ++ [[]]- where- f ('/':'/':p) x = rest p $ ("",x) : [(pre++"/",i) | (pre,'/':i) <- zip (inits x) (tails x)]- f ('*':p) x = rest p $ a ++ take 1 b- where (a,b) = break (isPrefixOf "/" . snd) $ zip (inits x) (tails x)- f (p:ps) (x:xs) | p == x = f ps xs- f [] [] = [[]]- f _ _ = []-- rest p xs = [skip:res | (skip,keep) <- xs, res <- f p keep]+extract p x = ms+ where (ms,_,_):_ = match (pattern $ lexer p) (True,x) -- | Given the result of 'extract', substitute it back in to a 'compatible' pattern. -- -- > p '?==' x ==> substitute (extract p x) p == x substitute :: [String] -> FilePattern -> FilePath-substitute = f+substitute ms p = f ms (lexer p) where- f (a:as) ('/':'/':ps) = a ++ f as ps- f (a:as) ('*':ps) = a ++ f as ps- f as (p:ps) = p : f as ps- f as [] = []+ f ms (Char p:ps) = p : f ms ps+ f (m:ms) (_:ps) = m ++ f ms ps+ f [] [] = []+ f _ _ = error $ "Substitution failed into pattern " ++ show p ++ " with " ++ show (length ms) ++ " matches, namely " ++ show ms
Examples/Test/Basic.hs view
@@ -25,14 +25,6 @@ test build obj = do- let f b pat file = assert (b == (pat ?== file)) $ show pat ++ " ?== " ++ show file ++ "\nEXPECTED: " ++ show b- f True "//*.c" "foo/bar/baz.c"- f True "*.c" "baz.c"- f True "//*.c" "baz.c"- f True "test.c" "test.c"- f False "*.c" "foor/bar.c"- f False "*/*.c" "foo/bar/baz.c"- writeFile (obj "A.txt") "AAA" writeFile (obj "B.txt") "BBB" build ["AB.txt"]
+ Examples/Test/FilePattern.hs view
@@ -0,0 +1,51 @@++module Examples.Test.FilePattern(main) where++import Development.Shake.FilePattern+import Examples.Util++main = shaken test $ \args obj -> return ()+++test build obj = do+ let f b pat file = assert (b == (pat ?== file)) $ show pat ++ " ?== " ++ show file ++ "\nEXPECTED: " ++ show b+ f True "//*.c" "foo/bar/baz.c"+ f True "*.c" "baz.c"+ f True "//*.c" "baz.c"+ f True "test.c" "test.c"+ f False "*.c" "foor/bar.c"+ f False "*/*.c" "foo/bar/baz.c"+ f False "foo//bar" "foobar"+ f False "foo//bar" "foobar/bar"+ f False "foo//bar" "foo/foobar"++ assert (compatible []) "compatible"+ assert (compatible ["//*a.txt","foo//a*.txt"]) "compatible"+ assert (not $ compatible ["//*a.txt","foo//a*.*txt"]) "compatible"+ extract "//*a.txt" "foo/bar/testa.txt" === ["foo/bar/","test"]+ extract "//*a.txt" "testa.txt" === ["","test"]+ extract "//*a*.txt" "testada.txt" === ["","test","da"]+ substitute ["","test","da"] "//*a*.txt" === "testada.txt"+ substitute ["foo/bar/","test"] "//*a.txt" === "foo/bar/testa.txt"+++---------------------------------------------------------------------+-- LAZY SMALLCHECK PROPERTIES++{-+newtype Pattern = Pattern FilePattern deriving (Show,Eq)+newtype Path = Path FilePath deriving (Show,Eq)++-- Since / and * are the only "interesting" elements, just add ab to round out the set++instance Serial Pattern where+ series = cons Pattern >< f+ where f = cons [] \/ cons (:) >< const (drawnFrom "/*ab") >< f++instance Serial Path where+ series = cons Path >< f+ where f = cons [] \/ cons (:) >< const (drawnFrom "/ab") >< f++testSmallCheck = do+ smallCheck 10 $ \(Pattern p) (Path x) -> p ?== x ==> substitute (extract p x) p == x+-}
Examples/Test/Files.hs view
@@ -2,7 +2,6 @@ module Examples.Test.Files(main) where import Development.Shake-import Development.Shake.FilePattern import Examples.Util import Control.Monad import Data.List@@ -23,15 +22,6 @@ test build obj = do- assert (compatible []) "compatible"- assert (compatible ["//*a.txt","foo//a*.txt"]) "compatible"- assert (not $ compatible ["//*a.txt","foo//a*.*txt"]) "compatible"- extract "//*a.txt" "foo/bar/testa.txt" === ["foo/bar/","test"]- extract "//*a.txt" "testa.txt" === ["","test"]- extract "//*a*.txt" "testada.txt" === ["","test","da"]- substitute ["","test","da"] "//*a*.txt" === "testada.txt"- substitute ["foo/bar/","test"] "//*a.txt" === "foo/bar/testa.txt"- forM_ [[],["fun"]] $ \args -> do sleepFileTime let nums = unlines . map show
Main.hs view
@@ -12,6 +12,7 @@ import qualified Examples.Test.Errors as Errors import qualified Examples.Test.Files as Files import qualified Examples.Test.FilePath as FilePath+import qualified Examples.Test.FilePattern as FilePattern import qualified Examples.Test.Journal as Journal import qualified Examples.Test.Pool as Pool import qualified Examples.Test.Random as Random@@ -23,8 +24,9 @@ mains = ["tar" * Tar.main, "self" * Self.main, "c" * C.main ,"basic" * Basic.main, "directory" * Directory.main, "errors" * Errors.main- ,"filepath" * FilePath.main, "files" * Files.main, "journal" * Journal.main- ,"pool" * Pool.main, "random" * Random.main,"resources" * Resources.main]+ ,"filepath" * FilePath.main, "filepattern" * FilePattern.main, "files" * Files.main+ ,"journal" * Journal.main, "pool" * Pool.main, "random" * Random.main+ ,"resources" * Resources.main] where (*) = (,)
shake.cabal view
@@ -1,7 +1,7 @@ cabal-version: >= 1.8 build-type: Simple name: shake-version: 0.3.4+version: 0.3.5 license: BSD3 license-file: LICENSE category: Development@@ -137,6 +137,7 @@ Examples.Test.Directory Examples.Test.Errors Examples.Test.FilePath+ Examples.Test.FilePattern Examples.Test.Files Examples.Test.Journal Examples.Test.Pool