From 105a46f78b0df2bea2b78ec1c9a3b7c08a84c8fd Mon Sep 17 00:00:00 2001 From: Kristen Kozak <grayjay@wordroute.com> Date: Tue, 22 Sep 2015 16:27:04 -0700 Subject: [PATCH] Prevent detailed-0.9 test suites from freezing on Windows (fixes #2762) --- Cabal/Distribution/Simple/Test/LibV09.hs | 40 ++++++++++--------- Cabal/Distribution/Simple/Utils.hs | 50 ++++++++++++++++-------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/Cabal/Distribution/Simple/Test/LibV09.hs b/Cabal/Distribution/Simple/Test/LibV09.hs index 859e6a9958..a2417bb53c 100644 --- a/Cabal/Distribution/Simple/Test/LibV09.hs +++ b/Cabal/Distribution/Simple/Test/LibV09.hs @@ -23,15 +23,14 @@ import Distribution.Simple.Setup ( TestFlags(..), TestShowDetails(..), fromFlag, configCoverage ) import Distribution.Simple.Test.Log import Distribution.Simple.Utils - ( die, notice, rawSystemIOWithEnv, addLibraryPath ) + ( die, debug, notice, createProcessWithEnv, addLibraryPath ) import Distribution.System ( Platform (..) ) import Distribution.TestSuite import Distribution.Text import Distribution.Verbosity ( normal ) -import Control.Concurrent (forkIO) import Control.Exception ( bracket ) -import Control.Monad ( when, unless, void ) +import Control.Monad ( when, unless ) import Data.Maybe ( mapMaybe ) import System.Directory ( createDirectoryIfMissing, doesDirectoryExist, doesFileExist @@ -40,6 +39,7 @@ import System.Directory import System.Exit ( ExitCode(..), exitWith ) import System.FilePath ( (</>), (<.>) ) import System.IO ( hClose, hGetContents, hPutStr ) +import System.Process (StdStream(..), waitForProcess) runTest :: PD.PackageDescription -> LBI.LocalBuildInfo @@ -74,22 +74,11 @@ runTest pkg_descr lbi flags suite = do suiteLog <- bracket openCabalTemp deleteIfExists $ \tempLog -> do - (rIn, wIn) <- createPipe (rOut, wOut) <- createPipe - -- Prepare standard input for test executable - --appendFile tempInput $ show (tempInput, PD.testName suite) - hPutStr wIn $ show (tempLog, PD.testName suite) - hClose wIn - - -- Append contents of temporary log file to the final human- - -- readable log file - logText <- hGetContents rOut - -- Force the IO manager to drain the test output pipe - void $ forkIO $ length logText `seq` return () - -- Run test executable - _ <- do let opts = map (testOption pkg_descr lbi suite) $ testOptions flags + (Just wIn, _, _, process) <- do + let opts = map (testOption pkg_descr lbi suite) $ testOptions flags dataDirPath = pwd </> PD.dataDir pkg_descr tixFile = pwd </> tixFilePath distPref way (PD.testName suite) pkgPathEnv = (pkgPathEnvVar pkg_descr "datadir", dataDirPath) @@ -108,9 +97,22 @@ runTest pkg_descr lbi flags suite = do True False lbi clbi return (addLibraryPath os paths shellEnv) else return shellEnv - rawSystemIOWithEnv verbosity cmd opts Nothing (Just shellEnv') - -- these handles are closed automatically - (Just rIn) (Just wOut) (Just wOut) + createProcessWithEnv verbosity cmd opts Nothing (Just shellEnv') + -- these handles are closed automatically + CreatePipe (UseHandle wOut) (UseHandle wOut) + + hPutStr wIn $ show (tempLog, PD.testName suite) + hClose wIn + + -- Append contents of temporary log file to the final human- + -- readable log file + logText <- hGetContents rOut + -- Force the IO manager to drain the test output pipe + length logText `seq` return () + + exitcode <- waitForProcess process + unless (exitcode == ExitSuccess) $ do + debug verbosity $ cmd ++ " returned " ++ show exitcode -- Generate final log file name let finalLogName l = testLogDir diff --git a/Cabal/Distribution/Simple/Utils.hs b/Cabal/Distribution/Simple/Utils.hs index 1ac3c61c8a..6560ac7290 100644 --- a/Cabal/Distribution/Simple/Utils.hs +++ b/Cabal/Distribution/Simple/Utils.hs @@ -33,6 +33,7 @@ module Distribution.Simple.Utils ( rawSystemStdout, rawSystemStdInOut, rawSystemIOWithEnv, + createProcessWithEnv, maybeExit, xargs, findProgramLocation, @@ -197,7 +198,7 @@ import Control.Concurrent (forkIO) import qualified System.Process as Process ( CreateProcess(..), StdStream(..), proc) import System.Process - ( createProcess, rawSystem, runInteractiveProcess + ( ProcessHandle, createProcess, rawSystem, runInteractiveProcess , showCommandForUser, waitForProcess) import Distribution.Compat.CopyFile ( copyFile, copyOrdinaryFile, copyExecutableFile @@ -450,29 +451,46 @@ rawSystemIOWithEnv :: Verbosity -> Maybe Handle -- ^ stderr -> IO ExitCode rawSystemIOWithEnv verbosity path args mcwd menv inp out err = do + (_,_,_,ph) <- createProcessWithEnv verbosity path args mcwd menv + (mbToStd inp) (mbToStd out) (mbToStd err) + exitcode <- waitForProcess ph + unless (exitcode == ExitSuccess) $ do + debug verbosity $ path ++ " returned " ++ show exitcode + return exitcode + where + mbToStd :: Maybe Handle -> Process.StdStream + mbToStd = maybe Process.Inherit Process.UseHandle + +createProcessWithEnv :: Verbosity + -> FilePath + -> [String] + -> Maybe FilePath -- ^ New working dir or inherit + -> Maybe [(String, String)] -- ^ New environment or inherit + -> Process.StdStream -- ^ stdin + -> Process.StdStream -- ^ stdout + -> Process.StdStream -- ^ stderr + -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) + -- ^ Any handles created for stdin, stdout, or stderr + -- with 'CreateProcess', and a handle to the process. +createProcessWithEnv verbosity path args mcwd menv inp out err = do printRawCommandAndArgsAndEnv verbosity path args menv hFlush stdout - (_,_,_,ph) <- createProcess $ - (Process.proc path args) { Process.cwd = mcwd - , Process.env = menv - , Process.std_in = mbToStd inp - , Process.std_out = mbToStd out - , Process.std_err = mbToStd err + (inp', out', err', ph) <- createProcess $ + (Process.proc path args) { + Process.cwd = mcwd + , Process.env = menv + , Process.std_in = inp + , Process.std_out = out + , Process.std_err = err #ifdef MIN_VERSION_process #if MIN_VERSION_process(1,2,0) -- delegate_ctlc has been added in process 1.2, and we still want to be able to -- bootstrap GHC on systems not having that version - , Process.delegate_ctlc = True + , Process.delegate_ctlc = True #endif #endif - } - exitcode <- waitForProcess ph - unless (exitcode == ExitSuccess) $ do - debug verbosity $ path ++ " returned " ++ show exitcode - return exitcode - where - mbToStd :: Maybe Handle -> Process.StdStream - mbToStd = maybe Process.Inherit Process.UseHandle + } + return (inp', out', err', ph) -- | Run a command and return its output. -- -- GitLab