[project @ 2005-03-18 17:17:02 by simonpj]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index ddbafe0..9710bcb 100644 (file)
@@ -11,23 +11,7 @@ module SysTools (
        -- Initialisation
        initSysTools,
 
-       setPgmL,                -- String -> IO ()
-       setPgmP,
-       setPgmF,
-       setPgmc,
-       setPgmm,
-       setPgms,
-       setPgma,
-       setPgml,
-       setPgmDLL,
-#ifdef ILX
-       setPgmI,
-       setPgmi,
-#endif
-                               -- Command-line override
-       setDryRun,
-
-       getTopDir,              -- IO String    -- The value of $libdir
+       getTopDir,              -- IO String    -- The value of $topdir
        getPackageConfigPath,   -- IO String    -- Where package.conf is
         getUsageMsgPaths,       -- IO (String,String)
 
@@ -37,10 +21,6 @@ module SysTools (
        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 ()
@@ -64,13 +44,12 @@ module SysTools (
 
 #include "HsVersions.h"
 
-import DriverUtil
 import DriverPhases     ( isHaskellUserSrcFilename )
 import Config
 import Outputable
 import Panic           ( GhcException(..) )
-import Util            ( global, notNull, toArgs )
-import CmdLineOpts     ( dynFlag, verbosity )
+import Util            ( Suffix, global, notNull, consIORef )
+import DynFlags                ( DynFlags(..), DynFlag(..), dopt, Option(..) )
 
 import EXCEPTION       ( throwDyn )
 import DATA_IOREF      ( IORef, readIORef, writeIORef )
@@ -84,8 +63,6 @@ import IO             ( try, catch,
 import Directory       ( doesFileExist, removeFile )
 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
@@ -106,8 +83,8 @@ import CString               ( CString, peekCString )
 #endif
 
 #if __GLASGOW_HASKELL__ < 603
-import Foreign         ( withMany, withArray0, nullPtr, Ptr )
-import CForeign                ( CString, withCString, throwErrnoIfMinus1 )
+-- rawSystem comes from libghccompat.a in stage1
+import Compat.RawSystem        ( rawSystem )
 #else
 import System.Cmd      ( rawSystem )
 #endif
@@ -166,7 +143,7 @@ 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_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
@@ -186,20 +163,6 @@ 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,[Option]))     -- cpp
-GLOBAL_VAR(v_Pgm_F,    error "pgm_F",   String)        -- pp
-GLOBAL_VAR(v_Pgm_c,    error "pgm_c",   (String,[Option])) -- gcc
-GLOBAL_VAR(v_Pgm_m,    error "pgm_m",   (String,[Option])) -- asm code mangler
-GLOBAL_VAR(v_Pgm_s,    error "pgm_s",   (String,[Option])) -- asm code splitter
-GLOBAL_VAR(v_Pgm_a,    error "pgm_a",   (String,[Option])) -- 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,[Option])) -- ld
-GLOBAL_VAR(v_Pgm_MkDLL, error "pgm_dll", (String,[Option])) -- mkdll
-
 GLOBAL_VAR(v_Pgm_T,    error "pgm_T",    String)       -- touch
 GLOBAL_VAR(v_Pgm_CP,   error "pgm_CP",          String)        -- cp
 
@@ -226,13 +189,14 @@ getTopDir      = readIORef v_TopDir
 \begin{code}
 initSysTools :: [String]       -- Command-line arguments starting "-B"
 
