filter the messages generated by gcc
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index 945ae44..5c434d0 100644 (file)
@@ -1,7 +1,8 @@
 -----------------------------------------------------------------------------
--- Access to system tools: gcc, cp, rm etc
 --
--- (c) The University of Glasgow 2000
+-- (c) The University of Glasgow 2001-2003
+--
+-- Access to system tools: gcc, cp, rm etc
 --
 -----------------------------------------------------------------------------
 
 module SysTools (
        -- Initialisation
        initSysTools,
-       setPgm,                 -- String -> IO ()
-                               -- Command-line override
-       setDryRun,
 
-       packageConfigPath,      -- IO String    
-                               -- Where package.conf is
+       getTopDir,              -- IO String    -- The value of $topdir
+       getPackageConfigPath,   -- IO String    -- Where package.conf is
+        getUsageMsgPaths,       -- IO (String,String)
 
        -- Interface to system tools
-       runUnlit, runCpp, runCc, -- [String] -> IO ()
-       runMangle, runSplit,     -- [String] -> IO ()
-       runAs, runLink,          -- [String] -> IO ()
+       runUnlit, runCpp, runCc, -- [Option] -> IO ()
+       runPp,                   -- [Option] -> IO ()
+       runMangle, runSplit,     -- [Option] -> IO ()
+       runAs, runLink,          -- [Option] -> IO ()
        runMkDLL,
 
        touch,                  -- String -> String -> IO ()
        copy,                   -- String -> String -> String -> IO ()
+       normalisePath,          -- FilePath -> FilePath
        
        -- Temporary-file management
        setTmpDir,
        newTempName,
-       cleanTempFiles, cleanTempFilesExcept, removeTmpFiles,
+       cleanTempFiles, cleanTempFilesExcept,
        addFilesToClean,
 
        -- System interface
-       getProcessID,           -- IO Int
-       System.system,          -- String -> IO Int     -- System.system
+       system,                 -- String -> IO ExitCode
 
        -- Misc
-       showGhcUsage,           -- IO ()        Shows usage message and exits
        getSysMan,              -- IO String    Parallel system only
+       
+       Option(..)
 
-       runSomething    -- ToDo: make private
  ) where
 
-import DriverUtil
+#include "HsVersions.h"
+
+import DriverPhases     ( isHaskellUserSrcFilename )
 import Config
 import Outputable
-import Panic           ( progName, GhcException(..) )
-import Util            ( global )
-import CmdLineOpts     ( dynFlag, verbosity )
-
-import List            ( intersperse, isPrefixOf )
-import Exception       ( throwDyn, catchAllIO )
-import IO              ( openFile, hClose, IOMode(..),
-                         hPutStr, hPutChar, hPutStrLn, hFlush, stderr
-                       )
-import Directory       ( doesFileExist, removeFile )
-import IOExts          ( IORef, readIORef, writeIORef )
+import ErrUtils                ( putMsg, debugTraceMsg, showPass, Severity(..), Messages )
+import Panic           ( GhcException(..) )
+import Util            ( Suffix, global, notNull, consIORef, joinFileName,
+                         normalisePath, pgmPath, platformPath, joinFileExt )
+import DynFlags                ( DynFlags(..), DynFlag(..), dopt, Option(..),
+                         setTmpDir, defaultDynFlags )
+
+import EXCEPTION       ( throwDyn, finally )
+import DATA_IOREF      ( IORef, readIORef, writeIORef )
+import DATA_INT
+    
 import Monad           ( when, unless )
-import qualified System
-import System          ( ExitCode(..) )
+import System          ( ExitCode(..), getEnv, system )
+import IO              ( try, catch, hGetContents,
+                         openFile, hPutStr, hClose, hFlush, IOMode(..), 
+                         stderr, ioError, isDoesNotExistError )
+import Directory       ( doesFileExist, removeFile )
+import Maybe           ( isJust )
+import List             ( partition )
 
-#include "../includes/config.h"
+-- GHC <= 4.08 didn't have rawSystem, and runs into problems with long command
+-- lines on mingw32, so we disallow it now.
+#if __GLASGOW_HASKELL__ < 500
+#error GHC >= 5.00 is required for bootstrapping GHC
+#endif
 
-#if !defined(mingw32_TARGET_OS)
+#ifndef mingw32_HOST_OS
+#if __GLASGOW_HASKELL__ > 504
+import qualified System.Posix.Internals
+#else
 import qualified Posix
 #endif
+#else /* Must be Win32 */
+import List            ( isPrefixOf )
+import Util            ( dropList )
+import Foreign
+import CString         ( CString, peekCString )
+#endif
 
