[project @ 2001-06-28 13:48:32 by rrt]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index 392b9b2..ab18451 100644 (file)
@@ -49,24 +49,32 @@ import Panic                ( progName, GhcException(..) )
 import Util            ( global )
 import CmdLineOpts     ( dynFlag, verbosity )
 
-import List            ( isPrefixOf )
-import Exception       ( throw, throwDyn, catchAllIO )
-import IO              ( hPutStr, hPutChar, hPutStrLn, hFlush, stderr )
-import IO              ( openFile, IOMode(..), hClose )        -- For temp "system"
+import Exception       ( throwDyn, catchAllIO )
+import IO
 import Directory       ( doesFileExist, removeFile )
 import IOExts          ( IORef, readIORef, writeIORef )
 import Monad           ( when, unless )
-import qualified System
-import System          ( ExitCode(..) )
+import System          ( system, ExitCode(..), exitWith )
+import CString
+import Int
+
+#if __GLASGOW_HASKELL__ < 500
+import Storable
+#else
+import MarshalArray
+#endif
 
 #include "../includes/config.h"
 
 #if !defined(mingw32_TARGET_OS)
 import qualified Posix
 #else
-import Addr              ( nullAddr )
+import Addr
+import List            ( isPrefixOf )
 #endif
 
+import List            ( isSuffixOf )
+
 #include "HsVersions.h"
 
 {-# DEPRECATED runSomething "runSomething should be private to SysTools" #-}
@@ -118,7 +126,7 @@ Config.hs contains two sorts of things
 %*                                                                     *
 %************************************************************************
 
-All these pathnames are maintained in Unix format. 
+All these pathnames are maintained IN THE NATIVE FORMAT OF THE HOST MACHINE.
 (See remarks under pathnames below)
 
 \begin{code}
@@ -163,10 +171,11 @@ initSysTools minusB_args
                -- 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_bin pgm   =  top_dir `slash` "bin" `slash` pgm
-             installed     file  =  top_dir `slash` file
-             inplace dir   pgm   =  top_dir `slash` dir `slash` pgm
+       ; let installed_bin pgm   =  pgmPath (top_dir `slash` "bin") pgm
+             installed     file  =  pgmPath top_dir file
+             inplace dir   pgm   =  pgmPath (top_dir `slash` dir) pgm
 
        ; let pkgconfig_path
                | am_installed = installed "package.conf"
@@ -195,7 +204,7 @@ initSysTools minusB_args
        ; config_exists <- doesFileExist pkgconfig_path
        ; when (not config_exists) $
             throwDyn (InstallationError 
-                        ("Can't find package.conf in " ++ pkgconfig_path))
+                        ("Can't find package.conf as " ++ pkgconfig_path))
 
 #if defined(mingw32_TARGET_OS)
        --              WINDOWS-SPECIFIC STUFF
@@ -228,7 +237,7 @@ initSysTools minusB_args
        ; let   cpp_path   = cRAWCPP
                gcc_path   = cGCC
                touch_path = cGHC_TOUCHY
-               mkdll_path = panic "Cant build DLLs on a non-Win32 system"
+               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
@@ -239,7 +248,7 @@ initSysTools minusB_args
 
 #endif
 
-       -- 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
        
@@ -312,7 +321,7 @@ setPgm pgm     = unknownFlagErr ("-pgm" ++ pgm)
 
 getTopDir :: [String]
          -> IO (Bool,          -- True <=> am installed, False <=> in-place
-                String)        -- TopDir
+                String)        -- TopDir (in Unix format '/' separated)
 
 getTopDir minusbs
   = do { top_dir1 <- get_proto
@@ -322,10 +331,7 @@ getTopDir minusbs
        -- by looking for the package configuration file.
        ; am_installed <- doesFileExist (top_dir2 `slash` "package.conf")
 
-       ; if am_installed then
-           return (True, top_dir2)
-        else
-           return (False, remove_suffix top_dir2)
+       ; return (am_installed, top_dir2)
        }
   where
     get_proto | not (null minusbs) 
