WIP on making --quiet silence progress, and infra for concurrent progress bars

This commit is contained in:
Joey Hess 2015-04-03 16:48:30 -04:00
parent c2c901a6e4
commit 20fb91a7ad
14 changed files with 194 additions and 93 deletions

View file

@ -142,7 +142,7 @@ pipeLazy params feeder reader = do
setup = liftIO . createProcess
cleanup p (_, _, _, pid) = liftIO $ forceSuccessProcess p pid
go p = do
let (to, from) = bothHandles p
let (to, from) = ioHandles p
liftIO $ void $ forkIO $ do
feeder to
hClose to

View file

@ -18,6 +18,7 @@ import Foreign.Storable (Storable(sizeOf))
import System.Posix.Types
import Data.Int
import Data.Bits.Utils
import Control.Concurrent.Async
{- An action that can be run repeatedly, updating it on the bytes processed.
-
@ -146,7 +147,7 @@ defaultChunkSize = 32 * k - chunkOverhead
chunkOverhead = 2 * sizeOf (undefined :: Int) -- GHC specific
{- Parses the String looking for a command's progress output, and returns
- Maybe the number of bytes rsynced so far, and any any remainder of the
- Maybe the number of bytes done so far, and any any remainder of the
- string that could be an incomplete progress output. That remainder
- should be prepended to future output, and fed back in. This interface
- allows the command's output to be read in any desired size chunk, or
@ -154,12 +155,23 @@ defaultChunkSize = 32 * k - chunkOverhead
-}
type ProgressParser = String -> (Maybe BytesProcessed, String)
data ProgressHandler = ProgressHandler
{ quietMode :: Bool -- don't forward output to stdout
, stderrHandler :: Handle -> IO () -- callback to handle stderr
, meterUpdate :: MeterUpdate -- the progress meter to update
}
{- Runs a command and runs a ProgressParser on its output, in order
- to update the meter. The command's output is also sent to stdout. -}
commandMeter :: ProgressParser -> MeterUpdate -> FilePath -> [CommandParam] -> IO Bool
commandMeter progressparser meterupdate cmd params = liftIO $ catchBoolIO $
withHandle StdoutHandle createProcessSuccess p $
feedprogress zeroBytesProcessed []
- to update a meter.
-}
commandMeter :: ProgressParser -> ProgressHandler -> FilePath -> [CommandParam] -> IO Bool
commandMeter progressparser progress cmd params =
liftIO $ catchBoolIO $
withOEHandles createProcessSuccess p $ \(outh, errh) -> do
ep <- async $ (stderrHandler progress) errh
op <- async $ feedprogress zeroBytesProcessed [] outh
wait ep
wait op
where
p = proc cmd (toCommand params)
@ -168,13 +180,14 @@ commandMeter progressparser meterupdate cmd params = liftIO $ catchBoolIO $
if S.null b
then return True
else do
S.hPut stdout b
hFlush stdout
unless (quietMode progress) $ do
S.hPut stdout b
hFlush stdout
let s = w82s (S.unpack b)
let (mbytes, buf') = progressparser (buf++s)
case mbytes of
Nothing -> feedprogress prev buf' h
(Just bytes) -> do
when (bytes /= prev) $
meterupdate bytes
(meterUpdate progress) bytes
feedprogress bytes buf' h

View file

@ -26,6 +26,7 @@ module Utility.Process (
processTranscript',
withHandle,
withIOHandles,
withOEHandles,
withQuietOutput,
createProcess,
startInteractiveProcess,
@ -268,6 +269,20 @@ withIOHandles creator p a = creator p' $ a . ioHandles
, std_err = Inherit
}
{- Like withHandle, but passes (stdout, stderr) handles to the action. -}
withOEHandles
:: CreateProcessRunner
-> CreateProcess
-> ((Handle, Handle) -> IO a)
-> IO a
withOEHandles creator p a = creator p' $ a . oeHandles
where
p' = p
{ std_in = Inherit
, std_out = CreatePipe
, std_err = CreatePipe
}
{- Forces the CreateProcessRunner to run quietly;
- both stdout and stderr are discarded. -}
withQuietOutput
@ -306,6 +321,8 @@ stderrHandle _ = error "expected stderrHandle"
ioHandles :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Handle, Handle)
ioHandles (Just hin, Just hout, _, _) = (hin, hout)
ioHandles _ = error "expected ioHandles"
oeHandles (_, Just hout, Just herr, _) = (hout, herr)
oeHandles _ = error "expected oeHandles"
processHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> ProcessHandle
processHandle (_, _, _, pid) = pid

View file

@ -92,13 +92,13 @@ rsyncUrlIsPath s
| rsyncUrlIsShell s = False
| otherwise = ':' `notElem` s
{- Runs rsync, but intercepts its progress output and updates a meter.
- The progress output is also output to stdout.
{- Runs rsync, but intercepts its progress output and updates a progress
- meter.
-
- The params must enable rsync's --progress mode for this to work.
-}
rsyncProgress :: MeterUpdate -> [CommandParam] -> IO Bool
rsyncProgress meterupdate = commandMeter parseRsyncProgress meterupdate "rsync" . rsyncParamsFixup
rsyncProgress :: ProgressHandler -> [CommandParam] -> IO Bool
rsyncProgress h = commandMeter parseRsyncProgress h "rsync" . rsyncParamsFixup
{- Strategy: Look for chunks prefixed with \r (rsync writes a \r before
- the first progress output, and each thereafter). The first number

View file

@ -81,7 +81,7 @@ splitWord = separate isSpace
- and duplicate stderr to stdout. Return two new handles
- that are duplicates of the original (stdin, stdout). -}
dupIoHandles :: IO (Handle, Handle)
duoIoHandles = do
dupIoHandles = do
readh <- hDuplicate stdin
writeh <- hDuplicate stdout
nullh <- openFile devNull ReadMode