-#include "HsVersions.h"
-
-{-# DEPRECATED runSomething "runSomething should be private to SysTools" #-}
-
+#if __GLASGOW_HASKELL__ < 603
+-- rawSystem comes from libghccompat.a in stage1
+import Compat.RawSystem        ( rawSystem )
+import GHC.IOBase       ( IOErrorType(..) ) 
+import System.IO.Error  ( ioeGetErrorType )
+#else
+import System.Process  ( runInteractiveProcess, getProcessExitCode )
+import System.IO        ( hSetBuffering, hGetLine, BufferMode(..) )
+import Control.Concurrent( forkIO, newChan, readChan, writeChan )
+import Text.Regex
+import Data.Char        ( isSpace )
+import FastString       ( mkFastString )
+import SrcLoc           ( SrcLoc, mkSrcLoc, noSrcSpan, mkSrcSpan )
+#endif
 \end{code}
 
 
@@ -105,37 +135,60 @@ Config.hs contains two sorts of things
   etc          They do *not* include paths
                                
 
-  cUNLIT_DIR   The *path* to the directory containing unlit, split etc
-  cSPLIT_DIR   *relative* to the root of the build tree,
-               for use when running *in-place* in a build tree (only)
+  cUNLIT_DIR_REL   The *path* to the directory containing unlit, split etc
+  cSPLIT_DIR_REL   *relative* to the root of the build tree,
+                  for use when running *in-place* in a build tree (only)
                
 
 
+---------------------------------------------
+NOTES for an ALTERNATIVE scheme (i.e *not* what is currently implemented):
+
+Another hair-brained scheme for simplifying the current tool location
+nightmare in GHC: Simon originally suggested using another
+configuration file along the lines of GCC's specs file - which is fine
+except that it means adding code to read yet another configuration
+file.  What I didn't notice is that the current package.conf is
+general enough to do this:
+
+Package
+    {name = "tools",    import_dirs = [],  source_dirs = [],
+     library_dirs = [], hs_libraries = [], extra_libraries = [],
+     include_dirs = [], c_includes = [],   package_deps = [],
+     extra_ghc_opts = ["-pgmc/usr/bin/gcc","-pgml${topdir}/bin/unlit", ... etc.],
+     extra_cc_opts = [], extra_ld_opts = []}
+
+Which would have the advantage that we get to collect together in one
+place the path-specific package stuff with the path-specific tool
+stuff.
+               End of NOTES
+---------------------------------------------
+
+
 %************************************************************************
 %*                                                                     *
 \subsection{Global variables to contain system programs}
 %*                                                                     *
 %************************************************************************
 
+All these pathnames are maintained IN THE NATIVE FORMAT OF THE HOST MACHINE.
+(See remarks under pathnames below)
+
 \begin{code}
-GLOBAL_VAR(v_Pgm_L,    error "pgm_L",   String)        -- unlit
-GLOBAL_VAR(v_Pgm_P,    error "pgm_P",   String)        -- cpp
-GLOBAL_VAR(v_Pgm_c,    error "pgm_c",   String)        -- gcc
-GLOBAL_VAR(v_Pgm_m,    error "pgm_m",   String)        -- asm code mangler
-GLOBAL_VAR(v_Pgm_s,    error "pgm_s",   String)        -- asm code splitter
-GLOBAL_VAR(v_Pgm_a,    error "pgm_a",   String)        -- as
-GLOBAL_VAR(v_Pgm_l,    error "pgm_l",   String)        -- ld
-GLOBAL_VAR(v_Pgm_MkDLL, error "pgm_dll", String)       -- mkdll
-
-GLOBAL_VAR(v_Pgm_PERL, error "pgm_PERL", String)       -- perl
 GLOBAL_VAR(v_Pgm_T,    error "pgm_T",    String)       -- touch
 GLOBAL_VAR(v_Pgm_CP,   error "pgm_CP",          String)        -- cp
 
 GLOBAL_VAR(v_Path_package_config, error "path_package_config", String)
-GLOBAL_VAR(v_Path_usage,         error "ghc_usage.txt",       String)
+GLOBAL_VAR(v_Path_usages,        error "ghc_usage.txt",       (String,String))
+
+GLOBAL_VAR(v_TopDir,   error "TopDir", String)         -- -B<dir>
 
 -- Parallel system only
 GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String)   -- system manager
+
+-- ways to get at some of these variables from outside this module
+getPackageConfigPath = readIORef v_Path_package_config
+getTopDir           = readIORef v_TopDir
 \end{code}
 
 
@@ -146,147 +199,200 @@ GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String)    -- system manager
 %************************************************************************
 
 \begin{code}
-initSysTools :: [String]       -- Command-line arguments starting "-B"
+initSysTools :: Maybe String   -- Maybe TopDir path (without the '-B' prefix)
 
-            -> IO String       -- Set all the mutable variables above, holding 
+            -> DynFlags
+            -> IO DynFlags     -- Set all the mutable variables above, holding 
                                --      (a) the system programs
                                --      (b) the package-config file
                                --      (c) the GHC usage message
