1 -----------------------------------------------------------------------------
3 -- (c) The University of Glasgow 2001
5 -- Access to system tools: gcc, cp, rm etc
7 -----------------------------------------------------------------------------
14 setPgmP, -- String -> IO ()
25 -- Command-line override
28 getTopDir, -- IO String -- The value of $libdir
29 getPackageConfigPath, -- IO String -- Where package.conf is
31 -- Interface to system tools
32 runUnlit, runCpp, runCc, -- [Option] -> IO ()
33 runPp, -- [Option] -> IO ()
34 runMangle, runSplit, -- [Option] -> IO ()
35 runAs, runLink, -- [Option] -> IO ()
38 runIlx2il, runIlasm, -- [String] -> IO ()
42 touch, -- String -> String -> IO ()
43 copy, -- String -> String -> String -> IO ()
44 unDosifyPath, -- String -> String
46 -- Temporary-file management
49 cleanTempFiles, cleanTempFilesExcept, removeTmpFiles,
53 getProcessID, -- IO Int
54 system, -- String -> IO ExitCode
57 showGhcUsage, -- IO () Shows usage message and exits
58 getSysMan, -- IO String Parallel system only
64 #include "HsVersions.h"
69 import Panic ( progName, GhcException(..) )
70 import Util ( global, notNull )
71 import CmdLineOpts ( dynFlag, verbosity )
73 import EXCEPTION ( throwDyn )
74 #if __GLASGOW_HASKELL__ > 408
75 import qualified EXCEPTION as Exception ( catch )
77 import EXCEPTION ( catchAllIO )
80 import DATA_IOREF ( IORef, readIORef, writeIORef )
83 import Monad ( when, unless )
84 import System ( ExitCode(..), exitWith, getEnv, system )
86 import Directory ( doesFileExist, removeFile )
88 #include "../includes/config.h"
90 -- GHC <= 4.08 didn't have rawSystem, and runs into problems with long command
91 -- lines on mingw32, so we disallow it now.
92 #if defined(mingw32_HOST_OS) && (__GLASGOW_HASKELL__ <= 408)
93 #error GHC <= 4.08 is not supported for bootstrapping GHC on i386-unknown-mingw32
96 #ifndef mingw32_HOST_OS
97 import qualified Posix
99 import List ( isPrefixOf )
100 import Util ( dropList )
105 #ifdef mingw32_HOST_OS
106 #if __GLASGOW_HASKELL__ > 504
107 import System.Cmd ( rawSystem )
109 import SystemExts ( rawSystem )
112 import System ( system )
115 -- Make catch work on older GHCs
116 #if __GLASGOW_HASKELL__ > 408
117 myCatch = Exception.catch
125 The configuration story
126 ~~~~~~~~~~~~~~~~~~~~~~~
128 GHC needs various support files (library packages, RTS etc), plus
129 various auxiliary programs (cp, gcc, etc). It finds these in one
132 * When running as an *installed program*, GHC finds most of this support
133 stuff in the installed library tree. The path to this tree is passed
134 to GHC via the -B flag, and given to initSysTools .
136 * When running *in-place* in a build tree, GHC finds most of this support
137 stuff in the build tree. The path to the build tree is, again passed
140 GHC tells which of the two is the case by seeing whether package.conf
141 is in TopDir [installed] or in TopDir/ghc/driver [inplace] (what a hack).
144 SysTools.initSysProgs figures out exactly where all the auxiliary programs
145 are, and initialises mutable variables to make it easy to call them.
146 To to this, it makes use of definitions in Config.hs, which is a Haskell
147 file containing variables whose value is figured out by the build system.
149 Config.hs contains two sorts of things
151 cGCC, The *names* of the programs
154 etc They do *not* include paths
157 cUNLIT_DIR_REL The *path* to the directory containing unlit, split etc
158 cSPLIT_DIR_REL *relative* to the root of the build tree,
159 for use when running *in-place* in a build tree (only)
163 ---------------------------------------------
164 NOTES for an ALTERNATIVE scheme (i.e *not* what is currently implemented):
166 Another hair-brained scheme for simplifying the current tool location
167 nightmare in GHC: Simon originally suggested using another
168 configuration file along the lines of GCC's specs file - which is fine
169 except that it means adding code to read yet another configuration
170 file. What I didn't notice is that the current package.conf is
171 general enough to do this:
174 {name = "tools", import_dirs = [], source_dirs = [],
175 library_dirs = [], hs_libraries = [], extra_libraries = [],
176 include_dirs = [], c_includes = [], package_deps = [],
177 extra_ghc_opts = ["-pgmc/usr/bin/gcc","-pgml${libdir}/bin/unlit", ... etc.],
178 extra_cc_opts = [], extra_ld_opts = []}
180 Which would have the advantage that we get to collect together in one
181 place the path-specific package stuff with the path-specific tool
184 ---------------------------------------------
187 %************************************************************************
189 \subsection{Global variables to contain system programs}
191 %************************************************************************
193 All these pathnames are maintained IN THE NATIVE FORMAT OF THE HOST MACHINE.
194 (See remarks under pathnames below)
197 GLOBAL_VAR(v_Pgm_L, error "pgm_L", String) -- unlit
198 GLOBAL_VAR(v_Pgm_P, error "pgm_P", String) -- cpp
199 GLOBAL_VAR(v_Pgm_F, error "pgm_F", String) -- pp
200 GLOBAL_VAR(v_Pgm_c, error "pgm_c", String) -- gcc
201 GLOBAL_VAR(v_Pgm_m, error "pgm_m", String) -- asm code mangler
202 GLOBAL_VAR(v_Pgm_s, error "pgm_s", String) -- asm code splitter
203 GLOBAL_VAR(v_Pgm_a, error "pgm_a", String) -- as
205 GLOBAL_VAR(v_Pgm_I, error "pgm_I", String) -- ilx2il
206 GLOBAL_VAR(v_Pgm_i, error "pgm_i", String) -- ilasm
208 GLOBAL_VAR(v_Pgm_l, error "pgm_l", String) -- ld
209 GLOBAL_VAR(v_Pgm_MkDLL, error "pgm_dll", String) -- mkdll
211 GLOBAL_VAR(v_Pgm_T, error "pgm_T", String) -- touch
212 GLOBAL_VAR(v_Pgm_CP, error "pgm_CP", String) -- cp
214 GLOBAL_VAR(v_Path_package_config, error "path_package_config", String)
215 GLOBAL_VAR(v_Path_usage, error "ghc_usage.txt", String)
217 GLOBAL_VAR(v_TopDir, error "TopDir", String) -- -B<dir>
219 -- Parallel system only
220 GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String) -- system manager
222 -- ways to get at some of these variables from outside this module
223 getPackageConfigPath = readIORef v_Path_package_config
224 getTopDir = readIORef v_TopDir
228 %************************************************************************
230 \subsection{Initialisation}
232 %************************************************************************
235 initSysTools :: [String] -- Command-line arguments starting "-B"
237 -> IO () -- Set all the mutable variables above, holding
238 -- (a) the system programs
239 -- (b) the package-config file
240 -- (c) the GHC usage message
243 initSysTools minusB_args
244 = do { (am_installed, top_dir) <- findTopDir minusB_args
245 ; writeIORef v_TopDir top_dir
247 -- for "installed" this is the root of GHC's support files
248 -- for "in-place" it is the root of the build tree
249 -- NB: top_dir is assumed to be in standard Unix format '/' separated
251 ; let installed, installed_bin :: FilePath -> FilePath
252 installed_bin pgm = pgmPath top_dir pgm
253 installed file = pgmPath top_dir file
254 inplace dir pgm = pgmPath (top_dir `slash`
255 cPROJECT_DIR `slash` dir) pgm
258 | am_installed = installed "package.conf"
259 | otherwise = inplace cGHC_DRIVER_DIR_REL "package.conf.inplace"
262 | am_installed = installed "ghc-usage.txt"
263 | otherwise = inplace cGHC_DRIVER_DIR_REL "ghc-usage.txt"
265 -- For all systems, unlit, split, mangle are GHC utilities
266 -- architecture-specific stuff is done when building Config.hs
268 | am_installed = installed_bin cGHC_UNLIT_PGM
269 | otherwise = inplace cGHC_UNLIT_DIR_REL cGHC_UNLIT_PGM
271 -- split and mangle are Perl scripts
273 | am_installed = installed_bin cGHC_SPLIT_PGM
274 | otherwise = inplace cGHC_SPLIT_DIR_REL cGHC_SPLIT_PGM
277 | am_installed = installed_bin cGHC_MANGLER_PGM
278 | otherwise = inplace cGHC_MANGLER_DIR_REL cGHC_MANGLER_PGM
280 #ifndef mingw32_HOST_OS
281 -- check whether TMPDIR is set in the environment
282 ; IO.try (do dir <- getEnv "TMPDIR" -- fails if not set
287 -- On Win32, consult GetTempPath() for a temp dir.
288 -- => it first tries TMP, TEMP, then finally the
289 -- Windows directory(!). The directory is in short-path
290 -- form and *does* have a trailing backslash.
292 let len = (2048::Int)
293 buf <- mallocArray len
294 ret <- getTempPath len buf
297 -- failed, consult TEMP.
305 -- strip the trailing backslash (awful, but
306 -- we only do this once).
316 -- Check that the package config exists
317 ; config_exists <- doesFileExist pkgconfig_path
318 ; when (not config_exists) $
319 throwDyn (InstallationError
320 ("Can't find package.conf as " ++ pkgconfig_path))
322 #if defined(mingw32_HOST_OS)
323 -- WINDOWS-SPECIFIC STUFF
324 -- On Windows, gcc and friends are distributed with GHC,
325 -- so when "installed" we look in TopDir/bin
326 -- When "in-place" we look wherever the build-time configure
328 -- When "install" we tell gcc where its specs file + exes are (-B)
329 -- and also some places to pick up include files. We need
330 -- to be careful to put all necessary exes in the -B place
331 -- (as, ld, cc1, etc) since if they don't get found there, gcc
332 -- then tries to run unadorned "as", "ld", etc, and will
333 -- pick up whatever happens to be lying around in the path,
334 -- possibly including those from a cygwin install on the target,
335 -- which is exactly what we're trying to avoid.
336 ; let gcc_path | am_installed = installed_bin ("gcc -B\"" ++ installed "gcc-lib/\"")
338 -- The trailing "/" is absolutely essential; gcc seems
339 -- to construct file names simply by concatenating to this
340 -- -B path with no extra slash
341 -- We use "/" rather than "\\" because otherwise "\\\" is mangled
342 -- later on; although gcc_path is in NATIVE format, gcc can cope
343 -- (see comments with declarations of global variables)
345 -- The quotes round the -B argument are in case TopDir has spaces in it
347 perl_path | am_installed = installed_bin cGHC_PERL
348 | otherwise = cGHC_PERL
350 -- 'touch' is a GHC util for Windows, and similarly unlit, mangle
351 ; let touch_path | am_installed = installed_bin cGHC_TOUCHY_PGM
352 | otherwise = inplace cGHC_TOUCHY_DIR_REL cGHC_TOUCHY_PGM
354 -- On Win32 we don't want to rely on #!/bin/perl, so we prepend
355 -- a call to Perl to get the invocation of split and mangle
356 ; let split_path = perl_path ++ " \"" ++ split_script ++ "\""
357 mangle_path = perl_path ++ " \"" ++ mangle_script ++ "\""
360 | am_installed = pgmPath (installed "gcc-lib/") cMKDLL ++
361 " --dlltool-name " ++ pgmPath (installed "gcc-lib/") "dlltool" ++
362 " --driver-name " ++ gcc_path
365 -- UNIX-SPECIFIC STUFF
366 -- On Unix, the "standard" tools are assumed to be
367 -- in the same place whether we are running "in-place" or "installed"
368 -- That place is wherever the build-time configure script found them.
369 ; let gcc_path = cGCC
371 mkdll_path = panic "Can't build DLLs on a non-Win32 system"
373 -- On Unix, scripts are invoked using the '#!' method. Binary
374 -- installations of GHC on Unix place the correct line on the front
375 -- of the script at installation time, so we don't want to wire-in
376 -- our knowledge of $(PERL) on the host system here.
377 ; let split_path = split_script
378 mangle_path = mangle_script
381 -- cpp is derived from gcc on all platforms
382 ; let cpp_path = gcc_path ++ " -E " ++ cRAWCPP_FLAGS
384 -- For all systems, copy and remove are provided by the host
385 -- system; architecture-specific stuff is done when building Config.hs
386 ; let cp_path = cGHC_CP
388 -- Other things being equal, as and ld are simply gcc
389 ; let as_path = gcc_path
393 -- ilx2il and ilasm are specified in Config.hs
394 ; let ilx2il_path = cILX2IL
398 -- Initialise the global vars
399 ; writeIORef v_Path_package_config pkgconfig_path
400 ; writeIORef v_Path_usage ghc_usage_msg_path
402 ; writeIORef v_Pgm_sysman (top_dir ++ "/ghc/rts/parallel/SysMan")
403 -- Hans: this isn't right in general, but you can
404 -- elaborate it in the same way as the others
406 ; writeIORef v_Pgm_L unlit_path
407 ; writeIORef v_Pgm_P cpp_path
408 ; writeIORef v_Pgm_F ""
409 ; writeIORef v_Pgm_c gcc_path
410 ; writeIORef v_Pgm_m mangle_path
411 ; writeIORef v_Pgm_s split_path
412 ; writeIORef v_Pgm_a as_path
414 ; writeIORef v_Pgm_I ilx2il_path
415 ; writeIORef v_Pgm_i ilasm_path
417 ; writeIORef v_Pgm_l ld_path
418 ; writeIORef v_Pgm_MkDLL mkdll_path
419 ; writeIORef v_Pgm_T touch_path
420 ; writeIORef v_Pgm_CP cp_path
425 #if defined(mingw32_HOST_OS)
426 foreign import stdcall "GetTempPathA" unsafe getTempPath :: Int -> CString -> IO Int32
430 The various setPgm functions are called when a command-line option
435 is used to override a particular program with a new one
438 setPgmP = writeIORef v_Pgm_P
439 setPgmF = writeIORef v_Pgm_F
440 setPgmc = writeIORef v_Pgm_c
441 setPgmm = writeIORef v_Pgm_m
442 setPgms = writeIORef v_Pgm_s
443 setPgma = writeIORef v_Pgm_a
444 setPgml = writeIORef v_Pgm_l
446 setPgmI = writeIORef v_Pgm_I
447 setPgmi = writeIORef v_Pgm_i
454 -- for "installed" this is the root of GHC's support files
455 -- for "in-place" it is the root of the build tree
458 -- 1. Set proto_top_dir
459 -- a) look for (the last) -B flag, and use it
460 -- b) if there are no -B flags, get the directory
461 -- where GHC is running (only on Windows)
463 -- 2. If package.conf exists in proto_top_dir, we are running
464 -- installed; and TopDir = proto_top_dir
466 -- 3. Otherwise we are running in-place, so
467 -- proto_top_dir will be /...stuff.../ghc/compiler
468 -- Set TopDir to /...stuff..., which is the root of the build tree
470 -- This is very gruesome indeed
472 findTopDir :: [String]
473 -> IO (Bool, -- True <=> am installed, False <=> in-place
474 String) -- TopDir (in Unix format '/' separated)
477 = do { top_dir <- get_proto
478 -- Discover whether we're running in a build tree or in an installation,
479 -- by looking for the package configuration file.
480 ; am_installed <- doesFileExist (top_dir `slash` "package.conf")
482 ; return (am_installed, top_dir)
485 -- get_proto returns a Unix-format path (relying on getExecDir to do so too)
486 get_proto | notNull minusbs
487 = return (unDosifyPath (drop 2 (last minusbs))) -- 2 for "-B"
489 = do { maybe_exec_dir <- getExecDir -- Get directory of executable
490 ; case maybe_exec_dir of -- (only works on Windows;
491 -- returns Nothing on Unix)
492 Nothing -> throwDyn (InstallationError "missing -B<dir> option")
493 Just dir -> return dir
498 %************************************************************************
500 \subsection{Command-line options}
502 %************************************************************************
504 When invoking external tools as part of the compilation pipeline, we
505 pass these a sequence of options on the command-line. Rather than
506 just using a list of Strings, we use a type that allows us to distinguish
507 between filepaths and 'other stuff'. [The reason being, of course, that
508 this type gives us a handle on transforming filenames, and filenames only,
509 to whatever format they're expected to be on a particular platform.]
513 = FileOption -- an entry that _contains_ filename(s) / filepaths.
514 String -- a non-filepath prefix that shouldn't be transformed (e.g., "/out="
515 String -- the filepath/filename portion
518 showOptions :: [Option] -> String
519 showOptions ls = unwords (map (quote.showOpt) ls)
521 showOpt (FileOption pre f) = pre ++ dosifyPath f
522 showOpt (Option s) = s
527 %************************************************************************
529 \subsection{Running an external program}
531 %************************************************************************
535 runUnlit :: [Option] -> IO ()
536 runUnlit args = do p <- readIORef v_Pgm_L
537 runSomething "Literate pre-processor" p args
539 runCpp :: [Option] -> IO ()
540 runCpp args = do p <- readIORef v_Pgm_P
541 runSomething "C pre-processor" p args
543 runPp :: [Option] -> IO ()
544 runPp args = do p <- readIORef v_Pgm_F
545 runSomething "Haskell pre-processor" p args
547 runCc :: [Option] -> IO ()
548 runCc args = do p <- readIORef v_Pgm_c
549 runSomething "C Compiler" p args
551 runMangle :: [Option] -> IO ()
552 runMangle args = do p <- readIORef v_Pgm_m
553 runSomething "Mangler" p args
555 runSplit :: [Option] -> IO ()
556 runSplit args = do p <- readIORef v_Pgm_s
557 runSomething "Splitter" p args
559 runAs :: [Option] -> IO ()
560 runAs args = do p <- readIORef v_Pgm_a
561 runSomething "Assembler" p args
563 runLink :: [Option] -> IO ()
564 runLink args = do p <- readIORef v_Pgm_l
565 runSomething "Linker" p args
568 runIlx2il :: [Option] -> IO ()
569 runIlx2il args = do p <- readIORef v_Pgm_I
570 runSomething "Ilx2Il" p args
572 runIlasm :: [Option] -> IO ()
573 runIlasm args = do p <- readIORef v_Pgm_i
574 runSomething "Ilasm" p args
577 runMkDLL :: [Option] -> IO ()
578 runMkDLL args = do p <- readIORef v_Pgm_MkDLL
579 runSomething "Make DLL" p args
581 touch :: String -> String -> IO ()
582 touch purpose arg = do p <- readIORef v_Pgm_T
583 runSomething purpose p [FileOption "" arg]
585 copy :: String -> String -> String -> IO ()
586 copy purpose from to = do
587 verb <- dynFlag verbosity
588 when (verb >= 2) $ hPutStrLn stderr ("*** " ++ purpose)
590 h <- openFile to WriteMode
591 ls <- readFile from -- inefficient, but it'll do for now.
592 -- ToDo: speed up via slurping.
598 getSysMan :: IO String -- How to invoke the system manager
599 -- (parallel system only)
600 getSysMan = readIORef v_Pgm_sysman
603 %************************************************************************
605 \subsection{GHC Usage message}
607 %************************************************************************
609 Show the usage message and exit
612 showGhcUsage = do { usage_path <- readIORef v_Path_usage
613 ; usage <- readFile usage_path
615 ; exitWith ExitSuccess }
618 dump ('$':'$':s) = hPutStr stderr progName >> dump s
619 dump (c:s) = hPutChar stderr c >> dump s
623 %************************************************************************
625 \subsection{Managing temporary files
627 %************************************************************************
630 GLOBAL_VAR(v_FilesToClean, [], [String] )
631 GLOBAL_VAR(v_TmpDir, cDEFAULT_TMPDIR, String )
632 -- v_TmpDir has no closing '/'
636 setTmpDir dir = writeIORef v_TmpDir dir
638 cleanTempFiles :: Int -> IO ()
639 cleanTempFiles verb = do fs <- readIORef v_FilesToClean
640 removeTmpFiles verb fs
642 cleanTempFilesExcept :: Int -> [FilePath] -> IO ()
643 cleanTempFilesExcept verb dont_delete
644 = do fs <- readIORef v_FilesToClean
645 let leftovers = filter (`notElem` dont_delete) fs
646 removeTmpFiles verb leftovers
647 writeIORef v_FilesToClean dont_delete
650 -- find a temporary name that doesn't already exist.
651 newTempName :: Suffix -> IO FilePath
653 = do x <- getProcessID
654 tmp_dir <- readIORef v_TmpDir
655 findTempName tmp_dir x
657 findTempName tmp_dir x
658 = do let filename = tmp_dir ++ "/ghc" ++ show x ++ '.':extn
659 b <- doesFileExist filename
660 if b then findTempName tmp_dir (x+1)
661 else do add v_FilesToClean filename -- clean it up later
664 addFilesToClean :: [FilePath] -> IO ()
665 -- May include wildcards [used by DriverPipeline.run_phase SplitMangle]
666 addFilesToClean files = mapM_ (add v_FilesToClean) files
668 removeTmpFiles :: Int -> [FilePath] -> IO ()
669 removeTmpFiles verb fs
670 = traceCmd "Deleting temp files"
671 ("Deleting: " ++ unwords fs)
674 rm f = removeFile f `myCatch`
677 hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
683 %************************************************************************
685 \subsection{Running a program}
687 %************************************************************************
690 GLOBAL_VAR(v_Dry_run, False, Bool)
693 setDryRun = writeIORef v_Dry_run True
695 -----------------------------------------------------------------------------
696 -- Running an external program
698 runSomething :: String -- For -v message
699 -> String -- Command name (possibly a full path)
700 -- assumed already dos-ified
701 -> [Option] -- Arguments
702 -- runSomething will dos-ify them
705 runSomething phase_name pgm args
706 = traceCmd phase_name cmd_line $
708 #ifndef mingw32_HOST_OS
709 exit_code <- system cmd_line
711 exit_code <- rawSystem cmd_line
713 ; if exit_code /= ExitSuccess
714 then throwDyn (PhaseFailed phase_name exit_code)
718 -- The pgm is already in native format (appropriate dir separators)
719 cmd_line = pgm ++ ' ':showOptions args
720 -- unwords (pgm : dosifyPaths (map quote args))
722 traceCmd :: String -> String -> IO () -> IO ()
723 -- a) trace the command (at two levels of verbosity)
724 -- b) don't do it at all if dry-run is set
725 traceCmd phase_name cmd_line action
726 = do { verb <- dynFlag verbosity
727 ; when (verb >= 2) $ hPutStrLn stderr ("*** " ++ phase_name)
728 ; when (verb >= 3) $ hPutStrLn stderr cmd_line
732 ; n <- readIORef v_Dry_run
736 ; action `myCatch` handle_exn verb
739 handle_exn verb exn = do { when (verb >= 2) (hPutStr stderr "\n")
740 ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line))
741 ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
745 %************************************************************************
747 \subsection{Path names}
749 %************************************************************************
751 We maintain path names in Unix form ('/'-separated) right until
752 the last moment. On Windows we dos-ify them just before passing them
753 to the Windows command.
755 The alternative, of using '/' consistently on Unix and '\' on Windows,
756 proved quite awkward. There were a lot more calls to dosifyPath,
757 and even on Windows we might invoke a unix-like utility (eg 'sh'), which
758 interpreted a command line 'foo\baz' as 'foobaz'.
761 -----------------------------------------------------------------------------
762 -- Convert filepath into MSDOS form.
764 dosifyPaths :: [String] -> [String]
765 -- dosifyPaths does two things
766 -- a) change '/' to '\'
767 -- b) remove initial '/cygdrive/'
769 unDosifyPath :: String -> String
770 -- Just change '\' to '/'
772 pgmPath :: String -- Directory string in Unix format
773 -> String -- Program name with no directory separators
775 -> String -- Program invocation string in native format
779 #if defined(mingw32_HOST_OS)
781 --------------------- Windows version ------------------
782 dosifyPaths xs = map dosifyPath xs
784 unDosifyPath xs = subst '\\' '/' xs
786 pgmPath dir pgm = dosifyPath dir ++ '\\' : pgm
789 = subst '/' '\\' real_stuff
791 -- fully convince myself that /cygdrive/ prefixes cannot
792 -- really appear here.
793 cygdrive_prefix = "/cygdrive/"
796 | cygdrive_prefix `isPrefixOf` stuff = dropList cygdrive_prefix stuff
801 --------------------- Unix version ---------------------
804 pgmPath dir pgm = dir ++ '/' : pgm
805 dosifyPath stuff = stuff
806 --------------------------------------------------------
809 subst a b ls = map (\ x -> if x == a then b else x) ls
813 -----------------------------------------------------------------------------
814 Path name construction
817 slash :: String -> String -> String
818 absPath, relPath :: [String] -> String
821 relPath xs = foldr1 slash xs
823 absPath xs = "" `slash` relPath xs
825 slash s1 s2 = s1 ++ ('/' : s2)
829 %************************************************************************
831 \subsection{Support code}
833 %************************************************************************
836 -----------------------------------------------------------------------------
837 -- Define getExecDir :: IO (Maybe String)
839 #if defined(mingw32_HOST_OS)
840 getExecDir :: IO (Maybe String)
841 getExecDir = do let len = (2048::Int) -- plenty, PATH_MAX is 512 under Win32.
842 buf <- mallocArray len
843 ret <- getModuleFileName nullPtr buf len
844 if ret == 0 then free buf >> return Nothing
845 else do s <- peekCString buf
847 return (Just (reverse (dropList "/bin/ghc.exe" (reverse (unDosifyPath s)))))
850 foreign import stdcall "GetModuleFileNameA" unsafe
851 getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
853 getExecDir :: IO (Maybe String) = do return Nothing
856 #ifdef mingw32_HOST_OS
857 foreign import "_getpid" unsafe getProcessID :: IO Int -- relies on Int == Int32 on Windows
859 getProcessID :: IO Int
860 getProcessID = Posix.getProcessID
863 quote :: String -> String
864 #if defined(mingw32_HOST_OS)
866 quote s = "\"" ++ s ++ "\""