[project @ 2002-09-01 00:34:21 by mthomas]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index 4e8c0bb..8b2ab35 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
+--
+-- Access to system tools: gcc, cp, rm etc
 --
 -----------------------------------------------------------------------------
 
 module SysTools (
        -- Initialisation
        initSysTools,
-       setPgm,                 -- String -> IO ()
+
+       setPgmP,                -- String -> IO ()
+       setPgmF,
+       setPgmc,
+       setPgmm,
+       setPgms,
+       setPgma,
+       setPgml,
+#ifdef ILX
+       setPgmI,
+       setPgmi,
+#endif
                                -- Command-line override
        setDryRun,
 
-       packageConfigPath,      -- IO String    
-                               -- Where package.conf is
+       getTopDir,              -- IO String    -- The value of $libdir
+       getPackageConfigPath,   -- IO String    -- Where package.conf is
 
        -- 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,
+#ifdef ILX
+        runIlx2il, runIlasm,     -- [String] -> IO ()
+#endif
+
 
        touch,                  -- String -> String -> IO ()
        copy,                   -- String -> String -> String -> IO ()
+       unDosifyPath,           -- String -> String
        
        -- Temporary-file management
        setTmpDir,
@@ -33,36 +51,74 @@ module SysTools (
 
        -- System interface
        getProcessID,           -- IO Int
-       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
 
+#include "HsVersions.h"
+
 import DriverUtil
 import Config
-import Outputable      ( panic )
+import Outputable
 import Panic           ( progName, GhcException(..) )
-import Util            ( global )
+import Util            ( global, notNull )
 import CmdLineOpts     ( dynFlag, verbosity )
 
-import List            ( intersperse )
-import Exception       ( throwDyn, catchAllIO )
-import IO              ( hPutStr, hPutChar, hPutStrLn, hFlush, stderr )
-import Directory       ( doesFileExist, removeFile )
-import IOExts          ( IORef, readIORef, writeIORef )
+import EXCEPTION       ( throwDyn )
+#if __GLASGOW_HASKELL__ > 408
+import qualified EXCEPTION as Exception ( catch )
+#else
+import EXCEPTION        ( catchAllIO )
+#endif
+
+import DATA_IOREF      ( IORef, readIORef, writeIORef )
+import DATA_INT
+    
 import Monad           ( when, unless )
-import qualified System
-import System          ( ExitCode(..) )
-import qualified Posix
+import System          ( ExitCode(..), exitWith, getEnv, system )
+import IO
+import Directory       ( doesFileExist, removeFile )
 
 #include "../includes/config.h"
-#include "HsVersions.h"
 
-{-# DEPRECATED runSomething "runSomething should be private to SysTools" #-}
+-- GHC <= 4.08 didn't have rawSystem, and runs into problems with long command
+-- lines on mingw32, so we disallow it now.
+#if defined(mingw32_HOST_OS) && (__GLASGOW_HASKELL__ <= 408)
+#error GHC <= 4.08 is not supported for bootstrapping GHC on i386-unknown-mingw32
+#endif
+
+#ifndef mingw32_HOST_OS
+import qualified Posix
+#else
+import List            ( isPrefixOf )
+import Util            ( dropList )
+import MarshalArray
+import Foreign
+import Foreign.C.String
+#endif
+
+#ifdef mingw32_HOST_OS
+#if __GLASGOW_HASKELL__ > 504
+import System.Cmd       ( rawSystem )
+#else
+import SystemExts       ( rawSystem )
+#endif
+#else
+import System          ( system )
+#endif
+
+-- Make catch work on older GHCs
+#if __GLASGOW_HASKELL__ > 408
+myCatch = Exception.catch
+#else
+myCatch = catchAllIO
+#endif
 
 \end{code}
 
@@ -99,37 +155,74 @@ 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${libdir}/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_F,    error "pgm_F",   String)        -- pp
 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
+#ifdef ILX
+GLOBAL_VAR(v_Pgm_I,     error "pgm_I",   String)        -- ilx2il
+GLOBAL_VAR(v_Pgm_i,     error "pgm_i",   String)        -- ilasm
+#endif
 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_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}
 
 
@@ -140,90 +233,156 @@ GLOBAL_VAR(v_Pgm_sysman, error "pgm_sysman", String)     -- system manager
 %************************************************************************
 
 \begin{code}
-initSysTools :: String -- TopDir
-                       --      for "installed" this is the root of GHC's support files
-                       --      for "in-place" it is the root of the build tree
-
-            -> IO ()   -- Set all the mutable variables above, holding 
-                       --      (a) the system programs
-                       --      (b) the package-config file
-                       --      (c) the GHC usage message
-
-initSysTools top_dir
-  = do  { let installed   pgm = top_dir `slash` "extra-bin" `slash` pgm
-             inplace dir pgm = top_dir `slash` dir         `slash` pgm
+initSysTools :: [String]       -- Command-line arguments starting "-B"
+
+            -> IO ()           -- Set all the mutable variables above, holding 
+                               --      (a) the system programs
+                               --      (b) the package-config file
+                               --      (c) the GHC usage message
+
+
+initSysTools minusB_args
+  = do  { (am_installed, top_dir) <- findTopDir minusB_args
+       ; 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, installed_bin :: FilePath -> FilePath
+              installed_bin pgm   =  pgmPath top_dir pgm
+             installed     file  =  pgmPath top_dir file
+             inplace dir   pgm   =  pgmPath (top_dir `slash` 
+                                               cPROJECT_DIR `slash` dir) pgm
+
+       ; let pkgconfig_path
+               | am_installed = installed "package.conf"
+               | otherwise    = inplace cGHC_DRIVER_DIR_REL "package.conf.inplace"
+
+             ghc_usage_msg_path
+               | am_installed = installed "ghc-usage.txt"
+               | otherwise    = inplace cGHC_DRIVER_DIR_REL "ghc-usage.txt"
+
+               -- 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_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
+
+#ifndef mingw32_HOST_OS
+       -- check whether TMPDIR is set in the environment
+       ; IO.try (do dir <- getEnv "TMPDIR" -- fails if not set
+                    setTmpDir dir
+                    return ()
+                 )
+#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 and *does* have a trailing backslash.
+       ; IO.try (do
+               let len = (2048::Int)
+               buf  <- mallocArray len
+               ret  <- getTempPath len buf
+               tdir <-
+                 if ret == 0 then do
+                     -- failed, consult TEMP.
+                    free buf
+                    getEnv "TMP"
+                  else do
+                    s <- peekCString buf
+                    free buf
+                    return s
+               let
+                 -- strip the trailing backslash (awful, but 
+                 -- we only do this once).
+                 tmpdir =
+                   case last tdir of
+                     '/'  -> init tdir
+                     '\\' -> init tdir
+                     _   -> tdir
+               setTmpDir tmpdir
+               return ())
+#endif
 
-             installed_pkgconfig = installed "package.conf"
-             inplace_pkgconfig   = inplace cGHC_DRIVER_DIR "package.conf.inplace"
+       -- 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))
 
-       -- Discover whether we're running in a build tree or in an installation,
-       -- by looking for the package configuration file.
-       ; am_installed <- doesFileExist installed_pkgconfig
-
-       -- Check that the in-place package config exists if 
-       -- the installed one does not (we need at least one!)
-       ; if am_installed then return () else
-         do config_exists <- doesFileExist inplace_pkgconfig
-            if config_exists then return () else
-               throwDyn (InstallationError 
-                            ("Can't find package.conf in " ++ 
-                             inplace_pkgconfig))
-
-       ; let pkgconfig_path | am_installed = installed_pkgconfig
-                            | otherwise    = inplace_pkgconfig
-                                       
-       -- 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"
-
-
-#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
+       -- 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_path  | am_installed = installed_bin ("gcc -B\"" ++ installed "gcc-lib/\"")
                        | otherwise    = cGCC
-             perl_path | am_installed = installed cGHC_PERL
+               -- 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_path is 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 mkdll_path = cMKDLL
+       ; 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 
+               | am_installed = pgmPath (installed "gcc-lib/") cMKDLL ++
+                                " --dlltool-name " ++ pgmPath (installed "gcc-lib/") "dlltool" ++
+                                " --driver-name " ++ gcc_path
+               | 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"
+       ; let   gcc_path   = cGCC
+               touch_path = "touch"
+               mkdll_path = panic "Can't build DLLs on a non-Win32 system"
+
+       -- 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_path  = split_script
+             mangle_path = mangle_script
 #endif
 
-       -- For all systems, unlit, split, mangle are GHC utilities
-       -- architecture-specific stuff is done when building Config.hs
-       --
-       -- However split and mangle are Perl scripts, and on Win32 at least
-       -- we don't want to rely on #!/bin/perl, so we prepend a call to Perl
-       ; let unlit_path  | am_installed = installed cGHC_UNLIT
-                         | otherwise    = inplace cGHC_UNLIT_DIR cGHC_UNLIT
-
-             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
+       -- cpp is derived from gcc on all platforms
+        ; let cpp_path  = gcc_path ++ " -E " ++ cRAWCPP_FLAGS
 
-             split_path  = perl_path ++ " " ++ split_script
-             mangle_path = perl_path ++ " " ++ mangle_script
-
-       -- For all systems, copy and remove are provided by the host 
+       -- 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
        
@@ -231,6 +390,11 @@ initSysTools top_dir
        ; let   as_path  = gcc_path
                ld_path  = gcc_path
 
+#ifdef ILX
+       -- ilx2il and ilasm are specified in Config.hs
+       ; let    ilx2il_path = cILX2IL
+               ilasm_path  = cILASM
+#endif
                                       
        -- Initialise the global vars
        ; writeIORef v_Path_package_config pkgconfig_path
@@ -242,85 +406,193 @@ initSysTools top_dir
 
        ; writeIORef v_Pgm_L               unlit_path
        ; writeIORef v_Pgm_P               cpp_path
+       ; writeIORef v_Pgm_F               ""
        ; writeIORef v_Pgm_c               gcc_path
        ; writeIORef v_Pgm_m               mangle_path
        ; writeIORef v_Pgm_s               split_path
        ; writeIORef v_Pgm_a               as_path
+#ifdef ILX
+       ; writeIORef v_Pgm_I               ilx2il_path
+       ; writeIORef v_Pgm_i               ilasm_path
+#endif
        ; 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 ()
        }
