SetupWrapper.hs 22.5 KB
Newer Older
1
2
-----------------------------------------------------------------------------
-- |
3
-- Module      :  Distribution.Client.SetupWrapper
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Copyright   :  (c) The University of Glasgow 2006,
--                    Duncan Coutts 2008
--
-- Maintainer  :  cabal-devel@haskell.org
-- Stability   :  alpha
-- Portability :  portable
--
-- An interface to building and installing Cabal packages.
-- If the @Built-Type@ field is specified as something other than
-- 'Custom', and the current version of Cabal is acceptable, this performs
-- setup actions directly.  Otherwise it builds the setup script and
-- runs it with the given arguments.

17
module Distribution.Client.SetupWrapper (
18
19
20
21
22
23
24
25
    setupWrapper,
    SetupScriptOptions(..),
    defaultSetupScriptOptions,
  ) where

import qualified Distribution.Make as Make
import qualified Distribution.Simple as Simple
import Distribution.Version
Duncan Coutts's avatar
Duncan Coutts committed
26
27
         ( Version(..), VersionRange, anyVersion
         , intersectVersionRanges, orLaterVersion
Duncan Coutts's avatar
Duncan Coutts committed
28
         , withinRange )
29
import Distribution.InstalledPackageInfo (installedPackageId)
30
import Distribution.Package
31
32
         ( InstalledPackageId(..), PackageIdentifier(..),
           PackageName(..), Package(..), packageName
33
         , packageVersion, Dependency(..) )
34
35
import Distribution.PackageDescription
         ( GenericPackageDescription(packageDescription)
36
37
         , PackageDescription(..), specVersion
         , BuildType(..), knownBuildTypes )
38
39
import Distribution.PackageDescription.Parse
         ( readPackageDescription )
40
import Distribution.Simple.Configure
41
         ( configCompilerEx )
42
import Distribution.Compiler ( buildCompilerId )
43
import Distribution.Simple.Compiler
44
         ( CompilerFlavor(GHC), Compiler(compilerId)
45
         , PackageDB(..), PackageDBStack )
46
47
import Distribution.Simple.PreProcess
         ( runSimplePreProcessor, ppUnlit )
48
49
import Distribution.Simple.Program
         ( ProgramConfiguration, emptyProgramConfiguration
50
51
52
53
54
         , getProgramSearchPath, getDbProgramOutput, runDbProgram, ghcProgram )
import Distribution.Simple.Program.Find
         ( programSearchPathAsPATHVar )
import Distribution.Simple.Program.Run
         ( getEffectiveEnvironment )
55
import Distribution.Simple.BuildPaths
Duncan Coutts's avatar
Duncan Coutts committed
56
         ( defaultDistPref, exeExtension )
57
58
import Distribution.Simple.Command
         ( CommandUI(..), commandShowOptions )
59
60
import Distribution.Simple.Program.GHC
         ( GhcMode(..), GhcOptions(..), renderGhcOptions )
61
62
import qualified Distribution.Simple.PackageIndex as PackageIndex
import Distribution.Simple.PackageIndex (PackageIndex)
refold's avatar
refold committed
63
64
import Distribution.Client.Config
         ( defaultCabalDir )
65
66
import Distribution.Client.IndexUtils
         ( getInstalledPackages )
refold's avatar
refold committed
67
import Distribution.Client.JobControl
refold's avatar
refold committed
68
         ( Lock, criticalSection )
69
70
import Distribution.Simple.Setup
         ( Flag(..) )
71
import Distribution.Simple.Utils
72
         ( die, debug, info, cabalVersion, findPackageDesc, comparing
refold's avatar
refold committed
73
         , createDirectoryIfMissingVerbose, installExecutableFile
74
         , copyFileVerbose, rewriteFile, intercalate )
75
import Distribution.Client.Utils
76
77
         ( inDir, tryCanonicalizePath
         , existsAndIsMoreRecentThan, moreRecentFile )
78
import Distribution.System ( Platform(..), buildPlatform )
79
80
81
82
import Distribution.Text
         ( display )
import Distribution.Verbosity
         ( Verbosity )
83
import Distribution.Compat.Exception
refold's avatar
refold committed
84
         ( catchIO )
85