-                               -- Return TopDir
 
 
-initSysTools minusB_args
-  = do  { (am_installed, top_dir) <- getTopDir minusB_args
+initSysTools mbMinusB dflags
+  = do  { (am_installed, top_dir) <- findTopDir mbMinusB
+       ; writeIORef v_TopDir top_dir
                -- top_dir
                --      for "installed" this is the root of GHC's support files
                --      for "in-place" it is the root of the build tree
+               -- NB: top_dir is assumed to be in standard Unix format '/' separated
 
-       ; let installed   pgm = top_dir `slash` "extra-bin" `slash` pgm
-             inplace dir pgm = top_dir `slash` dir         `slash` pgm
+       ; let installed, installed_bin :: FilePath -> FilePath
+              installed_bin pgm   =  pgmPath top_dir pgm
+             installed     file  =  pgmPath top_dir file
+             inplace dir   pgm   =  pgmPath (top_dir `joinFileName` 
+                                               cPROJECT_DIR `joinFileName` dir) pgm
 
-       ; let pkgconfig_path | am_installed = top_dir `slash` "package.conf"
-                            | otherwise    = top_dir `slash` cGHC_DRIVER_DIR `slash` "package.conf.inplace"
+       ; let pkgconfig_path
+               | am_installed = installed "package.conf"
+               | otherwise    = inplace cGHC_DRIVER_DIR_REL "package.conf.inplace"
 
-       -- Check that the in-place package config exists if 
-       -- the installed one does not (we need at least one!)
-       ; config_exists <- doesFileExist pkgconfig_path
-       ; if config_exists then return ()
-         else throwDyn (InstallationError 
-                          ("Can't find package.conf in " ++ pkgconfig_path))
+             ghc_usage_msg_path
+               | am_installed = installed "ghc-usage.txt"
+               | otherwise    = inplace cGHC_DRIVER_DIR_REL "ghc-usage.txt"
 
-       -- The GHC usage help message is found similarly to the package configuration
-       ; let ghc_usage_msg_path | am_installed = installed "ghc-usage.txt"
-                                | otherwise    = inplace cGHC_DRIVER_DIR "ghc-usage.txt"
+             ghci_usage_msg_path
+               | am_installed = installed "ghci-usage.txt"
+               | otherwise    = inplace cGHC_DRIVER_DIR_REL "ghci-usage.txt"
 
-
-       -- For all systems, unlit, split, mangle are GHC utilities
-       -- architecture-specific stuff is done when building Config.hs
-       ; let unlit_path  | am_installed = installed cGHC_UNLIT
-                         | otherwise    = inplace cGHC_UNLIT_DIR cGHC_UNLIT
+               -- For all systems, unlit, split, mangle are GHC utilities
+               -- architecture-specific stuff is done when building Config.hs
+             unlit_path
+               | am_installed = installed_bin cGHC_UNLIT_PGM
+               | otherwise    = inplace cGHC_UNLIT_DIR_REL cGHC_UNLIT_PGM
 
                -- split and mangle are Perl scripts
-             split_script  | am_installed = installed cGHC_SPLIT
-                           | otherwise    = inplace cGHC_SPLIT_DIR cGHC_SPLIT
-             mangle_script | am_installed = installed cGHC_MANGLER
-                           | otherwise    = inplace cGHC_MANGLER_DIR cGHC_MANGLER
+             split_script
+               | am_installed = installed_bin cGHC_SPLIT_PGM
+               | otherwise    = inplace cGHC_SPLIT_DIR_REL cGHC_SPLIT_PGM
+
+             mangle_script
+               | am_installed = installed_bin cGHC_MANGLER_PGM
+               | otherwise    = inplace cGHC_MANGLER_DIR_REL cGHC_MANGLER_PGM
+
+       ; let dflags0 = defaultDynFlags
+#ifndef mingw32_HOST_OS
+       -- check whether TMPDIR is set in the environment
+       ; e_tmpdir <- IO.try (getEnv "TMPDIR") -- fails if not set
+#else
+         -- On Win32, consult GetTempPath() for a temp dir.
+         --  => it first tries TMP, TEMP, then finally the
+         --   Windows directory(!). The directory is in short-path
+         --   form.
+       ; e_tmpdir <- 
+            IO.try (do
+               let len = (2048::Int)
+               buf  <- mallocArray len
+               ret  <- getTempPath len buf
+               if ret == 0 then do
+                     -- failed, consult TMPDIR.
+                    free buf
+                    getEnv "TMPDIR"
+                 else do
+                    s <- peekCString buf
+                    free buf
+                    return s)
+#endif
+        ; let dflags1 = case e_tmpdir of
+                         Left _  -> dflags0
+                         Right d -> setTmpDir d dflags0
 
+       -- Check that the package config exists
+       ; config_exists <- doesFileExist pkgconfig_path
+       ; when (not config_exists) $
+            throwDyn (InstallationError 
+                        ("Can't find package.conf as " ++ pkgconfig_path))
 
-#if defined(mingw32_TARGET_OS)
+#if defined(mingw32_HOST_OS)
        --              WINDOWS-SPECIFIC STUFF
        -- On Windows, gcc and friends are distributed with GHC,
        --      so when "installed" we look in TopDir/bin
        -- When "in-place" we look wherever the build-time configure 
        --      script found them
-       ; let cpp_path  | am_installed = installed cRAWCPP
-                       | otherwise    = cRAWCPP
-             gcc_path  | am_installed = installed cGCC
-                       | otherwise    = cGCC
-             perl_path | am_installed = installed cGHC_PERL
+       -- When "install" we tell gcc where its specs file + exes are (-B)
+       --      and also some places to pick up include files.  We need
+       --      to be careful to put all necessary exes in the -B place
+       --      (as, ld, cc1, etc) since if they don't get found there, gcc
+       --      then tries to run unadorned "as", "ld", etc, and will
+       --      pick up whatever happens to be lying around in the path,
+       --      possibly including those from a cygwin install on the target,
+       --      which is exactly what we're trying to avoid.
+       ; let gcc_b_arg = Option ("-B" ++ installed "gcc-lib/")
+             (gcc_prog,gcc_args)
+               | am_installed = (installed_bin "gcc", [gcc_b_arg])
+               | otherwise    = (cGCC, [])
+               -- The trailing "/" is absolutely essential; gcc seems
+               -- to construct file names simply by concatenating to
+               -- this -B path with no extra slash We use "/" rather
+               -- than "\\" because otherwise "\\\" is mangled
+               -- later on; although gcc_args are in NATIVE format,
+               -- gcc can cope
+               --      (see comments with declarations of global variables)
+               --
+               -- The quotes round the -B argument are in case TopDir
+               -- has spaces in it
+
+             perl_path | am_installed = installed_bin cGHC_PERL
                        | otherwise    = cGHC_PERL
 
        -- 'touch' is a GHC util for Windows, and similarly unlit, mangle
-       ; let touch_path  | am_installed = installed cGHC_TOUCHY
-                         | otherwise    = inplace cGHC_TOUCHY_DIR cGHC_TOUCHY
+       ; let touch_path  | am_installed = installed_bin cGHC_TOUCHY_PGM
+                         | otherwise    = inplace cGHC_TOUCHY_DIR_REL cGHC_TOUCHY_PGM
 
        -- On Win32 we don't want to rely on #!/bin/perl, so we prepend 
        -- a call to Perl to get the invocation of split and mangle
-       ; let split_path  = perl_path ++ " " ++ split_script
-             mangle_path = perl_path ++ " " ++ mangle_script
-
-       ; let mkdll_path = cMKDLL
+       ; let (split_prog,  split_args)  = (perl_path, [Option split_script])
+             (mangle_prog, mangle_args) = (perl_path, [Option mangle_script])
+
+       ; let (mkdll_prog, mkdll_args)
+               | am_installed = 
+                   (pgmPath (installed "gcc-lib/") cMKDLL,
+                    [ Option "--dlltool-name",
+                      Option (pgmPath (installed "gcc-lib/") "dlltool"),
+                      Option "--driver-name",
+                      Option gcc_prog, gcc_b_arg ])
+               | otherwise    = (cMKDLL, [])
 #else
        --              UNIX-SPECIFIC STUFF
        -- On Unix, the "standard" tools are assumed to be
        -- in the same place whether we are running "in-place" or "installed"
        -- That place is wherever the build-time configure script found them.
-       ; let   cpp_path   = cRAWCPP
-               gcc_path   = cGCC
-               touch_path = cGHC_TOUCHY
-               perl_path  = cGHC_PERL
-               mkdll_path = panic "Cant build DLLs on a non-Win32 system"
-
-       -- On Unix, for some historical reason, we do an install-time
-       -- configure to find Perl, and slam that on the front of
-       -- the installed script; so we can invoke them directly 
-       -- (not via perl)
-       -- a call to Perl to get the invocation of split and mangle
-       ; let split_path  = split_script
-             mangle_path = mangle_script
-
+       ; let   gcc_prog   = cGCC
+               gcc_args   = []
+               touch_path = "touch"
+               mkdll_prog = panic "Can't build DLLs on a non-Win32 system"
+               mkdll_args = []
+
+       -- On Unix, scripts are invoked using the '#!' method.  Binary
+       -- installations of GHC on Unix place the correct line on the front
+       -- of the script at installation time, so we don't want to wire-in
+       -- our knowledge of $(PERL) on the host system here.
+       ; let (split_prog,  split_args)  = (split_script,  [])
+             (mangle_prog, mangle_args) = (mangle_script, [])
 #endif
 
-       -- For all systems, copy and remove are provided by the host 
+       -- cpp is derived from gcc on all platforms
+        -- HACK, see setPgmP below. We keep 'words' here to remember to fix
+        -- Config.hs one day.
+        ; let cpp_path  = (gcc_prog, gcc_args ++ 
+                          (Option "-E"):(map Option (words cRAWCPP_FLAGS)))
+
+       -- For all systems, copy and remove are provided by the host
        -- system; architecture-specific stuff is done when building Config.hs
        ; let   cp_path = cGHC_CP
        
        -- Other things being equal, as and ld are simply gcc
-       ; let   as_path  = gcc_path
-               ld_path  = gcc_path
+       ; let   (as_prog,as_args)  = (gcc_prog,gcc_args)
+               (ld_prog,ld_args)  = (gcc_prog,gcc_args)
 
-                                      
        -- Initialise the global vars
        ; writeIORef v_Path_package_config pkgconfig_path
-       ; writeIORef v_Path_usage          ghc_usage_msg_path
+       ; writeIORef v_Path_usages         (ghc_usage_msg_path,
+                                           ghci_usage_msg_path)
 
        ; writeIORef v_Pgm_sysman          (top_dir ++ "/ghc/rts/parallel/SysMan")
                -- Hans: this isn't right in general, but you can 
                -- elaborate it in the same way as the others
 
-       ; writeIORef v_Pgm_L               unlit_path
-       ; writeIORef v_Pgm_P               cpp_path
-       ; writeIORef v_Pgm_c               gcc_path
-       ; writeIORef v_Pgm_m               mangle_path
-       ; writeIORef v_Pgm_s               split_path
-       ; writeIORef v_Pgm_a               as_path
-       ; writeIORef v_Pgm_l               ld_path
-       ; writeIORef v_Pgm_MkDLL           mkdll_path
        ; writeIORef v_Pgm_T               touch_path
        ; writeIORef v_Pgm_CP              cp_path
-       ; writeIORef v_Pgm_PERL            perl_path
 
-       ; return top_dir
+       ; return dflags1{
+                       pgm_L   = unlit_path,
+                       pgm_P   = cpp_path,
+                       pgm_F   = "",
+                       pgm_c   = (gcc_prog,gcc_args),
+                       pgm_m   = (mangle_prog,mangle_args),
+                       pgm_s   = (split_prog,split_args),
+                       pgm_a   = (as_prog,as_args),
+                       pgm_l   = (ld_prog,ld_args),
+                       pgm_dll = (mkdll_prog,mkdll_args) }
        }
-\end{code}
 
-setPgm is called when a command-line option like
-       -pgmLld
-is used to override a particular program with a new onw
-
-\begin{code}
-setPgm :: String -> IO ()
--- The string is the flag, minus the '-pgm' prefix
--- So the first character says which program to override
-
-setPgm ('P' : pgm) = writeIORef v_Pgm_P pgm
-setPgm ('c' : pgm) = writeIORef v_Pgm_c pgm
-setPgm ('m' : pgm) = writeIORef v_Pgm_m pgm
-setPgm ('s' : pgm) = writeIORef v_Pgm_s pgm
-setPgm ('a' : pgm) = writeIORef v_Pgm_a pgm
-setPgm ('l' : pgm) = writeIORef v_Pgm_l pgm
-setPgm pgm        = unknownFlagErr ("-pgm" ++ pgm)
+#if defined(mingw32_HOST_OS)
+foreign import stdcall unsafe "GetTempPathA" getTempPath :: Int -> CString -> IO Int32
+#endif
 \end{code}
 
-
 \begin{code}
 -- Find TopDir
 --     for "installed" this is the root of GHC's support files
@@ -294,9 +400,8 @@ setPgm pgm     = unknownFlagErr ("-pgm" ++ pgm)
 --
 -- Plan of action:
 -- 1. Set proto_top_dir
---     a) look for (the last) -B flag, and use it
---     b) if there are no -B flags, get the directory 
---        where GHC is running
+--     if there is no given TopDir path, get the directory 
+--     where GHC is running (only on Windows)
 --
 -- 2. If package.conf exists in proto_top_dir, we are running
 --     installed; and TopDir = proto_top_dir
