packages feed

redo (empty) → 0.1.0

raw patch · 4 files changed

+136/−0 lines, 4 filesdep +basedep +bytestringdep +containerssetup-changed

Dependencies added: base, bytestring, containers, directory, filepath, process, pureMD5

Files

+ LICENSE view
@@ -0,0 +1,1 @@+I hereby place the source code for "redo" into the public domain.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ redo.cabal view
@@ -0,0 +1,30 @@+name:                redo+version:             0.1.0+synopsis:            software build system, make replacement, implementation of djb's redo+description:         redo builds a target file from source files using a "do" script. It also tracks when source files have changed and rebuilds target files as necessary.+homepage:            https://github.com/jekor/redo+bug-reports:         https://github.com/jekor/redo/issues+license:             PublicDomain+license-file:        LICENSE+author:              Chris Forno (jekor)+maintainer:          jekor@jekor.com+category:            Distribution+build-type:          Simple+cabal-version:       >=1.8+tested-with:         GHC == 7.6.2++source-repository    head+  type:              git+  location:          git://github.com/jekor/redo.git++executable redo+  main-is:             redo.hs+  build-depends:       base >= 4 && < 5,+                       bytestring,+                       containers >= 0.5,+                       directory,+                       filepath,+                       process,+                       pureMD5+  extensions:          ScopedTypeVariables+  ghc-options:         -Wall -fno-warn-missing-signatures
+ redo.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE ScopedTypeVariables #-}++import Control.Exception (catch, catchJust, IOException)+import Control.Monad (filterM, liftM, unless, guard)+import qualified Data.ByteString.Lazy as BL+import qualified Data.Digest.Pure.MD5 as MD5+import Data.Map.Lazy (insert, fromList, toList, adjust)+import Data.Maybe (listToMaybe)+-- import Debug.Trace (traceShow)+import GHC.IO.Exception (IOErrorType(..))+import System.Directory (renameFile, removeFile, doesFileExist, getDirectoryContents, removeDirectoryRecursive, createDirectoryIfMissing, getCurrentDirectory, setCurrentDirectory)+import System.Environment (getArgs, getEnvironment, getProgName, lookupEnv)+import System.Exit (ExitCode(..))+import System.FilePath (hasExtension, replaceBaseName, takeBaseName, (</>), splitFileName)+import System.IO (hPutStrLn, stderr, hGetLine, withFile, IOMode(..), hFileSize)+import System.IO.Error (ioeGetErrorType, isDoesNotExistError)+import System.Process (createProcess, waitForProcess, shell, CreateProcess(..))++-- traceShow' arg = traceShow arg arg++metaDir = ".redo"++main :: IO ()+main = do+  topDir <- getCurrentDirectory+  getArgs >>= mapM_ (\arg -> do+    let (dir, file) = splitFileName arg+    setCurrentDirectory dir+    redo file dir+    setCurrentDirectory topDir)+  progName <- getProgName+  redoTarget' <- lookupEnv "REDO_TARGET"+  case (progName, redoTarget') of+    ("redo-ifchange", Just redoTarget) -> mapM_ (writeMD5 redoTarget) =<< getArgs+    ("redo-ifchange", Nothing) -> error "Missing REDO_TARGET environment variable."+    _ -> return ()++redo :: String -> FilePath -> IO ()+redo target dir = do+  upToDate' <- upToDate target+  unless upToDate' $ maybe missingDo redo' =<< doPath target+ where redo' :: FilePath -> IO ()+       redo' path = do+         hPutStrLn stderr $ "redo " ++ show (dir </> target)+         catchJust (guard . isDoesNotExistError)+                   (removeDirectoryRecursive metaDepsDir)+                   (\_ -> return ())+         createDirectoryIfMissing True metaDepsDir+         writeMD5 target path+         oldEnv <- getEnvironment+         let newEnv = toList $ adjust (++ ":.") "PATH" $ insert "REDO_TARGET" target $ fromList oldEnv+         (_, _, _, ph) <- createProcess $ (shell $ cmd path) {env = Just newEnv}+         exit <- waitForProcess ph+         case exit of+           ExitSuccess -> do+             size <- fileSize tmp+             if size > 0+               then renameFile tmp target+               else removeFile tmp+           ExitFailure code -> do hPutStrLn stderr $ "Redo script exited with non-zero exit code: " ++ show code+                                  removeFile tmp+       tmp = target ++ "---redoing"+       metaDepsDir = metaDir </> target+       missingDo = do+         exists <- doesFileExist target+         unless exists $ error $ "No .do file found for target '" ++ target ++ "'"+       cmd path = unwords ["sh -e", path, "0", takeBaseName target, tmp, ">", tmp]++doPath :: FilePath -> IO (Maybe FilePath)+doPath target = listToMaybe `liftM` filterM doesFileExist candidates+ where candidates = (target ++ ".do") : if hasExtension target+                                        then [replaceBaseName target "default" ++ ".do"]+                                        else []++upToDate :: FilePath -> IO Bool+upToDate target = catch+  (do exists <- doesFileExist target+      if exists+        then do md5s <- getDirectoryContents (metaDir </> target)+                and `liftM` mapM depUpToDate md5s+        else return False)+  (\(_ :: IOException) -> return False)+ where depUpToDate :: String -> IO Bool+       depUpToDate oldMD5 = catch+         (do dep <- withFile (metaDir </> target </> oldMD5) ReadMode hGetLine+             newMD5 <- fileMD5 dep+             doScript <- doPath dep+             case doScript of+               Nothing -> return $ oldMD5 == newMD5+               Just _ -> do upToDate' <- upToDate dep+                            return $ (oldMD5 == newMD5) && upToDate')+         (\e -> return (ioeGetErrorType e == InappropriateType))++fileMD5 :: FilePath -> IO String+fileMD5 path = (show . MD5.md5) `liftM` BL.readFile path++writeMD5 :: String -> FilePath -> IO ()+writeMD5 redoTarget dep = do+  md5 <- fileMD5 dep+  writeFile (metaDir </> redoTarget </> md5) dep++fileSize :: FilePath -> IO Integer+fileSize path = withFile path ReadMode hFileSize