Skip to content

Commit dde6255

Browse files
authored
Merge pull request #6865 from phadej/create-pipe
Use process createPipe
2 parents 36ab7a0 + 3c19578 commit dde6255

File tree

6 files changed

+125
-44
lines changed

6 files changed

+125
-44
lines changed

Cabal/Cabal.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,10 +669,12 @@ test-suite unit-tests
669669
main-is: UnitTests.hs
670670
build-depends:
671671
array,
672+
async >= 2.2.2 && <2.3,
672673
base,
673674
binary,
674675
bytestring,
675676
containers,
677+
deepseq,
676678
directory,
677679
filepath,
678680
integer-logarithms >= 1.0.2 && <1.1,

Cabal/Distribution/Compat/CreatePipe.hs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
module Distribution.Compat.CreatePipe (createPipe) where
66

7-
import System.IO (Handle, hSetEncoding, localeEncoding)
7+
#if MIN_VERSION_process(1,2,1)
8+
import System.Process (createPipe)
9+
#else
10+
import System.IO (Handle, hSetBinaryMode)
811

912
import Prelude ()
1013
import Distribution.Compat.Prelude
@@ -40,8 +43,8 @@ createPipe = do
4043
return (readfd, writefd)
4144
(do readh <- fdToHandle readfd ReadMode
4245
writeh <- fdToHandle writefd WriteMode
43-
hSetEncoding readh localeEncoding
44-
hSetEncoding writeh localeEncoding
46+
hSetBinaryMode readh True
47+
hSetBinaryMode writeh True
4548
return (readh, writeh)) `onException` (close readfd >> close writefd)
4649
where
4750
fdToHandle :: CInt -> IOMode -> IO Handle
@@ -69,9 +72,10 @@ createPipe = do
6972
(readfd, writefd) <- Posix.createPipe
7073
readh <- fdToHandle readfd
7174
writeh <- fdToHandle writefd
72-
hSetEncoding readh localeEncoding
73-
hSetEncoding writeh localeEncoding
75+
hSetBinaryMode readh True
76+
hSetBinaryMode writeh True
7477
return (readh, writeh)
7578
where
7679
_ = callStack
7780
#endif
81+
#endif

Cabal/Distribution/Simple/Test/ExeV10.hs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ import Distribution.TestSuite
2727
import Distribution.Pretty
2828
import Distribution.Verbosity
2929

30-
import Control.Concurrent (forkIO)
3130
import System.Directory
3231
( createDirectoryIfMissing, doesDirectoryExist, doesFileExist
3332
, getCurrentDirectory, removeDirectoryRecursive )
3433
import System.FilePath ( (</>), (<.>) )
35-
import System.IO ( hGetContents, stdout, stderr )
34+
import System.IO ( stdout, stderr )
35+
36+
import qualified Data.ByteString.Lazy as LBS
3637

3738
runTest :: PD.PackageDescription
3839
-> LBI.LocalBuildInfo
@@ -66,20 +67,6 @@ runTest pkg_descr lbi clbi flags suite = do
6667
-- Write summary notices indicating start of test suite
6768
notice verbosity $ summarizeSuiteStart $ testName'
6869

69-
(wOut, wErr, logText) <- case details of
70-
Direct -> return (stdout, stderr, "")
71-
_ -> do
72-
(rOut, wOut) <- createPipe
73-
74-
-- Read test executable's output lazily (returns immediately)
75-
logText <- hGetContents rOut
76-
-- Force the IO manager to drain the test output pipe
77-
void $ forkIO $ length logText `seq` return ()
78-
79-
-- '--show-details=streaming': print the log output in another thread
80-
when (details == Streaming) $ void $ forkIO $ putStr logText
81-
82-
return (wOut, wOut, logText)
8370

8471
-- Run the test executable
8572
let opts = map (testOption pkg_descr lbi suite)
@@ -97,14 +84,34 @@ runTest pkg_descr lbi clbi flags suite = do
9784
return (addLibraryPath os paths shellEnv)
9885
else return shellEnv
9986

