shake-0.10.6: Development/Ninja/All.hs
{-# LANGUAGE RecordWildCards, PatternGuards, ScopedTypeVariables #-}
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.FilePath
import Development.Shake.Timing
import qualified Data.ByteString.Char8 as BS
import System.Directory
import qualified Data.HashMap.Strict as Map
import Control.Monad
import Data.List
import Data.Char
runNinja :: FilePath -> [String] -> IO (Rules ())
runNinja file args = do
addTiming "Ninja parse"
ninja@Ninja{..} <- parse file
return $ do
phonys <- return $ Map.fromList phonys
singles <- return $ Map.fromList singles
multiples <- return $ Map.fromList [(x,(xs,b)) | (xs,b) <- multiples, x <- xs]
rules <- return $ Map.fromList rules
pools <- fmap Map.fromList $ forM pools $ \(name,depth) ->
fmap ((,) name) $ newResource (BS.unpack name) depth
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 phonys rules pools out2 $ snd $ multiples Map.! head out2
(flip Map.member singles . BS.pack) ?> \out -> let out2 = BS.pack out in
build phonys rules pools [out2] $ singles Map.! out2
resolvePhony :: Map.HashMap Str [Str] -> Str -> [Str]
resolvePhony mp = f $ Left 100
where
f (Left 0) x = f (Right []) x
f (Right xs) x | x `elem` xs = error $ "Recursive phony involving " ++ BS.unpack x
f a x = case Map.lookup x mp of
Nothing -> [x]
Just xs -> concatMap (f $ either (Left . subtract 1) (Right . (x:)) a) xs
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 <- 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
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
Nothing -> error $ "Ninja pool named " ++ BS.unpack pool ++ " not found, required to build " ++ BS.unpack (BS.unwords out)
Just r -> withResource r 1 act
when (description /= "") $ putNormal description
if deps == "msvc" then do
Stdout stdout <- withPool $ command [Shell, EchoStdout True] commandline []
need $ map normalise $ parseShowIncludes stdout
else
withPool $ command_ [Shell] commandline []
when (depfile /= "") $ do
when (deps /= "gcc") $ need [depfile]
depsrc <- liftIO $ readFile depfile
need $ map normalise $ concatMap snd $ parseMakefile depsrc
when (deps == "gcc") $ liftIO $ removeFile depfile
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
parseShowIncludes :: String -> [FilePath]
parseShowIncludes out = [y | x <- lines out, Just x <- [stripPrefix "Note: including file:" x]
, let y = dropWhile isSpace x, not $ isSystemInclude y]
-- Dodgy, but ported over from the original Ninja
isSystemInclude :: String -> Bool
isSystemInclude x = "program files" `isInfixOf` lx || "microsoft visual studio" `isInfixOf` lx
where lx = map toLower x
parseMakefile :: String -> [(FilePath, [FilePath])]
parseMakefile = concatMap f . join . lines
where
join (x1:x2:xs) | "\\" `isSuffixOf` x1 = join $ (init x1 ++ x2) : xs
join (x:xs) = x : join xs
join [] = []
f x = [(a, words $ drop 1 b) | a <- words a]
where (a,b) = break (== ':') $ takeWhile (/= '#') x