[project @ 2003-06-04 16:52:06 by stolz]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index cb4a6e7..5ca2765 100644 (file)
 module SysTools (
        -- Initialisation
        initSysTools,
-       setPgm,                 -- String -> IO ()
+
+       setPgmL,                -- String -> IO ()
+       setPgmP,
+       setPgmF,
+       setPgmc,
+       setPgmm,
+       setPgms,
+       setPgma,
+       setPgml,
+#ifdef ILX
+       setPgmI,
+       setPgmi,
+#endif
                                -- Command-line override
        setDryRun,
 
@@ -50,60 +62,63 @@ module SysTools (
 
  ) where
 
+#include "HsVersions.h"
+
 import DriverUtil
 import Config
 import Outputable
 import Panic           ( progName, GhcException(..) )
-import Util            ( global, dropList )
+import Util            ( global, notNull )
 import CmdLineOpts     ( dynFlag, verbosity )
 
-import Exception       ( throwDyn )
-#if __GLASGOW_HASKELL__ > 408
-import qualified Exception ( catch )
-#else
-import Exception        ( catchAllIO )
-#endif
-import IO
-import Directory       ( doesFileExist, removeFile )
-import IOExts          ( IORef, readIORef, writeIORef )
+import EXCEPTION       ( throwDyn )
+import DATA_IOREF      ( IORef, readIORef, writeIORef )
+import DATA_INT
+    
 import Monad           ( when, unless )
 import System          ( ExitCode(..), exitWith, getEnv, system )
-import CString
-import Int
-import Addr
-    
+import IO              ( try, catch,
+                         openFile, hPutChar, hPutStrLn, hPutStr, hClose, hFlush, IOMode(..),
+                         stderr )
+import Directory       ( doesFileExist, removeFile )
+import List             ( intersperse )
+
 #include "../includes/config.h"
 
-#ifndef mingw32_TARGET_OS
-import qualified Posix
+-- 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
+#if __GLASGOW_HASKELL__ > 504
+import qualified System.Posix.Internals
+import System.Posix.Process ( executeFile, getProcessStatus, forkProcess, ProcessStatus(..))
+import System.Posix.Signals ( installHandler, sigCHLD, sigCONT, Handler(..) )
 #else
+import qualified Posix
+import Posix ( executeFile, getProcessStatus, forkProcess, ProcessStatus(..), installHandler,
+               sigCHLD, sigCONT, Handler(..) )
+#endif
+#else /* Must be Win32 */
 import List            ( isPrefixOf )
-import MarshalArray
+import Util            ( dropList )
 import Foreign
+import CString         ( CString, peekCString )
 #endif
 
-#if __GLASGOW_HASKELL__ > 408
-# if __GLASGOW_HASKELL__ >= 503
-import GHC.IOBase
-# else
-# endif
-# ifdef mingw32_TARGET_OS
-import SystemExts       ( rawSystem )
-# endif
+#ifdef mingw32_HOST_OS
+#if __GLASGOW_HASKELL__ > 504
+import System.Cmd       ( rawSystem )
 #else
-import System          ( system )
+import SystemExts       ( rawSystem )
 #endif
 
+#else /* Not Win32 */
 
-#include "HsVersions.h"
-
--- Make catch work on older GHCs
-#if __GLASGOW_HASKELL__ > 408
-myCatch = Exception.catch
-#else
-myCatch = catchAllIO
+import System          ( system )
 #endif
-
 \end{code}
 
 
@@ -180,7 +195,7 @@ All these pathnames are maintained IN THE NATIVE FORMAT OF THE HOST MACHINE.
 
 \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_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)        -- gcc
 GLOBAL_VAR(v_Pgm_m,    error "pgm_m",   String)        -- asm code mangler
@@ -262,7 +277,7 @@ initSysTools minusB_args
                | am_installed = installed_bin cGHC_MANGLER_PGM
                | otherwise    = inplace cGHC_MANGLER_DIR_REL cGHC_MANGLER_PGM
 