86
87
88
89
90
91
92
93
94
95
96
import System.Directory    ( doesFileExist )
import System.FilePath     ( (</>), (<.>) )
import System.IO           ( Handle, hPutStr )
import System.Exit         ( ExitCode(..), exitWith )
import System.Process      ( runProcess, waitForProcess )
import Control.Applicative ( (<$>), (<*>) )
import Control.Monad       ( when, unless )
import Data.List           ( foldl1' )
import Data.Maybe          ( fromMaybe, isJust )
import Data.Monoid         ( mempty )
import Data.Char           ( isSpace )
97
98

data SetupScriptOptions = SetupScriptOptions {
99
100
    useCabalVersion          :: VersionRange,
    useCompiler              :: Maybe Compiler,
101
    usePlatform              :: Maybe Platform,
102
103
104
105
106
107
    usePackageDB             :: PackageDBStack,
    usePackageIndex          :: Maybe PackageIndex,
    useProgramConfig         :: ProgramConfiguration,
    useDistPref              :: FilePath,
    useLoggingHandle         :: Maybe Handle,
    useWorkingDir            :: Maybe FilePath,
refold's avatar
refold committed
108
    forceExternalSetupMethod :: Bool,
refold's avatar
refold committed
109

refold's avatar
refold committed
110
111
    -- Used only when calling setupWrapper from parallel code to serialise
    -- access to the setup cache; should be Nothing otherwise.
112
113
114
115
116
117
118
119
120
121
    --
    -- Note: setup exe cache
    ------------------------
    -- When we are installing in parallel, we always use the external setup
    -- method. Since compiling the setup script each time adds noticeable
    -- overhead, we use a shared setup script cache
    -- ('~/.cabal/setup-exe-cache'). For each (compiler, platform, Cabal
    -- version) combination the cache holds a compiled setup script
    -- executable. This only affects the Simple build type; for the Custom,
    -- Configure and Make build types we always compile the setup script anew.
refold's avatar
refold committed
122
    setupCacheLock           :: Maybe Lock
123
124
125
126
  }

defaultSetupScriptOptions :: SetupScriptOptions
defaultSetupScriptOptions = SetupScriptOptions {
127
128
    useCabalVersion          = anyVersion,
    useCompiler              = Nothing,
129
    usePlatform              = Nothing,
130
131
132
133
134
135
    usePackageDB             = [GlobalPackageDB, UserPackageDB],
    usePackageIndex          = Nothing,
    useProgramConfig         = emptyProgramConfiguration,
    useDistPref              = defaultDistPref,
    useLoggingHandle         = Nothing,
    useWorkingDir            = Nothing,
refold's avatar
refold committed
136
    forceExternalSetupMethod = False,
refold's avatar
refold committed
137
    setupCacheLock           = Nothing
138
139
140
141
142
143
  }

setupWrapper :: Verbosity
             -> SetupScriptOptions
             -> Maybe PackageDescription
             -> CommandUI flags
144
             -> (Version -> flags)
145
146
147
148
149
150
             -> [String]
             -> IO ()
setupWrapper verbosity options mpkg cmd flags extraArgs = do
  pkg <- maybe getPkg return mpkg
  let setupMethod = determineSetupMethod options' buildType'
      options'    = options {
Duncan Coutts's avatar
Duncan Coutts committed
151
                      useCabalVersion = intersectVersionRanges
152
                                          (useCabalVersion options)
Duncan Coutts's avatar
Duncan Coutts committed
153
                                          (orLaterVersion (specVersion pkg))
154
155
                    }
      buildType'  = fromMaybe Custom (buildType pkg)
156
      mkArgs cabalLibVersion = commandName cmd
157
                             : commandShowOptions cmd (flags cabalLibVersion)
158
                            ++ extraArgs
159
  checkBuildType buildType'
160
  setupMethod verbosity options' (packageId pkg) buildType' mkArgs
161
  where
162
    getPkg = findPackageDesc (fromMaybe "." (useWorkingDir options))
163
164
165
         >>= readPackageDescription verbosity
         >>= return . packageDescription

166
167
168
169
170
    checkBuildType (UnknownBuildType name) =
      die $ "The build-type '" ++ name ++ "' is not known. Use one of: "
         ++ intercalate ", " (map display knownBuildTypes) ++ "."
    checkBuildType _ = return ()

