Capture command output and print it prettier
Getty Ritter
7 years ago
| 73 | 73 | where |
| 74 | 74 | doReplace file frag = do |
| 75 | 75 | new <- case findFragments root frag e of |
| 76 |
Left err -> c |
|
| 76 | Left err -> cDie ("Fragment error: " % text) err | |
| 77 | 77 | Right x -> x |
| 78 | 78 | return (TL.replace (toIdent frag) new file) |
| 79 | 79 | |
| 81 | 81 | readSection path f = do |
| 82 | 82 | contents <- cOpenFile "section source" path |
| 83 | 83 | case TL.breakOn (toIdent f) contents of |
| 84 | (_, "") -> cError ("Unable to find section " % shown % | |
| 85 | " in file " % string) f path | |
| 84 | (_, "") -> cDie ("Unable to find section " % shown % | |
| 85 | " in file " % string) f path | |
| 86 | 86 | (_, rs) -> |
| 87 | 87 | let (_, skipLine) = TL.breakOn "\n" rs |
| 88 | 88 | (section, _) = TL.breakOn "«end»" (TL.drop 1 skipLine) |
| 3 | 3 | module Bricoleur.Commands.Test (runTest) where |
| 4 | 4 | |
| 5 | 5 | import Control.Monad (forM_) |
| 6 | import qualified Data.ByteString.Lazy.Char8 as B | |
| 7 | import qualified GHC.IO.Exception as Exn | |
| 6 | 8 | import System.FilePath ((</>)) |
| 7 | 9 | import qualified System.FilePath as Sys |
| 8 | 10 | import qualified System.Process as Sys |
| 10 | 12 | import Bricoleur.Opts |
| 11 | 13 | import Bricoleur.Config |
| 12 | 14 | import Bricoleur.Utils |
| 15 | ||
| 13 | 16 | |
| 14 | 17 | runTest :: Config -> Options -> IO () |
| 15 | 18 | runTest conf opts = do |
| 19 | 22 | cDebug ("- running test for " % stext) (sourceName samp) |
| 20 | 23 | runCommand root samp |
| 21 | 24 | |
| 25 | ||
| 22 | 26 | runCommand :: FilePath -> Source -> IO () |
| 23 | 27 | runCommand root src = do |
| 24 | 28 | forM_ (sourceCommands src) $ \ln -> do |
| 25 | 29 | let dir = root </> sourceDir src |
| 26 | cDebug (" - $ " % shown % " in " % string) ln dir | |
| 27 | let process = (Sys.shell ln) { Sys.cwd = Just (root </> sourceDir src) } | |
| 28 | (_, _, _, h) <- Sys.createProcess process | |
| 29 | Sys.waitForProcess h | |
| 30 | cDebug (" $ " % string % " (in " % string % ")") ln dir | |
| 31 | (outH, inH) <- Sys.createPipe | |
| 32 | let process = (Sys.shell ln) { Sys.cwd = Just (root </> sourceDir src) | |
| 33 | , Sys.std_out = Sys.UseHandle inH | |
| 34 | , Sys.std_err = Sys.UseHandle inH | |
| 35 | } | |
| 36 | (_, _, _, p) <- Sys.createProcess process | |
| 37 | bufOutput <- B.hGetContents outH | |
| 38 | code <- Sys.waitForProcess p | |
| 39 | case code of | |
| 40 | Exn.ExitSuccess -> return () | |
| 41 | Exn.ExitFailure n -> do | |
| 42 | let idtext = map (B.append " - ") (B.lines bufOutput) | |
| 43 | cError ("\nCommand '" % string % | |
| 44 | "' exited with error (" % shown % | |
| 45 | ")") ln n | |
| 46 | mapM_ bsErrorLn idtext | |
| 21 | 21 | } deriving (Eq, Show) |
| 22 | 22 | |
| 23 | 23 | desc :: String |
| 24 |
desc = " |
|
| 24 | desc = "Bricoleur: a tool for testing and stiching code into documents" | |
| 25 | 25 | |
| 26 | 26 | opts :: Opt.ParserInfo Options |
| 27 | 27 | opts = Opt.info (p Opt.<**> Opt.helper) |
| 5 | 5 | , cDebug |
| 6 | 6 | , cWarn |
| 7 | 7 | , cError |
| 8 | , cDie | |
| 9 | , bsErrorLn | |
| 8 | 10 | |
| 9 | 11 | , cOpenFile |
| 10 | 12 | |
| 19 | 21 | ) where |
| 20 | 22 | |
| 21 | 23 | import qualified Formatting as F |
| 24 | import qualified Data.ByteString.Lazy.Char8 as BS | |
| 22 | 25 | import qualified Data.Text.Lazy as TL |
| 23 | 26 | import qualified Data.Text.Lazy.Builder as TL |
| 24 | 27 | import qualified Data.Text.Lazy.IO as TL |
| 30 | 33 | |
| 31 | 34 | import Prelude (FilePath, IO, Either(Left), ($)) |
| 32 | 35 | |
| 36 | -- | Produce a 'Left' value from a format string | |
| 33 | 37 | throw :: F.Format (Either TL.Text r) a -> a |
| 34 | 38 | throw f = |
| 35 | 39 | F.runFormat f (\ b -> Left (TL.toLazyText b)) |
| 36 | 40 | |
| 41 | ||
| 37 | 42 | stderr :: TL.Text -> IO () |
| 38 | 43 | stderr = TL.hPutStr Sys.stderr |
| 44 | ||
| 39 | 45 | |
| 40 | 46 | -- | Write output to stdout |
| 41 | 47 | cOutput :: TL.Text -> IO () |
| 42 | 48 | cOutput = TL.putStrLn |
| 43 | 49 | |
| 50 | ||
| 44 | 51 | -- | Write a debug message to stderr. |
| 45 | 52 | cDebug :: F.Format (IO ()) a -> a |
| 46 | 53 | cDebug msg = F.runFormat msg $ \ b -> |
| 47 | 54 | TL.hPutStrLn Sys.stderr (TL.toLazyText b) |
| 55 | ||
| 48 | 56 | |
| 49 | 57 | -- | Write a warning message to stderr. If we are connected to a TTY, |
| 50 | 58 | -- then this will write in an orange color. |
| 57 | 65 | stderr "\x1b[39m\n" |
| 58 | 66 | else TL.hPutStrLn Sys.stderr (TL.toLazyText b) |
| 59 | 67 | |
| 68 | ||
| 60 | 69 | -- | Write an error message to stderr and exit. If we are connected to |
| 61 | 70 | -- a TTY, this message will be in red. |
| 62 |
cError :: F.Format (IO |
|
| 71 | cError :: F.Format (IO ()) a -> a | |
| 63 | 72 | cError msg = F.runFormat msg $ \b -> do |
| 73 | isTTY <- Posix.queryTerminal Posix.stdOutput | |
| 74 | if isTTY | |
| 75 | then do stderr "\x1b[91m" | |
| 76 | stderr (TL.toLazyText b) | |
| 77 | stderr "\x1b[39m\n" | |
| 78 | else TL.hPutStrLn Sys.stderr (TL.toLazyText b) | |
| 79 | ||
| 80 | -- | Write an error message to stderr and exit. If we are connected to | |
| 81 | -- a TTY, this message will be in red. | |
| 82 | cDie :: F.Format (IO r) a -> a | |
| 83 | cDie msg = F.runFormat msg $ \ b -> do | |
| 64 | 84 | isTTY <- Posix.queryTerminal Posix.stdOutput |
| 65 | 85 | if isTTY |
| 66 | 86 | then do stderr "\x1b[91m" |
| 70 | 90 | Sys.exitFailure |
| 71 | 91 | |
| 72 | 92 | |
| 93 | bsErrorLn :: BS.ByteString -> IO () | |
| 94 | bsErrorLn bs = do | |
| 95 | isTTY <- Posix.queryTerminal Posix.stdOutput | |
| 96 | if isTTY | |
| 97 | then do BS.hPutStr Sys.stderr "\x1b[91m" | |
| 98 | BS.hPutStr Sys.stderr bs | |
| 99 | BS.hPutStr Sys.stderr "\x1b[39m\n" | |
| 100 | else BS.hPutStrLn Sys.stderr bs | |
| 101 | ||
| 73 | 102 | cOpenFile :: TL.Text -> FilePath -> IO TL.Text |
| 74 | 103 | cOpenFile purpose path = do |
| 75 | 104 | exists <- Sys.doesFileExist path |
| 76 | 105 | if exists |
| 77 | 106 | then TL.readFile path |
| 78 | else cError ("Unable to open " F.% F.text F.% | |
| 79 | " file at " F.% F.string) purpose path | |
| 107 | else cDie ("Unable to open " F.% F.text F.% | |
| 108 | " file at " F.% F.string) purpose path | |