+
+#if defined(mingw32_HOST_OS)
+foreign import stdcall "GetTempPathA" unsafe getTempPath :: Int -> CString -> IO Int32
+#endif
 \end{code}
 
-setPgm is called when a command-line option like
+The various setPgm functions are called when a command-line option
+like
+
        -pgmLld
-is used to override a particular program with a new onw
+
+is used to override a particular program with a new one
+
+\begin{code}
+setPgmP = writeIORef v_Pgm_P
+setPgmF = writeIORef v_Pgm_F
+setPgmc = writeIORef v_Pgm_c
+setPgmm = writeIORef v_Pgm_m
+setPgms = writeIORef v_Pgm_s
+setPgma = writeIORef v_Pgm_a
+setPgml = writeIORef v_Pgm_l
+#ifdef ILX
+setPgmI = writeIORef v_Pgm_I
+setPgmi = writeIORef v_Pgm_i
+#endif
+\end{code}
+
+
+\begin{code}
+-- Find TopDir
+--     for "installed" this is the root of GHC's support files
+--     for "in-place" it is the root of the build tree
+--
+-- 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 (only on Windows)
+--
+-- 2. If package.conf exists in proto_top_dir, we are running
+--     installed; and TopDir = proto_top_dir
+--
+-- 3. Otherwise we are running in-place, so
+--     proto_top_dir will be /...stuff.../ghc/compiler
+--     Set TopDir to /...stuff..., which is the root of the build tree
+--
+-- This is very gruesome indeed
+
+findTopDir :: [String]
+         -> IO (Bool,          -- True <=> am installed, False <=> in-place
+                String)        -- TopDir (in Unix format '/' separated)
+
+findTopDir minusbs
+  = 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 (top_dir `slash` "package.conf")
+
+       ; return (am_installed, top_dir)
+       }
+  where
+    -- get_proto returns a Unix-format path (relying on getExecDir to do so too)
+    get_proto | notNull minusbs
+             = return (unDosifyPath (drop 2 (last minusbs)))   -- 2 for "-B"
+             | otherwise          
+             = do { maybe_exec_dir <- getExecDir -- 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{Command-line options}
+n%*                                                                    *
+%************************************************************************
+
+When invoking external tools as part of the compilation pipeline, we
+pass these a sequence of options on the command-line. Rather than
+just using a list of Strings, we use a type that allows us to distinguish
+between filepaths and 'other stuff'. [The reason being, of course, that
+this type gives us a handle on transforming filenames, and filenames only,
+to whatever format they're expected to be on a particular platform.]
 
 \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)
+data Option
+ = FileOption -- an entry that _contains_ filename(s) / filepaths.
+              String  -- a non-filepath prefix that shouldn't be transformed (e.g., "/out=" 
+             String  -- the filepath/filename portion
+ | Option     String
+showOptions :: [Option] -> String
+showOptions ls = unwords (map (quote.showOpt) ls)
+ where
+   showOpt (FileOption pre f) = pre ++ dosifyPath f
+   showOpt (Option s)     = s
+
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
 \subsection{Running an external program}
-n%*                                                                    *
+%*                                                                     *
 %************************************************************************
 
 
 \begin{code}
-runUnlit :: [String] -> IO ()
+runUnlit :: [Option] -> IO ()
 runUnlit args = do p <- readIORef v_Pgm_L
                   runSomething "Literate pre-processor" p args
 
-runCpp :: [String] -> IO ()
+runCpp :: [Option] -> IO ()
 runCpp args =   do p <- readIORef v_Pgm_P
                   runSomething "C pre-processor" p args
 
-runCc :: [String] -> IO ()
+runPp :: [Option] -> IO ()
+runPp args =   do p <- readIORef v_Pgm_F
+                 runSomething "Haskell pre-processor" p args
+
+runCc :: [Option] -> IO ()
 runCc args =   do p <- readIORef v_Pgm_c
                  runSomething "C Compiler" p args
 
-runMangle :: [String] -> IO ()
+runMangle :: [Option] -> IO ()
 runMangle args = do p <- readIORef v_Pgm_m
                    runSomething "Mangler" p args
 
-runSplit :: [String] -> IO ()
+runSplit :: [Option] -> IO ()
 runSplit args = do p <- readIORef v_Pgm_s
                   runSomething "Splitter" p args
 
-runAs :: [String] -> IO ()
+runAs :: [Option] -> IO ()
 runAs args = do p <- readIORef v_Pgm_a
                runSomething "Assembler" p args
 
-runLink :: [String] -> IO ()
+runLink :: [Option] -> IO ()
 runLink args = do p <- readIORef v_Pgm_l
                  runSomething "Linker" p args
 
-runMkDLL :: [String] -> IO ()
+#ifdef ILX
+runIlx2il :: [Option] -> IO ()
+runIlx2il args = do p <- readIORef v_Pgm_I
+                   runSomething "Ilx2Il" p args
+
+runIlasm :: [Option] -> IO ()
+runIlasm args = do p <- readIORef v_Pgm_i
+                  runSomething "Ilasm" p args
+#endif
+
+runMkDLL :: [Option] -> 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]
+                       runSomething purpose p [FileOption "" arg]
 
 copy :: String -> String -> String -> IO ()
