forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRun.hs
More file actions
138 lines (127 loc) · 5.07 KB
/
Run.hs
File metadata and controls
138 lines (127 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
-- | Run sub-processes.
module System.Process.Run
(runCmd
,runCmd'
,callProcess
,callProcess'
,callProcessInheritStderrStdout
,callProcessObserveStdout
,createProcess'
,ProcessExitedUnsuccessfully
,Cmd(..)
)
where
import Stack.Prelude
import Data.Conduit.Process hiding (callProcess)
import qualified Data.Text as T
import System.Exit (exitWith, ExitCode (..))
import System.IO
import qualified System.Process
import System.Process.Log
import System.Process.Read
-- | Cmd holds common infos needed to running a process in most cases
data Cmd = Cmd
{ cmdDirectoryToRunIn :: Maybe (Path Abs Dir) -- ^ directory to run in
, cmdCommandToRun :: FilePath -- ^ command to run
, cmdEnvOverride :: EnvOverride
, cmdCommandLineArguments :: [String] -- ^ command line arguments
}
-- | Run the given command in the given directory, inheriting stdout and stderr.
--
-- If it exits with anything but success, prints an error
-- and then calls 'exitWith' to exit the program.
runCmd :: forall (m :: * -> *).
(MonadLogger m, MonadUnliftIO m)
=> Cmd
-> Maybe Text -- ^ optional additional error message
-> m ()
runCmd = runCmd' id
runCmd' :: forall (m :: * -> *).
(MonadLogger m, MonadUnliftIO m)
=> (CreateProcess -> CreateProcess)
-> Cmd
-> Maybe Text -- ^ optional additional error message
-> m ()
runCmd' modCP cmd@Cmd{..} mbErrMsg = do
result <- try (callProcess' modCP cmd)
case result of
Left (ProcessExitedUnsuccessfully _ ec) -> do
logError $
T.pack $
concat $
[ "Exit code "
, show ec
, " while running "
, show (cmdCommandToRun : cmdCommandLineArguments)
] ++ (case cmdDirectoryToRunIn of
Nothing -> []
Just mbDir -> [" in ", toFilePath mbDir]
)
forM_ mbErrMsg logError
liftIO (exitWith ec)
Right () -> return ()
-- | Like 'System.Process.callProcess', but takes an optional working directory and
-- environment override, and throws 'ProcessExitedUnsuccessfully' if the
-- process exits unsuccessfully and a 'ReadProcessException' if the executable is not found.
--
-- Inherits stdout and stderr.
callProcess :: (MonadIO m, MonadLogger m) => Cmd -> m ()
callProcess = callProcess' id
-- | Like 'System.Process.callProcess', but takes an optional working directory and
-- environment override, and throws 'ProcessExitedUnsuccessfully' if the
-- process exits unsuccessfully and a 'ReadProcessException' if the executable is not found.
--
-- Inherits stdout and stderr.
callProcess' :: (MonadIO m, MonadLogger m)
=> (CreateProcess -> CreateProcess) -> Cmd -> m ()
callProcess' modCP cmd = do
c <- liftM modCP (cmdToCreateProcess cmd)
logCreateProcess c
liftIO $ do
(_, _, _, p) <- System.Process.createProcess c
exit_code <- waitForProcess p
case exit_code of
ExitSuccess -> return ()
ExitFailure _ -> throwM (ProcessExitedUnsuccessfully c exit_code)
callProcessInheritStderrStdout :: (MonadIO m, MonadLogger m) => Cmd -> m ()
callProcessInheritStderrStdout cmd = do
let inheritOutput cp = cp { std_in = CreatePipe, std_out = Inherit, std_err = Inherit }
callProcess' inheritOutput cmd
callProcessObserveStdout :: (MonadIO m, MonadLogger m) => Cmd -> m String
callProcessObserveStdout cmd = do
c <- liftM modCP (cmdToCreateProcess cmd)
logCreateProcess c
liftIO $ do
(_, Just hStdout, _, p) <- System.Process.createProcess c
hSetBuffering hStdout NoBuffering
exit_code <- waitForProcess p
case exit_code of
ExitSuccess -> hGetLine hStdout
ExitFailure _ -> throwM (ProcessExitedUnsuccessfully c exit_code)
where
modCP c = c { std_in = CreatePipe, std_out = CreatePipe, std_err = Inherit }
-- | Like 'System.Process.Internal.createProcess_', but taking a 'Cmd'.
-- Note that the 'Handle's provided by 'UseHandle' are not closed
-- automatically.
createProcess' :: (MonadIO m, MonadLogger m)
=> String
-> (CreateProcess -> CreateProcess)
-> Cmd
-> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
createProcess' tag modCP cmd = do
c <- liftM modCP (cmdToCreateProcess cmd)
logCreateProcess c
liftIO $ System.Process.createProcess_ tag c
-- Throws a 'ReadProcessException' if process is not found.
cmdToCreateProcess :: MonadIO m => Cmd -> m CreateProcess
cmdToCreateProcess (Cmd wd cmd0 menv args) = do
cmd <- preProcess wd menv cmd0
return $ (proc cmd args) { delegate_ctlc = True
, cwd = fmap toFilePath wd
, env = envHelper menv }