packages feed

shake 0.10.5 → 0.10.6

raw patch · 34 files changed

+354/−158 lines, 34 files

Files

Development/Ninja/All.hs view
@@ -2,10 +2,10 @@ 
 module Development.Ninja.All(runNinja) where
 
+import Development.Ninja.Env
 import Development.Ninja.Type
 import Development.Ninja.Parse
 import Development.Shake hiding (Rule)
-import Development.Shake.Command
 import Development.Shake.FilePath
 import Development.Shake.Timing
 import qualified Data.ByteString.Char8 as BS
@@ -29,13 +29,16 @@         pools <- fmap Map.fromList $ forM pools $ \(name,depth) ->
             fmap ((,) name) $ newResource (BS.unpack name) depth
 
-        want $ map (normalise . BS.unpack) $ concatMap (resolvePhony phonys) $ if null args then defaults else map BS.pack args
+        want $ map (normalise . BS.unpack) $ concatMap (resolvePhony phonys) $
+            if not $ null args then map BS.pack args
+            else if not $ null defaults then defaults
+            else Map.keys singles ++ Map.keys multiples
 
         (\x -> fmap (map BS.unpack . fst) $ Map.lookup (BS.pack x) multiples) ?>> \out -> let out2 = map BS.pack out in
-            build defines phonys rules pools out2 $ snd $ multiples Map.! head out2
+            build phonys rules pools out2 $ snd $ multiples Map.! head out2
 
         (flip Map.member singles . BS.pack) ?> \out -> let out2 = BS.pack out in
-            build defines phonys rules pools [out2] $ singles Map.! out2
+            build phonys rules pools [out2] $ singles Map.! out2
 
 
 resolvePhony :: Map.HashMap Str [Str] -> Str -> [Str]
@@ -48,24 +51,27 @@             Just xs -> concatMap (f $ either (Left . subtract 1) (Right . (x:)) a) xs
 
 
-build :: Env -> Map.HashMap Str [Str] -> Map.HashMap Str Rule -> Map.HashMap Str Resource -> [Str] -> Build -> Action ()
-build env phonys rules pools out Build{..} = do
+build :: Map.HashMap Str [Str] -> Map.HashMap Str Rule -> Map.HashMap Str Resource -> [Str] -> Build -> Action ()
+build phonys rules pools out Build{..} = do
     need $ map (normalise . BS.unpack) $ concatMap (resolvePhony phonys) $ depsNormal ++ depsImplicit ++ depsOrderOnly
     case Map.lookup ruleName rules of
         Nothing -> error $ "Ninja rule named " ++ BS.unpack ruleName ++ " is missing, required to build " ++ BS.unpack (BS.unwords out)
         Just Rule{..} -> do
-            env <- return $
-                addBinds ruleBind $ addBinds buildBind $
-                addEnv (BS.pack "in_newline") (BS.unlines depsNormal) $
-                addEnv (BS.pack "in") (BS.unwords depsNormal) $
-                addEnv (BS.pack "out") (BS.unwords out) env
+            env <- liftIO $ scopeEnv env
+            liftIO $ do
+                -- the order of adding new environment variables matters
+                addEnv env (BS.pack "out") (BS.unwords out)
+                addEnv env (BS.pack "in") (BS.unwords depsNormal)
+                addEnv env (BS.pack "in_newline") (BS.unlines depsNormal)
+                addBinds env buildBind
+                addBinds env ruleBind
 
             applyRspfile env $ do
-                let commandline = BS.unpack $ askVar env $ BS.pack "command"
-                let depfile = BS.unpack $ askVar env $ BS.pack "depfile"
-                let deps = BS.unpack $ askVar env $ BS.pack "deps"
-                let description = BS.unpack $ askVar env $ BS.pack "description"
-                let pool = askVar env $ BS.pack "pool"
+                commandline <- liftIO $ fmap BS.unpack $ askVar env $ BS.pack "command"
+                depfile <- liftIO $ fmap BS.unpack $ askVar env $ BS.pack "depfile"
+                deps <- liftIO $ fmap BS.unpack $ askVar env $ BS.pack "deps"
+                description <- liftIO $ fmap BS.unpack $ askVar env $ BS.pack "description"
+                pool <- liftIO $ askVar env $ BS.pack "pool"
 
                 let withPool act = case Map.lookup pool pools of
                         _ | BS.null pool -> act
@@ -85,17 +91,17 @@                     when (deps == "gcc") $ liftIO $ removeFile depfile
 
 
-applyRspfile :: Env -> Action a -> Action a
-applyRspfile env act
-    | rspfile == "" = act
-    | otherwise = do
+applyRspfile :: Env Str Str -> Action a -> Action a
+applyRspfile env act = do
+    rspfile <- liftIO $ fmap BS.unpack $ askVar env $ BS.pack "rspfile"
+    rspfile_content <- liftIO $ askVar env $ BS.pack "rspfile_content"
+    if rspfile == "" then
+        act
+     else do
         liftIO $ BS.writeFile rspfile rspfile_content
         res <- act
         liftIO $ removeFile rspfile
         return res
-    where
-        rspfile = BS.unpack $ askVar env $ BS.pack "rspfile"
-        rspfile_content = askVar env $ BS.pack "rspfile_content"
 
 
 parseShowIncludes :: String -> [FilePath]