-copy purpose from to = do p <- readIORef v_Pgm_CP
-                         runSomething purpose p [from,to]
+copy purpose from to = do
+  verb <- dynFlag verbosity
+  when (verb >= 2) $ hPutStrLn stderr ("*** " ++ 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
 \end{code}
 
 \begin{code}
@@ -341,13 +613,11 @@ Show the usage message and exit
 showGhcUsage = do { usage_path <- readIORef v_Path_usage
                  ; usage      <- readFile usage_path
                  ; dump usage
-                 ; System.exitWith System.ExitSuccess }
+                 ; exitWith 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
 \end{code}
 
 
@@ -357,9 +627,6 @@ 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   )
@@ -402,12 +669,14 @@ addFilesToClean files = mapM_ (add v_FilesToClean) files
 removeTmpFiles :: Int -> [FilePath] -> IO ()
 removeTmpFiles verb fs
   = traceCmd "Deleting temp files" 
-            ("Deleting: " ++ concat (intersperse " " fs))
+            ("Deleting: " ++ unwords fs)
             (mapM_ rm fs)
   where
-    rm f = removeFile f `catchAllIO`
-               (\exn -> hPutStrLn stderr ("Warning: deleting non-existent " ++ f) >>
-                        return ())
+    rm f = removeFile f `myCatch` 
+               (\_ignored -> 
+                   when (verb >= 2) $
+                     hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
+               )
 
 \end{code}
 
