1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE RankNTypes #-}
5 {-# LANGUAGE ScopedTypeVariables #-}
6 {-# LANGUAGE TypeApplications #-}
8 -----------------------------------------------------------------------------
11 -- Module : Distribution.Simple.Program.Run
12 -- Copyright : Duncan Coutts 2009
14 -- Maintainer : cabal-devel@haskell.org
15 -- Portability : portable
17 -- This module provides a data type for program invocations and functions to
19 module Distribution
.Simple
.Program
.Run
20 ( ProgramInvocation
(..)
22 , emptyProgramInvocation
23 , simpleProgramInvocation
25 , programInvocationCwd
26 , multiStageProgramInvocation
27 , runProgramInvocation
28 , getProgramInvocationOutput
29 , getProgramInvocationLBS
30 , getProgramInvocationOutputAndErrors
31 , getProgramInvocationLBSAndErrors
32 , getEffectiveEnvironment
35 import Distribution
.Compat
.Prelude
38 import Distribution
.Compat
.Environment
39 import Distribution
.Simple
.Errors
40 import Distribution
.Simple
.Program
.Types
41 import Distribution
.Simple
.Utils
42 import Distribution
.Utils
.Generic
43 import Distribution
.Utils
.Path
44 import Distribution
.Verbosity
46 import qualified Data
.ByteString
.Lazy
as LBS
47 import qualified Data
.Map
as Map
49 -- | Represents a specific invocation of a specific program.
51 -- This is used as an intermediate type between deciding how to call a program
52 -- and actually doing it. This provides the opportunity to the caller to
53 -- adjust how the program will be called. These invocations can either be run
54 -- directly or turned into shell or batch scripts.
55 data ProgramInvocation
= ProgramInvocation
56 { progInvokePath
:: FilePath
57 , progInvokeArgs
:: [String]
58 , progInvokeEnv
:: [(String, Maybe String)]
59 , progInvokeCwd
:: Maybe FilePath
60 , progInvokeInput
:: Maybe IOData
61 , progInvokeInputEncoding
:: IOEncoding
62 -- ^ TODO: remove this, make user decide when constructing 'progInvokeInput'.
63 , progInvokeOutputEncoding
:: IOEncoding
67 = IOEncodingText
-- locale mode text
68 | IOEncodingUTF8
-- always utf8
70 encodeToIOData
:: IOEncoding
-> IOData
-> IOData
71 encodeToIOData _ iod
@(IODataBinary _
) = iod
72 encodeToIOData IOEncodingText iod
@(IODataText _
) = iod
73 encodeToIOData IOEncodingUTF8
(IODataText str
) = IODataBinary
(toUTF8LBS str
)
75 emptyProgramInvocation
:: ProgramInvocation
76 emptyProgramInvocation
=
81 , progInvokeCwd
= Nothing
82 , progInvokeInput
= Nothing
83 , progInvokeInputEncoding
= IOEncodingText
84 , progInvokeOutputEncoding
= IOEncodingText
87 simpleProgramInvocation
91 simpleProgramInvocation path args
=
92 emptyProgramInvocation
93 { progInvokePath
= path
94 , progInvokeArgs
= args
101 programInvocation prog args
=
102 emptyProgramInvocation
103 { progInvokePath
= programPath prog
105 programDefaultArgs prog
107 ++ programOverrideArgs prog
108 , progInvokeEnv
= programOverrideEnv prog
113 . Maybe (SymbolicPath CWD
(Dir to
))
117 programInvocationCwd mbWorkDir prog args
=
118 (programInvocation prog args
)
119 { progInvokeCwd
= fmap getSymbolicPath mbWorkDir
122 runProgramInvocation
:: Verbosity
-> ProgramInvocation
-> IO ()
126 { progInvokePath
= path
127 , progInvokeArgs
= args
129 , progInvokeCwd
= Nothing
130 , progInvokeInput
= Nothing
132 rawSystemExit verbosity Nothing path args
136 { progInvokePath
= path
137 , progInvokeArgs
= args
138 , progInvokeEnv
= envOverrides
139 , progInvokeCwd
= mcwd
140 , progInvokeInput
= Nothing
142 menv
<- getEffectiveEnvironment envOverrides
156 { progInvokePath
= path
157 , progInvokeArgs
= args
158 , progInvokeEnv
= envOverrides
159 , progInvokeCwd
= mcwd
160 , progInvokeInput
= Just inputStr
161 , progInvokeInputEncoding
= encoding
163 menv
<- getEffectiveEnvironment envOverrides
164 (_
, errors
, exitCode
) <-
173 when (exitCode
/= ExitSuccess
) $
174 dieWithException verbosity
$
175 RunProgramInvocationException path errors
177 input
= encodeToIOData encoding inputStr
179 getProgramInvocationOutput
:: Verbosity
-> ProgramInvocation
-> IO String
180 getProgramInvocationOutput verbosity inv
= do
181 (output
, errors
, exitCode
) <- getProgramInvocationOutputAndErrors verbosity inv
182 when (exitCode
/= ExitSuccess
) $
184 "'" ++ progInvokePath inv
++ "' exited with an error:\n" ++ errors
187 getProgramInvocationLBS
:: Verbosity
-> ProgramInvocation
-> IO LBS
.ByteString
188 getProgramInvocationLBS verbosity inv
= do
189 (output
, errors
, exitCode
) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary
190 when (exitCode
/= ExitSuccess
) $
191 dieWithException verbosity
$
192 GetProgramInvocationLBSException
(progInvokePath inv
) errors
195 getProgramInvocationOutputAndErrors
198 -> IO (String, String, ExitCode)
199 getProgramInvocationOutputAndErrors verbosity inv
= case progInvokeOutputEncoding inv
of
201 (output
, errors
, exitCode
) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeText
202 return (output
, errors
, exitCode
)
204 (output
', errors
, exitCode
) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary
205 return (normaliseLineEndings
(fromUTF8LBS output
'), errors
, exitCode
)
207 getProgramInvocationLBSAndErrors
210 -> IO (LBS
.ByteString
, String, ExitCode)
211 getProgramInvocationLBSAndErrors verbosity inv
=
212 getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary
214 getProgramInvocationIODataAndErrors
215 :: KnownIODataMode mode
219 -> IO (mode
, String, ExitCode)
220 getProgramInvocationIODataAndErrors
223 { progInvokePath
= path
224 , progInvokeArgs
= args
225 , progInvokeEnv
= envOverrides
226 , progInvokeCwd
= mcwd
227 , progInvokeInput
= minputStr
228 , progInvokeInputEncoding
= encoding
231 menv
<- getEffectiveEnvironment envOverrides
232 rawSystemStdInOut verbosity path args mcwd menv input mode
234 input
= encodeToIOData encoding
<$> minputStr
236 -- | Return the current environment extended with the given overrides.
237 -- If an entry is specified twice in @overrides@, the second entry takes
239 getEffectiveEnvironment
240 :: [(String, Maybe String)]
241 -> IO (Maybe [(String, String)])
242 getEffectiveEnvironment
[] = return Nothing
243 getEffectiveEnvironment overrides
=
244 fmap (Just
. Map
.toList
. apply overrides
. Map
.fromList
) getEnvironment
246 apply os env
= foldl' (flip update
) env os
247 update
(var
, Nothing
) = Map
.delete var
248 update
(var
, Just val
) = Map
.insert var val
250 -- | Like the unix xargs program. Useful for when we've got very long command
251 -- lines that might overflow an OS limit on command line length and so you
252 -- need to invoke a command multiple times to get all the args in.
254 -- It takes four template invocations corresponding to the simple, initial,
255 -- middle and last invocations. If the number of args given is small enough
256 -- that we can get away with just a single invocation then the simple one is
261 -- If the number of args given means that we need to use multiple invocations
262 -- then the templates for the initial, middle and last invocations are used:
264 -- > $ initial args_0
269 multiStageProgramInvocation
271 -> (ProgramInvocation
, ProgramInvocation
, ProgramInvocation
)
273 -> [ProgramInvocation
]
274 multiStageProgramInvocation simple
(initial
, middle
, final
) args
=
276 length (progInvokePath inv
)
277 + foldl' (\s a
-> length a
+ 1 + s
) 1 (progInvokeArgs inv
)
278 fixedArgSize
= maximum (map argSize
[simple
, initial
, middle
, final
])
279 chunkSize
= maxCommandLineSize
- fixedArgSize
280 in case splitChunks chunkSize args
of
282 [c
] -> [simple `appendArgs` c
]
284 |
(xs
, x
) <- unsnocNE
(c2
:| cs
) ->
285 [initial `appendArgs` c
]
286 ++ [middle `appendArgs` c
' | c
' <- xs
]
287 ++ [final `appendArgs` x
]
289 appendArgs
:: ProgramInvocation
-> [String] -> ProgramInvocation
290 inv `appendArgs`
as = inv
{progInvokeArgs
= progInvokeArgs inv
++ as}
292 splitChunks
:: Int -> [[a
]] -> [[[a
]]]
293 splitChunks len
= unfoldr $ \s
->
296 else Just
(chunk len s
)
298 chunk
:: Int -> [[a
]] -> ([[a
]], [[a
]])
299 chunk len
(s
: _
) |
length s
>= len
= error toolong
300 chunk len ss
= chunk
' [] len ss
302 chunk
' :: [[a
]] -> Int -> [[a
]] -> ([[a
]], [[a
]])
303 chunk
' acc len
(s
: ss
)
304 | len
' < len
= chunk
' (s
: acc
) (len
- len
' - 1) ss
307 chunk
' acc _ ss
= (reverse acc
, ss
)
310 "multiStageProgramInvocation: a single program arg is larger "
311 ++ "than the maximum command line length!"
313 -- FIXME: discover this at configure time or runtime on unix
314 -- The value is 32k on Windows and posix specifies a minimum of 4k
315 -- but all sensible unixes use more than 4k.
316 -- we could use getSysVar ArgumentLimit but that's in the unix lib
318 maxCommandLineSize
:: Int
319 maxCommandLineSize
= 30 * 1024