process 1.1.0.1 → 1.1.0.2
raw patch · 3 files changed
+124/−69 lines, 3 filesdep +deepseqdep ~basedep ~directory
Dependencies added: deepseq
Dependency ranges changed: base, directory
Files
- System/Process.hs +117/−65
- System/Process/Internals.hs +3/−1
- process.cabal +4/−3
System/Process.hs view
@@ -22,7 +22,7 @@ -- * Flag to control whether exiting the parent also kills the child. {- NOTES on createPipe:- + createPipe is no longer exported, because of the following problems: - it wasn't used to implement runInteractiveProcess on Unix, because@@ -70,7 +70,9 @@ #ifndef __HUGS__ import System.Process.Internals -import System.IO.Error+import Control.Exception (SomeException, mask, try, onException, throwIO)+import Control.DeepSeq (rnf)+import System.IO.Error (mkIOError, ioeSetErrorString) #if !defined(mingw32_HOST_OS) import System.Posix.Types #if MIN_VERSION_unix(2,5,0)@@ -89,7 +91,7 @@ #ifdef __GLASGOW_HASKELL__ #if __GLASGOW_HASKELL__ >= 611-import GHC.IO.Exception ( ioException, IOErrorType(..) )+import GHC.IO.Exception ( ioException, IOErrorType(..), IOException(..) ) #else import GHC.IOBase ( ioException, IOErrorType(..) ) #endif@@ -132,14 +134,14 @@ process (otherwise these handles are inherited from the current process). - Any 'Handle's passed to 'runProcess' are placed immediately in the + Any 'Handle's passed to 'runProcess' are placed immediately in the closed state. Note: consider using the more general 'createProcess' instead of 'runProcess'. -} runProcess- :: FilePath -- ^ Filename of the executable+ :: FilePath -- ^ Filename of the executable (see 'proc' for details) -> [String] -- ^ Arguments to pass to the executable -> Maybe FilePath -- ^ Optional path to the working directory -> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit)@@ -176,6 +178,23 @@ -- | Construct a 'CreateProcess' record for passing to 'createProcess', -- representing a raw command with arguments.+--+-- The @FilePath@ names the executable, and is interpreted according+-- to the platform's standard policy for searching for+-- executables. Specifically:+--+-- * on Unix systems the @execvp@ semantics is used, where if the+-- filename does not contain a slash (@/@) then the @PATH@+-- environment variable is searched for the executable.+--+-- * on Windows systems the Win32 @CreateProcess@ semantics is used.+-- Briefly: if the filename does not contain a path, then the+-- directory containing the parent executable is searched, followed+-- by the current directory, then some some standard locations, and+-- finally the current @PATH@. An @.exe@ extension is added if the+-- filename does not already have an extension. For full details+-- see the documentation for the Windows @SearchPath@ API.+ proc :: FilePath -> [String] -> CreateProcess proc cmd args = CreateProcess { cmdspec = RawCommand cmd args, cwd = Nothing,@@ -212,7 +231,7 @@ needed. 'createProcess' returns @(mb_stdin_hdl, mb_stdout_hdl, mb_stderr_hdl, p)@,-where +where * if @std_in == CreatePipe@, then @mb_stdin_hdl@ will be @Just h@, where @h@ is the write end of the pipe connected to the child@@ -275,7 +294,7 @@ with the process via its @stdin@, @stdout@ and @stderr@ respectively. For example, to start a process and feed a string to its stdin:- + > (inp,out,err,pid) <- runInteractiveProcess "..." > forkIO (hPutStr inp str) @@ -283,14 +302,14 @@ in text mode then use 'hSetBinaryMode'. -} runInteractiveProcess- :: FilePath -- ^ Filename of the executable+ :: FilePath -- ^ Filename of the executable (see 'proc' for details) -> [String] -- ^ Arguments to pass to the executable -> Maybe FilePath -- ^ Optional path to the working directory -> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit) -> IO (Handle,Handle,Handle,ProcessHandle) runInteractiveProcess cmd args mb_cwd mb_env = do- runInteractiveProcess1 "runInteractiveProcess" + runInteractiveProcess1 "runInteractiveProcess" (proc cmd args){ cwd = mb_cwd, env = mb_env } runInteractiveProcess1@@ -298,11 +317,11 @@ -> CreateProcess -> IO (Handle,Handle,Handle,ProcessHandle) runInteractiveProcess1 fun cmd = do- (mb_in, mb_out, mb_err, p) <- + (mb_in, mb_out, mb_err, p) <- runGenProcess_ fun cmd{ std_in = CreatePipe, std_out = CreatePipe,- std_err = CreatePipe } + std_err = CreatePipe } Nothing Nothing return (fromJust mb_in, fromJust mb_out, fromJust mb_err, p) @@ -310,7 +329,7 @@ -- waitForProcess {- | Waits for the specified process to terminate, and returns its exit code.- + GHC Note: in order to call @waitForProcess@ without blocking all the other threads in the system, you must compile the program with @-threaded@.@@ -341,10 +360,14 @@ -- ----------------------------------------------------------------------------- ----- | readProcess forks an external process, reads its standard output+-- | @readProcess@ forks an external process, reads its standard output -- strictly, blocking until the process terminates, and returns the output -- string. --+-- If an asynchronous exception is thrown to the thread executing+-- @readProcess@. The forked process will be terminated and @readProcess@ will+-- wait (block) until the process has been terminated.+-- -- Output is returned strictly, so this is not suitable for -- interactive applications. --@@ -366,47 +389,54 @@ -- -- * A string to pass on the standard input to the program. ---readProcess - :: FilePath -- ^ command to run+readProcess+ :: FilePath -- ^ Filename of the executable (see 'proc' for details) -> [String] -- ^ any arguments -> String -- ^ standard input -> IO String -- ^ stdout-readProcess cmd args input = do- (Just inh, Just outh, _, pid) <-+readProcess cmd args input =+ mask $ \restore -> do+ (Just inh, Just outh, _, pid) <- createProcess (proc cmd args){ std_in = CreatePipe, std_out = CreatePipe, std_err = Inherit }-- -- fork off a thread to start consuming the output- output <- hGetContents outh- outMVar <- newEmptyMVar- _ <- forkIO $ C.evaluate (length output) >> putMVar outMVar ()+ flip onException+ (do hClose inh; hClose outh;+ terminateProcess pid; waitForProcess pid) $ restore $ do+ -- fork off a thread to start consuming the output+ output <- hGetContents outh+ waitOut <- forkWait $ C.evaluate $ rnf output - -- now write and flush any input- when (not (null input)) $ do hPutStr inh input; hFlush inh- hClose inh -- done with stdin+ -- now write and flush any input+ when (not (null input)) $ do hPutStr inh input; hFlush inh+ hClose inh -- done with stdin - -- wait on the output- takeMVar outMVar- hClose outh+ -- wait on the output+ waitOut+ hClose outh - -- wait on the process- ex <- waitForProcess pid+ -- wait on the process+ ex <- waitForProcess pid - case ex of- ExitSuccess -> return output- ExitFailure r -> - ioError (mkIOError OtherError ("readProcess: " ++ cmd ++ - ' ':unwords (map show args) ++ - " (exit " ++ show r ++ ")")- Nothing Nothing)+ case ex of+ ExitSuccess -> return output+ ExitFailure r ->+ ioError (mkIOError OtherError ("readProcess: " ++ cmd +++ ' ':unwords (map show args) +++ " (exit " ++ show r ++ ")")+ Nothing Nothing) {- |-readProcessWithExitCode creates an external process, reads its+@readProcessWithExitCode@ creates an external process, reads its standard output and standard error strictly, waits until the process terminates, and then returns the 'ExitCode' of the process, the standard output, and the standard error. +If an asynchronous exception is thrown to the thread executing+@readProcessWithExitCode@. The forked process will be terminated and+@readProcessWithExitCode@ will wait (block) until the process has been+terminated.+ 'readProcess' and 'readProcessWithExitCode' are fairly simple wrappers around 'createProcess'. Constructing variants of these functions is quite easy: follow the link to the source code to see how@@ -414,46 +444,68 @@ -} readProcessWithExitCode- :: FilePath -- ^ command to run+ :: FilePath -- ^ Filename of the executable (see 'proc' for details) -> [String] -- ^ any arguments -> String -- ^ standard input -> IO (ExitCode,String,String) -- ^ exitcode, stdout, stderr-readProcessWithExitCode cmd args input = do- (Just inh, Just outh, Just errh, pid) <-- createProcess (proc cmd args){ std_in = CreatePipe,- std_out = CreatePipe,- std_err = CreatePipe }+readProcessWithExitCode cmd args input =+ mask $ \restore -> do+ (Just inh, Just outh, Just errh, pid) <- createProcess (proc cmd args)+ { std_in = CreatePipe,+ std_out = CreatePipe,+ std_err = CreatePipe }+ flip onException+ (do hClose inh; hClose outh; hClose errh;+ terminateProcess pid; waitForProcess pid) $ restore $ do+ -- fork off a thread to start consuming stdout+ out <- hGetContents outh+ waitOut <- forkWait $ C.evaluate $ rnf out - outMVar <- newEmptyMVar+ -- fork off a thread to start consuming stderr+ err <- hGetContents errh+ waitErr <- forkWait $ C.evaluate $ rnf err - -- fork off a thread to start consuming stdout- out <- hGetContents outh- _ <- forkIO $ C.evaluate (length out) >> putMVar outMVar ()+ -- now write and flush any input+ let writeInput = do+ unless (null input) $ do+ hPutStr inh input+ hFlush inh+ hClose inh - -- fork off a thread to start consuming stderr- err <- hGetContents errh- _ <- forkIO $ C.evaluate (length err) >> putMVar outMVar ()+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 611+ C.catch writeInput $ \e -> case e of+ IOError { ioe_type = ResourceVanished+ , ioe_errno = Just ioe }+ | Errno ioe == ePIPE -> return ()+ _ -> throwIO e+#else+ writeInput+#endif - -- now write and flush any input- when (not (null input)) $ do hPutStr inh input; hFlush inh- hClose inh -- done with stdin+ -- wait on the output+ waitOut+ waitErr - -- wait on the output- takeMVar outMVar- takeMVar outMVar- hClose outh- hClose errh+ hClose outh+ hClose errh - -- wait on the process- ex <- waitForProcess pid+ -- wait on the process+ ex <- waitForProcess pid - return (ex, out, err)+ return (ex, out, err)++forkWait :: IO a -> IO (IO a)+forkWait a = do+ res <- newEmptyMVar+ _ <- mask $ \restore -> forkIO $ try (restore a) >>= putMVar res+ return (takeMVar res >>= either (\ex -> throwIO (ex :: SomeException)) return)+ #endif /* !__HUGS__ */ -- --------------------------------------------------------------------------- -- system -{-| +{-| Computation @system cmd@ returns the exit code produced when the operating system runs the shell command @cmd@. @@ -552,7 +604,7 @@ terminateProcess :: ProcessHandle -> IO () terminateProcess ph = do withProcessHandle_ ph $ \p_ ->- case p_ of + case p_ of ClosedHandle _ -> return p_ OpenHandle h -> do throwErrnoIfMinus1Retry_ "terminateProcess" $ c_terminateProcess h@@ -600,7 +652,7 @@ -- ---------------------------------------------------------------------------- -- getProcessExitCode -{- | +{- | This is a non-blocking version of 'waitForProcess'. If the process is still running, 'Nothing' is returned. If the process has exited, then @'Just' e@ is returned where @e@ is the exit code of the process.
System/Process/Internals.hs view
@@ -199,7 +199,9 @@ = ShellCommand String -- ^ a command line to execute using the shell | RawCommand FilePath [String]- -- ^ the filename of an executable with a list of arguments+ -- ^ the filename of an executable with a list of arguments.+ -- see 'System.Process.proc' for the precise interpretation of+ -- the @FilePath@ field. data StdStream = Inherit -- ^ Inherit Handle from parent
process.cabal view
@@ -1,5 +1,5 @@ name: process-version: 1.1.0.1+version: 1.1.0.2 license: BSD3 license-file: LICENSE maintainer: libraries@haskell.org@@ -57,8 +57,9 @@ cpp-options: -Dbase3 } - build-depends: directory >= 1.0 && < 1.2,- filepath >= 1.1 && < 1.4+ build-depends: directory >= 1.0 && < 1.3,+ filepath >= 1.1 && < 1.4,+ deepseq >= 1.1 && < 1.4 extensions: CPP }