-            -> IO ()           -- 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
 
 
-initSysTools minusB_args
+initSysTools minusB_args dflags
   = do  { (am_installed, top_dir) <- findTopDir minusB_args
        ; writeIORef v_TopDir top_dir
                -- top_dir
@@ -388,12 +352,6 @@ initSysTools minusB_args
        ; let   (as_prog,as_args)  = (gcc_prog,gcc_args)
                (ld_prog,ld_args)  = (gcc_prog,gcc_args)
 
-#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
        ; writeIORef v_Path_usages         (ghc_usage_msg_path,
@@ -403,56 +361,26 @@ initSysTools minusB_args
                -- 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_F               ""
-       ; writeIORef v_Pgm_c               (gcc_prog,gcc_args)
-       ; writeIORef v_Pgm_m               (mangle_prog,mangle_args)
-       ; writeIORef v_Pgm_s               (split_prog,split_args)
-       ; writeIORef v_Pgm_a               (as_prog,as_args)
-#ifdef ILX
-       ; writeIORef v_Pgm_I               ilx2il_path
-       ; writeIORef v_Pgm_i               ilasm_path
-#endif
-       ; writeIORef v_Pgm_l               (ld_prog,ld_args)
-       ; writeIORef v_Pgm_MkDLL           (mkdll_prog,mkdll_args)
        ; writeIORef v_Pgm_T               touch_path
        ; writeIORef v_Pgm_CP              cp_path
 
-       ; return ()
+       ; return dflags{
+                       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) }
        }
 
 #if defined(mingw32_HOST_OS)
-foreign import stdcall "GetTempPathA" unsafe getTempPath :: Int -> CString -> IO Int32
+foreign import stdcall unsafe "GetTempPathA" getTempPath :: Int -> CString -> IO Int32
 #endif
 \end{code}
 
-The various setPgm functions are called when a command-line option
-like
-
-       -pgmLld
-
-is used to override a particular program with a new one
-
-\begin{code}
-setPgmL = writeIORef v_Pgm_L
--- XXX HACK: Prelude> words "'does not' work" ===> ["'does","not'","work"]
--- Config.hs should really use Option.
-setPgmP arg = let (pgm:args) = words arg in writeIORef v_Pgm_P (pgm,map Option args)
-setPgmF = writeIORef v_Pgm_F
-setPgmc prog = writeIORef v_Pgm_c (prog,[])
-setPgmm prog = writeIORef v_Pgm_m (prog,[])
-setPgms prog = writeIORef v_Pgm_s (prog,[])
-setPgma prog = writeIORef v_Pgm_a (prog,[])
-setPgml prog = writeIORef v_Pgm_l (prog,[])
-setPgmDLL prog = writeIORef v_Pgm_MkDLL (prog,[])
-#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
@@ -501,99 +429,72 @@ findTopDir minusbs
 
 %************************************************************************
 %*                                                                     *
-\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}
-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
-showOpt (FileOption pre f) = pre ++ platformPath f
-showOpt (Option "") = ""
-showOpt (Option s)  = s
-
-\end{code}
-
-
-%************************************************************************
-%*                                                                     *
 \subsection{Running an external program}
 %*                                                                     *
 %************************************************************************
 
 
 \begin{code}
-runUnlit :: [Option] -> IO ()
-runUnlit args = do p <- readIORef v_Pgm_L
-                  runSomething "Literate pre-processor" p args
-
-runCpp :: [Option] -> IO ()
-runCpp args =   do (p,baseArgs) <- readIORef v_Pgm_P
-                  runSomething "C pre-processor" p (baseArgs ++ args)
-
-runPp :: [Option] -> IO ()
-runPp args =   do p <- readIORef v_Pgm_F
-                 runSomething "Haskell pre-processor" p args
-
-runCc :: [Option] -> IO ()
-runCc args =   do (p,args0) <- readIORef v_Pgm_c
-                 runSomething "C Compiler" p (args0++args)
-
-runMangle :: [Option] -> IO ()
-runMangle args = do (p,args0) <- readIORef v_Pgm_m
-                   runSomething "Mangler" p (args0++args)
-
-runSplit :: [Option] -> IO ()
-runSplit args = do (p,args0) <- readIORef v_Pgm_s
-                  runSomething "Splitter" p (args0++args)
-
-runAs :: [Option] -> IO ()
-runAs args = do (p,args0) <- readIORef v_Pgm_a
-               runSomething "Assembler" p (args0++args)
-
-runLink :: [Option] -> IO ()
-runLink args = do (p,args0) <- readIORef v_Pgm_l
-                 runSomething "Linker" p (args0++args)
-
-#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,args0) <- readIORef v_Pgm_MkDLL
-                  runSomething "Make DLL" p (args0++args)
-
-touch :: String -> String -> IO ()
-touch purpose arg =  do p <- readIORef v_Pgm_T
-                       runSomething purpose p [FileOption "" arg]
-
-copy :: String -> String -> String -> IO ()
-copy purpose from to = do
-  verb <- dynFlag verbosity
-  when (verb >= 2) $ hPutStrLn stderr ("*** " ++ purpose)
+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
+  runSomething dflags "C Compiler" p (args0++args)
+
+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
+  when (verbosity dflags >= 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}
@@ -653,17 +554,17 @@ setTmpDir dir = writeIORef v_TmpDir (canonicalise dir)
          _    -> path
 #endif
 
