11{-# LANGUAGE NoImplicitPrelude #-}
22{-# LANGUAGE OverloadedStrings #-}
3+ {-# LANGUAGE ScopedTypeVariables #-}
34module Stack.Prelude
45 ( withSourceFile
56 , withSinkFile
67 , withSinkFileCautious
78 , withSystemTempDir
9+ , sinkProcessStderrStdout
10+ , sinkProcessStdout
11+ , logProcessStderrStdout
812 , module X
913 ) where
1014
@@ -13,13 +17,19 @@ import Path as X (Abs, Dir, File, Path, Rel,
1317 toFilePath )
1418import qualified Path.IO
1519
16- import Data.Conduit.Binary (sourceHandle , sinkHandle )
17-
1820import qualified System.IO as IO
1921import qualified System.Directory as Dir
2022import qualified System.FilePath as FP
2123import System.IO.Error (isDoesNotExistError )
2224
25+ import Data.Conduit.Binary (sourceHandle , sinkHandle )
26+ import qualified Data.Conduit.Binary as CB
27+ import qualified Data.Conduit.List as CL
28+ import Data.Conduit.Process.Typed (withLoggedProcess_ , createSource )
29+ import RIO.Process (HasEnvOverride , setStdin , closed , getStderr , getStdout , withProc , withProcess_ , setStdout , setStderr )
30+ import Data.Text.Encoding (decodeUtf8With )
31+ import Data.Text.Encoding.Error (lenientDecode )
32+
2333-- | Get a source for a file. Unlike @sourceFile@, doesn't require
2434-- @ResourceT@. Unlike explicit @withBinaryFile@ and @sourceHandle@
2535-- usage, you can't accidentally use @WriteMode@ instead of
@@ -53,3 +63,51 @@ withSinkFileCautious fp inner =
5363-- | Path version
5464withSystemTempDir :: MonadUnliftIO m => String -> (Path Abs Dir -> m a ) -> m a
5565withSystemTempDir str inner = withRunInIO $ \ run -> Path.IO. withSystemTempDir str $ run . inner
66+
67+ -- | Consume the stdout and stderr of a process feeding strict 'ByteString's to the consumers.
68+ --
69+ -- Throws a 'ReadProcessException' if unsuccessful in launching, or 'ProcessExitedUnsuccessfully' if the process itself fails.
70+ sinkProcessStderrStdout
71+ :: forall e o env . HasEnvOverride env
72+ => String -- ^ Command
73+ -> [String ] -- ^ Command line arguments
74+ -> ConduitM ByteString Void (RIO env ) e -- ^ Sink for stderr
75+ -> ConduitM ByteString Void (RIO env ) o -- ^ Sink for stdout
76+ -> RIO env (e ,o )
77+ sinkProcessStderrStdout name args sinkStderr sinkStdout =
78+ withProc name args $ \ pc0 -> do
79+ let pc = setStdin closed
80+ $ setStdout createSource
81+ $ setStderr createSource
82+ pc0
83+ withProcess_ pc $ \ p ->
84+ runConduit (getStderr p .| sinkStderr) `concurrently`
85+ runConduit (getStdout p .| sinkStdout)
86+
87+ -- | Consume the stdout of a process feeding strict 'ByteString's to a consumer.
88+ -- If the process fails, spits out stdout and stderr as error log
89+ -- level. Should not be used for long-running processes or ones with
90+ -- lots of output; for that use 'sinkProcessStdoutLogStderr'.
91+ --
92+ -- Throws a 'ReadProcessException' if unsuccessful.
93+ sinkProcessStdout
94+ :: HasEnvOverride env
95+ => String -- ^ Command
96+ -> [String ] -- ^ Command line arguments
97+ -> ConduitM ByteString Void (RIO env ) a -- ^ Sink for stdout
98+ -> RIO env a
99+ sinkProcessStdout name args sinkStdout =
100+ withProc name args $ \ pc ->
101+ withLoggedProcess_ (setStdin closed pc) $ \ p -> runConcurrently
102+ $ Concurrently (runConduit $ getStderr p .| CL. sinkNull)
103+ *> Concurrently (runConduit $ getStdout p .| sinkStdout)
104+
105+ logProcessStderrStdout
106+ :: (HasCallStack , HasEnvOverride env )
107+ => String
108+ -> [String ]
109+ -> RIO env ()
110+ logProcessStderrStdout name args = do
111+ let logLines = CB. lines .| CL. mapM_ (logInfo . decodeUtf8With lenientDecode)
112+ (() , () ) <- sinkProcessStderrStdout name args logLines logLines
113+ return ()
0 commit comments