@@ -430,19 +699,26 @@ setDryRun = writeIORef v_Dry_run True
 runSomething :: 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 cmd_line
+   do   {
+#ifndef mingw32_HOST_OS
+         exit_code <- system cmd_line
+#else
+          exit_code <- rawSystem cmd_line
+#endif
        ; if exit_code /= ExitSuccess
          then throwDyn (PhaseFailed phase_name exit_code)
          else return ()
        }
   where
-    cmd_line = unwords (pgm : dosifyPaths args)
+       -- The pgm is already in native format (appropriate dir separators)
+    cmd_line = pgm ++ ' ':showOptions args 
+                -- unwords (pgm : dosifyPaths (map quote args))
 
 traceCmd :: String -> String -> IO () -> IO ()
 -- a) trace the command (at two levels of verbosity)
@@ -458,7 +734,7 @@ traceCmd phase_name cmd_line action
        ; unless n $ do {
 
           -- And run it!
-       ; action `catchAllIO` handle_exn verb
+       ; action `myCatch` handle_exn verb
        }}
   where
     handle_exn verb exn = do { when (verb >= 2) (hPutStr   stderr "\n")
@@ -469,24 +745,47 @@ traceCmd phase_name cmd_line action
 
 %************************************************************************
 %*                                                                     *
-\subsection{Support code}
+\subsection{Path names}
 %*                                                                     *
 %************************************************************************
 
+We maintain path names in Unix form ('/'-separated) right until 
+the last moment.  On Windows we dos-ify them just before passing them
+to the Windows command.
+
+The alternative, of using '/' consistently on Unix and '\' on Windows,
+proved quite awkward.  There were a lot more calls to dosifyPath,
+and even on Windows we might invoke a unix-like utility (eg 'sh'), which
+interpreted a command line 'foo\baz' as 'foobaz'.
 
 \begin{code}
 -----------------------------------------------------------------------------
 -- Convert filepath into MSDOS form.
 
 dosifyPaths :: [String] -> [String]
--- dosifyPath does two things
+-- dosifyPaths does two things
 -- a) change '/' to '\'
 -- b) remove initial '/cygdrive/'
 