@@ -307,93 +412,113 @@ setPgm pgm          = unknownFlagErr ("-pgm" ++ pgm)
 --
 -- This is very gruesome indeed
 
-getTopDir :: [String]
-         -> IO (Bool,          -- True <=> am installed, False <=> in-place
-                String)        -- TopDir
-
-getTopDir minusbs
-  = do { proto_top_dir <- get_proto
+findTopDir :: Maybe String   -- Maybe TopDir path (without the '-B' prefix).
+           -> IO (Bool,      -- True <=> am installed, False <=> in-place
+                  String)    -- TopDir (in Unix format '/' separated)
 
-       -- Discover whether we're running in a build tree or in an installation,
+findTopDir mbMinusB
+  = do { top_dir <- get_proto
+        -- Discover whether we're running in a build tree or in an installation,
        -- by looking for the package configuration file.
-       ; am_installed <- doesFileExist (proto_top_dir `slash` "package.conf")
+       ; am_installed <- doesFileExist (top_dir `joinFileName` "package.conf")
 
-       ; if am_installed then
-           return (True, proto_top_dir)
-        else
-           return (False, remove_suffix proto_top_dir)
+       ; return (am_installed, top_dir)
        }
   where
-    get_proto | not (null minusbs) 
-             = return (dosifyPath (drop 2 (last minusbs)))
-             | otherwise          
-             = do { maybe_exec_dir <- getExecDir       -- Get directory of executable
-                  ; case maybe_exec_dir of             -- (only works on Windows)
-                       Nothing  -> throwDyn (InstallationError ("missing -B<dir> option"))
-                       Just dir -> return dir }
-
-    remove_suffix dir  -- "/...stuff.../ghc/compiler" --> "/...stuff..."
-       = ASSERT2( not (null p1) && 
-                  not (null p2) && 
-                  dosifyPath dir == dosifyPath (top_dir ++ "/ghc/compiler"),
-                  text dir )
-         top_dir
-       where
-        p1      = dropWhile (not . isSlash) (reverse dir)
-        p2      = dropWhile (not . isSlash) (tail p1)  -- head is '/'
-        top_dir = reverse (tail p2)                    -- head is '/'
-
-getExecDir = return Nothing
+    -- get_proto returns a Unix-format path (relying on getBaseDir to do so too)
+    get_proto = case mbMinusB of
+                  Just minusb -> return (normalisePath minusb)
+                  Nothing
+                      -> do maybe_exec_dir <- getBaseDir -- Get directory of executable
+                           case maybe_exec_dir of       -- (only works on Windows; 
+                                                         --  returns Nothing on Unix)
+                              Nothing  -> throwDyn (InstallationError "missing -B<dir> option")
+                              Just dir -> return dir
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
 \subsection{Running an external program}
-n%*                                                                    *
+%*                                                                     *
 %************************************************************************
 
 
 \begin{code}
-runUnlit :: [String] -> IO ()
-runUnlit args = do p <- readIORef v_Pgm_L
-                  runSomething "Literate pre-processor" p args
-
-runCpp :: [String] -> IO ()
-runCpp args =   do p <- readIORef v_Pgm_P
-                  runSomething "C pre-processor" p args
-
-runCc :: [String] -> IO ()
-runCc args =   do p <- readIORef v_Pgm_c
-                 runSomething "C Compiler" p args
-
-runMangle :: [String] -> IO ()
-runMangle args = do p <- readIORef v_Pgm_m
-                   runSomething "Mangler" p args
-
-runSplit :: [String] -> IO ()
-runSplit args = do p <- readIORef v_Pgm_s
-                  runSomething "Splitter" p args
-
-runAs :: [String] -> IO ()
-runAs args = do p <- readIORef v_Pgm_a
-               runSomething "Assembler" p args
-
-runLink :: [String] -> IO ()
-runLink args = do p <- readIORef v_Pgm_l
-                 runSomething "Linker" p args
-
-runMkDLL :: [String] -> IO ()
-runMkDLL args = do p <- readIORef v_Pgm_MkDLL
-                  runSomething "Make DLL" p args
-
-touch :: String -> String -> IO ()
-touch purpose arg =  do p <- readIORef v_Pgm_T
-                       runSomething purpose p [arg]
+runUnlit :: DynFlags -> [Option] -> IO ()
+runUnlit dflags args = do 
+  let p = pgm_L dflags
+  runSomething dflags "Literate pre-processor" p args
+
+runCpp :: DynFlags -> [Option] -> IO ()
+runCpp dflags args =   do 
+  let (p,args0) = pgm_P dflags
+  runSomething dflags "C pre-processor" p (args0 ++ args)
+
+runPp :: DynFlags -> [Option] -> IO ()
+runPp dflags args =   do 
+  let p = pgm_F dflags
+  runSomething dflags "Haskell pre-processor" p args
+
+runCc :: DynFlags -> [Option] -> IO ()
+runCc dflags args =   do 
+  let (p,args0) = pgm_c dflags
+  runSomethingFiltered dflags cc_filter "C Compiler" p (args0++args)
+ where
+  -- discard some harmless warnings from gcc that we can't turn off
+  cc_filter str = unlines (do_filter (lines str))
+
+  do_filter [] = []
+  do_filter ls@(l:ls')
+      | (w:rest) <- dropWhile (isJust .matchRegex r_from) ls, 
+        isJust (matchRegex r_warn w)
+      = do_filter rest
+      | otherwise
+      = l : do_filter ls'
+
+  r_from = mkRegex "from.*:[0-9]+"
+  r_warn = mkRegex "warning: call-clobbered register used"
+
+runMangle :: DynFlags -> [Option] -> IO ()
+runMangle dflags args = do 
+  let (p,args0) = pgm_m dflags
+  runSomething dflags "Mangler" p (args0++args)
+
+runSplit :: DynFlags -> [Option] -> IO ()
+runSplit dflags args = do 
+  let (p,args0) = pgm_s dflags
+  runSomething dflags "Splitter" p (args0++args)
+
+runAs :: DynFlags -> [Option] -> IO ()
+runAs dflags args = do 
+  let (p,args0) = pgm_a dflags
+  runSomething dflags "Assembler" p (args0++args)
+
+runLink :: DynFlags -> [Option] -> IO ()
+runLink dflags args = do 
+  let (p,args0) = pgm_l dflags
+  runSomething dflags "Linker" p (args0++args)
+
+runMkDLL :: DynFlags -> [Option] -> IO ()
+runMkDLL dflags args = do
+  let (p,args0) = pgm_dll dflags
+  runSomething dflags "Make DLL" p (args0++args)
+
+touch :: DynFlags -> String -> String -> IO ()
+touch dflags purpose arg =  do 
+  p <- readIORef v_Pgm_T
+  runSomething dflags purpose p [FileOption "" arg]
+
+copy :: DynFlags -> String -> String -> String -> IO ()
+copy dflags purpose from to = do
+  showPass dflags purpose
+
+  h <- openFile to WriteMode
+  ls <- readFile from -- inefficient, but it'll do for now.
+                     -- ToDo: speed up via slurping.
+  hPutStr h ls
+  hClose h
 
-copy :: String -> String -> String -> IO ()
-copy purpose from to = do p <- readIORef v_Pgm_CP
-                         runSomething purpose p [from,to]
 \end{code}
 
 \begin{code}
@@ -402,25 +527,10 @@ getSysMan :: IO String    -- How to invoke the system manager
 getSysMan = readIORef v_Pgm_sysman
 \end{code}
 
-%************************************************************************
-%*                                                                     *
-\subsection{GHC Usage message}
-%*                                                                     *
-%************************************************************************
-
-Show the usage message and exit
-
 \begin{code}
-showGhcUsage = do { usage_path <- readIORef v_Path_usage
-                 ; usage      <- readFile usage_path
-                 ; dump usage
-                 ; System.exitWith System.ExitSuccess }
-  where
-     dump ""         = return ()
-     dump ('$':'$':s) = hPutStr stderr progName >> dump s
-     dump (c:s)              = hPutChar stderr c >> dump s
-
-packageConfigPath = readIORef v_Path_package_config
+getUsageMsgPaths :: IO (FilePath,FilePath)
+         -- the filenames of the usage messages (ghc, ghci)
+getUsageMsgPaths = readIORef v_Path_usages
 \end{code}
 
 
@@ -430,185 +540,277 @@ packageConfigPath = readIORef v_Path_package_config
 %*                                                                     *
 %************************************************************************
 
-One reason this code is here is because SysTools.system needs to make
-a temporary file.
-
 \begin{code}
 GLOBAL_VAR(v_FilesToClean, [],               [String] )
-GLOBAL_VAR(v_TmpDir,       cDEFAULT_TMPDIR,  String   )
-       -- v_TmpDir has no closing '/'
 \end{code}
 
 \begin{code}
-setTmpDir dir = writeIORef v_TmpDir dir
+cleanTempFiles :: DynFlags -> IO ()
+cleanTempFiles dflags
+   = do fs <- readIORef v_FilesToClean
+       removeTmpFiles dflags fs
+       writeIORef v_FilesToClean []
 
-cleanTempFiles :: Int -> IO ()
-cleanTempFiles verb = do fs <- readIORef v_FilesToClean
-                        removeTmpFiles verb fs
-
-cleanTempFilesExcept :: Int -> [FilePath] -> IO ()
-cleanTempFilesExcept verb dont_delete
-  = do fs <- readIORef v_FilesToClean
-       let leftovers = filter (`notElem` dont_delete) fs
-       removeTmpFiles verb leftovers
-       writeIORef v_FilesToClean dont_delete
+cleanTempFilesExcept :: DynFlags -> [FilePath] -> IO ()
+cleanTempFilesExcept dflags dont_delete
+   = do files <- readIORef v_FilesToClean
+       let (to_keep, to_delete) = partition (`elem` dont_delete) files
+       removeTmpFiles dflags to_delete
+       writeIORef v_FilesToClean to_keep
 
 
 -- find a temporary name that doesn't already exist.
-newTempName :: Suffix -> IO FilePath
-newTempName extn
+newTempName :: DynFlags -> Suffix -> IO FilePath
+newTempName DynFlags{tmpDir=tmp_dir} extn
   = do x <- getProcessID
-       tmp_dir <- readIORef v_TmpDir
-       findTempName tmp_dir x
+       findTempName (tmp_dir ++ "/ghc" ++ show x ++ "_") 0
   where 
-    findTempName tmp_dir x
-      = do let filename = tmp_dir ++ "/ghc" ++ show x ++ '.':extn
+    findTempName prefix x
+      = do let filename = (prefix ++ show x) `joinFileExt` extn
           b  <- doesFileExist filename
-          if b then findTempName tmp_dir (x+1)
-               else do add v_FilesToClean filename -- clean it up later
+          if b then findTempName prefix (x+1)
+               else do consIORef v_FilesToClean filename -- clean it up later
                        return filename
 
 addFilesToClean :: [FilePath] -> IO ()
 -- May include wildcards [used by DriverPipeline.run_phase SplitMangle]
-addFilesToClean files = mapM_ (add v_FilesToClean) files
-
-removeTmpFiles :: Int -> [FilePath] -> IO ()
-removeTmpFiles verb fs
-  = traceCmd "Deleting temp files" 
-            ("Deleting: " ++ concat (intersperse " " fs))
-            (mapM_ rm fs)
+addFilesToClean files = mapM_ (consIORef v_FilesToClean) files
+
+removeTmpFiles :: DynFlags -> [FilePath] -> IO ()
+removeTmpFiles dflags fs
+  = warnNon $
+    traceCmd dflags "Deleting temp files" 
+            ("Deleting: " ++ unwords deletees)
+            (mapM_ rm deletees)
   where
-    rm f = removeFile f `catchAllIO`
-               (\exn -> hPutStrLn stderr ("Warning: deleting non-existent " ++ f) >>
-                        return ())
-
-\end{code}
-
-
-%************************************************************************
-%*                                                                     *
-\subsection{Running a program}
-%*                                                                     *
-%************************************************************************
-
-\begin{code}
-GLOBAL_VAR(v_Dry_run, False, Bool)
+     -- Flat out refuse to delete files that are likely to be source input
+     -- files (is there a worse bug than having a compiler delete your source
+     -- files?)
+     -- 
+     -- Deleting source files is a sign of a bug elsewhere, so prominently flag
+     -- the condition.
+    warnNon act
+     | null non_deletees = act
+     | otherwise         = do
+        putMsg dflags (text "WARNING - NOT deleting source files:" <+> hsep (map text non_deletees))
+       act
+
+    (non_deletees, deletees) = partition isHaskellUserSrcFilename fs
+
+    rm f = removeFile f `IO.catch` 
+               (\_ignored -> 
+                   debugTraceMsg dflags 2 (ptext SLIT("Warning: deleting non-existent") <+> text f)
+               )
 
-setDryRun :: IO () 
-setDryRun = writeIORef v_Dry_run True
 
 -----------------------------------------------------------------------------
 -- Running an external program
 
-runSomething :: String         -- For -v message
+runSomething :: DynFlags
+            -> String          -- For -v message
             -> String          -- Command name (possibly a full path)
                                --      assumed already dos-ified
-            -> [String]        -- Arguments
-                               --      runSomthing will dos-ify them
+            -> [Option]        -- Arguments
+                               --      runSomething will dos-ify them
             -> IO ()
 
-runSomething phase_name pgm args
- = traceCmd phase_name cmd_line $
-   do   { exit_code <- System.system cmd_line
-       ; if exit_code /= ExitSuccess
-         then throwDyn (PhaseFailed phase_name exit_code)
-         else return ()
-       }
+runSomething dflags phase_name pgm args = 
+  runSomethingFiltered dflags id phase_name pgm args
+
+runSomethingFiltered
+  :: DynFlags -> (String->String) -> String -> String -> [Option] -> IO ()
+
+runSomethingFiltered dflags filter_fn phase_name pgm args = do
+  let real_args = filter notNull (map showOpt args)
+  traceCmd dflags phase_name (unwords (pgm:real_args)) $ do
+  (exit_code, doesn'tExist) <- 
+     IO.catch (do
+         rc <- builderMainLoop dflags filter_fn pgm real_args
+        case rc of
+          ExitSuccess{} -> return (rc, False)
+          ExitFailure n 
+             -- rawSystem returns (ExitFailure 127) if the exec failed for any
+             -- reason (eg. the program doesn't exist).  This is the only clue
+             -- we have, but we need to report something to the user because in
+             -- the case of a missing program there will otherwise be no output
+             -- at all.
+           | n == 127  -> return (rc, True)
+           | otherwise -> return (rc, False))
+               -- Should 'rawSystem' generate an IO exception indicating that
+               -- 'pgm' couldn't be run rather than a funky return code, catch
+               -- this here (the win32 version does this, but it doesn't hurt
+               -- to test for this in general.)
+              (\ err -> 
+               if IO.isDoesNotExistError err 
+#if defined(mingw32_HOST_OS) && __GLASGOW_HASKELL__ < 604
+               -- the 'compat' version of rawSystem under mingw32 always
+               -- maps 'errno' to EINVAL to failure.
+                  || case (ioeGetErrorType err ) of { InvalidArgument{} -> True ; _ -> False}
+#endif
+                then return (ExitFailure 1, True)
+                else IO.ioError err)
+  case (doesn'tExist, exit_code) of
+     (True, _)        -> throwDyn (InstallationError ("could not execute: " ++ pgm))
+     (_, ExitSuccess) -> return ()
+     _                -> throwDyn (PhaseFailed phase_name exit_code)
+
+
+
+#if __GLASGOW_HASKELL__ < 603
+builderMainLoop dflags filter_fn pgm real_args = do
+  rawSystem pgm real_args
+#else
+builderMainLoop dflags filter_fn pgm real_args = do
+  chan <- newChan
+  (hStdIn, hStdOut, hStdErr, hProcess) <- runInteractiveProcess pgm real_args Nothing Nothing
+
+  -- and run a loop piping the output from the compiler to the log_action in DynFlags
+  hSetBuffering hStdOut LineBuffering
+  hSetBuffering hStdErr LineBuffering
+  forkIO (readerProc chan hStdOut filter_fn)
+  forkIO (readerProc chan hStdErr filter_fn)
+  rc <- loop chan hProcess 2 1 ExitSuccess
+  hClose hStdIn
+  hClose hStdOut
+  hClose hStdErr
+  return rc
   where
-    cmd_line = unwords (pgm : dosifyPaths args)
+    -- status starts at zero, and increments each time either
+    -- a reader process gets EOF, or the build proc exits.  We wait
+    -- for all of these to happen (status==3).
+    -- ToDo: we should really have a contingency plan in case any of
+    -- the threads dies, such as a timeout.
+    loop chan hProcess 0 0 exitcode = return exitcode
+    loop chan hProcess t p exitcode = do
+      mb_code <- if p > 0
+                   then getProcessExitCode hProcess
+                   else return Nothing
+      case mb_code of
+        Just code -> loop chan hProcess t (p-1) code
+       Nothing 
+         | t > 0 -> do 
+             msg <- readChan chan
+              case msg of
+                BuildMsg msg -> do
+                  log_action dflags SevInfo noSrcSpan defaultUserStyle msg
+                  loop chan hProcess t p exitcode
+                BuildError loc msg -> do
+                  log_action dflags SevError (mkSrcSpan loc loc) defaultUserStyle msg
+                  loop chan hProcess t p exitcode
+                EOF ->
+                  loop chan hProcess (t-1) p exitcode
+          | otherwise -> loop chan hProcess t p exitcode
+
+readerProc chan hdl filter_fn =
+    (do str <- hGetContents hdl
+        loop (lines (filter_fn str)) Nothing) 
+    `finally`
+       writeChan chan EOF
+       -- ToDo: check errors more carefully
+       -- ToDo: in the future, the filter should be implemented as
+       -- a stream transformer.
+    where
+       loop []     Nothing    = return ()      
+       loop []     (Just err) = writeChan chan err
+       loop (l:ls) in_err     =
+               case in_err of
+                 Just err@(BuildError srcLoc msg)
+                   | leading_whitespace l -> do
+                       loop ls (Just (BuildError srcLoc (msg $$ text l)))
+                   | otherwise -> do
+                       writeChan chan err
+                       checkError l ls
+                 Nothing -> do
+                       checkError l ls
+
+       checkError l ls
+          = case matchRegex errRegex l of
+               Nothing -> do
+                   writeChan chan (BuildMsg (text l))
+                   loop ls Nothing
+               Just (file':lineno':colno':msg:_) -> do
+                   let file   = mkFastString file'
+                       lineno = read lineno'::Int
+                       colno  = case colno' of
+                                  "" -> 0
+                                  _  -> read (init colno') :: Int
+                       srcLoc = mkSrcLoc file lineno colno
+                   loop ls (Just (BuildError srcLoc (text msg)))
+
+       leading_whitespace []    = False
+       leading_whitespace (x:_) = isSpace x
+
+errRegex = mkRegex "^([^:]*):([0-9]+):([0-9]+:)?(.*)"
+
+data BuildMessage
+  = BuildMsg   !SDoc
+  | BuildError !SrcLoc !SDoc
+  | EOF
+#endif
+
+showOpt (FileOption pre f) = pre ++ platformPath f
+showOpt (Option "") = ""
+showOpt (Option s)  = s
 
-traceCmd :: String -> String -> IO () -> IO ()
+traceCmd :: DynFlags -> String -> String -> IO () -> IO ()
 -- a) trace the command (at two levels of verbosity)
 -- b) don't do it at all if dry-run is set
-traceCmd phase_name cmd_line action
- = do  { verb <- dynFlag verbosity
-       ; when (verb >= 2) $ hPutStrLn stderr ("*** " ++ phase_name)
-       ; when (verb >= 3) $ hPutStrLn stderr cmd_line
+traceCmd dflags phase_name cmd_line action
+ = do  { let verb = verbosity dflags
+       ; showPass dflags phase_name
+       ; debugTraceMsg dflags 3 (text cmd_line)
        ; hFlush stderr
        
           -- Test for -n flag
-       ; n <- readIORef v_Dry_run
-       ; unless n $ do {
+       ; unless (dopt Opt_DryRun dflags) $ do {
 
           -- And run it!
-       ; action `catchAllIO` handle_exn verb
+       ; action `IO.catch` handle_exn verb
        }}
   where
-    handle_exn verb exn = do { when (verb >= 2) (hPutStr   stderr "\n")
-                            ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line))
+    handle_exn verb exn = do { debugTraceMsg dflags 2 (char '\n')
+                            ; debugTraceMsg dflags 2 (ptext SLIT("Failed:") <+> text cmd_line <+> text (show exn))
                             ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
 \end{code}
 
-
 %************************************************************************
 %*                                                                     *
 \subsection{Support code}
 %*                                                                     *
 %************************************************************************
 
-
 \begin{code}
 -----------------------------------------------------------------------------
--- Convert filepath into MSDOS form.
-
-dosifyPaths :: [String] -> [String]
-dosifyPath  :: String -> String
--- dosifyPath does two things
--- a) change '/' to '\'
--- b) remove initial '/cygdrive/'
-
-#if defined(mingw32_TARGET_OS)
-dosifyPaths xs = map dosifyPath xs
-
-dosifyPath stuff
-  = subst '/' '\\' real_stuff
- where
-   -- fully convince myself that /cygdrive/ prefixes cannot
-   -- really appear here.
-  cygdrive_prefix = "/cygdrive/"
-
-  real_stuff
-    | cygdrive_prefix `isPrefixOf` stuff = drop (length cygdrive_prefix) stuff
-    | otherwise = stuff
-   
-  subst a b ls = map (\ x -> if x == a then b else x) ls
-#else
-dosifyPaths xs = xs
-dosifyPath  xs = xs
-#endif
-
------------------------------------------------------------------------------
--- Path name construction
---     At the moment, we always use '/' and rely on dosifyPath 
---     to switch to DOS pathnames when necessary
-
-slash           :: String -> String -> String
-absPath, relPath :: [String] -> String
-
-isSlash '/'   = True
-isSlash '\\'  = True
-isSlash other = False
-
-relPath [] = ""
-relPath xs = foldr1 slash xs
-
-absPath xs = "" `slash` relPath xs
+-- Define      getBaseDir     :: IO (Maybe String)
+
+getBaseDir :: IO (Maybe String)
+#if defined(mingw32_HOST_OS)
+-- Assuming we are running ghc, accessed by path  $()/bin/ghc.exe,
+-- return the path $(stuff).  Note that we drop the "bin/" directory too.
+getBaseDir = do let len = (2048::Int) -- plenty, PATH_MAX is 512 under Win32.
+               buf <- mallocArray len
+               ret <- getModuleFileName nullPtr buf len
+               if ret == 0 then free buf >> return Nothing
+                           else do s <- peekCString buf
+                                   free buf
+                                   return (Just (rootDir s))
+  where
+    rootDir s = reverse (dropList "/bin/ghc.exe" (reverse (normalisePath s)))
 
-#if defined(mingw32_TARGET_OS)
-slash s1 s2 = s1 ++ ('\\' : s2)
+foreign import stdcall unsafe "GetModuleFileNameA"
+  getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
 #else
-slash s1 s2 = s1 ++ ('/' : s2)
+getBaseDir = return Nothing
 #endif
 
------------------------------------------------------------------------------
--- Convert filepath into MSDOS form.
--- 
--- Define      myGetProcessId :: IO Int
-
-#ifdef mingw32_TARGET_OS
-foreign import "_getpid" getProcessID :: IO Int 
+#ifdef mingw32_HOST_OS
+foreign import ccall unsafe "_getpid" getProcessID :: IO Int -- relies on Int == Int32 on Windows
+#elif __GLASGOW_HASKELL__ > 504
+getProcessID :: IO Int
+getProcessID = System.Posix.Internals.c_getpid >>= return . fromIntegral
 #else
 getProcessID :: IO Int
 getProcessID = Posix.getProcessID
 #endif
+
 \end{code}