[project @ 2002-09-01 00:34:21 by mthomas]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index e3eedf9..8b2ab35 100644 (file)
@@ -1,5 +1,4 @@
 -----------------------------------------------------------------------------
--- $Id: SysTools.lhs,v 1.48 2001/08/13 15:49:38 simonmar Exp $
 --
 -- (c) The University of Glasgow 2001
 --
 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,
 
@@ -20,9 +30,14 @@ module SysTools (
 
        -- Interface to system tools
        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 ()
@@ -46,34 +61,64 @@ module SysTools (
 
  ) where
 
+#include "HsVersions.h"
+
 import DriverUtil
 import Config
 import Outputable
 import Panic           ( progName, GhcException(..) )
-import Util            ( global )
+import Util            ( global, notNull )
 import CmdLineOpts     ( dynFlag, verbosity )
 
-import Exception       ( throwDyn, catchAllIO )
-import IO
-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 System          ( ExitCode(..), exitWith, getEnv, system )
-import CString
-import Int
-import Addr
-    
+import IO
+import Directory       ( doesFileExist, removeFile )
+
 #include "../includes/config.h"
 
-#ifndef mingw32_TARGET_OS
+-- 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
 
-#include "HsVersions.h"
+-- Make catch work on older GHCs
+#if __GLASGOW_HASKELL__ > 408
+myCatch = Exception.catch
+#else
+myCatch = catchAllIO
+#endif
 
 \end{code}
 
@@ -110,9 +155,9 @@ 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)
                
 
 
@@ -152,10 +197,15 @@ 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_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
 
@@ -200,39 +250,68 @@ initSysTools minusB_args
                -- NB: top_dir is assumed to be in standard Unix format '/' separated
 
        ; let installed, installed_bin :: FilePath -> FilePath
-              installed_bin pgm   =  pgmPath (top_dir `slash` "extra-bin") pgm
+              installed_bin pgm   =  pgmPath top_dir pgm
              installed     file  =  pgmPath top_dir file
-             inplace dir   pgm   =  pgmPath (top_dir `slash` dir) pgm
+             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 "package.conf.inplace"
+               | otherwise    = inplace cGHC_DRIVER_DIR_REL "package.conf.inplace"
 
              ghc_usage_msg_path
                | am_installed = installed "ghc-usage.txt"
-               | otherwise    = inplace cGHC_DRIVER_DIR "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
-               | otherwise    = inplace cGHC_UNLIT_DIR cGHC_UNLIT
+               | 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
-               | otherwise    = inplace cGHC_SPLIT_DIR cGHC_SPLIT
+               | am_installed = installed_bin cGHC_SPLIT_PGM
+               | otherwise    = inplace cGHC_SPLIT_DIR_REL cGHC_SPLIT_PGM
 
              mangle_script
-               | am_installed = installed_bin cGHC_MANGLER
-               | otherwise    = inplace cGHC_MANGLER_DIR cGHC_MANGLER
+               | 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
                     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
 
        -- Check that the package config exists
@@ -241,7 +320,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
@@ -270,22 +349,26 @@ initSysTools minusB_args
                        | otherwise    = cGHC_PERL
 
        -- 'touch' is a GHC util for Windows, and similarly unlit, mangle
-       ; let touch_path  | am_installed = installed_bin 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 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   gcc_path   = cGCC
-               touch_path = cGHC_TOUCHY
+               touch_path = "touch"
                mkdll_path = panic "Can't build DLLs on a non-Win32 system"
 
        -- On Unix, scripts are invoked using the '#!' method.  Binary
@@ -307,6 +390,11 @@ initSysTools minusB_args
        ; 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
@@ -318,10 +406,15 @@ initSysTools minusB_args
 
        ; 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
@@ -329,24 +422,31 @@ initSysTools minusB_args
 
        ; 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}
-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)
+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}
 
 
@@ -384,7 +484,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
@@ -409,32 +509,26 @@ 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 String
+ = 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 f) = dosifyPath f
+   showOpt (FileOption pre f) = pre ++ dosifyPath f
    showOpt (Option s)     = s
 
-#if defined(mingw32_TARGET_OS)
-   quote "" = ""
-   quote s  = "\"" ++ s ++ "\""
-#else
-   quote = id
-#endif
-
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
 \subsection{Running an external program}
-n%*                                                                    *
+%*                                                                     *
 %************************************************************************
 
 
@@ -447,6 +541,10 @@ runCpp :: [Option] -> IO ()
 runCpp args =   do p <- readIORef v_Pgm_P
                   runSomething "C pre-processor" p 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 <- readIORef v_Pgm_c
                  runSomething "C Compiler" p args
@@ -467,13 +565,23 @@ runLink :: [Option] -> IO ()
 runLink args = do p <- readIORef v_Pgm_l
                  runSomething "Linker" p 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 <- 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 [FileOption arg]
+                       runSomething purpose p [FileOption "" arg]
 
 copy :: String -> String -> String -> IO ()
 copy purpose from to = do
@@ -564,7 +672,7 @@ removeTmpFiles verb fs
             ("Deleting: " ++ unwords fs)
             (mapM_ rm fs)
   where
-    rm f = removeFile f `catchAllIO` 
+    rm f = removeFile f `myCatch` 
                (\_ignored -> 
                    when (verb >= 2) $
                      hPutStrLn stderr ("Warning: deleting non-existent " ++ f)
@@ -598,7 +706,7 @@ runSomething :: String              -- For -v message
 runSomething phase_name pgm args
  = traceCmd phase_name cmd_line $
    do   {
-#ifndef mingw32_TARGET_OS
+#ifndef mingw32_HOST_OS
          exit_code <- system cmd_line
 #else
           exit_code <- rawSystem cmd_line
@@ -608,14 +716,9 @@ runSomething phase_name pgm args
          else 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))
 
 traceCmd :: String -> String -> IO () -> IO ()
 -- a) trace the command (at two levels of verbosity)
@@ -631,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")
@@ -674,7 +777,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
@@ -691,7 +794,7 @@ 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
    
 #else
@@ -715,9 +818,6 @@ subst a b ls = map (\ x -> if x == a then b else x) ls
 slash           :: String -> String -> String
 absPath, relPath :: [String] -> String
 
-isSlash '/'   = True
-isSlash other = False
-
 relPath [] = ""
 relPath xs = foldr1 slash xs
 
@@ -737,26 +837,36 @@ 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 -- plenty, PATH_MAX is 512 under Win32.
-               buf <- mallocArray (fromIntegral len)
-               ret <- getModuleFileName nullAddr buf len
-               if ret == 0 then return Nothing
+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
-                                   destructArray (fromIntegral len) buf
-                                   return (Just (reverse (drop (length "/bin/ghc.exe") (reverse (unDosifyPath s)))))
+                                   free buf
+                                   return (Just (reverse (dropList "/bin/ghc.exe" (reverse (unDosifyPath s)))))
 
 
-foreign import stdcall "GetModuleFileNameA" getModuleFileName :: Addr -> CString -> Int32 -> 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" getProcessID :: IO Int -- relies on Int == Int32 on Windows
+#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
+quote s = s
+#endif
+
 \end{code}