-#if defined(mingw32_TARGET_OS) && defined(MINIMAL_UNIX_DEPS)
+unDosifyPath :: String -> String
+-- Just change '\' to '/'
+
+pgmPath :: String              -- Directory string in Unix format
+       -> String               -- Program name with no directory separators
+                               --      (e.g. copy /y)
+       -> String               -- Program invocation string in native format
+
+
+
+#if defined(mingw32_HOST_OS)
+
+--------------------- Windows version ------------------
 dosifyPaths xs = map dosifyPath xs
 
-dosifyPath  :: String -> String
+unDosifyPath xs = subst '\\' '/' xs
+
+pgmPath dir pgm = dosifyPath dir ++ '\\' : pgm
+
 dosifyPath stuff
   = subst '/' '\\' real_stuff
  where
@@ -495,70 +794,79 @@ dosifyPath stuff
   cygdrive_prefix = "/cygdrive/"
 
   real_stuff
-    | cygdrive_prefix `isPrefixOf` stuff = drop (length cygdrive_prefix) stuff
+    | cygdrive_prefix `isPrefixOf` stuff = dropList cygdrive_prefix stuff
     | otherwise = stuff
    
-  subst a b ls = map (\ x -> if x == a then b else x) ls
 #else
-dosifyPaths xs = xs
+
+--------------------- Unix version ---------------------
+dosifyPaths  ps  = ps
+unDosifyPath xs  = xs
+pgmPath dir pgm  = dir ++ '/' : pgm
+dosifyPath stuff = stuff
+--------------------------------------------------------
 #endif
 