100-
exit <- case testWrapper flags of
101-
Flag path -> rawSystemIOWithEnv verbosity path (cmd:opts) Nothing (Just shellEnv')
102-
-- these handles are automatically closed
103-
Nothing (Just wOut) (Just wErr)
87+
-- Output logger
88+
(wOut, wErr, getLogText) <- case details of
89+
Direct -> return (stdout, stderr, return LBS.empty)
90+
_ -> do
91+
(rOut, wOut) <- createPipe
92+
93+
return $ (,,) wOut wOut $ do
94+
-- Read test executables' output
95+
logText <- LBS.hGetContents rOut
96+
97+
-- '--show-details=streaming': print the log output in another thread
98+
when (details == Streaming) $ LBS.putStr logText
99+
100+
-- drain the output.
101+
evaluate (force logText)
102+
103+
(exit, logText) <- case testWrapper flags of
104+
Flag path -> rawSystemIOWithEnvAndAction
105+
verbosity path (cmd:opts) Nothing (Just shellEnv')
106+
getLogText
107+
-- these handles are automatically closed
108+
Nothing (Just wOut) (Just wErr)
104109

105-
NoFlag -> rawSystemIOWithEnv verbosity cmd opts Nothing (Just shellEnv')
106-
-- these handles are automatically closed
107-
Nothing (Just wOut) (Just wErr)
110+
NoFlag -> rawSystemIOWithEnvAndAction
111+
verbosity cmd opts Nothing (Just shellEnv')
112+
getLogText
113+
-- these handles are automatically closed
114+
Nothing (Just wOut) (Just wErr)
108115

109116
-- Generate TestSuiteLog from executable exit code and a machine-
110117
-- readable test log.
@@ -115,7 +122,7 @@ runTest pkg_descr lbi clbi flags suite = do
115122

116123
-- Append contents of temporary log file to the final human-
117124
-- readable log file
118-
appendFile (logFile suiteLog) logText
125+
LBS.appendFile (logFile suiteLog) logText
119126

120127
-- Write end-of-suite summary notice to log file
121128
appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog
@@ -127,7 +134,9 @@ runTest pkg_descr lbi clbi flags suite = do
127134
details == Failures && not (suitePassed $ testLogs suiteLog))
128135
-- verbosity overrides show-details
129136
&& verbosity >= normal
130-
whenPrinting $ putStr $ unlines $ lines logText
137+
whenPrinting $ do
138+
LBS.putStr logText
139+
putChar '\n'
131140

132141
-- Write summary notice to terminal indicating end of test suite
133142
notice verbosity $ summarizeSuiteFinish suiteLog

Cabal/Distribution/Simple/Test/LibV09.hs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ import Distribution.Pretty
3434
import Distribution.Verbosity
3535

3636
import qualified Control.Exception as CE
37+
import qualified Data.ByteString.Lazy as LBS
3738
import System.Directory
3839
( createDirectoryIfMissing, canonicalizePath
3940
, doesDirectoryExist, doesFileExist
4041
, getCurrentDirectory, removeDirectoryRecursive, removeFile
4142
, setCurrentDirectory )
4243
import System.FilePath ( (</>), (<.>) )
43-
import System.IO ( hClose, hGetContents, hPutStr )
44+
import System.IO ( hClose, hPutStr )
4445
import System.Process (StdStream(..), waitForProcess)
4546

4647
runTest :: PD.PackageDescription
@@ -78,6 +79,8 @@ runTest pkg_descr lbi clbi flags suite = do
7879

7980
suiteLog <- CE.bracket openCabalTemp deleteIfExists $ \tempLog -> do
8081

82+
-- TODO: this setup is broken,
83+
-- if the test output is too big, we will deadlock.
8184
(rOut, wOut) <- createPipe
8285

8386
-- Run test executable
@@ -112,9 +115,9 @@ runTest pkg_descr lbi clbi flags suite = do
112115

113116
-- Append contents of temporary log file to the final human-
114117
-- readable log file
115-
logText <- hGetContents rOut
118+
logText <- LBS.hGetContents rOut
116119
-- Force the IO manager to drain the test output pipe
117-
length logText `seq` return ()
120+
_ <- evaluate (force logText)
118121

119122
exitcode <- waitForProcess process
120123
unless (exitcode == ExitSuccess) $ do
@@ -134,7 +137,7 @@ runTest pkg_descr lbi clbi flags suite = do
134137
-- Write summary notice to log file indicating start of test suite
135138
appendFile (logFile suiteLog) $ summarizeSuiteStart testName'
136139

137-
appendFile (logFile suiteLog) logText
140+
LBS.appendFile (logFile suiteLog) logText
138141

139142
-- Write end-of-suite summary notice to log file
140143
appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog
@@ -145,7 +148,9 @@ runTest pkg_descr lbi clbi flags suite = do
145148
whenPrinting = when $ (details > Never)
146149
&& (not (suitePassed $ testLogs suiteLog) || details == Always)
147150
&& verbosity >= normal
148-
whenPrinting $ putStr $ unlines $ lines logText
151+
whenPrinting $ do
152+
LBS.putStr logText
153+
putChar '\n'
149154

150155
return suiteLog
151156

@@ -158,7 +163,7 @@ runTest pkg_descr lbi clbi flags suite = do
158163
return suiteLog
159164
where
160165
testName' = unUnqualComponentName $ PD.testName suite
161-
166+
162167
deleteIfExists file = do
163168
exists <- doesFileExist file
164169
when exists $ removeFile file

Cabal/Distribution/Simple/Utils.hs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ module Distribution.Simple.Utils (
5050
rawSystemStdout,
5151
rawSystemStdInOut,
5252
rawSystemIOWithEnv,
53+
rawSystemIOWithEnvAndAction,
5354
createProcessWithEnv,
5455
maybeExit,
5556
xargs,
@@ -765,6 +766,29 @@ rawSystemIOWithEnv verbosity path args mcwd menv inp out err = withFrozenCallSta
765766
mbToStd :: Maybe Handle -> Process.StdStream
766767
mbToStd = maybe Process.Inherit Process.UseHandle
767768

769+
rawSystemIOWithEnvAndAction
770+
:: Verbosity
771+
-> FilePath
772+
-> [String]
773+
-> Maybe FilePath -- ^ New working dir or inherit
774+
-> Maybe [(String, String)] -- ^ New environment or inherit
775+
-> IO a -- ^ action to perform after process is created, but before 'waitForProcess'.
776+
-> Maybe Handle -- ^ stdin
777+
-> Maybe Handle -- ^ stdout
778+
-> Maybe Handle -- ^ stderr
779+
-> IO (ExitCode, a)
780+
rawSystemIOWithEnvAndAction verbosity path args mcwd menv action inp out err = withFrozenCallStack $ do
781+
(_,_,_,ph) <- createProcessWithEnv verbosity path args mcwd menv
782+
(mbToStd inp) (mbToStd out) (mbToStd err)
783+
a <- action
784+
exitcode <- waitForProcess ph
785+
unless (exitcode == ExitSuccess) $ do
786+
debug verbosity $ path ++ " returned " ++ show exitcode
787+
return (exitcode, a)
788+
where
789+
mbToStd :: Maybe Handle -> Process.StdStream
790+
mbToStd = maybe Process.Inherit Process.UseHandle
791+
768792
createProcessWithEnv ::
769793
Verbosity
770794
-> FilePath
Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,56 @@
11
module UnitTests.Distribution.Compat.CreatePipe (tests) where
22

3+
import Control.Concurrent.Async (async, wait)
4+
import Control.DeepSeq (force)
5+
import Control.Exception (evaluate)
6+
import System.IO (hClose, hGetContents, hPutStr, hSetEncoding, localeEncoding)
7+
import Test.Tasty (TestTree)
8+
import Test.Tasty.HUnit (Assertion, assertEqual, testCase)
9+
10+
import qualified Data.ByteString as BS
11+
312
import Distribution.Compat.CreatePipe
4-
import System.IO (hClose, hGetContents, hPutStr, hSetEncoding, localeEncoding)
5-
import Test.Tasty
6-
import Test.Tasty.HUnit
713

814
tests :: [TestTree]
9-
tests = [testCase "Locale Encoding" case_Locale_Encoding]
15+
tests =
16+
[ testCase "Locale Encoding" case_Locale_Encoding
17+
, testCase "Binary ByteStrings are not affected" case_ByteString
18+
]
1019

1120
case_Locale_Encoding :: Assertion
1221
case_Locale_Encoding = do
13-
let str = "\0252"
22+
let str = "\0252foobar"
1423
(r, w) <- createPipe
1524
hSetEncoding w localeEncoding
16-
out <- hGetContents r
17-
hPutStr w str
18-
hClose w
25+
hSetEncoding r localeEncoding
26+
27+
ra <- async $ do
28+
out <- hGetContents r
29+
evaluate (force out)
30+
31+
wa <- async $ do
32+
hPutStr w str
33+
hClose w
34+
35+
out <- wait ra
36+
wait wa
37+
1938
assertEqual "createPipe should support Unicode roundtripping" str out
39+
40+
case_ByteString :: Assertion
41+
case_ByteString = do
42+
let bs = BS.pack[ 1..255]
43+
(r, w) <- createPipe
44+
45+
ra <- async $ do
46+
out <- BS.hGetContents r
47+
evaluate (force out)
48+
49+
wa <- async $ do
50+
BS.hPutStr w bs
51+
hClose w
52+
53+
out <- wait ra
54+
wait wa
55+
56+
assertEqual "createPipe should support Unicode roundtripping" bs out

0 commit comments

Comments
 (0)