@@ -335,19 +341,23 @@ getTopDir minusbs
                   ; case maybe_exec_dir of       -- (only works on Windows; 
                                                  --  returns Nothing on Unix)
                        Nothing  -> throwDyn (InstallationError "missing -B<dir> option")
-                       Just dir -> return dir
+                       Just dir -> return (remove_suffix dir)
                   }
 
-    remove_suffix dir  -- "/...stuff.../ghc/compiler" --> "/...stuff..."
-       = ASSERT2( not (null p1) && 
-                  not (null p2) && 
-                  dir == top_dir ++ "/ghc/compiler",
-                  text dir )
-         top_dir
+    -- In an installed tree, the ghc binary lives in $libexecdir, which
+    -- is normally $libdir/bin.  So we strip off a /bin suffix here.
+    -- In a build tree, the ghc binary lives in $fptools/ghc/compiler,
+    -- so we strip off the /ghc/compiler suffix here too, leaving a
+    -- standard TOPDIR.
+    remove_suffix ghc_bin_dir  -- ghc_bin_dir is in standard Unix format
+       | "/ghc/compiler" `isSuffixOf` ghc_bin_dir      = back_two
+       | "/bin" `isSuffixOf` ghc_bin_dir               = back_one
+       | otherwise                                     = ghc_bin_dir
        where
-        p1      = dropWhile (not . isSlash) (reverse dir)
+        p1      = dropWhile (not . isSlash) (reverse ghc_bin_dir)
         p2      = dropWhile (not . isSlash) (tail p1)  -- head is '/'
-        top_dir = reverse (tail p2)                    -- head is '/'
+        back_two = reverse (tail p2)                   -- head is '/'
+        back_one = reverse (tail p1)
 \end{code}
 
 
@@ -396,8 +406,14 @@ touch purpose arg =  do p <- readIORef v_Pgm_T
                        runSomething purpose p [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
+      h <- openFile to WriteMode
+      ls <- readFile from -- inefficient, but it'll do for now.
+                             -- ToDo: speed up via slurping.
+      hPutStrLn h ls
+      hClose h) `catchAllIO`
+                (\_ -> throwDyn (PhaseFailed purpose (ExitFailure 1)))
 \end{code}
 
 \begin{code}
@@ -418,7 +434,7 @@ 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
@@ -434,9 +450,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   )
@@ -508,7 +521,7 @@ runSomething :: String              -- For -v message
             -> String          -- Command name (possibly a full path)
                                --      assumed already dos-ified
             -> [String]        -- Arguments
-                               --      runSomthing will dos-ify them
+                               --      runSomething will dos-ify them
             -> IO ()
 
 runSomething phase_name pgm args
@@ -519,13 +532,8 @@ runSomething phase_name pgm args
          else return ()
        }
   where
--- Don't convert paths to DOS format when using the kludged
--- version of 'system' on mingw32.  See comments with 'system' below.
-#if __GLASGOW_HASKELL__ > 501
-    cmd_line = unwords (dosifyPaths (pgm : args))
-#else
-    cmd_line = unwords (pgm : args)
-#endif
+    cmd_line = unwords (pgm : dosifyPaths args)
+       -- The pgm is already in native format (appropriate dir separators)
 
 traceCmd :: String -> String -> IO () -> IO ()
 -- a) trace the command (at two levels of verbosity)
@@ -569,15 +577,21 @@ interpreted a command line 'foo\baz' as 'foobaz'.
 -----------------------------------------------------------------------------
 -- Convert filepath into MSDOS form.
 
-dosifyPath  :: String -> String
 dosifyPaths :: [String] -> [String]
--- dosifyPath does two things
+-- dosifyPaths does two things
 -- a) change '/' to '\'
 -- b) remove initial '/cygdrive/'
 
 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_TARGET_OS)
 
 --------------------- Windows version ------------------
