[project @ 2005-03-31 10:16:33 by simonmar]
[ghc-hetmet.git] / ghc / compiler / main / SysTools.lhs
index 787222f..d919bcf 100644 (file)
@@ -11,22 +11,6 @@ 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 $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,14 @@ module SysTools (
 
 #include "HsVersions.h"
 
-import DriverUtil
 import DriverPhases     ( isHaskellUserSrcFilename )
 import Config
 import Outputable
 import Panic           ( GhcException(..) )
-import Util            ( global, notNull )
-import CmdLineOpts     ( DynFlags(..) )
+import Util            ( Suffix, global, notNull, consIORef,
+                         normalisePath, pgmPath, platformPath )
+import DynFlags                ( DynFlags(..), DynFlag(..), dopt, Option(..),
+                         setTmpDir, defaultDynFlags )
 
 import EXCEPTION       ( throwDyn )
 import DATA_IOREF      ( IORef, readIORef, writeIORef )
@@ -84,8 +65,6 @@ import IO             ( try, catch,
 import Directory       ( doesFileExist, removeFile )
 import List             ( partition )
 
-#include "../includes/ghcconfig.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
@@ -186,20 +165,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 +191,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
@@ -273,32 +239,32 @@ initSysTools minusB_args
                | am_installed = installed_bin cGHC_MANGLER_PGM
                | otherwise    = inplace cGHC_MANGLER_DIR_REL cGHC_MANGLER_PGM
 
+       ; let dflags0 = defaultDynFlags
 #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 ()
-                 )
+       ; e_tmpdir <- IO.try (getEnv "TMPDIR") -- fails if not set
 #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.
-       ; IO.try (do
+       ; e_tmpdir <- 
+            IO.try (do
                let len = (2048::Int)
                buf  <- mallocArray len
                ret  <- getTempPath len buf
-               tdir <-
-                 if ret == 0 then do
+               if ret == 0 then do
                      -- failed, consult TMPDIR.
                     free buf
                     getEnv "TMPDIR"
-                  else do
+                 else do
                     s <- peekCString buf
                     free buf
-                    return s
-               setTmpDir tdir)
+                    return s)
 #endif
+        ; let dflags1 = case e_tmpdir of
+                         Left _  -> dflags0
+                         Right d -> setTmpDir d dflags0
 
        -- Check that the package config exists
        ; config_exists <- doesFileExist pkgconfig_path
@@ -388,12 +354,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,23 +363,19 @@ 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 dflags1{
+                       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)
@@ -427,32 +383,6 @@ foreign import stdcall unsafe "GetTempPathA" getTempPath :: Int -> CString -> IO
 #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,33 +431,6 @@ 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}
 %*                                                                     *
 %************************************************************************
@@ -536,59 +439,47 @@ showOpt (Option s)  = s
 \begin{code}
 runUnlit :: DynFlags -> [Option] -> IO ()
 runUnlit dflags args = do 
-  p <- readIORef v_Pgm_L
+  let p = pgm_L dflags
   runSomething dflags "Literate pre-processor" p args
 
 runCpp :: DynFlags -> [Option] -> IO ()
 runCpp dflags args =   do 
-  (p,baseArgs) <- readIORef v_Pgm_P
-  runSomething dflags "C pre-processor" p (baseArgs ++ args)
+  let (p,args0) = pgm_P dflags
+  runSomething dflags "C pre-processor" p (args0 ++ args)
 
 runPp :: DynFlags -> [Option] -> IO ()
 runPp dflags args =   do 
-  p <- readIORef v_Pgm_F
+  let p = pgm_F dflags
   runSomething dflags "Haskell pre-processor" p args
 
 runCc :: DynFlags -> [Option] -> IO ()
 runCc dflags args =   do 
-  (p,args0) <- readIORef v_Pgm_c
+  let (p,args0) = pgm_c dflags
   runSomething dflags "C Compiler" p (args0++args)
 
 runMangle :: DynFlags -> [Option] -> IO ()
 runMangle dflags args = do 
-  (p,args0) <- readIORef v_Pgm_m
+  let (p,args0) = pgm_m dflags
   runSomething dflags "Mangler" p (args0++args)
 
 runSplit :: DynFlags -> [Option] -> IO ()
 runSplit dflags args = do 
-  (p,args0) <- readIORef v_Pgm_s
+  let (p,args0) = pgm_s dflags
   runSomething dflags "Splitter" p (args0++args)
 
 runAs :: DynFlags -> [Option] -> IO ()
 runAs dflags args = do 
-  (p,args0) <- readIORef v_Pgm_a
+  let (p,args0) = pgm_a dflags
   runSomething dflags "Assembler" p (args0++args)
 
 runLink :: DynFlags -> [Option] -> IO ()
 runLink dflags args = do 
-  (p,args0) <- readIORef v_Pgm_l
+  let (p,args0) = pgm_l dflags
   runSomething dflags "Linker" p (args0++args)
 
-#ifdef ILX
-runIlx2il :: DynFlags -> [Option] -> IO ()
-runIlx2il dflags args = do 
-  p <- readIORef v_Pgm_I
-  runSomething dflags "Ilx2Il" p args
-
-runIlasm :: DynFlags -> [Option] -> IO ()
-runIlasm dflags args = do 
-  p <- readIORef v_Pgm_i
-  runSomething dflags "Ilasm" p args
-#endif
-
 runMkDLL :: DynFlags -> [Option] -> IO ()
 runMkDLL dflags args = do
-  (p,args0) <- readIORef v_Pgm_MkDLL
+  let (p,args0) = pgm_dll dflags
   runSomething dflags "Make DLL" p (args0++args)
 
 touch :: DynFlags -> String -> String -> IO ()
@@ -605,6 +496,7 @@ copy dflags purpose from to = do
                      -- ToDo: speed up via slurping.
   hPutStr h ls
   hClose h
+
 \end{code}
 
 \begin{code}
@@ -628,42 +520,9 @@ getUsageMsgPaths = readIORef v_Path_usages
 
 \begin{code}
 GLOBAL_VAR(v_FilesToClean, [],               [String] )
-GLOBAL_VAR(v_TmpDir,       cDEFAULT_TMPDIR,  String   )
-       -- v_TmpDir has no closing '/'
 \end{code}
 
 \begin{code}
-setTmpDir dir = writeIORef v_TmpDir (canonicalise dir)
-    where
-#if !defined(mingw32_HOST_OS)
-     canonicalise p = normalisePath p
-#else
-       -- Canonicalisation of temp path under win32 is a bit more
-       -- involved: (a) strip trailing slash, 
-       --           (b) normalise slashes
-       --           (c) just in case, if there is a prefix /cygdrive/x/, change to x:
-       -- 
-     canonicalise path = normalisePath (xltCygdrive (removeTrailingSlash path))
-
-        -- if we're operating under cygwin, and TMP/TEMP is of
-       -- the form "/cygdrive/drive/path", translate this to
-       -- "drive:/path" (as GHC isn't a cygwin app and doesn't
-       -- understand /cygdrive paths.)
-     xltCygdrive path
-      | "/cygdrive/" `isPrefixOf` path = 
-         case drop (length "/cygdrive/") path of
-           drive:xs@('/':_) -> drive:':':xs
-           _ -> path
-      | otherwise = path
-
-        -- strip the trailing backslash (awful, but we only do this once).
-     removeTrailingSlash path = 
-       case last path of
-         '/'  -> init path
-         '\\' -> init path
-         _    -> path
-#endif
-
 cleanTempFiles :: DynFlags -> IO ()
 cleanTempFiles dflags
    = do fs <- readIORef v_FilesToClean
@@ -679,22 +538,21 @@ cleanTempFilesExcept dflags dont_delete
 
 
 -- find a temporary name that doesn't already exist.
-newTempName :: Suffix -> IO FilePath
-newTempName extn
+newTempName :: DynFlags -> Suffix -> IO FilePath
+newTempName DynFlags{tmpDir=tmp_dir} extn
   = do x <- getProcessID
-       tmp_dir <- readIORef v_TmpDir
        findTempName tmp_dir x
   where 
     findTempName tmp_dir x
       = 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 :: DynFlags -> [FilePath] -> IO ()
 removeTmpFiles dflags fs
@@ -725,20 +583,6 @@ removeTmpFiles dflags 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
@@ -768,6 +612,10 @@ runSomething dflags phase_name pgm args = do
      ExitFailure _other ->
        throwDyn (PhaseFailed phase_name exit_code)
 
+showOpt (FileOption pre f) = pre ++ platformPath f
+showOpt (Option "") = ""
+showOpt (Option s)  = s
+
 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
@@ -778,8 +626,7 @@ traceCmd dflags phase_name cmd_line action
        ; 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
@@ -790,54 +637,6 @@ traceCmd dflags phase_name cmd_line action
                             ; throwDyn (PhaseFailed phase_name (ExitFailure 1)) }
 \end{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 platformPath,
-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 platform / MSDOS form.
-
-normalisePath :: String -> String
--- Just changes '\' 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 ------------------
-normalisePath xs = subst '\\' '/' xs
-platformPath p   = subst '/' '\\' p
-pgmPath dir pgm  = platformPath dir ++ '\\' : pgm
-
-subst a b ls = map (\ x -> if x == a then b else x) ls
-#else
---------------------- Non-Windows version --------------
-normalisePath xs   = xs
-pgmPath dir pgm    = dir ++ '/' : pgm
-platformPath stuff = stuff
---------------------------------------------------------
-#endif
-
-\end{code}
-
-
 -----------------------------------------------------------------------------
    Path name construction
 
@@ -857,8 +656,8 @@ slash s1 s2 = s1 ++ ('/' : s2)
 -----------------------------------------------------------------------------
 -- Define      getBaseDir     :: IO (Maybe String)
 
-#if defined(mingw32_HOST_OS)
 getBaseDir :: IO (Maybe String)
+#if defined(mingw32_HOST_OS)
 -- Assuming we are running ghc, accessed by path  $()/bin/ghc.exe,
 -- return the path $(stuff).  Note that we drop the "bin/" directory too.
 getBaseDir = do let len = (2048::Int) -- plenty, PATH_MAX is 512 under Win32.
@@ -874,7 +673,7 @@ getBaseDir = do let len = (2048::Int) -- plenty, PATH_MAX is 512 under Win32.
 foreign import stdcall unsafe "GetModuleFileNameA"
   getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
 #else
-getBaseDir :: IO (Maybe String) = do return Nothing
+getBaseDir = return Nothing
 #endif
 
 #ifdef mingw32_HOST_OS