171
172
173
174
175
176
-- | Decide if we're going to be able to do a direct internal call to the
-- entry point in the Cabal library or if we're going to have to compile
-- and execute an external Setup.hs script.
--
determineSetupMethod :: SetupScriptOptions -> BuildType -> SetupMethod
determineSetupMethod options buildType'
177
  | forceExternalSetupMethod options = externalSetupMethod
178
  | isJust (useLoggingHandle options)
179
 || buildType' == Custom             = externalSetupMethod
180
  | cabalVersion `withinRange`
181
182
      useCabalVersion options        = internalSetupMethod
  | otherwise                        = externalSetupMethod
183

184
185
type SetupMethod = Verbosity
                -> SetupScriptOptions
186
                -> PackageIdentifier
187
                -> BuildType
188
                -> (Version -> [String]) -> IO ()
189
190
191
192
193
194

-- ------------------------------------------------------------
-- * Internal SetupMethod
-- ------------------------------------------------------------

internalSetupMethod :: SetupMethod
195
internalSetupMethod verbosity options _ bt mkargs = do
196
  let args = mkargs cabalVersion
197
198
  debug verbosity $ "Using internal setup method with build-type " ++ show bt
                 ++ " and args:\n  " ++ show args
199
200
  inDir (useWorkingDir options) $
    buildTypeAction bt args
201
202
203
204
205
206
207
208
209
210
211
212
213

buildTypeAction :: BuildType -> ([String] -> IO ())
buildTypeAction Simple    = Simple.defaultMainArgs
buildTypeAction Configure = Simple.defaultMainWithHooksArgs
                              Simple.autoconfUserHooks
buildTypeAction Make      = Make.defaultMainArgs
buildTypeAction Custom               = error "buildTypeAction Custom"
buildTypeAction (UnknownBuildType _) = error "buildTypeAction UnknownBuildType"

-- ------------------------------------------------------------
-- * External SetupMethod
-- ------------------------------------------------------------

214
215
externalSetupMethod :: SetupMethod
externalSetupMethod verbosity options pkg bt mkargs = do
216
  debug verbosity $ "Using external setup method with build-type " ++ show bt
217
  createDirectoryIfMissingVerbose verbosity True setupDir
