[project @ 2001-10-18 13:56:09 by rrt]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
1 -----------------------------------------------------------------------------
2 --
3 -- (c) The University of Glasgow 2001
4 --
5 -- Access to system tools: gcc, cp, rm etc
6 --
7 -----------------------------------------------------------------------------
8
9 \begin{code}
10 module SysTools (
11         -- Initialisation
12         initSysTools,
13         setPgm,                 -- String -> IO ()
14                                 -- Command-line override
15         setDryRun,
16
17         getTopDir,              -- IO String    -- The value of $libdir
18         getPackageConfigPath,   -- IO String    -- Where package.conf is
19
20         -- Interface to system tools
21         runUnlit, runCpp, runCc, -- [Option] -> IO ()
22         runMangle, runSplit,     -- [Option] -> IO ()
23         runAs, runLink,          -- [Option] -> IO ()
24         runMkDLL,
25 #ifdef ILX
26         runIlx2il, runIlasm,     -- [String] -> IO ()
27 #endif
28
29
30         touch,                  -- String -> String -> IO ()
31         copy,                   -- String -> String -> String -> IO ()
32         unDosifyPath,           -- String -> String
33         
34         -- Temporary-file management
35         setTmpDir,
36         newTempName,
37         cleanTempFiles, cleanTempFilesExcept, removeTmpFiles,
38         addFilesToClean,
39
40         -- System interface
41         getProcessID,           -- IO Int
42         system,                 -- String -> IO ExitCode
43
44         -- Misc
45         showGhcUsage,           -- IO ()        Shows usage message and exits
46         getSysMan,              -- IO String    Parallel system only
47         
48         Option(..)
49
50  ) where
51
52 import DriverUtil
53 import Config
54 import Outputable
55 import Panic            ( progName, GhcException(..) )
56 import Util             ( global )
57 import CmdLineOpts      ( dynFlag, verbosity )
58
59 import Exception        ( throwDyn )
60 #if __GLASGOW_HASKELL__ > 408
61 import qualified Exception ( catch )
62 #else
63 import Exception        ( catchAllIO )
64 #endif
65 import IO
66 import Directory        ( doesFileExist, removeFile )
67 import IOExts           ( IORef, readIORef, writeIORef )
68 import Monad            ( when, unless )
69 import System           ( ExitCode(..), exitWith, getEnv, system )
70 import CString
71 import Int
72 import Addr
73     
74 #include "../includes/config.h"
75
76 #ifndef mingw32_TARGET_OS
77 import qualified Posix
78 #else
79 import List             ( isPrefixOf )
80 import MarshalArray
81 #endif
82
83 -- This is a kludge for bootstrapping with 4.08.X.  Given that
84 -- all distributed compilers >= 5.0 will be compiled with themselves.
85 -- I don't think this kludge is a problem.  And we have to start
86 -- building with >= 5.0 on Win32 anyway.
87 #if __GLASGOW_HASKELL__ > 408
88 -- use the line below when we can be sure of compiling with GHC >=
89 -- 5.02, and remove the implementation of rawSystem at the end of this
90 -- file
91 import PrelIOBase -- this can be removed when SystemExts is used
92 import CError     ( throwErrnoIfMinus1 ) -- as can this
93 -- import SystemExts       ( rawSystem )
94 #else
95 import System           ( system )
96 #endif
97
98
99 #include "HsVersions.h"
100
101 -- Make catch work on older GHCs
102 #if __GLASGOW_HASKELL__ > 408
103 myCatch = Exception.catch
104 #else
105 myCatch = catchAllIO
106 #endif
107
108 \end{code}
109
110
111                 The configuration story
112                 ~~~~~~~~~~~~~~~~~~~~~~~
113
114 GHC needs various support files (library packages, RTS etc), plus
115 various auxiliary programs (cp, gcc, etc).  It finds these in one
116 of two places:
117
118 * When running as an *installed program*, GHC finds most of this support
119   stuff in the installed library tree.  The path to this tree is passed
120   to GHC via the -B flag, and given to initSysTools .
121
122 * When running *in-place* in a build tree, GHC finds most of this support
123   stuff in the build tree.  The path to the build tree is, again passed
124   to GHC via -B. 
125
126 GHC tells which of the two is the case by seeing whether package.conf
127 is in TopDir [installed] or in TopDir/ghc/driver [inplace] (what a hack).
128
129
130 SysTools.initSysProgs figures out exactly where all the auxiliary programs
131 are, and initialises mutable variables to make it easy to call them.
132 To to this, it makes use of definitions in Config.hs, which is a Haskell
133 file containing variables whose value is figured out by the build system.
134
135 Config.hs contains two sorts of things
136
137   cGCC,         The *names* of the programs
138   cCPP            e.g.  cGCC = gcc
139   cUNLIT                cCPP = gcc -E
140   etc           They do *not* include paths
141                                 
142
143   cUNLIT_DIR    The *path* to the directory containing unlit, split etc
144   cSPLIT_DIR    *relative* to the root of the build tree,
145                 for use when running *in-place* in a build tree (only)
146                 
147
148
149 ---------------------------------------------
150 NOTES for an ALTERNATIVE scheme (i.e *not* what is currently implemented):
151
152 Another hair-brained scheme for simplifying the current tool location
153 nightmare in GHC: Simon originally suggested using another
154 configuration file along the lines of GCC's specs file - which is fine
155 except that it means adding code to read yet another configuration
156 file.  What I didn't notice is that the current package.conf is
157 general enough to do this:
158
159 Package
160     {name = "tools",    import_dirs = [],  source_dirs = [],
161      library_dirs = [], hs_libraries = [], extra_libraries = [],
162      include_dirs = [], c_includes = [],   package_deps = [],
163      extra_ghc_opts = ["-pgmc/usr/bin/gcc","-pgml${libdir}/bin/unlit", ... etc.],
164      extra_cc_opts = [], extra_ld_opts = []}
165
166 Which would have the advantage that we get to collect together in one
167 place the path-specific package stuff with the path-specific tool
168 stuff.
169                 End of NOTES
170 ---------------------------------------------
171
172
173 %************************************************************************
174 %*                                                                      *
175 \subsection{Global variables to contain system programs}
176 %*                                                                      *
177 %************************************************************************
178
179 All these pathnames are maintained IN THE NATIVE FORMAT OF THE HOST MACHINE.
180 (See remarks under pathnames below)
181
182 \begin{code}
183 GLOBAL_VAR(v_Pgm_L,     error "pgm_L",   String)        -- unlit
184 GLOBAL_VAR(v_Pgm_P,     error "pgm_P",   String)        -- cpp
185 GLOBAL_VAR(v_Pgm_c,     error "pgm_c",   String)        -- gcc
186 GLOBAL_VAR(v_Pgm_m,     error "pgm_m",   String)        -- asm code mangler
187 GLOBAL_VAR(v_Pgm_s,     error "pgm_s",   String)        -- asm code splitter
188 GLOBAL_VAR(v_Pgm_a,     error "pgm_a",   String)        -- as
189 #ifdef ILX
190 GLOBAL_VAR(v_Pgm_I,     error "pgm_I",   String)        -- ilx2il
191 GLOBAL_VAR(v_Pgm_i,     error "pgm_i",   String)        -- ilasm
192 #endif
193 GLOBAL_VAR(v_Pgm_l,     error "pgm_l",   String)        -- ld
194 GLOBAL_VAR(v_Pgm_MkDLL, error "pgm_dll", String)        -- mkdll
195
196 GLOBAL_VAR(v_Pgm_T,    error "pgm_T",    String)        -- touch
197 GLOBAL_VAR(v_Pgm_CP,   error "pgm_CP",   String)        -- cp
198
199 GLOBAL_VAR(v_Path_package_config, error "path_package_config", String)
200 GLOBAL_VAR(v_Path_usage,          error "ghc_usage.txt",       String)
201
202 GLOBAL_VAR(v_TopDir,    error "TopDir", String)         -- -B<dir>
203
204 -- Parallel system only
205 GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String)    -- system manager
206
207 -- ways to get at some of these variables from outside this module
208 getPackageConfigPath = readIORef v_Path_package_config
209 getTopDir            = readIORef v_TopDir
210 \end{code}
211
212
213 %************************************************************************
214 %*                                                                      *
215 \subsection{Initialisation}
216 %*                                                                      *
217 %************************************************************************
218
219 \begin{code}
220 initSysTools :: [String]        -- Command-line arguments starting "-B"
221
222              -> IO ()           -- Set all the mutable variables above, holding 
223                                 --      (a) the system programs
224                                 --      (b) the package-config file
225                                 --      (c) the GHC usage message
226
227
228 initSysTools minusB_args
229   = do  { (am_installed, top_dir) <- findTopDir minusB_args
230         ; writeIORef v_TopDir top_dir
231                 -- top_dir
232                 --      for "installed" this is the root of GHC's support files
233                 --      for "in-place" it is the root of the build tree
234                 -- NB: top_dir is assumed to be in standard Unix format '/' separated
235
236         ; let installed, installed_bin :: FilePath -> FilePath
237               installed_bin pgm   =  pgmPath top_dir pgm
238               installed     file  =  pgmPath top_dir file
239               inplace dir   pgm   =  pgmPath (top_dir `slash` dir) pgm
240
241         ; let pkgconfig_path
242                 | am_installed = installed "package.conf"
243                 | otherwise    = inplace cGHC_DRIVER_DIR "package.conf.inplace"
244
245               ghc_usage_msg_path
246                 | am_installed = installed "ghc-usage.txt"
247                 | otherwise    = inplace cGHC_DRIVER_DIR "ghc-usage.txt"
248
249                 -- For all systems, unlit, split, mangle are GHC utilities
250                 -- architecture-specific stuff is done when building Config.hs
251               unlit_path
252                 | am_installed = installed_bin cGHC_UNLIT
253                 | otherwise    = inplace cGHC_UNLIT_DIR cGHC_UNLIT
254
255                 -- split and mangle are Perl scripts
256               split_script
257                 | am_installed = installed_bin cGHC_SPLIT
258                 | otherwise    = inplace cGHC_SPLIT_DIR cGHC_SPLIT
259
260               mangle_script
261                 | am_installed = installed_bin cGHC_MANGLER
262                 | otherwise    = inplace cGHC_MANGLER_DIR cGHC_MANGLER
263
264 #ifndef mingw32_TARGET_OS
265         -- check whether TMPDIR is set in the environment
266         ; IO.try (do dir <- getEnv "TMPDIR" -- fails if not set
267                      setTmpDir dir
268                      return ()
269                  )
270 #else
271           -- On Win32, consult GetTempPath() for a temp dir.
272           --  => it first tries TMP, TEMP, then finally the
273           --   Windows directory(!). The directory is in short-path
274           --   form and *does* have a trailing backslash.
275         ; IO.try (do
276                 let len = (2048::Int)
277                 buf  <- mallocArray len
278                 ret  <- getTempPath len buf
279                 tdir <-
280                   if ret == 0 then do
281                       -- failed, consult TEMP.
282                      destructArray len buf
283                      getEnv "TMP"
284                    else do
285                      s <- peekCString buf
286                      destructArray len buf
287                      return s
288                 let
289                   -- strip the trailing backslash (awful, but 
290                   -- we only do this once).
291                   tmpdir =
292                     case last tdir of
293                       '/'  -> init tdir
294                       '\\' -> init tdir
295                       _   -> tdir
296                 setTmpDir tmpdir
297                 return ())
298 #endif
299
300         -- Check that the package config exists
301         ; config_exists <- doesFileExist pkgconfig_path
302         ; when (not config_exists) $
303              throwDyn (InstallationError 
304                          ("Can't find package.conf as " ++ pkgconfig_path))
305
306 #if defined(mingw32_TARGET_OS)
307         --              WINDOWS-SPECIFIC STUFF
308         -- On Windows, gcc and friends are distributed with GHC,
309         --      so when "installed" we look in TopDir/bin
310         -- When "in-place" we look wherever the build-time configure 
311         --      script found them
312         -- When "install" we tell gcc where its specs file + exes are (-B)
313         --      and also some places to pick up include files.  We need
314         --      to be careful to put all necessary exes in the -B place
315         --      (as, ld, cc1, etc) since if they don't get found there, gcc
316         --      then tries to run unadorned "as", "ld", etc, and will
317         --      pick up whatever happens to be lying around in the path,
318         --      possibly including those from a cygwin install on the target,
319         --      which is exactly what we're trying to avoid.
320         ; let gcc_path  | am_installed = installed_bin ("gcc -B\"" ++ installed "gcc-lib/\"")
321                         | otherwise    = cGCC
322                 -- The trailing "/" is absolutely essential; gcc seems
323                 -- to construct file names simply by concatenating to this
324                 -- -B path with no extra slash
325                 -- We use "/" rather than "\\" because otherwise "\\\" is mangled
326                 -- later on; although gcc_path is in NATIVE format, gcc can cope
327                 --      (see comments with declarations of global variables)
328                 --
329                 -- The quotes round the -B argument are in case TopDir has spaces in it
330
331               perl_path | am_installed = installed_bin cGHC_PERL
332                         | otherwise    = cGHC_PERL
333
334         -- 'touch' is a GHC util for Windows, and similarly unlit, mangle
335         ; let touch_path  | am_installed = installed_bin cGHC_TOUCHY
336                           | otherwise    = inplace cGHC_TOUCHY_DIR cGHC_TOUCHY
337
338         -- On Win32 we don't want to rely on #!/bin/perl, so we prepend 
339         -- a call to Perl to get the invocation of split and mangle
340         ; let split_path  = perl_path ++ " \"" ++ split_script ++ "\""
341               mangle_path = perl_path ++ " \"" ++ mangle_script ++ "\""
342
343         ; let mkdll_path = cMKDLL
344 #else
345         --              UNIX-SPECIFIC STUFF
346         -- On Unix, the "standard" tools are assumed to be
347         -- in the same place whether we are running "in-place" or "installed"
348         -- That place is wherever the build-time configure script found them.
349         ; let   gcc_path   = cGCC
350                 touch_path = cGHC_TOUCHY
351                 mkdll_path = panic "Can't build DLLs on a non-Win32 system"
352
353         -- On Unix, scripts are invoked using the '#!' method.  Binary
354         -- installations of GHC on Unix place the correct line on the front
355         -- of the script at installation time, so we don't want to wire-in
356         -- our knowledge of $(PERL) on the host system here.
357         ; let split_path  = split_script
358               mangle_path = mangle_script
359 #endif
360
361         -- cpp is derived from gcc on all platforms
362         ; let cpp_path  = gcc_path ++ " -E " ++ cRAWCPP_FLAGS
363
364         -- For all systems, copy and remove are provided by the host
365         -- system; architecture-specific stuff is done when building Config.hs
366         ; let   cp_path = cGHC_CP
367         
368         -- Other things being equal, as and ld are simply gcc
369         ; let   as_path  = gcc_path
370                 ld_path  = gcc_path
371
372 #ifdef ILX
373        -- ilx2il and ilasm are specified in Config.hs
374        ; let    ilx2il_path = cILX2IL
375                 ilasm_path  = cILASM
376 #endif
377                                        
378         -- Initialise the global vars
379         ; writeIORef v_Path_package_config pkgconfig_path
380         ; writeIORef v_Path_usage          ghc_usage_msg_path
381
382         ; writeIORef v_Pgm_sysman          (top_dir ++ "/ghc/rts/parallel/SysMan")
383                 -- Hans: this isn't right in general, but you can 
384                 -- elaborate it in the same way as the others
385
386         ; writeIORef v_Pgm_L               unlit_path
387         ; writeIORef v_Pgm_P               cpp_path
388         ; writeIORef v_Pgm_c               gcc_path
389         ; writeIORef v_Pgm_m               mangle_path
390         ; writeIORef v_Pgm_s               split_path
391         ; writeIORef v_Pgm_a               as_path
392 #ifdef ILX
393         ; writeIORef v_Pgm_I               ilx2il_path
394         ; writeIORef v_Pgm_i               ilasm_path
395 #endif
396         ; writeIORef v_Pgm_l               ld_path
397         ; writeIORef v_Pgm_MkDLL           mkdll_path
398         ; writeIORef v_Pgm_T               touch_path
399         ; writeIORef v_Pgm_CP              cp_path
400
401         ; return ()
402         }
403
404 #if defined(mingw32_TARGET_OS)
405 foreign import stdcall "GetTempPathA" getTempPath :: Int -> CString -> IO Int32
406 #endif
407 \end{code}
408
409 setPgm is called when a command-line option like
410         -pgmLld
411 is used to override a particular program with a new one
412
413 \begin{code}
414 setPgm :: String -> IO ()
415 -- The string is the flag, minus the '-pgm' prefix
416 -- So the first character says which program to override
417
418 setPgm ('P' : pgm) = writeIORef v_Pgm_P pgm
419 setPgm ('c' : pgm) = writeIORef v_Pgm_c pgm
420 setPgm ('m' : pgm) = writeIORef v_Pgm_m pgm
421 setPgm ('s' : pgm) = writeIORef v_Pgm_s pgm
422 setPgm ('a' : pgm) = writeIORef v_Pgm_a pgm
423 setPgm ('l' : pgm) = writeIORef v_Pgm_l pgm
424 #ifdef ILX
425 setPgm ('I' : pgm) = writeIORef v_Pgm_I pgm
426 setPgm ('i' : pgm) = writeIORef v_Pgm_i pgm
427 #endif
428 setPgm pgm         = unknownFlagErr ("-pgm" ++ pgm)
429 \end{code}
430
431
432 \begin{code}
433 -- Find TopDir
434 --      for "installed" this is the root of GHC's support files
435 --      for "in-place" it is the root of the build tree
436 --
437 -- Plan of action:
438 -- 1. Set proto_top_dir
439 --      a) look for (the last) -B flag, and use it
440 --      b) if there are no -B flags, get the directory 
441 --         where GHC is running (only on Windows)
442 --
443 -- 2. If package.conf exists in proto_top_dir, we are running
444 --      installed; and TopDir = proto_top_dir
445 --
446 -- 3. Otherwise we are running in-place, so
447 --      proto_top_dir will be /...stuff.../ghc/compiler
448 --      Set TopDir to /...stuff..., which is the root of the build tree
449 --
450 -- This is very gruesome indeed
451
452 findTopDir :: [String]
453           -> IO (Bool,          -- True <=> am installed, False <=> in-place
454                  String)        -- TopDir (in Unix format '/' separated)
455
456 findTopDir minusbs
457   = do { top_dir <- get_proto
458         -- Discover whether we're running in a build tree or in an installation,
459         -- by looking for the package configuration file.
460        ; am_installed <- doesFileExist (top_dir `slash` "package.conf")
461
462        ; return (am_installed, top_dir)
463        }
464   where
465     -- get_proto returns a Unix-format path (relying on getExecDir to do so too)
466     get_proto | not (null minusbs)
467               = return (unDosifyPath (drop 2 (last minusbs)))   -- 2 for "-B"
468               | otherwise          
469               = do { maybe_exec_dir <- getExecDir -- Get directory of executable
470                    ; case maybe_exec_dir of       -- (only works on Windows; 
471                                                   --  returns Nothing on Unix)
472                         Nothing  -> throwDyn (InstallationError "missing -B<dir> option")
473                         Just dir -> return dir
474                    }
475 \end{code}
476
477
478 %************************************************************************
479 %*                                                                      *
480 \subsection{Command-line options}
481 n%*                                                                     *
482 %************************************************************************
483
484 When invoking external tools as part of the compilation pipeline, we
485 pass these a sequence of options on the command-line. Rather than
486 just using a list of Strings, we use a type that allows us to distinguish
487 between filepaths and 'other stuff'. [The reason being, of course, that
488 this type gives us a handle on transforming filenames, and filenames only,
489 to whatever format they're expected to be on a particular platform.]
490
491 \begin{code}
492 data Option
493  = FileOption -- an entry that _contains_ filename(s) / filepaths.
494               String  -- a non-filepath prefix that shouldn't be transformed (e.g., "/out=" 
495               String  -- the filepath/filename portion
496  | Option     String
497  
498 showOptions :: [Option] -> String
499 showOptions ls = unwords (map (quote.showOpt) ls)
500  where
501    showOpt (FileOption pre f) = pre ++ dosifyPath f
502    showOpt (Option s)     = s
503
504 #if defined(mingw32_TARGET_OS)
505    quote "" = ""
506    quote s  = "\"" ++ s ++ "\""
507 #else
508    quote = id
509 #endif
510
511 \end{code}
512
513
514 %************************************************************************
515 %*                                                                      *
516 \subsection{Running an external program}
517 n%*                                                                     *
518 %************************************************************************
519
520
521 \begin{code}
522 runUnlit :: [Option] -> IO ()
523 runUnlit args = do p <- readIORef v_Pgm_L
524                    runSomething "Literate pre-processor" p args
525
526 runCpp :: [Option] -> IO ()
527 runCpp args =   do p <- readIORef v_Pgm_P
528                    runSomething "C pre-processor" p args
529
530 runCc :: [Option] -> IO ()
531 runCc args =   do p <- readIORef v_Pgm_c
532                   runSomething "C Compiler" p args
533
534 runMangle :: [Option] -> IO ()
535 runMangle args = do p <- readIORef v_Pgm_m
536                     runSomething "Mangler" p args
537
538 runSplit :: [Option] -> IO ()
539 runSplit args = do p <- readIORef v_Pgm_s
540                    runSomething "Splitter" p args
541
542 runAs :: [Option] -> IO ()
543 runAs args = do p <- readIORef v_Pgm_a
544                 runSomething "Assembler" p args
545
546 runLink :: [Option] -> IO ()
547 runLink args = do p <- readIORef v_Pgm_l
548                   runSomething "Linker" p args
549
550 #ifdef ILX
551 runIlx2il :: [Option] -> IO ()
552 runIlx2il args = do p <- readIORef v_Pgm_I
553                     runSomething "Ilx2Il" p args
554
555 runIlasm :: [Option] -> IO ()
556 runIlasm args = do p <- readIORef v_Pgm_i
557                    runSomething "Ilasm" p args
558 #endif
559
560 runMkDLL :: [Option] -> IO ()
561 runMkDLL args = do p <- readIORef v_Pgm_MkDLL
562                    runSomething "Make DLL" p args
563
564 touch :: String -> String -> IO ()
565 touch purpose arg =  do p <- readIORef v_Pgm_T
566                         runSomething purpose p [FileOption "" arg]
567
568 copy :: String -> String -> String -> IO ()
569 copy purpose from to = do
570   verb <- dynFlag verbosity
571   when (verb >= 2) $ hPutStrLn stderr ("*** " ++ purpose)
572
573   h <- openFile to WriteMode
574   ls <- readFile from -- inefficient, but it'll do for now.
575                       -- ToDo: speed up via slurping.
576   hPutStr h ls
577   hClose h
578 \end{code}
579
580 \begin{code}
581 getSysMan :: IO String  -- How to invoke the system manager 
582                         -- (parallel system only)
583 getSysMan = readIORef v_Pgm_sysman
584 \end{code}
585
586 %************************************************************************
587 %*                                                                      *
588 \subsection{GHC Usage message}
589 %*                                                                      *
590 %************************************************************************
591
592 Show the usage message and exit
593
594 \begin{code}
595 showGhcUsage = do { usage_path <- readIORef v_Path_usage
596                   ; usage      <- readFile usage_path
597                   ; dump usage
598                   ; exitWith ExitSuccess }
599   where
600      dump ""          = return ()
601      dump ('$':'$':s) = hPutStr stderr progName >> dump s
602      dump (c:s)       = hPutChar stderr c >> dump s
603 \end{code}
604
605
606 %************************************************************************
607 %*                                                                      *
608 \subsection{Managing temporary files
609 %*                                                                      *
610 %************************************************************************
611
612 \begin{code}
613 GLOBAL_VAR(v_FilesToClean, [],               [String] )
614 GLOBAL_VAR(v_TmpDir,       cDEFAULT_TMPDIR,  String   )
615         -- v_TmpDir has no closing '/'
616 \end{code}
617
618 \begin{code}
619 setTmpDir dir = writeIORef v_TmpDir dir
620
621 cleanTempFiles :: Int -> IO ()
622 cleanTempFiles verb = do fs <- readIORef v_FilesToClean
623                          removeTmpFiles verb fs
624
625 cleanTempFilesExcept :: Int -> [FilePath] -> IO ()
626 cleanTempFilesExcept verb dont_delete
627   = do fs <- readIORef v_FilesToClean
628        let leftovers = filter (`notElem` dont_delete) fs
629        removeTmpFiles verb leftovers
630        writeIORef v_FilesToClean dont_delete
631
632
633 -- find a temporary name that doesn't already exist.
634 newTempName :: Suffix -> IO FilePath
635 newTempName extn
636   = do x <- getProcessID
637        tmp_dir <- readIORef v_TmpDir
638        findTempName tmp_dir x
639   where 
640     findTempName tmp_dir x
641       = do let filename = tmp_dir ++ "/ghc" ++ show x ++ '.':extn
642            b  <- doesFileExist filename
643            if b then findTempName tmp_dir (x+1)
644                 else do add v_FilesToClean filename -- clean it up later
645                         return filename
646
647 addFilesToClean :: [FilePath] -> IO ()
648 -- May include wildcards [used by DriverPipeline.run_phase SplitMangle]
649 addFilesToClean files = mapM_ (add v_FilesToClean) files
650
651 removeTmpFiles :: Int -> [FilePath] -> IO ()
652 removeTmpFiles verb fs
653   = traceCmd "Deleting temp files" 
654              ("Deleting: " ++ unwords fs)
655              (mapM_ rm fs)
656   where
657     rm f = removeFile f `myCatch` 
658                 (\_ignored -> 
659                     when (verb >= 2) $
660                       hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
661                 )
662
663 \end{code}
664
665
666 %************************************************************************
667 %*                                                                      *
668 \subsection{Running a program}
669 %*                                                                      *
670 %************************************************************************
671
672 \begin{code}
673 GLOBAL_VAR(v_Dry_run, False, Bool)
674
675 setDryRun :: IO () 
676 setDryRun = writeIORef v_Dry_run True
677
678 -----------------------------------------------------------------------------
679 -- Running an external program
680
681 runSomething :: String          -- For -v message
682              -> String          -- Command name (possibly a full path)
683                                 --      assumed already dos-ified
684              -> [Option]        -- Arguments
685                                 --      runSomething will dos-ify them
686              -> IO ()
687
688 runSomething phase_name pgm args
689  = traceCmd phase_name cmd_line $
690    do   {
691 #ifndef mingw32_TARGET_OS
692           exit_code <- system cmd_line
693 #else
694           exit_code <- rawSystem cmd_line
695 #endif
696         ; if exit_code /= ExitSuccess
697           then throwDyn (PhaseFailed phase_name exit_code)
698           else return ()
699         }
700   where
701     cmd_line = pgm ++ ' ':showOptions args -- unwords (pgm : dosifyPaths (map quote args))
702         -- The pgm is already in native format (appropriate dir separators)
703 #if defined(mingw32_TARGET_OS)
704     quote "" = ""
705     quote s  = "\"" ++ s ++ "\""
706 #else
707     quote = id
708 #endif
709
710 traceCmd :: String -> String -> IO () -> IO ()
711 -- a) trace the command (at two levels of verbosity)
712 -- b) don't do it at all if dry-run is set
713 traceCmd phase_name cmd_line action
714  = do   { verb <- dynFlag verbosity
715         ; when (verb >= 2) $ hPutStrLn stderr ("*** " ++ phase_name)
716         ; when (verb >= 3) $ hPutStrLn stderr cmd_line
717         ; hFlush stderr
718         
719            -- Test for -n flag
720         ; n <- readIORef v_Dry_run
721         ; unless n $ do {
722
723            -- And run it!
724         ; action `myCatch` handle_exn verb
725         }}
726   where
727     handle_exn verb exn = do { when (verb >= 2) (hPutStr   stderr "\n")
728                              ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line))
729                              ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
730 \end{code}
731
732
733 %************************************************************************
734 %*                                                                      *
735 \subsection{Path names}
736 %*                                                                      *
737 %************************************************************************
738
739 We maintain path names in Unix form ('/'-separated) right until 
740 the last moment.  On Windows we dos-ify them just before passing them
741 to the Windows command.
742
743 The alternative, of using '/' consistently on Unix and '\' on Windows,
744 proved quite awkward.  There were a lot more calls to dosifyPath,
745 and even on Windows we might invoke a unix-like utility (eg 'sh'), which
746 interpreted a command line 'foo\baz' as 'foobaz'.
747
748 \begin{code}
749 -----------------------------------------------------------------------------
750 -- Convert filepath into MSDOS form.
751
752 dosifyPaths :: [String] -> [String]
753 -- dosifyPaths does two things
754 -- a) change '/' to '\'
755 -- b) remove initial '/cygdrive/'
756
757 unDosifyPath :: String -> String
758 -- Just change '\' to '/'
759
760 pgmPath :: String               -- Directory string in Unix format
761         -> String               -- Program name with no directory separators
762                                 --      (e.g. copy /y)
763         -> String               -- Program invocation string in native format
764
765
766
767 #if defined(mingw32_TARGET_OS)
768
769 --------------------- Windows version ------------------
770 dosifyPaths xs = map dosifyPath xs
771
772 unDosifyPath xs = subst '\\' '/' xs
773
774 pgmPath dir pgm = dosifyPath dir ++ '\\' : pgm
775
776 dosifyPath stuff
777   = subst '/' '\\' real_stuff
778  where
779    -- fully convince myself that /cygdrive/ prefixes cannot
780    -- really appear here.
781   cygdrive_prefix = "/cygdrive/"
782
783   real_stuff
784     | cygdrive_prefix `isPrefixOf` stuff = drop (length cygdrive_prefix) stuff
785     | otherwise = stuff
786    
787 #else
788
789 --------------------- Unix version ---------------------
790 dosifyPaths  ps  = ps
791 unDosifyPath xs  = xs
792 pgmPath dir pgm  = dir ++ '/' : pgm
793 dosifyPath stuff = stuff
794 --------------------------------------------------------
795 #endif
796
797 subst a b ls = map (\ x -> if x == a then b else x) ls
798 \end{code}
799
800
801 -----------------------------------------------------------------------------
802    Path name construction
803
804 \begin{code}
805 slash            :: String -> String -> String
806 absPath, relPath :: [String] -> String
807
808 relPath [] = ""
809 relPath xs = foldr1 slash xs
810
811 absPath xs = "" `slash` relPath xs
812
813 slash s1 s2 = s1 ++ ('/' : s2)
814 \end{code}
815
816
817 %************************************************************************
818 %*                                                                      *
819 \subsection{Support code}
820 %*                                                                      *
821 %************************************************************************
822
823 \begin{code}
824 -----------------------------------------------------------------------------
825 -- Define       getExecDir     :: IO (Maybe String)
826
827 #if defined(mingw32_TARGET_OS)
828 getExecDir :: IO (Maybe String)
829 getExecDir = do let len = (2048::Int) -- plenty, PATH_MAX is 512 under Win32.
830                 buf <- mallocArray len
831                 ret <- getModuleFileName nullAddr buf len
832                 if ret == 0 then destructArray len buf >> return Nothing
833                             else do s <- peekCString buf
834                                     destructArray len buf
835                                     return (Just (reverse (drop (length "/bin/ghc.exe") (reverse (unDosifyPath s)))))
836
837
838 foreign import stdcall "GetModuleFileNameA" getModuleFileName :: Addr -> CString -> Int -> IO Int32
839 #else
840 getExecDir :: IO (Maybe String) = do return Nothing
841 #endif
842
843 #ifdef mingw32_TARGET_OS
844 foreign import "_getpid" getProcessID :: IO Int -- relies on Int == Int32 on Windows
845 #else
846 getProcessID :: IO Int
847 getProcessID = Posix.getProcessID
848 #endif
849
850 rawSystem :: String -> IO ExitCode
851 #if __GLASGOW_HASKELL__ > 408
852 rawSystem "" = ioException (IOError Nothing InvalidArgument "rawSystem" "null command" Nothing)
853 rawSystem cmd =
854   withCString cmd $ \s -> do
855     status <- throwErrnoIfMinus1 "rawSystem" (primRawSystem s)
856     case status of
857         0  -> return ExitSuccess
858         n  -> return (ExitFailure n)
859
860 foreign import ccall "rawSystemCmd" unsafe primRawSystem :: CString -> IO Int
861 #else
862 rawSystem = System.system
863 #endif
864
865
866 \end{code}