-cleanTempFiles :: Int -> IO ()
-cleanTempFiles verb
+cleanTempFiles :: DynFlags -> IO ()
+cleanTempFiles dflags
    = do fs <- readIORef v_FilesToClean
-       removeTmpFiles verb fs
+       removeTmpFiles dflags fs
        writeIORef v_FilesToClean []
 
-cleanTempFilesExcept :: Int -> [FilePath] -> IO ()
-cleanTempFilesExcept verb 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 verb to_delete
+       removeTmpFiles dflags to_delete
        writeIORef v_FilesToClean to_keep
 
 
@@ -678,20 +579,22 @@ newTempName extn
       = do let filename = tmp_dir ++ "/ghc" ++ show x ++ '.':extn
           b  <- doesFileExist filename
           if b then findTempName tmp_dir (x+1)
-               else do add v_FilesToClean filename -- clean it up later
+               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
+addFilesToClean files = mapM_ (consIORef v_FilesToClean) files
 
-removeTmpFiles :: Int -> [FilePath] -> IO ()
-removeTmpFiles verb fs
+removeTmpFiles :: DynFlags -> [FilePath] -> IO ()
+removeTmpFiles dflags fs
   = warnNon $
-    traceCmd "Deleting temp files" 
+    traceCmd dflags "Deleting temp files" 
             ("Deleting: " ++ unwords deletees)
             (mapM_ rm deletees)
   where
+    verb = verbosity dflags
+
      -- 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?)
@@ -712,51 +615,50 @@ removeTmpFiles verb fs
                      hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
                )
 
-\end{code}
-
-
-%************************************************************************
-%*                                                                     *
-\subsection{Running a program}
-%*                                                                     *
-%************************************************************************
-
-\begin{code}
-GLOBAL_VAR(v_Dry_run, False, Bool)
-
-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
             -> [Option]        -- Arguments
                                --      runSomething will dos-ify them
             -> IO ()
 
-runSomething phase_name pgm args = do
+runSomething dflags phase_name pgm args = do
   let real_args = filter notNull (map showOpt args)
-  traceCmd phase_name (unwords (pgm:real_args)) $ do
+  traceCmd dflags phase_name (unwords (pgm:real_args)) $ do
   exit_code <- rawSystem pgm real_args
-  if (exit_code /= ExitSuccess)
-       then throwDyn (PhaseFailed phase_name exit_code)
-       else return ()
+  case exit_code of
+     ExitSuccess -> 
+       return ()
+     -- 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.
+     ExitFailure 127 -> 
+       throwDyn (InstallationError ("could not execute: " ++ pgm))
+     ExitFailure _other ->
+       throwDyn (PhaseFailed phase_name exit_code)
+
+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
+traceCmd dflags phase_name cmd_line action
+ = do  { let verb = verbosity dflags
        ; when (verb >= 2) $ hPutStrLn stderr ("*** " ++ phase_name)
        ; when (verb >= 3) $ hPutStrLn stderr 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 `IO.catch` handle_exn verb
@@ -765,75 +667,6 @@ traceCmd phase_name cmd_line action
     handle_exn verb exn = do { when (verb >= 2) (hPutStr   stderr "\n")
                             ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line ++ (show exn)))
                             ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