+ Development/Ninja/Env.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE RecordWildCards, PatternGuards #-}++-- | A Ninja style environment, basically a linked-list of mutable hash tables.+module Development.Ninja.Env(+    Env, newEnv, scopeEnv, addEnv, askEnv+    ) where++import qualified Data.HashMap.Strict as Map+import Data.Hashable+import Data.IORef+++data Env k v = Env (IORef (Map.HashMap k v)) (Maybe (Env k v))++instance Show (Env k v) where show _ = "Env"+++newEnv :: IO (Env k v)+newEnv = do ref <- newIORef Map.empty; return $ Env ref Nothing+++scopeEnv :: Env k v -> IO (Env k v)+scopeEnv e = do ref <- newIORef Map.empty; return $ Env ref $ Just e+++addEnv :: (Eq k, Hashable k) => Env k v -> k -> v -> IO ()+addEnv (Env ref _) k v = modifyIORef ref $ Map.insert k v+++askEnv :: (Eq k, Hashable k) => Env k v -> k -> IO (Maybe v)+askEnv (Env ref e) k = do+    mp <- readIORef ref+    case Map.lookup k mp of+        Just v -> return $ Just v+        Nothing | Just e <- e -> askEnv e k+        _ -> return Nothing
Development/Ninja/Parse.hs view
@@ -4,8 +4,8 @@ module Development.Ninja.Parse(parse) where
 
 import qualified Data.ByteString.Char8 as BS
+import Development.Ninja.Env
 import Development.Ninja.Type
-import Control.Arrow
 import Control.Monad
 import Data.Char
 import Data.Maybe
@@ -36,13 +36,13 @@ isVar x = isAlphaNum x || x == '_'
 
 parse :: FilePath -> IO Ninja
-parse file = parseFile file newNinja
+parse file = do env <- newEnv; parseFile file env newNinja
 
 
-parseFile :: FilePath -> Ninja -> IO Ninja
-parseFile file ninja = do
+parseFile :: FilePath -> Env Str Str -> Ninja -> IO Ninja
+parseFile file env ninja = do
     src <- if file == "-" then BS.getContents else BS.readFile file
-    foldM applyStmt ninja $ splitStmts src
+    foldM (applyStmt env) ninja $ splitStmts src
 
 
 data Stmt = Stmt Str [Str] deriving Show
@@ -61,41 +61,47 @@             where (a,b) = span startsSpace xs
 
 
-applyStmt :: Ninja -> Stmt -> IO Ninja
-applyStmt ninja@Ninja{..} (Stmt x xs)
+applyStmt :: Env Str Str -> Ninja -> Stmt -> IO Ninja
+applyStmt env ninja@Ninja{..} (Stmt x xs)
     | key == BS.pack "rule" =
         return ninja{rules = (BS.takeWhile isVar rest, Rule $ parseBinds xs) : rules}
-    | key == BS.pack "default" =
-        return ninja{defaults = parseStrs defines rest ++ defaults}
-    | key == BS.pack "pool" =
-        return ninja{pools = (BS.takeWhile isVar rest, getDepth $ map (second $ askExpr defines) $ parseBinds xs) : pools}
-    | key == BS.pack "build",
-        (out,rest) <- splitColon rest,
-        outputs <- parseStrs defines out,
-        (rule,deps) <- word1 $ dropSpace rest,
-        (normal,implicit,orderOnly) <- splitDeps $ parseStrs defines deps,
-        let build = Build rule normal implicit orderOnly $ parseBinds xs
-        = return $
+    | key == BS.pack "default" = do
+        xs <- parseStrs env rest
+        return ninja{defaults = xs ++ defaults}
+    | key == BS.pack "pool" = do
+        depth <- getDepth env $ parseBinds xs
+        return ninja{pools = (BS.takeWhile isVar rest, depth) : pools}
+    | key == BS.pack "build" = do
+        (out,rest) <- return $ splitColon rest
+        outputs <- parseStrs env out
+        (rule,deps) <- return $ word1 $ dropSpace rest
+        (normal,implicit,orderOnly) <- fmap splitDeps $ parseStrs env deps
+        let build = Build rule env normal implicit orderOnly $ parseBinds xs
+        return $
             if rule == BS.pack "phony" then ninja{phonys = [(x, normal) | x <- outputs] ++ phonys}
             else if length outputs == 1 then ninja{singles = (head outputs, build) : singles}
             else ninja{multiples = (outputs, build) : multiples}
-    | key == BS.pack "include" = parseFile (BS.unpack rest) ninja
+    | key == BS.pack "include" = parseFile (BS.unpack rest) env ninja
     | key == BS.pack "subninja" = do
-        ninja <- parseFile (BS.unpack rest) ninja
-        -- restore to the original environment after, which is what Ninja actually does
-        return ninja{defines = defines}
-    | Just (a,b) <- parseBind x = return ninja{defines = addBind a b defines}
+        e <- scopeEnv env
+        parseFile (BS.unpack rest) e ninja
+    | Just (a,b) <- parseBind x = do
+        addBind env a b
+        return ninja
     | BS.null $ dropSpace x = return ninja
     | BS.pack "#" `BS.isPrefixOf` dropSpace x = return ninja -- comments can only occur on their own line
     | otherwise = error $ "Cannot parse line: " ++ BS.unpack x
     where (key,rest) = word1 x
 
 