218
  (cabalLibVersion, mCabalLibInstalledPkgId, options') <- cabalLibVersionToUse
219
  debug verbosity $ "Using Cabal library version " ++ display cabalLibVersion
220
221
222
223
224
  path <- if useCachedSetupExecutable
          then getCachedSetupExecutable options'
               cabalLibVersion mCabalLibInstalledPkgId
          else compileSetupExecutable   options'
               cabalLibVersion mCabalLibInstalledPkgId False
225
  invokeSetupScript options' path (mkargs cabalLibVersion)
226
227

  where
228
229
230
  workingDir       = case fromMaybe "" (useWorkingDir options) of
                       []  -> "."
                       dir -> dir
231
  setupDir         = workingDir </> useDistPref options </> "setup"
232
233
234
  setupVersionFile = setupDir   </> "setup" <.> "version"
  setupHs          = setupDir   </> "setup" <.> "hs"
  setupProgFile    = setupDir   </> "setup" <.> exeExtension
235

236
237
  useCachedSetupExecutable = (bt == Simple || bt == Configure || bt == Make)

238
239
240
241
242
243
244
245
  maybeGetInstalledPackages :: SetupScriptOptions -> Compiler
                               -> ProgramConfiguration -> IO PackageIndex
  maybeGetInstalledPackages options' comp conf =
    case usePackageIndex options' of
      Just index -> return index
      Nothing    -> getInstalledPackages verbosity
                    comp (usePackageDB options') conf

246
247
  cabalLibVersionToUse :: IO (Version, (Maybe InstalledPackageId)
                             ,SetupScriptOptions)
248
  cabalLibVersionToUse = do
249
250
    savedVer <- savedVersion
    case savedVer of
251
      Just version | version `withinRange` useCabalVersion options
252
        -> do updateSetupScript version bt
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
253
254
              -- Does the previously compiled setup executable still exist and
              -- is it up-to date?
255
256
257
258
259
260
              useExisting <- canUseExistingSetup version
              if useExisting
                then return (version, Nothing, options)
                else installedVersion
      _ -> installedVersion
    where
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
261
262
263
264
      -- This check duplicates the checks in 'getCachedSetupExecutable' /
      -- 'compileSetupExecutable'. Unfortunately, we have to perform it twice
      -- because the selected Cabal version may change as a result of this
      -- check.
265
      canUseExistingSetup :: Version -> IO Bool
266
267
268
      canUseExistingSetup version =
        if useCachedSetupExecutable
        then do
269
270
          (_, cachedSetupProgFile) <- cachedSetupDirAndProg options version
          doesFileExist cachedSetupProgFile
271
        else
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
          (&&) <$> setupProgFile `existsAndIsMoreRecentThan` setupHs
               <*> setupProgFile `existsAndIsMoreRecentThan` setupVersionFile

      installedVersion :: IO (Version, Maybe InstalledPackageId
                             ,SetupScriptOptions)
      installedVersion = do
        (comp,    conf,    options')  <- configureCompiler options
        (version, mipkgid, options'') <- installedCabalVersion options' comp conf
        updateSetupScript version bt
        writeFile setupVersionFile (show version ++ "\n")
        return (version, mipkgid, options'')

      savedVersion :: IO (Maybe Version)
      savedVersion = do
        versionString <- readFile setupVersionFile `catchIO` \_ -> return ""
        case reads versionString of
          [(version,s)] | all isSpace s -> return (Just version)
          _                             -> return Nothing

291
292
293
294
295
296
297
298
299
  -- | Update a Setup.hs script, creating it if necessary.
  updateSetupScript :: Version -> BuildType -> IO ()
  updateSetupScript _ Custom = do
    useHs  <- doesFileExist customSetupHs
    useLhs <- doesFileExist customSetupLhs
    unless (useHs || useLhs) $ die
      "Using 'build-type: Custom' but there is no Setup.hs or Setup.lhs script."
    let src = (if useHs then customSetupHs else customSetupLhs)
    srcNewer <- src `moreRecentFile` setupHs
300
301
302
    when srcNewer $ if useHs
                    then copyFileVerbose verbosity src setupHs
                    else runSimplePreProcessor ppUnlit src setupHs verbosity
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
    where
      customSetupHs   = workingDir </> "Setup.hs"
      customSetupLhs  = workingDir </> "Setup.lhs"

  updateSetupScript cabalLibVersion _ =
    rewriteFile setupHs (buildTypeScript cabalLibVersion)

  buildTypeScript :: Version -> String
  buildTypeScript cabalLibVersion = case bt of
    Simple    -> "import Distribution.Simple; main = defaultMain\n"
    Configure -> "import Distribution.Simple; main = defaultMainWithHooks "
              ++ if cabalLibVersion >= Version [1,3,10] []
                   then "autoconfUserHooks\n"
                   else "defaultUserHooks\n"
    Make      -> "import Distribution.Make; main = defaultMain\n"
    Custom             -> error "buildTypeScript Custom"
    UnknownBuildType _ -> error "buildTypeScript UnknownBuildType"

321
322
323
324
325
326
327
328
329
  installedCabalVersion :: SetupScriptOptions -> Compiler -> ProgramConfiguration
                        -> IO (Version, Maybe InstalledPackageId
                              ,SetupScriptOptions)
  installedCabalVersion options' _ _ | packageName pkg == PackageName "Cabal" =
    return (packageVersion pkg, Nothing, options')
  installedCabalVersion options' compiler conf = do
    index <- maybeGetInstalledPackages options' compiler conf
    let cabalDep   = Dependency (PackageName "Cabal") (useCabalVersion options')
        options''  = options' { usePackageIndex = Just index }
330
    case PackageIndex.lookupDependency index cabalDep of
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
331
332
      []   -> die $ "The package '" ++ display (packageName pkg)
                 ++ "' requires Cabal library version "
333
334
                 ++ display (useCabalVersion options)
                 ++ " but no suitable version is installed."
335
336
337
      pkgs -> let ipkginfo = head . snd . bestVersion fst $ pkgs
              in return (packageVersion ipkginfo
                        ,Just . installedPackageId $ ipkginfo, options'')
338
339

  bestVersion :: (a -> Version) -> [a] -> a
340
  bestVersion f = firstMaximumBy (comparing (preference . f))
341
    where
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
      -- Like maximumBy, but picks the first maximum element instead of the
      -- last. In general, we expect the preferred version to go first in the
      -- list. For the default case, this has the effect of choosing the version
      -- installed in the user package DB instead of the global one. See #1463.
      --
      -- Note: firstMaximumBy could be written as just
      -- `maximumBy cmp . reverse`, but the problem is that the behaviour of
      -- maximumBy is not fully specified in the case when there is not a single
      -- greatest element.
      firstMaximumBy :: (a -> a -> Ordering) -> [a] -> a
      firstMaximumBy _ []   =
        error "Distribution.Client.firstMaximumBy: empty list"
      firstMaximumBy cmp xs =  foldl1' maxBy xs
        where
          maxBy x y = case cmp x y of { GT -> x; EQ -> x; LT -> y; }

358
359
360
361
362
363
364
365
366
367
      preference version   = (sameVersion, sameMajorVersion
                             ,stableVersion, latestVersion)
        where
          sameVersion      = version == cabalVersion
          sameMajorVersion = majorVersion version == majorVersion cabalVersion
          majorVersion     = take 2 . versionBranch
          stableVersion    = case versionBranch version of
                               (_:x:_) -> even x
                               _       -> False
          latestVersion    = version
368
369
370
371
372
373

  configureCompiler :: SetupScriptOptions
                    -> IO (Compiler, ProgramConfiguration, SetupScriptOptions)
  configureCompiler options' = do
    (comp, conf) <- case useCompiler options' of
      Just comp -> return (comp, useProgramConfig options')
374
      Nothing   -> do (comp, _, conf) <-
375
                        configCompilerEx (Just GHC) Nothing Nothing
376
377
                        (useProgramConfig options') verbosity
                      return (comp, conf)
378
379
380
381
382
    -- Whenever we need to call configureCompiler, we also need to access the
    -- package index, so let's cache it here.
    index <- maybeGetInstalledPackages options' comp conf
    return (comp, conf, options' { useCompiler      = Just comp,
                                   usePackageIndex  = Just index,
383
384
                                   useProgramConfig = conf })

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
  -- | Path to the setup exe cache directory and path to the cached setup
  -- executable.
  cachedSetupDirAndProg :: SetupScriptOptions -> Version
                        -> IO (FilePath, FilePath)
  cachedSetupDirAndProg options' cabalLibVersion = do
    cabalDir <- defaultCabalDir
    let setupCacheDir       = cabalDir </> "setup-exe-cache"
        cachedSetupProgFile = setupCacheDir
                              </> ("setup-" ++ buildTypeString ++ "-"
                                   ++ cabalVersionString ++ "-"
                                   ++ platformString ++ "-"
                                   ++ compilerVersionString)
                              <.> exeExtension
    return (setupCacheDir, cachedSetupProgFile)
      where
        buildTypeString       = show bt
        cabalVersionString    = "Cabal-" ++ (display cabalLibVersion)
        compilerVersionString = display $
                                fromMaybe buildCompilerId
                                (fmap compilerId . useCompiler $ options')
        platformString        = display $
                                fromMaybe buildPlatform (usePlatform options')
407

refold's avatar
refold committed
408
409
  -- | Look up the setup executable in the cache; update the cache if the setup
  -- executable is not found.
410
411
  getCachedSetupExecutable :: SetupScriptOptions
                           -> Version -> Maybe InstalledPackageId
refold's avatar
refold committed
412
                           -> IO FilePath
413
414
415
416
417
418
  getCachedSetupExecutable options' cabalLibVersion
                           maybeCabalLibInstalledPkgId = do
    (setupCacheDir, cachedSetupProgFile) <-
      cachedSetupDirAndProg options' cabalLibVersion
    cachedSetupExists <- doesFileExist cachedSetupProgFile
    if cachedSetupExists
refold's avatar
refold committed
419
      then debug verbosity $
420
           "Found cached setup executable: " ++ cachedSetupProgFile
refold's avatar
refold committed
421
      else criticalSection' $ do
refold's avatar
refold committed
422
        -- The cache may have been populated while we were waiting.
423
424
        cachedSetupExists' <- doesFileExist cachedSetupProgFile
        if cachedSetupExists'
refold's avatar
refold committed
425
          then debug verbosity $
426
               "Found cached setup executable: " ++ cachedSetupProgFile
refold's avatar
refold committed
427
428
          else do
          debug verbosity $ "Setup executable not found in the cache."
429
430
          src <- compileSetupExecutable options'
                 cabalLibVersion maybeCabalLibInstalledPkgId True
refold's avatar
refold committed
431
          createDirectoryIfMissingVerbose verbosity True setupCacheDir
432
433
434
435
436
437
          installExecutableFile verbosity src cachedSetupProgFile
    return cachedSetupProgFile
      where
        criticalSection'      = fromMaybe id
                                (fmap criticalSection $ setupCacheLock options')

438
439
440
  -- | If the Setup.hs is out of date wrt the executable then recompile it.
  -- Currently this is GHC only. It should really be generalised.
  --
441
442
  compileSetupExecutable :: SetupScriptOptions
                         -> Version -> Maybe InstalledPackageId -> Bool
refold's avatar
refold committed
443
                         -> IO FilePath
444
445
446
  compileSetupExecutable options' cabalLibVersion maybeCabalLibInstalledPkgId
                         forceCompile = do
    setupHsNewer      <- setupHs          `moreRecentFile` setupProgFile
447
448
    cabalVersionNewer <- setupVersionFile `moreRecentFile` setupProgFile
    let outOfDate = setupHsNewer || cabalVersionNewer
449
450
    when (outOfDate || forceCompile) $ do
      debug verbosity "Setup executable needs to be updated, compiling..."
451
      (compiler, conf, options'') <- configureCompiler options'
452
      let cabalPkgid = PackageIdentifier (PackageName "Cabal") cabalLibVersion
453
454
455
      let ghcOptions = mempty {
              ghcOptVerbosity       = Flag verbosity
            , ghcOptMode            = Flag GhcModeMake
456
            , ghcOptInputFiles      = [setupHs]
457
458
459
460
461
462
            , ghcOptOutputFile      = Flag setupProgFile
            , ghcOptObjDir          = Flag setupDir
            , ghcOptHiDir           = Flag setupDir
            , ghcOptSourcePathClear = Flag True
            , ghcOptSourcePath      = [workingDir]
            , ghcOptPackageDBs      = usePackageDB options''
463
464
465
            , ghcOptPackages        = maybe []
                                      (\ipkgid -> [(ipkgid, cabalPkgid)])
                                      maybeCabalLibInstalledPkgId
466
            }
467
      let ghcCmdLine = renderGhcOptions compiler ghcOptions
refold's avatar
refold committed
468
469
470
471
472
473
474
      case useLoggingHandle options of
        Nothing          -> runDbProgram verbosity ghcProgram conf ghcCmdLine

        -- If build logging is enabled, redirect compiler output to the log file.
        (Just logHandle) -> do output <- getDbProgramOutput verbosity ghcProgram
                                         conf ghcCmdLine
                               hPutStr logHandle output
refold's avatar
refold committed
475
    return setupProgFile
476

477
478
  invokeSetupScript :: SetupScriptOptions -> FilePath -> [String] -> IO ()
  invokeSetupScript options' path args = do
refold's avatar
refold committed
479
    info verbosity $ unwords (path : args)
480
    case useLoggingHandle options' of
481
482
483
      Nothing        -> return ()
      Just logHandle -> info verbosity $ "Redirecting build log to "
                                      ++ show logHandle
484
485
486
487
488

    -- Since useWorkingDir can change the relative path, the path argument must
    -- be turned into an absolute path. On some systems, runProcess will take
    -- path as relative to the new working directory instead of the current
    -- working directory.
489
    path' <- tryCanonicalizePath path
490

491
492
493
494
    searchpath <- programSearchPathAsPATHVar
                    (getProgramSearchPath (useProgramConfig options'))
    env        <- getEffectiveEnvironment [("PATH", Just searchpath)]

495
    process <- runProcess path' args
496
497
                 (useWorkingDir options') env
                 Nothing (useLoggingHandle options') (useLoggingHandle options')
498
499
    exitCode <- waitForProcess process
    unless (exitCode == ExitSuccess) $ exitWith exitCode