yesod/Scaffold/Build.hs
2011-04-18 17:28:34 +03:00

126 lines
4.1 KiB
Haskell

{-# LANGUAGE OverloadedStrings #-}
module Scaffold.Build
( build
, getDeps
, touchDeps
, findHaskellFiles
) where
-- FIXME there's a bug when getFileStatus applies to a file temporary deleted (e.g., Vim saving a file)
import qualified Distribution.Simple.Build as B
import System.Directory (getDirectoryContents, doesDirectoryExist)
import Data.List (isSuffixOf)
import Distribution.Simple.Setup (defaultBuildFlags)
import Distribution.Simple.Configure (getPersistBuildConfig)
import Distribution.Simple.LocalBuildInfo
import qualified Data.Attoparsec.Text.Lazy as A
import qualified Data.Text.Lazy.IO as TIO
import Control.Applicative ((<|>))
import Data.Char (isSpace)
import Data.Maybe (mapMaybe)
import Data.Monoid (mappend)
import qualified Data.Map as Map
import qualified Data.Set as Set
import System.PosixCompat.Files (accessTime, modificationTime, getFileStatus, setFileTimes)
build :: IO ()
build = do
{-
cabal <- defaultPackageDesc normal
gpd <- readPackageDescription normal cabal
putStrLn $ showPackageDescription $ packageDescription gpd
-}
hss <- findHaskellFiles "."
deps' <- mapM determineHamletDeps hss
let deps = fixDeps $ zip hss deps'
touchDeps deps
lbi <- getPersistBuildConfig "dist"
B.build
(localPkgDescr lbi)
lbi
defaultBuildFlags
[]
type Deps = Map.Map FilePath (Set.Set FilePath)
getDeps :: IO Deps
getDeps = do
hss <- findHaskellFiles "."
deps' <- mapM determineHamletDeps hss
return $ fixDeps $ zip hss deps'
touchDeps :: Deps -> IO ()
touchDeps =
mapM_ go . Map.toList
where
go (x, ys) = do
fs <- getFileStatus x
flip mapM_ (Set.toList ys) $ \y -> do
fs' <- getFileStatus y
if modificationTime fs' < modificationTime fs
then do
putStrLn $ "Touching " ++ y ++ " because of " ++ x
setFileTimes y (accessTime fs') (modificationTime fs)
else return ()
fixDeps :: [(FilePath, [FilePath])] -> Deps
fixDeps =
Map.unionsWith mappend . map go
where
go :: (FilePath, [FilePath]) -> Deps
go (x, ys) = Map.fromList $ map (\y -> (y, Set.singleton x)) ys
findHaskellFiles :: FilePath -> IO [FilePath]
findHaskellFiles path = do
contents <- getDirectoryContents path
fmap concat $ mapM go contents
where
go ('.':_) = return []
go "dist" = return []
go x = do
let y = path ++ '/' : x
d <- doesDirectoryExist y
if d
then findHaskellFiles y
else if ".hs" `isSuffixOf` x || ".lhs" `isSuffixOf` x
then return [y]
else return []
data TempType = Hamlet | Cassius | Lucius | Julius | Widget | Verbatim
deriving Show
determineHamletDeps :: FilePath -> IO [FilePath]
determineHamletDeps x = do
y <- TIO.readFile x
let z = A.parse (A.many $ (parser <|> (A.anyChar >> return Nothing))) y
case z of
A.Fail{} -> return []
A.Done _ r -> return $ mapMaybe go r
where
go (Just (Hamlet, f)) = Just $ "hamlet/" ++ f ++ ".hamlet"
go (Just (Widget, f)) = Just $ "hamlet/" ++ f ++ ".hamlet"
go (Just (Verbatim, f)) = Just f
go _ = Nothing
parser = do
ty <- (A.string "$(hamletFile " >> return Hamlet)
<|> (A.string "$(cassiusFile " >> return Cassius)
<|> (A.string "$(luciusFile " >> return Lucius)
<|> (A.string "$(juliusFile " >> return Julius)
<|> (A.string "$(widgetFile " >> return Widget)
<|> (A.string "$(Settings.hamletFile " >> return Hamlet)
<|> (A.string "$(Settings.cassiusFile " >> return Cassius)
<|> (A.string "$(Settings.luciusFile " >> return Lucius)
<|> (A.string "$(Settings.juliusFile " >> return Julius)
<|> (A.string "$(Settings.widgetFile " >> return Widget)
<|> (A.string "$(persistFile " >> return Verbatim)
<|> (A.string "$(parseRoutesFile " >> return Verbatim)
A.skipWhile isSpace
_ <- A.char '"'
y <- A.many1 $ A.satisfy (/= '"')
_ <- A.char '"'
A.skipWhile isSpace
_ <- A.char ')'
return $ Just (ty, y)