-getDepth :: [(Str, Str)] -> Int
-getDepth xs = case lookup (BS.pack "depth") xs of
-    Nothing -> 1
-    Just x | Just (i, n) <- BS.readInt x, BS.null n -> i
-           | otherwise -> error $ "Could not parse depth field in pool, got: " ++ BS.unpack x
+getDepth :: Env Str Str -> [(Str, Expr)] -> IO Int
+getDepth env xs = case lookup (BS.pack "depth") xs of
+    Nothing -> return 1
+    Just x -> do
+        x <- askExpr env x
+        case BS.readInt x of
+            Just (i, n) | BS.null n -> return i
+            _ -> error $ "Could not parse depth field in pool, got: " ++ BS.unpack x
 
 
 parseBind :: Str -> Maybe (Str, Expr)
@@ -129,8 +135,8 @@                 | otherwise -> let (a,b) = BS.span isVar x in Var a : f b
 
 
-parseStrs :: Env -> Str -> [Str]
-parseStrs env = map (askExpr env) . parseExprs
+parseStrs :: Env Str Str -> Str -> IO [Str]
+parseStrs env = mapM (askExpr env) . parseExprs
 
 
 
Development/Ninja/Type.hs view
@@ -8,8 +8,8 @@     Ninja(..), newNinja, Build(..), Rule(..),     ) where +import Development.Ninja.Env import qualified Data.ByteString.Char8 as BS-import qualified Data.HashMap.Strict as Map import Data.Maybe  @@ -18,40 +18,31 @@   ------------------------------------------------------------------------ ENVIRONMENT--newtype Env = Env (Map.HashMap Str Str) deriving Show+-- EXPRESSIONS AND BINDINGS  data Expr = Exprs [Expr] | Lit Str | Var Str deriving Show -newEnv :: Env-newEnv = Env Map.empty--askExpr :: Env -> Expr -> Str+askExpr :: Env Str Str -> Expr -> IO Str askExpr e = f-    where f (Exprs xs) = BS.concat $ map f xs-          f (Lit x) = x+    where f (Exprs xs) = fmap BS.concat $ mapM f xs+          f (Lit x) = return x           f (Var x) = askVar e x -askVar :: Env -> Str -> Str-askVar (Env e) x = fromMaybe BS.empty $ Map.lookup x e--addEnv :: Str -> Str -> Env -> Env-addEnv k v (Env e) = Env $ Map.insert k v e+askVar :: Env Str Str -> Str -> IO Str+askVar e x = fmap (fromMaybe BS.empty) $ askEnv e x -addBind :: Str -> Expr -> Env -> Env-addBind k v env = addEnv k (askExpr env v) env+addBind :: Env Str Str -> Str -> Expr -> IO ()+addBind e k v = addEnv e k =<< askExpr e v -addBinds :: [(Str, Expr)] -> Env -> Env-addBinds kvs e = foldl (\e (k,v) -> addBind k v e) e kvs+addBinds :: Env Str Str -> [(Str, Expr)] -> IO ()+addBinds e = mapM_ (uncurry $ addBind e)   --------------------------------------------------------------------- -- STRUCTURE  data Ninja = Ninja-    {defines :: !Env-    ,rules :: [(Str,Rule)]+    {rules :: [(Str,Rule)]     ,singles :: [(FileStr,Build)]     ,multiples :: [([FileStr], Build)]     ,phonys :: ([(Str, [FileStr])])@@ -61,10 +52,11 @@     deriving Show  newNinja :: Ninja-newNinja = Ninja newEnv [] [] [] [] [] []+newNinja = Ninja [] [] [] [] [] []  data Build = Build     {ruleName :: Str+    ,env :: Env Str Str     ,depsNormal :: [FileStr]     ,depsImplicit :: [FileStr]     ,depsOrderOnly :: [FileStr]
Development/Shake.hs view
@@ -12,7 +12,7 @@ --    \"*.tar\" '*>' \\out -> do --        contents \<- 'readFileLines' $ out 'Development.Shake.FilePath.-<.>' \"txt\" --        'need' contents---        'system'' \"tar\" $ [\"-cf\",out] ++ contents+--        'cmd' \"tar -cf\" [out] contents -- @ -- --   We start by importing the modules defining both Shake and routines for manipulating 'FilePath' values.@@ -29,7 +29,7 @@ --   change, then @result.tar@ will be rebuilt. -- --   When writing a Shake build system, start by defining what you 'want', then write rules---   with '*>' to produce the results. Before calling 'system'' you should ensure that any files the command+--   with '*>' to produce the results. Before calling 'cmd' you should ensure that any files the command --   requires are demanded with calls to 'need'. We offer the following advice to Shake users: -- -- * If @ghc --make@ or @cabal@ is capable of building your project, use that instead. Custom build systems are@@ -78,6 +78,10 @@     Progress(..), progressSimple, progressDisplay, progressTitlebar,     -- ** Verbosity     Verbosity(..), getVerbosity, putLoud, putNormal, putQuiet, quietly,+    -- * Running commands+    command, command_, cmd,+    Stdout(..), Stderr(..), Exit(..),+    CmdResult, CmdOption(..),     -- * Utility functions     module Development.Shake.Derived,     removeFiles, removeFilesAfter,@@ -111,6 +115,7 @@ import Development.Shake.Args import Development.Shake.Shake +import Development.Shake.Command import Development.Shake.Directory import Development.Shake.File import Development.Shake.FilePattern
Development/Shake/Args.hs view
@@ -20,6 +20,7 @@ import Data.List import Data.Maybe import Data.Time+import Data.Version(showVersion) import System.Console.GetOpt import System.Directory import System.Environment@@ -94,7 +95,7 @@ --     let compiler = if DistCC \`elem\` flags then \"distcc\" else \"gcc\" --     \"*.o\" '*>' \\out -> do --         'need' ...---         'Development.Shake.system'' compiler ...+--         'cmd' compiler ... --     ... -- @ --@@ -125,7 +126,7 @@     if Help `elem` flagsExtra then do         showHelp      else if Version `elem` flagsExtra then-        putStrLn $ "Shake build system, version " ++ show version+        putStrLn $ "Shake build system, version " ++ showVersion version      else do         when (Sleep `elem` flagsExtra) $ threadDelay 1000000         start <- getCurrentTime@@ -149,9 +150,12 @@          else             let esc code = if Color `elem` flagsExtra then escape code else id             in case res of-                Left err -> do-                    putStrLn $ esc "31" $ show (err :: SomeException)-                    exitFailure+                Left err ->+                    if Exception `elem` flagsExtra then+                        throw err+                    else do+                        putStrLn $ esc "31" $ show (err :: SomeException)+                        exitFailure                 Right () -> do                     stop <- getCurrentTime                     let tot = diffUTCTime stop start@@ -204,6 +208,7 @@            | Help            | Sleep            | NoTime+           | Exception              deriving Eq  @@ -224,6 +229,7 @@     ,no  $ Option "C" ["directory"] (ReqArg (\x -> Right ([ChangeDirectory x],id)) "DIRECTORY") "Change to DIRECTORY before doing anything."     ,yes $ Option ""  ["color","colour"] (NoArg $ Right ([Color], \s -> s{shakeOutput=outputColor (shakeOutput s)})) "Colorize the output."     ,yes $ Option "d" ["debug"] (OptArg (\x -> Right ([], \s -> s{shakeVerbosity=Diagnostic, shakeOutput=outputDebug (shakeOutput s) x})) "FILE") "Print lots of debugging information."+    ,no  $ Option ""  ["exception"] (NoArg $ Right ([Exception], id)) "Throw exceptions directly."     ,yes $ Option ""  ["flush"] (intArg "flush" "N" (\i s -> s{shakeFlush=Just i})) "Flush metadata every N seconds."     ,yes $ Option ""  ["never-flush"] (noArg $ \s -> s{shakeFlush=Nothing}) "Never explicitly flush metadata."     ,no  $ Option "h" ["help"] (NoArg $ Right ([Help],id)) "Print this message and exit."
Development/Shake/Command.hs view
@@ -1,9 +1,7 @@ {-# LANGUAGE FlexibleInstances, TypeSynonymInstances, TypeOperators #-} --- | /Future plans: I intend to merge this module into "Development.Shake" itself./------   This module provides more powerful and flexible versions of 'Development.Shake.system''.---   I recommend looking at 'command', followed by 'cmd'.+-- | /Deprecated:/ This module should no longer be imported as all the functions are available directly+--   from "Development.Shake". In future versions this module will be removed. module Development.Shake.Command(     command, command_, cmd,     Stdout(..), Stderr(..), Exit(..),
Development/Shake/Core.hs view
@@ -434,8 +434,8 @@   -- | Write an action to the trace list, along with the start/end time of running the IO action.---   The 'Develoment.Shake.system'' command automatically calls 'traced'. The trace list is used for profile reports---   (see 'shakeReport').+--   The 'Develoment.Shake.cmd' and 'Develoment.Shake.command' functions automatically call 'traced'.+--   The trace list is used for profile reports (see 'shakeReport'). traced :: String -> IO a -> Action a traced msg act = do     s <- Action State.get@@ -481,7 +481,7 @@   -- | Run an action with 'Quiet' verbosity, in particular messages produced by 'traced'---   (including from 'Development.Shake.system'') will not be printed to the screen.+--   (including from 'Development.Shake.cmd' or 'Development.Shake.command') will not be printed to the screen. quietly :: Action a -> Action a quietly = withVerbosity Quiet 
Development/Shake/Derived.hs view
@@ -35,7 +35,10 @@     checkExitCode cmd res  --- | Execute a system command with a specified current working directory (first argument).+-- | /Deprecated:/ Please use 'command' or 'cmd' instead, with 'Cwd'.+--   This function will be removed in a future version.+--+--   Execute a system command with a specified current working directory (first argument). --   This function will raise an error if the exit code is non-zero. --   Before running 'systemCwd' make sure you 'need' any required files. --@@ -55,7 +58,10 @@     checkExitCode cmd res  --- | Execute a system command, returning @(stdout,stderr)@.+-- | /Deprecated:/ Please use 'command' or 'cmd' instead, with 'Stdout' or 'Stderr'.+--   This function will be removed in a future version.+--+--   Execute a system command, returning @(stdout,stderr)@. --   This function will raise an error if the exit code is non-zero. --   Before running 'systemOutput' make sure you 'need' any required files. systemOutput :: FilePath -> [String] -> Action (String, String)
Development/Shake/Directory.hs view
@@ -173,6 +173,12 @@ -- > getDirectoryFiles "Modules" ["*.hs","*.lhs"] -- >     -- All .hs or .lhs in the Modules directory -- >     -- If Modules/foo.hs and Modules/foo.lhs exist, it will return ["foo.hs","foo.lhs"]+--+--   If you require a qualified file name it is often easier to use @\"\"@ as 'FilePath' argument,+--   for example the following two expressions are equivalent:+--+-- > fmap (map ("Config" </>)) (getDirectoryFiles "Config" ["//*.xml"])+-- > getDirectoryFiles "" ["Config//*.xml"] getDirectoryFiles :: FilePath -> [FilePattern] -> Action [FilePath] getDirectoryFiles x f = getDirAction $ GetDirFiles x f 
Development/Shake/File.hs view
@@ -99,13 +99,13 @@   -- | Require that the following files are built before continuing. Particularly---   necessary when calling 'Development.Shake.system''. As an example:+--   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.system'' \"rot13\" [src,\"-o\",out]+--     'Development.Shake.cmd' \"rot13\" [src] \"-o\" [out] -- @ need :: [FilePath] -> Action () need xs = (apply $ map (FileQ . pack) xs :: Action [FileA]) >> return ()@@ -167,7 +167,7 @@ -- \"*.asm.o\" '*>' \\out -> do --     let src = 'Development.Shake.FilePath.dropExtension' out --     'need' [src]---     'Development.Shake.system'' \"as\" [src,\"-o\",out]+--     'Development.Shake.cmd' \"as\" [src] \"-o\" [out] -- @ -- --   To define a build system for multiple compiled languages, we recommend using @.asm.o@,
Development/Shake/Files.hs view
@@ -38,7 +38,7 @@ -- [\"*.o\",\"*.hi\"] '*>>' \\[o,hi] -> do --     let hs = o 'Development.Shake.FilePath.-<.>' \"hs\" --     'Development.Shake.need' ... -- all files the .hs import---     'Development.Shake.system'' \"ghc\" [\"-c\",hs]+--     'Development.Shake.cmd' \"ghc -c\" [hs] -- @ -- --   However, in practice, it's usually easier to define rules with '*>' and make the @.hi@ depend
Development/Shake/Locks.hs view
@@ -91,14 +91,14 @@ --    excel <- 'Development.Shake.newResource' \"Excel\" 1 --    \"*.xls\" 'Development.Shake.*>' \\out -> --        'Development.Shake.withResource' excel 1 $---            'Development.Shake.system'' \"excel\" [out,...]+--            'Development.Shake.cmd' \"excel\" out ... -- @ -- --   Now the two calls to @excel@ will not happen in parallel. Using 'Resource' --   is better than 'MVar' as it will not block any other threads from executing. Be careful that the --   actions run within 'Development.Shake.withResource' do not themselves require further quantities of this resource, or --   you may get a \"thread blocked indefinitely in an MVar operation\" exception. Typically only---   system commands (such as 'Development.Shake.system'') should be run inside 'Development.Shake.withResource',+--   system commands (such as 'Development.Shake.cmd') should be run inside 'Development.Shake.withResource', --   not commands such as 'Development.Shake.need'. If an action requires multiple resources, use --   'Development.Shake.withResources' to avoid deadlock. --@@ -111,9 +111,9 @@ -- 'Development.Shake.want' [show i 'Development.Shake.FilePath.<.>' \"exe\" | i <- [1..100]] -- \"*.exe\" 'Development.Shake.*>' \\out -> --     'Development.Shake.withResource' disk 1 $---         'Development.Shake.system'' \"ld\" [\"-o\",out,...]+--         'Development.Shake.cmd' \"ld -o\" [out] ... -- \"*.o\" 'Development.Shake.*>' \\out ->---     'Development.Shake.system'' \"cl\" [\"-o\",out,...]+--     'Development.Shake.cmd' \"cl -o\" [out] ... -- @ data Resource = Resource {resourceKey :: Int, resourceName :: String, resourceMax :: Int, resourceVar :: Var (Int,[(Int,IO ())])} instance Show Resource where show Resource{..} = "Resource " ++ resourceName
Development/Shake/Oracle.hs view
@@ -39,7 +39,7 @@ -- @ -- newtype GhcVersion = GhcVersion () deriving (Show,Typeable,Eq,Hashable,Binary,NFData) -- rules = do---     'addOracle' $ \\(GhcVersion _) -> fmap (last . words . fst) $ 'Development.Shake.systemOutput' \"ghc\" [\"--version\"]+--     'addOracle' $ \\(GhcVersion _) -> fmap (last . words . 'Development.Shake.fromStdout') $ 'Development.Shake.cmd' \"ghc --version\" --     ... rules ... -- @ --@@ -66,7 +66,7 @@ -- --rules = do --    getPkgList \<- 'addOracle' $ \\GhcPkgList{} -> do---        (out,_) <- 'Development.Shake.systemOutput' \"ghc-pkg\" [\"list\",\"--simple-output\"]+--        Stdout out <- 'Development.Shake.cmd' \"ghc-pkg list --simple-output\" --        return [(reverse b, reverse a) | x <- words out, let (a,_:b) = break (== \'-\') $ reverse x] --    -- --    getPkgVersion \<- 'addOracle' $ \\(GhcPkgVersion pkg) -> do
Development/Shake/Rerun.hs view
@@ -27,7 +27,7 @@ -- @ -- \"ghcVersion.txt\" 'Development.Shake.*>' \\out -> do --     'alwaysRerun'---     (stdout,_) <- 'Development.Shake.systemOutput' \"ghc\" [\"--version\"]+--     Stdout stdout <- 'Development.Shake.cmd' \"ghc --version\" --     'Development.Shake.writeFileChanged' out stdout -- @ alwaysRerun :: Action ()
Development/Shake/Sys.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleInstances, TypeSynonymInstances, TypeOperators #-} --- | /Warning: I intend to remove this module. Please use "Development.Shake.Command" instead./+-- | /Warning: I intend to remove this module. Please use 'command' or 'cmd' instead./ -- --   This module provides versions of the 'Development.Shake.Derived.system'' family of functions --   which take a variable number of arguments.
Examples/C/Main.hs view
@@ -3,7 +3,6 @@  import Development.Shake import Development.Shake.FilePath-import Development.Shake.Command import Examples.Util  main = shaken noTest $ \args obj -> do
+ Examples/Ninja/Main.hs view
@@ -0,0 +1,41 @@++module Examples.Ninja.Main(main) where++import Development.Shake+import Development.Shake.FilePath+import System.Directory(copyFile)+import Examples.Util+import Data.List+import qualified Start+import System.Environment+++main = shaken test $ \args obj -> do+    let args2 = ("-C" ++ obj "") : map tail (filter ("@" `isPrefixOf`) args)+    let real = "real" `elem` args+    action $+        if real then cmd "ninja" args2 else liftIO $ withArgs args2 Start.main+++test build obj = do+    let run xs = build $ map ('@':) $ words xs+    build ["clean"]+    run "-f../../Examples/Ninja/test1.ninja"+    assertExists $ obj "out1.txt"++    run "-f../../Examples/Ninja/test2.ninja"+    assertExists $ obj "out2.2"+    assertMissing $ obj "out2.1"+    build ["clean"]+    run "-f../../Examples/Ninja/test2.ninja out2.1"+    assertExists $ obj "out2.1"+    assertMissing $ obj "out2.2"++    copyFile "Examples/Ninja/test3-sub.ninja" $ obj "test3-sub.ninja"+    copyFile "Examples/Ninja/test3-inc.ninja" $ obj "test3-inc.ninja"+    copyFile ("Examples/Ninja/" ++ if null exe then "test3-unix.ninja" else "test3-win.ninja") $ obj "test3-platform.ninja"+    run "-f../../Examples/Ninja/test3.ninja"+    assertNonSpace (obj "out3.1") "g4+b1+++i1"+    assertNonSpace (obj "out3.2") "g4++++i1"+    assertNonSpace (obj "out3.3") "g4++++i1"+    assertNonSpace (obj "out3.4") "g4+++s1+s2"
+ Examples/Ninja/test1.ninja view
@@ -0,0 +1,5 @@++rule run+    command = touch $out++build out1.txt: run
+ Examples/Ninja/test2.ninja view
@@ -0,0 +1,8 @@+
+rule run
+    command = touch $out
+
+build out2.1: run
+build out2.2: run
+
+default out2.2
+ Examples/Ninja/test3-inc.ninja view
@@ -0,0 +1,3 @@+v5 = i1
+
+build out3.3: dump
+ Examples/Ninja/test3-sub.ninja view
@@ -0,0 +1,6 @@+v4 = s1
+v5 = s1
+
+build out3.4: dump
+
+v5 = s2
+ Examples/Ninja/test3-unix.ninja view
@@ -0,0 +1,3 @@+
+rule dump
+    command = echo "$v1+$v2+$v3+$v4+$v5" > $out
+ Examples/Ninja/test3-win.ninja view
@@ -0,0 +1,3 @@+
+rule dump
+    command = cmd /c "echo $v1+$v2+$v3+$v4+$v5 > $out"
+ Examples/Ninja/test3.ninja view
@@ -0,0 +1,21 @@+
+
+v1 = g1
+v5 = g1
+
+include test3-platform.ninja
+
+v1 = g2
+
+build out3.1: dump
+    v2 = b1
+
+v1 = g3
+
+subninja test3-sub.ninja
+
+include test3-inc.ninja
+
+build out3.2: dump
+
+v1 = g4
Examples/Self/Main.hs view
@@ -4,7 +4,6 @@  import Development.Shake import Development.Shake.Classes-import Development.Shake.Command import Development.Shake.FilePath import Examples.Util 
Examples/Tar/Main.hs view
@@ -10,4 +10,4 @@     obj "result.tar" *> \out -> do         contents <- readFileLines "Examples/Tar/list.txt"         need contents-        system' "tar" $ ["-cf",out] ++ contents+        cmd "tar -cf" [out] contents
Examples/Test/Command.hs view
@@ -6,7 +6,6 @@ import Control.Monad import Data.List import Development.Shake-import Development.Shake.Command import Examples.Util  
Examples/Test/Docs.hs view
@@ -21,7 +21,7 @@     index *> \_ -> do         xs <- getDirectoryFiles "Development" ["//*.hs"]         need $ map ("Development" </>) xs-        system' "cabal" ["haddock"]+        cmd "cabal haddock"      obj "Paths_shake.hs" *> \out -> do         copyFile' "Paths.hs" out@@ -78,7 +78,7 @@      obj "Success.txt" *> \out -> do         need [obj "Main.hs"]-        system' "runhaskell" ["-ignore-package=hashmap","-i" ++ obj "",obj "Main.hs"]+        () <- cmd "runhaskell -ignore-package=hashmap" ["-i" ++ obj "", obj "Main.hs"]         writeFile' out ""  @@ -113,8 +113,13 @@ dropComment ('-':'-':_) = [] dropComment xs = onTail dropComment xs -undefDots ('.':'.':'.':xs) = "undefined" ++ (if "..." `isSuffixOf` xs then "" else undefDots xs)-undefDots xs = onTail undefDots xs++undefDots o = f o+    where+        f ('.':'.':'.':xs) =+            (if "cmd" `elem` words o then "[\"\"]" else "undefined") +++            (if "..." `isSuffixOf` xs then "" else undefDots xs)+        f xs = onTail f xs  strip :: String -> String strip x
Examples/Test/Errors.hs view
@@ -26,7 +26,7 @@         need [out]      obj "systemcmd" *> \_ ->-        system' "random_missing_command" []+        cmd "random_missing_command"      obj "stack1" *> \_ -> need [obj "stack2"]     obj "stack2" *> \_ -> need [obj "stack3"]
Examples/Test/Random.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE PatternGuards #-}  module Examples.Test.Random(main) where @@ -6,6 +7,10 @@ import Control.Exception import Control.Monad import Data.List+import Data.Maybe+import Data.Time+import System.Environment+import System.Exit import System.Random import qualified Data.ByteString.Char8 as BS @@ -32,7 +37,7 @@             i <- randomRIO (0, 25)             sleep $ fromInteger i / 100 -    forM_ (map read args) $ \x -> case x of+    forM_ (map read $ filter (isNothing . asDuration) args) $ \x -> case x of         Want xs -> want $ map (toFile . Output) xs         Logic out srcs -> toFile (Output out) *> \out -> do             res <- fmap (show . Multiple) $ forM srcs $ \src -> do@@ -43,50 +48,67 @@             writeFileChanged out res  -test build obj = forM_ [1..] $ \count -> do-    putStrLn $ "* PERFORMING RANDOM TEST " ++ show count-    build ["clean"]-    build [] -- to create the directory-    forM_ inputRange $ \i ->-        writeFile (obj $ "input-" ++ show i ++ ".txt") $ show $ Single i-    logic <- randomLogic-    runLogic [] logic-    chng <- filterM (const randomIO) inputRange   -    forM_ chng $ \i ->-        writeFile (obj $ "input-" ++ show i ++ ".txt") $ show $ Single $ negate i-    runLogic chng logic-    forM_ inputRange $ \i ->-        writeFile (obj $ "input-" ++ show i ++ ".txt") $ show $ Single i-    logicBang <- addBang =<< addBang logic-    j <- randomRIO (1::Int,8)-    res <- try $ build $ ("-j" ++ show j) : map show (logicBang ++ [Want [i | Logic i _ <- logicBang]])-    case res of-        Left err-            | "BANG" `isInfixOf` show (err :: SomeException) -> return () -- error I expected-            | otherwise -> error $ "UNEXPECTED ERROR: " ++ show err-        _ -> return () -- occasionally we only put BANG in places with no dependenies that don't get rebuilt-    runLogic [] $ logic ++ [Want [i | Logic i _ <- logic]]-    where-        runLogic :: [Int] -> [Logic] -> IO ()-        runLogic negated xs = do-            let poss = [i | Logic i _ <- xs]-            i <- randomRIO (0, 7)-            wants <- replicateM i $ do-                i <- randomRIO (0, 5)-                replicateM i $ randomElem poss-            sleepFileTime-            j <- randomRIO (1::Int,8)-            build $ ("-j" ++ show j) : map show (xs ++ map Want wants)+asDuration :: String -> Maybe Double+asDuration x+    | "s" `isSuffixOf` x, [(i,"")] <- reads $ init x = Just i+    | "m" `isSuffixOf` x, [(i,"")] <- reads $ init x = Just $ i * 60+    | otherwise = Nothing -            let value i = case [ys | Logic j ys <- xs, j == i] of-                    [ys] -> Multiple $ flip map ys $ map $ \i -> case i of-                        Input i -> Single $ if i `elem` negated then negate i else i-                        Output i -> value i-            forM_ (concat wants) $ \i -> do-                let wanted = value i-                got <- fmap read $ readFileStrict $ obj $ "output-" ++ show i ++ ".txt"-                when (wanted /= got) $-                    error $ "INCORRECT VALUE for " ++ show i++test build obj = do+    limit <- do+        args <- getArgs+        let bound = listToMaybe $ reverse $ mapMaybe asDuration args+        start <- getCurrentTime+        return $ when (isJust bound) $ do+            now <- getCurrentTime+            when (fromRational (toRational $ now `diffUTCTime` start) > fromJust bound) exitSuccess++    forM_ [1..] $ \count -> do+        limit+        putStrLn $ "* PERFORMING RANDOM TEST " ++ show count+        build ["clean"]+        build [] -- to create the directory+        forM_ inputRange $ \i ->+            writeFile (obj $ "input-" ++ show i ++ ".txt") $ show $ Single i+        logic <- randomLogic+        runLogic [] logic+        chng <- filterM (const randomIO) inputRange   +        forM_ chng $ \i ->+            writeFile (obj $ "input-" ++ show i ++ ".txt") $ show $ Single $ negate i+        runLogic chng logic+        forM_ inputRange $ \i ->+            writeFile (obj $ "input-" ++ show i ++ ".txt") $ show $ Single i+        logicBang <- addBang =<< addBang logic+        j <- randomRIO (1::Int,8)+        res <- try $ build $ "--exception" : ("-j" ++ show j) : map show (logicBang ++ [Want [i | Logic i _ <- logicBang]])+        case res of+            Left err+                | "BANG" `isInfixOf` show (err :: SomeException) -> return () -- error I expected+                | otherwise -> error $ "UNEXPECTED ERROR: " ++ show err+            _ -> return () -- occasionally we only put BANG in places with no dependenies that don't get rebuilt+        runLogic [] $ logic ++ [Want [i | Logic i _ <- logic]]+        where+            runLogic :: [Int] -> [Logic] -> IO ()+            runLogic negated xs = do+                let poss = [i | Logic i _ <- xs]+                i <- randomRIO (0, 7)+                wants <- replicateM i $ do+                    i <- randomRIO (0, 5)+                    replicateM i $ randomElem poss+                sleepFileTime+                j <- randomRIO (1::Int,8)+                build $ ("-j" ++ show j) : map show (xs ++ map Want wants)++                let value i = case [ys | Logic j ys <- xs, j == i] of+                        [ys] -> Multiple $ flip map ys $ map $ \i -> case i of+                            Input i -> Single $ if i `elem` negated then negate i else i+                            Output i -> value i+                forM_ (concat wants) $ \i -> do+                    let wanted = value i+                    got <- fmap read $ readFileStrict $ obj $ "output-" ++ show i ++ ".txt"+                    when (wanted /= got) $+                        error $ "INCORRECT VALUE for " ++ show i   addBang :: [Logic] -> IO [Logic]
Examples/Util.hs view
@@ -6,6 +6,7 @@  import Control.Concurrent import Control.Monad+import Data.Char import Data.List import System.Directory as IO import System.Environment@@ -85,10 +86,26 @@ a === b = assert (a == b) $ "failed in ===\nLHS: " ++ show a ++ "\nRHS: " ++ show b  +assertExists :: FilePath -> IO ()+assertExists file = do+    b <- IO.doesFileExist file+    assert b $ "File was expected to exist, but is missing: " ++ file++assertMissing :: FilePath -> IO ()+assertMissing file = do+    b <- IO.doesFileExist file+    assert (not b) $ "File was expected to be missing, but exists: " ++ file+ assertContents :: FilePath -> String -> IO () assertContents file want = do     got <- readFile file     assert (want == got) $ "File contents are wrong: " ++ file ++ "\nWANT: " ++ want ++ "\nGOT: " ++ got++assertNonSpace :: FilePath -> String -> IO ()+assertNonSpace file want = do+    got <- readFile file+    let f = filter (not . isSpace)+    assert (f want == f got) $ "File contents are wrong: " ++ file ++ "\nWANT: " ++ want ++ "\nGOT: " ++ got  assertContentsInfix :: FilePath -> String -> IO () assertContentsInfix file want = do
Test.hs view
@@ -17,6 +17,7 @@ import qualified Examples.Tar.Main as Tar import qualified Examples.Self.Main as Self import qualified Examples.C.Main as C+import qualified Examples.Ninja.Main as Ninja import qualified Examples.Test.Assume as Assume import qualified Examples.Test.Basic as Basic import qualified Examples.Test.Benchmark as Benchmark@@ -48,7 +49,7 @@         ,"docs" * Docs.main, "errors" * Errors.main         ,"filepath" * FilePath.main, "filepattern" * FilePattern.main, "files" * Files.main         ,"journal" * Journal.main, "lint" * Lint.main, "makefile" * Makefile.main-        ,"pool" * Pool.main, "random" * Random.main+        ,"pool" * Pool.main, "random" * Random.main, "ninja" * Ninja.main         ,"resources" * Resources.main, "assume" * Assume.main, "benchmark" * Benchmark.main         ,"oracle" * Oracle.main, "progress" * Progress.main]     where (*) = (,)
shake.cabal view
@@ -1,7 +1,7 @@ cabal-version:      >= 1.8 build-type:         Simple name:               shake-version:            0.10.5+version:            0.10.6 license:            BSD3 license-file:       LICENSE category:           Development@@ -48,6 +48,7 @@     Examples/MakeTutor/hellomake.c     Examples/MakeTutor/hellomake.h     Examples/Tar/list.txt+    Examples/Ninja/*.ninja     Paths.hs  data-files:@@ -160,6 +161,7 @@         Development.Make.Rules         Development.Make.Type         Development.Ninja.All+        Development.Ninja.Env         Development.Ninja.Parse         Development.Ninja.Type         Start@@ -204,6 +206,7 @@         Development.Ninja.Type         Examples.Util         Examples.C.Main+        Examples.Ninja.Main         Examples.Self.Main         Examples.Tar.Main         Examples.Test.Assume