-#ifndef mingw32_TARGET_OS
+#ifndef mingw32_HOST_OS
        -- check whether TMPDIR is set in the environment
        ; IO.try (do dir <- getEnv "TMPDIR" -- fails if not set
                     setTmpDir dir
@@ -304,7 +319,7 @@ initSysTools minusB_args
             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
@@ -341,7 +356,11 @@ initSysTools minusB_args
        ; let split_path  = perl_path ++ " \"" ++ split_script ++ "\""
              mangle_path = perl_path ++ " \"" ++ mangle_script ++ "\""
 
-       ; let mkdll_path = cMKDLL
+       ; 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
@@ -360,7 +379,9 @@ initSysTools minusB_args
 #endif
 
        -- cpp is derived from gcc on all platforms
-        ; let cpp_path  = gcc_path ++ " -E " ++ cRAWCPP_FLAGS
+        -- HACK, see setPgmP below. We keep 'words' here to remember to fix
+        -- Config.hs one day.
+        ; let cpp_path  = (gcc_path, (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
@@ -403,32 +424,33 @@ initSysTools minusB_args
        ; return ()
        }
 
-#if defined(mingw32_TARGET_OS)
+#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 one
 
 \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 ('F' : pgm) = writeIORef v_Pgm_F 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
+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 = 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
-setPgm ('I' : pgm) = writeIORef v_Pgm_I pgm
-setPgm ('i' : pgm) = writeIORef v_Pgm_i pgm
+setPgmI = writeIORef v_Pgm_I
+setPgmi = writeIORef v_Pgm_i
 #endif
-setPgm pgm        = unknownFlagErr ("-pgm" ++ pgm)
 \end{code}
 
 
@@ -466,7 +488,7 @@ findTopDir minusbs
        }
   where
     -- get_proto returns a Unix-format path (relying on getExecDir to do so too)
-    get_proto | not (null minusbs)
+    get_proto | notNull minusbs
              = return (unDosifyPath (drop 2 (last minusbs)))   -- 2 for "-B"
              | otherwise          
              = do { maybe_exec_dir <- getExecDir -- Get directory of executable
@@ -500,16 +522,10 @@ data Option
  
 showOptions :: [Option] -> String
 showOptions ls = unwords (map (quote.showOpt) ls)
- where
-   showOpt (FileOption pre f) = pre ++ dosifyPath f
-   showOpt (Option s)     = s
 
-#if defined(mingw32_TARGET_OS)
-   quote "" = ""
-   quote s  = "\"" ++ s ++ "\""
-#else
-   quote = id
-#endif
+showOpt (FileOption pre f) = pre ++ dosifyPath f
+showOpt (Option "") = ""
+showOpt (Option s)  = s
 
 \end{code}
 
@@ -527,8 +543,8 @@ runUnlit args = do p <- readIORef v_Pgm_L
                   runSomething "Literate pre-processor" p args
 
 runCpp :: [Option] -> IO ()
-runCpp args =   do p <- readIORef v_Pgm_P
-                  runSomething "C pre-processor" p args
+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
@@ -661,7 +677,7 @@ removeTmpFiles verb fs
             ("Deleting: " ++ unwords fs)
             (mapM_ rm fs)
   where
-    rm f = removeFile f `myCatch` 
+    rm f = removeFile f `IO.catch` 
                (\_ignored -> 
                    when (verb >= 2) $
                      hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
@@ -693,26 +709,33 @@ runSomething :: String            -- For -v message
             -> IO ()
 
 runSomething phase_name pgm args
- = traceCmd phase_name cmd_line $
-   do   {
-#ifndef mingw32_TARGET_OS
-         exit_code <- system cmd_line
+ = traceCmd phase_name (concat (intersperse " " (pgm:quoteargs))) $
+   do
+#ifndef mingw32_HOST_OS
+          installHandler sigCHLD Ignore Nothing
+          -- avoid strange interaction with waitpid():
+          installHandler sigCONT Ignore Nothing
+          mpid <- forkProcess
+          exit_code <- case mpid of
+            Nothing -> do -- Child
+             executeFile pgm True quoteargs Nothing
+              exitWith (ExitFailure 127)
+             -- NOT REACHED
+              return ExitSuccess
+            Just child -> do -- Parent
+              Just (Exited res) <- getProcessStatus True True child
+              return res
 #else
           exit_code <- rawSystem cmd_line
 #endif
-       ; if exit_code /= ExitSuccess
-         then throwDyn (PhaseFailed phase_name exit_code)
-         else return ()
-       }
+         when (exit_code /= ExitSuccess)
+           $ throwDyn (PhaseFailed phase_name exit_code)
+          return ()    
   where
-    cmd_line = pgm ++ ' ':showOptions args -- unwords (pgm : dosifyPaths (map quote args))
        -- The pgm is already in native format (appropriate dir separators)
-#if defined(mingw32_TARGET_OS)
-    quote "" = ""
-    quote s  = "\"" ++ s ++ "\""
-#else
-    quote = id
-#endif
+    cmd_line = pgm ++ ' ':showOptions args
+                -- unwords (pgm : dosifyPaths (map quote args))
+    quoteargs = filter (not.null) (map showOpt args)
 
 traceCmd :: String -> String -> IO () -> IO ()
 -- a) trace the command (at two levels of verbosity)
@@ -728,11 +751,11 @@ traceCmd phase_name cmd_line action
        ; unless n $ do {
 
           -- And run it!
-       ; action `myCatch` 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))
+                            ; when (verb >= 3) (hPutStrLn stderr ("Failed: " ++ cmd_line ++ (show exn)))
                             ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
 \end{code}
 
@@ -771,7 +794,7 @@ pgmPath :: String           -- Directory string in Unix format
 
 
 
-#if defined(mingw32_TARGET_OS)
+#if defined(mingw32_HOST_OS)
 
 --------------------- Windows version ------------------
 dosifyPaths xs = map dosifyPath xs
@@ -831,38 +854,35 @@ slash s1 s2 = s1 ++ ('/' : s2)
 -----------------------------------------------------------------------------
 -- Define      getExecDir     :: IO (Maybe String)
 
-#if defined(mingw32_TARGET_OS)
+#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 nullAddr buf 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 :: Addr -> CString -> Int -> IO Int32
+foreign import stdcall "GetModuleFileNameA" unsafe
+  getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
 #else
 getExecDir :: IO (Maybe String) = do return Nothing
 #endif
 
-#ifdef mingw32_TARGET_OS
-foreign import "_getpid" unsafe getProcessID :: IO Int -- relies on Int == Int32 on Windows
+#ifdef mingw32_HOST_OS
+foreign import ccall "_getpid" unsafe 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
 
-#if defined(mingw32_TARGET_OS) && (__GLASGOW_HASKELL__ <= 408)
-rawSystem :: String -> IO ExitCode
-rawSystem cmd = system cmd
- -- mingw only: if you try to build a stage2 compiler with a stage1
- -- that has been bootstrapped with 4.08 (or earlier), this will run
- -- into problems with limits on command-line lengths with the std.
- -- Win32 command interpreters. So don't this - use 5.00 or later
- -- to compile up the GHC sources.
-#endif
-
+quote :: String -> String
+quote "" = ""
+quote s  = "\"" ++ s ++ "\""
 
 \end{code}