sinkProcessStderrStdout :: forall m e o. (MonadIO m, MonadLogger m)
                        => Maybe (Path Abs Dir) -- ^ Optional directory to run in
                        -> EnvOverride
                        -> String -- ^ Command
                        -> [String] -- ^ Command line arguments
                        -> Sink S.ByteString IO e -- ^ Sink for stderr
                        -> Sink S.ByteString IO o -- ^ Sink for stdout
                        -> m (e,o)
sinkProcessStderrStdout wd menv name args sinkStderr sinkStdout = do
  name' <- preProcess wd menv name
  $withProcessTimeLog name' args $
      liftIO $ withCheckedProcess
          (proc name' args) { env = envHelper menv, cwd = fmap toFilePath wd }
          (\ClosedStream out err -> f err out)
  where

    -- There is a bug in streaming-commons or conduit-extra which
    -- leads to a file descriptor leak. Ideally, we should be able to
    -- simply use the following code. Instead, we're using the code
    -- below it, which is explicit in closing Handles. When the
    -- upstream bug is fixed, we can consider moving back to the
    -- simpler code, though there's really no downside to the more
    -- complex version used here.
    --
    -- f :: Source IO S.ByteString -> Source IO S.ByteString -> IO (e, o)
    -- f err out = (err $$ sinkStderr) `concurrently` (out $$ sinkStdout)

    f :: Handle -> Handle -> IO (e, o)
    f err out = ((CB.sourceHandle err $$ sinkStderr) `concurrently` (CB.sourceHandle out $$ sinkStdout))
        `finally` hClose err `finally` hClose out

-- | Like sinkProcessStderrStdout, but receives Handles for stderr and stdout instead of 'Sink's.
--
-- Throws a 'ReadProcessException' if unsuccessful in launching, or 'ProcessExitedUnsuccessfully' if the process itself fails.
sinkProcessStderrStdout :: forall m e o. (MonadIO m, MonadLogger m)
                        => Maybe (Path Abs Dir) -- ^ Optional directory to run in
                        -> EnvOverride
                        -> String -- ^ Command
                        -> [String] -- ^ Command line arguments
                        -> Sink S.ByteString IO e -- ^ Sink for stderr
                        -> Sink S.ByteString IO o -- ^ Sink for stdout
                        -> m (e,o)
sinkProcessStderrStdout wd menv name args sinkStderr sinkStdout = do
  name' <- preProcess wd menv name
  $withProcessTimeLog name' args $
      liftIO $ withCheckedProcess
          (proc name' args) { env = envHelper menv, cwd = fmap toFilePath wd }
          (\ClosedStream out err -> f err out)
  where

    -- There is a bug in streaming-commons or conduit-extra which
    -- leads to a file descriptor leak. Ideally, we should be able to
    -- simply use the following code. Instead, we're using the code
    -- below it, which is explicit in closing Handles. When the
    -- upstream bug is fixed, we can consider moving back to the
    -- simpler code, though there's really no downside to the more
    -- complex version used here.
    --
    -- f :: Source IO S.ByteString -> Source IO S.ByteString -> IO (e, o)
    -- f err out = (err $$ sinkStderr) `concurrently` (out $$ sinkStdout)

    f :: Handle -> Handle -> IO (e, o)
    f err out = ((CB.sourceHandle err $$ sinkStderr) `concurrently` (CB.sourceHandle out $$ sinkStdout))
        `finally` hClose err `finally` hClose out

-- | Like sinkProcessStderrStdout, but receives Handles for stderr and stdout instead of 'Sink's.
--
-- Throws a 'ReadProcessException' if unsuccessful in launching, or 'ProcessExitedUnsuccessfully' if the process itself fails.
sinkProcessStderrStdout wd menv name args sinkStderr sinkStdout = do
  name' <- preProcess wd menv name
  $withProcessTimeLog name' args $
      liftIO $ withCheckedProcess
          (proc name' args) { env = envHelper menv, cwd = fmap toFilePath wd }
          (\ClosedStream out err -> f err out)
  where

    -- There is a bug in streaming-commons or conduit-extra which
    -- leads to a file descriptor leak. Ideally, we should be able to
    -- simply use the following code. Instead, we're using the code
    -- below it, which is explicit in closing Handles. When the
    -- upstream bug is fixed, we can consider moving back to the
    -- simpler code, though there's really no downside to the more
    -- complex version used here.
    --
    -- f :: Source IO S.ByteString -> Source IO S.ByteString -> IO (e, o)
    -- f err out = (err $$ sinkStderr) `concurrently` (out $$ sinkStdout)

    f :: Handle -> Handle -> IO (e, o)
    f err out = ((CB.sourceHandle err $$ sinkStderr) `concurrently` (CB.sourceHandle out $$ sinkStdout))
        `finally` hClose err `finally` hClose out

-- | Like sinkProcessStderrStdout, but receives Handles for stderr and stdout instead of 'Sink's.
--
-- Throws a 'ReadProcessException' if unsuccessful in launching, or 'ProcessExitedUnsuccessfully' if the process itself fails.