Skip to content

Commit e4db2dc

Browse files
committed
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 1a31242 commit e4db2dc

File tree

1 file changed

+51
-2
lines changed

1 file changed

+51
-2
lines changed

Cabal/Distribution/Simple/Utils.hs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,14 @@ 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)
238+
( CreateProcess(..), StdStream(..), proc, createProcess )
238239
import System.Process
239-
( ProcessHandle, createProcess, rawSystem, runInteractiveProcess
240+
( CreateProcess, ProcessHandle
240241
, showCommandForUser, waitForProcess)
241242

242243
import qualified GHC.IO.Exception as GHC
@@ -680,6 +681,54 @@ maybeExit cmd = do
680681
res <- cmd
681682
unless (res == ExitSuccess) $ exitWith res
682683

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

0 commit comments

Comments
 (0)