Skip to content

Commit 6e88f4a

Browse files
bgamariphadej
authored andcommitted
Use jobs when calling subprocesses
Many toolchain tools written for POSIX systems rely on the exec system call. Unfortunately, it is not possible to implement `exec` in a POSIX-compliant manner on Windows. In particular, the semantics of the `exec` implementation provided by the widely-used `msvcrt` C runtime will cause process's waiting on the `exec`'ing process to incorrectly conclude that the process has successfully terminated when in fact it is still running in another process. For this reason, the `process` library exposes the `use_process_jobs` flag to use a more strict (although still not POSIX-compliant) mechanism for tracking process completion. This is explained in this comment [2]. Unfortunately, job support in the `process` library is currently quite broken and was only recently fixed [1]. Consequently, we only enable job object support for process releases >= 1.6.8. [1] haskell/process#168 [2] https://github.com/haskell/process/blob/master/System/Process.hs#L399
1 parent 202a178 commit 6e88f4a

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

Cabal/Distribution/Simple/Utils.hs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,13 @@ import System.IO.Unsafe
230230
import qualified Control.Exception as Exception
231231

232232
import Foreign.C.Error (Errno (..), ePIPE)
233+
import Data.Maybe (fromJust)
233234
import Data.Time.Clock.POSIX (getPOSIXTime, POSIXTime)
234235
import Control.Exception (IOException, evaluate, throwIO, fromException)
235236
import Numeric (showFFloat)
236237
import qualified System.Process as Process
237-
( CreateProcess(..), StdStream(..), proc)
238238
import System.Process
239-
( ProcessHandle, createProcess, rawSystem, runInteractiveProcess
239+
( CreateProcess, ProcessHandle
240240
, showCommandForUser, waitForProcess)
241241

242242
import qualified GHC.IO.Exception as GHC
@@ -680,6 +680,59 @@ maybeExit cmd = do
680680
res <- cmd
681681
unless (res == ExitSuccess) $ exitWith res
682682

683+
-- | Enable process jobs to ensure accurate determination of process completion
684+
-- in the presence of @exec(3)@ on Windows.
685+
--
686+
-- Unfortunately the process job support is badly broken in @process@ releases
687+
-- prior to 1.6.8, so we disable it in these versions, despite the fact that
688+
-- this means we may see sporatic build failures without jobs.
689+
enableProcessJobs :: CreateProcess -> CreateProcess
690+
#ifdef MIN_VERSION_process
691+
#if MIN_VERSION_process(1,6,8)
692+
enableProcessJobs cp = cp {Process.use_process_jobs = True}
693+
#else
694+
enableProcessJobs cp = cp
695+
#endif
696+
#else
697+
enableProcessJobs cp = cp
698+
#endif
699+
700+
-- | 'System.Process.createProcess' with process jobs enabled when appropriate.
701+
-- See 'enableProcessJobs'.
702+
createProcess :: CreateProcess
703+
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
704+
createProcess = Process.createProcess . enableProcessJobs
705+
706+
-- | 'System.Process.rawSystem' with process jobs enabled when appropriate.
707+
-- See 'enableProcessJobs'.
708+
rawSystem :: String -> [String] -> IO ExitCode
709+
rawSystem cmd args = do
710+
#if MIN_VERSION_process(1,2,0)
711+
(_,_,_,p) <- createProcess (Process.proc cmd args) { Process.delegate_ctlc = True }
712+
waitForProcess p
713+
#else
714+
-- With very old 'process', just do its rawSystem
715+
Process.rawSystem cmd args
716+
#endif
717+
718+
-- | 'System.Process.runInteractiveProcess' with process jobs enabled when
719+
-- appropriate. See 'enableProcessJobs'.
720+
runInteractiveProcess
721+
:: FilePath -- ^ Filename of the executable (see 'RawCommand' for details)
722+
-> [String] -- ^ Arguments to pass to the executable
723+
-> Maybe FilePath -- ^ Optional path to the working directory
724+
-> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit)
725+
-> IO (Handle,Handle,Handle,ProcessHandle)
726+
runInteractiveProcess cmd args mb_cwd mb_env = do
727+
(mb_in, mb_out, mb_err, p) <-
728+
createProcess (Process.proc cmd args)
729+
{ Process.std_in = Process.CreatePipe,
730+
Process.std_out = Process.CreatePipe,
731+
Process.std_err = Process.CreatePipe,
732+
Process.env = mb_env,
733+
Process.cwd = mb_cwd }
734+
return (fromJust mb_in, fromJust mb_out, fromJust mb_err, p)
735+
683736
printRawCommandAndArgs :: Verbosity -> FilePath -> [String] -> IO ()
684737
printRawCommandAndArgs verbosity path args = withFrozenCallStack $
685738
printRawCommandAndArgsAndEnv verbosity path args Nothing Nothing

0 commit comments

Comments
 (0)