@@ -585,6 +599,8 @@ unDosifyPath xs = xs
 
 dosifyPaths xs = map dosifyPath xs
 
+pgmPath dir pgm = dosifyPath dir ++ '\\' : pgm
+
 dosifyPath stuff
   = subst '/' '\\' real_stuff
  where
@@ -599,9 +615,9 @@ dosifyPath stuff
 #else
 
 --------------------- Unix version ---------------------
-dosifyPath   p  = p
 dosifyPaths  ps = ps
 unDosifyPath xs = subst '\\' '/' xs
+pgmPath dir pgm = dir ++ '/' : pgm
 --------------------------------------------------------
 #endif
 
@@ -642,61 +658,31 @@ slash s1 s2 = s1 ++ ('/' : s2)
 #ifdef mingw32_TARGET_OS
 foreign import "_getpid" getProcessID :: IO Int -- relies on Int == Int32 on Windows
 
-getExecDir :: IO (Maybe String)
-getExecDir = return Nothing
-{-
+#if __GLASGOW_HASKELL__ >= 500
 foreign import stdcall "GetCurrentDirectoryA" getCurrentDirectory :: Int32 -> CString -> IO Int32
-getExecDir = do len <- getCurrentDirectory 0 nullAddr
+foreign import stdcall "GetCurrentDirectoryA" getCurrentDirectoryLen :: Int32 -> Addr -> IO Int32
+getExecDir :: IO (Maybe String)
+getExecDir = do len <- getCurrentDirectoryLen 0 nullAddr
                buf <- mallocArray (fromIntegral len)
                ret <- getCurrentDirectory len buf
                if ret == 0 then return Nothing
                            else do s <- peekCString buf
                                    destructArray (fromIntegral len) buf
                                    return (Just s)
--}
+#else
+foreign import stdcall "GetCurrentDirectoryA" getCurrentDirectory :: Int32 -> Addr -> IO Int32
+getExecDir :: IO (Maybe String)
+getExecDir = do len <- getCurrentDirectory 0 nullAddr
+               buf <- malloc (fromIntegral len)
+               ret <- getCurrentDirectory len buf
+               if ret == 0 then return Nothing
+                           else do s <- unpackCStringIO buf
+                                   free buf
+                                   return (Just s)
+#endif
 #else
 getProcessID :: IO Int
 getProcessID = Posix.getProcessID
 getExecDir :: IO (Maybe String) = do return Nothing
 #endif
 \end{code}
-
-%************************************************************************
-%*                                                                     *
-\subsection{System}
-%*                                                                     *
-%************************************************************************
-
-In GHC prior to 5.01 (or so), on Windows, the implementation
-of "system" in the library System.system does not work for very 
-long command lines.  But GHC may need to make a system call with
-a very long command line, notably when it links itself during
-bootstrapping.  
-
-Solution: when compiling SysTools for Windows, using GHC prior
-to 5.01, write the command to a file and use "sh" (not cmd.exe)
-to execute it.  Such GHCs require "sh" on the path, but once
-bootstrapped this problem goes away.
-
-ToDo: remove when compiling with GHC < 5 is not relevant any more
-
-\begin{code}
-system cmd
-
-#if !defined(mingw32_TARGET_OS) || __GLASGOW_HASKELL__ > 501
-    -- The usual case
- = System.system cmd
-
-#else  -- The Hackoid case
- = do pid     <- getProcessID
-      tmp_dir <- readIORef v_TmpDir
-      let tmp = tmp_dir++"/sh"++show pid
-      h <- openFile tmp WriteMode
-      hPutStrLn h cmd
-      hClose h
-      exit_code <- System.system ("sh - " ++ tmp) `catchAllIO` 
-                                (\exn -> removeFile tmp >> throw exn)
-      removeFile tmp
-      return exit_code
-#endif
-\end{code}