-
--- -----------------------------------------------------------------------------
--- rawSystem: run an external command
---
--- In GHC 6.2.1 there's a correct implementation of rawSystem in the
--- library System.Cmd.  If we are compiling with an earlier version of
--- GHC than this, we'd better have a copy of the correct implementation
--- right here.
-
--- If you ever alter this code, you must alter 
---     libraries/base/System/Cmd.hs
--- at the same time!  There are also exensive comments in System.Cmd
--- thare are not repeated here -- go look!
-
-
-#if __GLASGOW_HASKELL__ < 603
-
-rawSystem :: FilePath -> [String] -> IO ExitCode
-
-#ifndef mingw32_TARGET_OS
-
-rawSystem cmd args =
-  withCString cmd $ \pcmd ->
-    withMany withCString (cmd:args) $ \cstrs ->
-      withArray0 nullPtr cstrs $ \arr -> do
-       status <- throwErrnoIfMinus1 "rawSystem" (c_rawSystem pcmd arr)
-        case status of
-            0  -> return ExitSuccess
-            n  -> return (ExitFailure n)
-
-foreign import ccall "rawSystem" unsafe
-  c_rawSystem :: CString -> Ptr CString -> IO Int
-
-#else
-
--- On Windows, the command line is passed to the operating system as
--- a single string.  Command-line parsing is done by the executable
--- itself.
-rawSystem cmd args = do
-       -- NOTE: 'cmd' is assumed to contain the application to run _only_,
-       -- as it'll be surrounded in quotes here.
-  let cmdline = translate cmd ++ concat (map ((' ':) . translate) args)
-  withCString cmdline $ \pcmdline -> do
-    status <- throwErrnoIfMinus1 "rawSystem" (c_rawSystem pcmdline)
-    case status of
-       0  -> return ExitSuccess
-       n  -> return (ExitFailure n)
-
-translate :: String -> String
-translate str = '"' : snd (foldr escape (True,"\"") str)
-  where escape '"'  (b,     str) = (True,  '\\' : '"'  : str)
-        escape '\\' (True,  str) = (True,  '\\' : '\\' : str)
-        escape '\\' (False, str) = (False, '\\' : str)
-       escape c    (b,     str) = (False, c : str)
-       -- This function attempts to invert the Microsoft C runtime's
-       -- quoting rules, which can be found here:
-       --     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/progs_12.asp
-       -- (if this URL stops working, you might be able to find it by
-       -- searching for "Parsing C Command-Line Arguments" on MSDN).
-       --
-       -- The Bool passed back along the string is True iff the
-       -- rest of the string is a sequence of backslashes followed by
-       -- a double quote.
-
-foreign import ccall "rawSystem" unsafe
-  c_rawSystem :: CString -> IO Int
-
-#endif
-#endif
 \end{code}
 
 
@@ -917,14 +750,14 @@ getBaseDir = do let len = (2048::Int) -- plenty, PATH_MAX is 512 under Win32.
   where
     rootDir s = reverse (dropList "/bin/ghc.exe" (reverse (normalisePath s)))
 
-foreign import stdcall "GetModuleFileNameA" unsafe
+foreign import stdcall unsafe "GetModuleFileNameA"
   getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
 #else
 getBaseDir :: IO (Maybe String) = do return Nothing
 #endif
 
 #ifdef mingw32_HOST_OS
-foreign import ccall "_getpid" unsafe getProcessID :: IO Int -- relies on Int == Int32 on Windows
+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