[project @ 2001-06-15 15:20:20 by simonpj]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
1 -----------------------------------------------------------------------------
2 -- Access to system tools: gcc, cp, rm etc
3 --
4 -- (c) The University of Glasgow 2000
5 --
6 -----------------------------------------------------------------------------
7
8 \begin{code}
9 module SysTools (
10         -- Initialisation
11         initSysTools,
12         setPgm,                 -- String -> IO ()
13                                 -- Command-line override
14         setDryRun,
15
16         packageConfigPath,      -- IO String    
17                                 -- Where package.conf is
18
19         -- Interface to system tools
20         runUnlit, runCpp, runCc, -- [String] -> IO ()
21         runMangle, runSplit,     -- [String] -> IO ()
22         runAs, runLink,          -- [String] -> IO ()
23         runMkDLL,
24
25         touch,                  -- String -> String -> IO ()
26         copy,                   -- String -> String -> String -> IO ()
27         
28         -- Temporary-file management
29         setTmpDir,
30         newTempName,
31         cleanTempFiles, cleanTempFilesExcept, removeTmpFiles,
32         addFilesToClean,
33
34         -- System interface
35         getProcessID,           -- IO Int
36         system,                 -- String -> IO Int
37
38         -- Misc
39         showGhcUsage,           -- IO ()        Shows usage message and exits
40         getSysMan,              -- IO String    Parallel system only
41         dosifyPath,             -- String -> String
42
43         runSomething    -- ToDo: make private
44  ) where
45
46 import DriverUtil
47 import Config
48 import Outputable
49 import Panic            ( progName, GhcException(..) )
50 import Util             ( global )
51 import CmdLineOpts      ( dynFlag, verbosity )
52
53 import List             ( intersperse, isPrefixOf )
54 import Exception        ( throw, throwDyn, catchAllIO )
55 import IO               ( hPutStr, hPutChar, hPutStrLn, hFlush, stderr )
56 import IO               ( openFile, IOMode(..), hClose )        -- For temp "system"
57 import Directory        ( doesFileExist, removeFile )
58 import IOExts           ( IORef, readIORef, writeIORef )
59 import Monad            ( when, unless )
60 import qualified System
61 import System           ( ExitCode(..) )
62
63 #include "../includes/config.h"
64
65 #if !defined(mingw32_TARGET_OS)
66 import qualified Posix
67 #else
68 import Addr              ( nullAddr )
69 #endif
70
71 #include "HsVersions.h"
72
73 {-# DEPRECATED runSomething "runSomething should be private to SysTools" #-}
74
75 \end{code}
76
77
78                 The configuration story
79                 ~~~~~~~~~~~~~~~~~~~~~~~
80
81 GHC needs various support files (library packages, RTS etc), plus
82 various auxiliary programs (cp, gcc, etc).  It finds these in one
83 of two places:
84
85 * When running as an *installed program*, GHC finds most of this support
86   stuff in the installed library tree.  The path to this tree is passed
87   to GHC via the -B flag, and given to initSysTools .
88
89 * When running *in-place* in a build tree, GHC finds most of this support
90   stuff in the build tree.  The path to the build tree is, again passed
91   to GHC via -B. 
92
93 GHC tells which of the two is the case by seeing whether package.conf
94 is in TopDir [installed] or in TopDir/ghc/driver [inplace] (what a hack).
95
96
97 SysTools.initSysProgs figures out exactly where all the auxiliary programs
98 are, and initialises mutable variables to make it easy to call them.
99 To to this, it makes use of definitions in Config.hs, which is a Haskell
100 file containing variables whose value is figured out by the build system.
101
102 Config.hs contains two sorts of things
103
104   cGCC,         The *names* of the programs
105   cCPP            e.g.  cGCC = gcc
106   cUNLIT                cCPP = gcc -E
107   etc           They do *not* include paths
108                                 
109
110   cUNLIT_DIR    The *path* to the directory containing unlit, split etc
111   cSPLIT_DIR    *relative* to the root of the build tree,
112                 for use when running *in-place* in a build tree (only)
113                 
114
115
116 %************************************************************************
117 %*                                                                      *
118 \subsection{Global variables to contain system programs}
119 %*                                                                      *
120 %************************************************************************
121
122 \begin{code}
123 GLOBAL_VAR(v_Pgm_L,     error "pgm_L",   String)        -- unlit
124 GLOBAL_VAR(v_Pgm_P,     error "pgm_P",   String)        -- cpp
125 GLOBAL_VAR(v_Pgm_c,     error "pgm_c",   String)        -- gcc
126 GLOBAL_VAR(v_Pgm_m,     error "pgm_m",   String)        -- asm code mangler
127 GLOBAL_VAR(v_Pgm_s,     error "pgm_s",   String)        -- asm code splitter
128 GLOBAL_VAR(v_Pgm_a,     error "pgm_a",   String)        -- as
129 GLOBAL_VAR(v_Pgm_l,     error "pgm_l",   String)        -- ld
130 GLOBAL_VAR(v_Pgm_MkDLL, error "pgm_dll", String)        -- mkdll
131
132 GLOBAL_VAR(v_Pgm_T,    error "pgm_T",    String)        -- touch
133 GLOBAL_VAR(v_Pgm_CP,   error "pgm_CP",   String)        -- cp
134
135 GLOBAL_VAR(v_Path_package_config, error "path_package_config", String)
136 GLOBAL_VAR(v_Path_usage,          error "ghc_usage.txt",       String)
137
138 -- Parallel system only
139 GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String)    -- system manager
140 \end{code}
141
142
143 %************************************************************************
144 %*                                                                      *
145 \subsection{Initialisation}
146 %*                                                                      *
147 %************************************************************************
148
149 \begin{code}
150 initSysTools :: [String]        -- Command-line arguments starting "-B"
151
152              -> IO String       -- Set all the mutable variables above, holding 
153                                 --      (a) the system programs
154                                 --      (b) the package-config file
155                                 --      (c) the GHC usage message
156                                 -- Return TopDir
157
158
159 initSysTools minusB_args
160   = do  { (am_installed, top_dir) <- getTopDir minusB_args
161                 -- top_dir
162                 --      for "installed" this is the root of GHC's support files
163                 --      for "in-place" it is the root of the build tree
164
165         ; let installed_bin pgm   =  top_dir `slash` "bin" `slash` pgm
166               installed     file  =  top_dir `slash` file
167               inplace dir   pgm   =  top_dir `slash` dosifyPath dir `slash` pgm
168
169         ; let pkgconfig_path
170                 | am_installed = installed "package.conf"
171                 | otherwise    = inplace cGHC_DRIVER_DIR "package.conf.inplace"
172
173               ghc_usage_msg_path
174                 | am_installed = installed "ghc-usage.txt"
175                 | otherwise    = inplace cGHC_DRIVER_DIR "ghc-usage.txt"
176
177                 -- For all systems, unlit, split, mangle are GHC utilities
178                 -- architecture-specific stuff is done when building Config.hs
179               unlit_path
180                 | am_installed = installed_bin cGHC_UNLIT
181                 | otherwise    = inplace cGHC_UNLIT_DIR cGHC_UNLIT
182
183                 -- split and mangle are Perl scripts
184               split_script
185                 | am_installed = installed_bin cGHC_SPLIT
186                 | otherwise    = inplace cGHC_SPLIT_DIR cGHC_SPLIT
187
188               mangle_script
189                 | am_installed = installed_bin cGHC_MANGLER
190                 | otherwise    = inplace cGHC_MANGLER_DIR cGHC_MANGLER
191
192         -- Check that the package config exists
193         ; config_exists <- doesFileExist pkgconfig_path
194         ; when (not config_exists) $
195              throwDyn (InstallationError 
196                          ("Can't find package.conf in " ++ pkgconfig_path))
197
198 #if defined(mingw32_TARGET_OS)
199         --              WINDOWS-SPECIFIC STUFF
200         -- On Windows, gcc and friends are distributed with GHC,
201         --      so when "installed" we look in TopDir/bin
202         -- When "in-place" we look wherever the build-time configure 
203         --      script found them
204         ; let cpp_path  | am_installed = installed cRAWCPP
205                         | otherwise    = cRAWCPP
206               gcc_path  | am_installed = installed cGCC
207                         | otherwise    = cGCC
208               perl_path | am_installed = installed cGHC_PERL
209                         | otherwise    = cGHC_PERL
210
211         -- 'touch' is a GHC util for Windows, and similarly unlit, mangle
212         ; let touch_path  | am_installed = installed cGHC_TOUCHY
213                           | otherwise    = inplace cGHC_TOUCHY_DIR cGHC_TOUCHY
214
215         -- On Win32 we don't want to rely on #!/bin/perl, so we prepend 
216         -- a call to Perl to get the invocation of split and mangle
217         ; let split_path  = perl_path ++ " " ++ split_script
218               mangle_path = perl_path ++ " " ++ mangle_script
219
220         ; let mkdll_path = cMKDLL
221 #else
222         --              UNIX-SPECIFIC STUFF
223         -- On Unix, the "standard" tools are assumed to be
224         -- in the same place whether we are running "in-place" or "installed"
225         -- That place is wherever the build-time configure script found them.
226         ; let   cpp_path   = cRAWCPP
227                 gcc_path   = cGCC
228                 touch_path = cGHC_TOUCHY
229                 mkdll_path = panic "Cant build DLLs on a non-Win32 system"
230
231         -- On Unix, scripts are invoked using the '#!' method.  Binary
232         -- installations of GHC on Unix place the correct line on the front
233         -- of the script at installation time, so we don't want to wire-in
234         -- our knowledge of $(PERL) on the host system here.
235         ; let split_path  = split_script
236               mangle_path = mangle_script
237
238 #endif
239
240         -- For all systems, copy and remove are provided by the host 
241         -- system; architecture-specific stuff is done when building Config.hs
242         ; let   cp_path = cGHC_CP
243         
244         -- Other things being equal, as and ld are simply gcc
245         ; let   as_path  = gcc_path
246                 ld_path  = gcc_path
247
248                                        
249         -- Initialise the global vars
250         ; writeIORef v_Path_package_config pkgconfig_path
251         ; writeIORef v_Path_usage          ghc_usage_msg_path
252
253         ; writeIORef v_Pgm_sysman          (top_dir ++ "/ghc/rts/parallel/SysMan")
254                 -- Hans: this isn't right in general, but you can 
255                 -- elaborate it in the same way as the others
256
257         ; writeIORef v_Pgm_L               unlit_path
258         ; writeIORef v_Pgm_P               cpp_path
259         ; writeIORef v_Pgm_c               gcc_path
260         ; writeIORef v_Pgm_m               mangle_path
261         ; writeIORef v_Pgm_s               split_path
262         ; writeIORef v_Pgm_a               as_path
263         ; writeIORef v_Pgm_l               ld_path
264         ; writeIORef v_Pgm_MkDLL           mkdll_path
265         ; writeIORef v_Pgm_T               touch_path
266         ; writeIORef v_Pgm_CP              cp_path
267
268         ; return top_dir
269         }
270 \end{code}
271
272 setPgm is called when a command-line option like
273         -pgmLld
274 is used to override a particular program with a new onw
275
276 \begin{code}
277 setPgm :: String -> IO ()
278 -- The string is the flag, minus the '-pgm' prefix
279 -- So the first character says which program to override
280
281 setPgm ('P' : pgm) = writeIORef v_Pgm_P pgm
282 setPgm ('c' : pgm) = writeIORef v_Pgm_c pgm
283 setPgm ('m' : pgm) = writeIORef v_Pgm_m pgm
284 setPgm ('s' : pgm) = writeIORef v_Pgm_s pgm
285 setPgm ('a' : pgm) = writeIORef v_Pgm_a pgm
286 setPgm ('l' : pgm) = writeIORef v_Pgm_l pgm
287 setPgm pgm         = unknownFlagErr ("-pgm" ++ pgm)
288 \end{code}
289
290
291 \begin{code}
292 -- Find TopDir
293 --      for "installed" this is the root of GHC's support files
294 --      for "in-place" it is the root of the build tree
295 --
296 -- Plan of action:
297 -- 1. Set proto_top_dir
298 --      a) look for (the last) -B flag, and use it
299 --      b) if there are no -B flags, get the directory 
300 --         where GHC is running
301 --
302 -- 2. If package.conf exists in proto_top_dir, we are running
303 --      installed; and TopDir = proto_top_dir
304 --
305 -- 3. Otherwise we are running in-place, so
306 --      proto_top_dir will be /...stuff.../ghc/compiler
307 --      Set TopDir to /...stuff..., which is the root of the build tree
308 --
309 -- This is very gruesome indeed
310
311 getTopDir :: [String]
312           -> IO (Bool,          -- True <=> am installed, False <=> in-place
313                  String)        -- TopDir
314
315 getTopDir minusbs
316   = do { proto_top_dir <- get_proto
317
318         -- Discover whether we're running in a build tree or in an installation,
319         -- by looking for the package configuration file.
320        ; am_installed <- doesFileExist (proto_top_dir `slash` "package.conf")
321
322        ; if am_installed then
323             return (True, proto_top_dir)
324          else
325             return (False, remove_suffix proto_top_dir)
326        }
327   where
328     get_proto | not (null minusbs) 
329               = return (dosifyPath (drop 2 (last minusbs)))
330               | otherwise          
331               = do { maybe_exec_dir <- getExecDir -- Get directory of executable
332                    ; case maybe_exec_dir of       -- (only works on Windows)
333                         Nothing  -> throwDyn (InstallationError 
334                                                 "missing -B<dir> option")
335                         Just dir -> return dir
336                    }
337
338     remove_suffix dir   -- "/...stuff.../ghc/compiler" --> "/...stuff..."
339         = ASSERT2( not (null p1) && 
340                    not (null p2) && 
341                    dosifyPath dir == dosifyPath (top_dir ++ "/ghc/compiler"),
342                    text dir )
343           top_dir
344         where
345          p1      = dropWhile (not . isSlash) (reverse dir)
346          p2      = dropWhile (not . isSlash) (tail p1)  -- head is '/'
347          top_dir = reverse (tail p2)                    -- head is '/'
348 \end{code}
349
350
351 %************************************************************************
352 %*                                                                      *
353 \subsection{Running an external program}
354 n%*                                                                     *
355 %************************************************************************
356
357
358 \begin{code}
359 runUnlit :: [String] -> IO ()
360 runUnlit args = do p <- readIORef v_Pgm_L
361                    runSomething "Literate pre-processor" p args
362
363 runCpp :: [String] -> IO ()
364 runCpp args =   do p <- readIORef v_Pgm_P
365                    runSomething "C pre-processor" p args
366
367 runCc :: [String] -> IO ()
368 runCc args =   do p <- readIORef v_Pgm_c
369                   runSomething "C Compiler" p args
370
371 runMangle :: [String] -> IO ()
372 runMangle args = do p <- readIORef v_Pgm_m
373                     runSomething "Mangler" p args
374
375 runSplit :: [String] -> IO ()
376 runSplit args = do p <- readIORef v_Pgm_s
377                    runSomething "Splitter" p args
378
379 runAs :: [String] -> IO ()
380 runAs args = do p <- readIORef v_Pgm_a
381                 runSomething "Assembler" p args
382
383 runLink :: [String] -> IO ()
384 runLink args = do p <- readIORef v_Pgm_l
385                   runSomething "Linker" p args
386
387 runMkDLL :: [String] -> IO ()
388 runMkDLL args = do p <- readIORef v_Pgm_MkDLL
389                    runSomething "Make DLL" p args
390
391 touch :: String -> String -> IO ()
392 touch purpose arg =  do p <- readIORef v_Pgm_T
393                         runSomething purpose p [arg]
394
395 copy :: String -> String -> String -> IO ()
396 copy purpose from to = do p <- readIORef v_Pgm_CP
397                           runSomething purpose p [from,to]
398 \end{code}
399
400 \begin{code}
401 getSysMan :: IO String  -- How to invoke the system manager 
402                         -- (parallel system only)
403 getSysMan = readIORef v_Pgm_sysman
404 \end{code}
405
406 %************************************************************************
407 %*                                                                      *
408 \subsection{GHC Usage message}
409 %*                                                                      *
410 %************************************************************************
411
412 Show the usage message and exit
413
414 \begin{code}
415 showGhcUsage = do { usage_path <- readIORef v_Path_usage
416                   ; usage      <- readFile usage_path
417                   ; dump usage
418                   ; System.exitWith System.ExitSuccess }
419   where
420      dump ""          = return ()
421      dump ('$':'$':s) = hPutStr stderr progName >> dump s
422      dump (c:s)       = hPutChar stderr c >> dump s
423
424 packageConfigPath = readIORef v_Path_package_config
425 \end{code}
426
427
428 %************************************************************************
429 %*                                                                      *
430 \subsection{Managing temporary files
431 %*                                                                      *
432 %************************************************************************
433
434 One reason this code is here is because SysTools.system needs to make
435 a temporary file.
436
437 \begin{code}
438 GLOBAL_VAR(v_FilesToClean, [],               [String] )
439 GLOBAL_VAR(v_TmpDir,       cDEFAULT_TMPDIR,  String   )
440         -- v_TmpDir has no closing '/'
441 \end{code}
442
443 \begin{code}
444 setTmpDir dir = writeIORef v_TmpDir dir
445
446 cleanTempFiles :: Int -> IO ()
447 cleanTempFiles verb = do fs <- readIORef v_FilesToClean
448                          removeTmpFiles verb fs
449
450 cleanTempFilesExcept :: Int -> [FilePath] -> IO ()
451 cleanTempFilesExcept verb dont_delete
452   = do fs <- readIORef v_FilesToClean
453        let leftovers = filter (`notElem` dont_delete) fs
454        removeTmpFiles verb leftovers
455        writeIORef v_FilesToClean dont_delete
456
457
458 -- find a temporary name that doesn't already exist.
459 newTempName :: Suffix -> IO FilePath
460 newTempName extn
461   = do x <- getProcessID
462        tmp_dir <- readIORef v_TmpDir
463        findTempName tmp_dir x
464   where 
465     findTempName tmp_dir x
466       = do let filename = tmp_dir ++ "/ghc" ++ show x ++ '.':extn
467            b  <- doesFileExist filename
468            if b then findTempName tmp_dir (x+1)
469                 else do add v_FilesToClean filename -- clean it up later
470                         return filename
471
472 addFilesToClean :: [FilePath] -> IO ()
473 -- May include wildcards [used by DriverPipeline.run_phase SplitMangle]
474 addFilesToClean files = mapM_ (add v_FilesToClean) files
475
476 removeTmpFiles :: Int -> [FilePath] -> IO ()
477 removeTmpFiles verb fs
478   = traceCmd "Deleting temp files" 
479              ("Deleting: " ++ concat (intersperse " " fs))
480              (mapM_ rm fs)
481   where
482     rm f = removeFile f `catchAllIO`
483                 (\exn -> hPutStrLn stderr ("Warning: deleting non-existent " ++ f) >>
484                          return ())
485
486 \end{code}
487
488
489 %************************************************************************
490 %*                                                                      *
491 \subsection{Running a program}
492 %*                                                                      *
493 %************************************************************************
494
495 \begin{code}
496 GLOBAL_VAR(v_Dry_run, False, Bool)
497
498 setDryRun :: IO () 
499 setDryRun = writeIORef v_Dry_run True
500
501 -----------------------------------------------------------------------------
502 -- Running an external program
503
504 runSomething :: String          -- For -v message
505              -> String          -- Command name (possibly a full path)
506                                 --      assumed already dos-ified
507              -> [String]        -- Arguments
508                                 --      runSomthing will dos-ify them
509              -> IO ()
510
511 runSomething phase_name pgm args
512  = traceCmd phase_name cmd_line $
513    do   { exit_code <- system cmd_line
514         ; if exit_code /= ExitSuccess
515           then throwDyn (PhaseFailed phase_name exit_code)
516           else return ()
517         }
518   where
519     cmd_line = unwords (pgm : dosifyPaths args)
520
521 traceCmd :: String -> String -> IO () -> IO ()
522 -- a) trace the command (at two levels of verbosity)
523 -- b) don't do it at all if dry-run is set
524 traceCmd phase_name cmd_line action
525  = do   { verb <- dynFlag verbosity
526         ; when (verb >= 2) $ hPutStrLn stderr ("*** " ++ phase_name)
527         ; when (verb >= 3) $ hPutStrLn stderr cmd_line
528         ; hFlush stderr
529         
530            -- Test for -n flag
531         ; n <- readIORef v_Dry_run
532         ; unless n $ do {
533
534            -- And run it!
535         ; action `catchAllIO` handle_exn verb
536         }}
537   where
538     handle_exn verb exn = do { when (verb >= 2) (hPutStr   stderr "\n")
539                              ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line))
540                              ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
541 \end{code}
542
543
544 %************************************************************************
545 %*                                                                      *
546 \subsection{Support code}
547 %*                                                                      *
548 %************************************************************************
549
550
551 \begin{code}
552 -----------------------------------------------------------------------------
553 -- Convert filepath into MSDOS form.
554
555 dosifyPaths :: [String] -> [String]
556 dosifyPath  :: String -> String
557 -- dosifyPath does two things
558 -- a) change '/' to '\'
559 -- b) remove initial '/cygdrive/'
560
561 #if defined(mingw32_TARGET_OS)
562 dosifyPaths xs = map dosifyPath xs
563
564 dosifyPath stuff
565   = subst '/' '\\' real_stuff
566  where
567    -- fully convince myself that /cygdrive/ prefixes cannot
568    -- really appear here.
569   cygdrive_prefix = "/cygdrive/"
570
571   real_stuff
572     | cygdrive_prefix `isPrefixOf` stuff = drop (length cygdrive_prefix) stuff
573     | otherwise = stuff
574    
575   subst a b ls = map (\ x -> if x == a then b else x) ls
576 #else
577 dosifyPaths xs = xs
578 dosifyPath  xs = xs
579 #endif
580
581 -----------------------------------------------------------------------------
582 -- Path name construction
583 --      At the moment, we always use '/' and rely on dosifyPath 
584 --      to switch to DOS pathnames when necessary
585
586 slash            :: String -> String -> String
587 absPath, relPath :: [String] -> String
588
589 isSlash '/'   = True
590 isSlash '\\'  = True
591 isSlash other = False
592
593 relPath [] = ""
594 relPath xs = foldr1 slash xs
595
596 absPath xs = "" `slash` relPath xs
597
598 #if defined(mingw32_TARGET_OS)
599 slash s1 s2 = s1 ++ ('\\' : s2)
600 #else
601 slash s1 s2 = s1 ++ ('/' : s2)
602 #endif
603
604 -----------------------------------------------------------------------------
605 -- Define       myGetProcessId :: IO Int
606 --              getExecDir     :: IO (Maybe String)
607
608 #ifdef mingw32_TARGET_OS
609 foreign import "_getpid" getProcessID :: IO Int -- relies on Int == Int32 on Windows
610
611 getExecDir :: IO (Maybe String)
612 getExecDir = return Nothing
613 {-
614 foreign import stdcall "GetCurrentDirectoryA" getCurrentDirectory :: Int32 -> CString -> IO Int32
615 getExecDir = do len <- getCurrentDirectory 0 nullAddr
616                 buf <- mallocArray (fromIntegral len)
617                 ret <- getCurrentDirectory len buf
618                 if ret == 0 then return Nothing
619                             else do s <- peekCString buf
620                                     destructArray (fromIntegral len) buf
621                                     return (Just s)
622 -}
623 #else
624 getProcessID :: IO Int
625 getProcessID = Posix.getProcessID
626 getExecDir :: IO (Maybe String) = do return Nothing
627 #endif
628 \end{code}
629
630 %************************************************************************
631 %*                                                                      *
632 \subsection{System}
633 %*                                                                      *
634 %************************************************************************
635
636 In GHC prior to 5.01 (or so), on Windows, the implementation
637 of "system" in the library System.system does not work for very 
638 long command lines.  But GHC may need to make a system call with
639 a very long command line, notably when it links itself during
640 bootstrapping.  
641
642 Solution: when compiling SysTools for Windows, using GHC prior
643 to 5.01, write the command to a file and use "sh" (not cmd.exe)
644 to execute it.  Such GHCs require "sh" on the path, but once
645 bootstrapped this problem goes away.
646
647 ToDo: remove when compiling with GHC < 5 is not relevant any more
648
649 \begin{code}
650 system cmd
651
652 #if !defined(mingw32_TARGET_OS) || __GLASGOW_HASKELL__ > 501
653     -- The usual case
654  = System.system cmd
655
656 #else   -- The Hackoid case
657  = do pid     <- getProcessID
658       tmp_dir <- readIORef v_TmpDir
659       let tmp = tmp_dir++"/sh"++show pid
660       h <- openFile tmp WriteMode
661       hPutStrLn h cmd
662       hClose h
663       exit_code <- System.system ("sh - " ++ tmp) `catchAllIO` 
664                                  (\exn -> removeFile tmp >> throw exn)
665       removeFile tmp
666       return exit_code
667 #endif
668 \end{code}