+subst a b ls = map (\ x -> if x == a then b else x) ls
+\end{code}
+
+
 -----------------------------------------------------------------------------
--- Path name construction
---     At the moment, we always use '/' and rely on dosifyPath 
---     to switch to DOS pathnames when necessary
+   Path name construction
 
+\begin{code}
 slash           :: String -> String -> String
 absPath, relPath :: [String] -> String
 
-slash s1 s2 = s1 ++ ('/' : s2)
-
-
 relPath [] = ""
 relPath xs = foldr1 slash xs
 
 absPath xs = "" `slash` relPath xs
 
------------------------------------------------------------------------------
--- Convert filepath into MSDOS form.
--- 
--- Define      myGetProcessId :: IO Int
-
-#ifdef mingw32_TARGET_OS
-foreign import "_getpid" getProcessID :: IO Int 
-#else
-getProcessID :: IO Int
-getProcessID = Posix.getProcessID
-#endif
+slash s1 s2 = s1 ++ ('/' : s2)
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
-\subsection{System}
+\subsection{Support code}
 %*                                                                     *
 %************************************************************************
 
--- This procedure executes system calls.  In pre-GHC-5.00 and earlier, 
--- the System.system implementation didn't work, so this acts as a fix-up
--- by passing the command line to 'sh'.
 \begin{code}
-system :: String -> IO ExitCode
-system cmd
- = do
-#if !defined(mingw32_TARGET_OS)
-    -- in the case where we do want to use an MSDOS command shell, we assume
-    -- that files and paths have been converted to a form that's
-    -- understandable to the command we're invoking.
-   System.system cmd
+-----------------------------------------------------------------------------
+-- Define      getExecDir     :: IO (Maybe String)
+
+#if defined(mingw32_HOST_OS)
+getExecDir :: IO (Maybe String)
+getExecDir = 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 (reverse (dropList "/bin/ghc.exe" (reverse (unDosifyPath s)))))
+
+
+foreign import stdcall "GetModuleFileNameA" unsafe 
+  getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
+#else
+getExecDir :: IO (Maybe String) = do return Nothing
+#endif
+
+#ifdef mingw32_HOST_OS
+foreign import "_getpid" unsafe getProcessID :: IO Int -- relies on Int == Int32 on Windows
+#else
+getProcessID :: IO Int
+getProcessID = Posix.getProcessID
+#endif
+
+quote :: String -> String
+#if defined(mingw32_HOST_OS)
+quote "" = ""
+quote s  = "\"" ++ s ++ "\""
 #else
-   tmp <- newTempName "sh"
-   h   <- openFile tmp WriteMode
-   hPutStrLn h cmd
-   hClose h
-   exit_code <- system ("sh - " ++ tmp) `catchAllIO` 
-                      (\exn -> removeFile tmp >> ioError exn)
-   removeFile tmp
-   return exit_code
+quote s = s
 #endif
+
 \end{code}