[project @ 2000-10-10 13:25:19 by simonmar]
authorsimonmar <unknown>
Tue, 10 Oct 2000 13:25:19 +0000 (13:25 +0000)
committersimonmar <unknown>
Tue, 10 Oct 2000 13:25:19 +0000 (13:25 +0000)
The driver, more or less verbatim copied from ghc/driver/Main.hs.

THE PLAN: the main body of the driver will eventually sit between the
compilation manager and HscMain, providing all the grotty services
like compilation of .hc files.

Some other parts of the driver, eg. flag parsing, will remain at the
front end.

ghc/compiler/main/Main.hs [new file with mode: 0644]

diff --git a/ghc/compiler/main/Main.hs b/ghc/compiler/main/Main.hs
new file mode 100644 (file)
index 0000000..dee1e11
--- /dev/null
@@ -0,0 +1,2461 @@
+{-# OPTIONS -W -fno-warn-incomplete-patterns #-}
+-----------------------------------------------------------------------------
+-- $Id: Main.hs,v 1.1 2000/10/10 13:25:19 simonmar Exp $
+--
+-- GHC Driver program
+--
+-- (c) Simon Marlow 2000
+--
+-----------------------------------------------------------------------------
+
+-- with path so that ghc -M can find config.h
+#include "../includes/config.h"
+
+module Main (main) where
+
+import CmSummarise ( getImports )
+import CmStaticInfo ( Package(..) )
+import TmpFiles
+import Config
+
+import RegexString
+import Concurrent
+#ifndef mingw32_TARGET_OS
+import Posix
+#endif
+import Directory
+import IOExts
+import Exception
+import Dynamic
+
+import IO
+import Monad
+import List
+import System
+import Maybe
+import Char
+
+-----------------------------------------------------------------------------
+-- ToDo:
+
+-- certain options in OPTIONS pragmas are persistent through subsequent compilations.
+-- new mkdependHS doesn't support all the options that the old one did (-X et al.)
+-- time commands when run with -v
+-- split marker
+-- mkDLL
+-- java generation
+-- user ways
+-- Win32 support: proper signal handling
+-- make sure OPTIONS in .hs file propogate to .hc file if -C or -keep-hc-file-too
+-- reading the package configuration file is too slow
+-- -H, -K, -Rghc-timing
+
+-----------------------------------------------------------------------------
+-- Differences vs. old driver:
+
+-- No more "Enter your Haskell program, end with ^D (on a line of its own):"
+-- consistency checking removed (may do this properly later)
+-- removed -noC
+-- no hi diffs (could be added later)
+-- no -Ofile
+
+-----------------------------------------------------------------------------
+-- non-configured things
+
+cHaskell1Version = "5" -- i.e., Haskell 98
+
+-----------------------------------------------------------------------------
+-- Usage Message
+
+short_usage = "Usage: For basic information, try the `--help' option."
+   
+long_usage = do
+  let usage_file = "ghc-usage.txt"
+      usage_path = findFile usage_file (cGHC_DRIVER_DIR ++ '/':usage_file)
+  usage <- readFile usage_path
+  dump usage
+  exitWith ExitSuccess
+  where
+     dump "" = return ()
+     dump ('$':'$':s) = hPutStr stderr get_prog_name >> dump s
+     dump (c:s) = hPutChar stderr c >> dump s
+
+version_str = cProjectVersion
+
+-----------------------------------------------------------------------------
+-- Driver state
+
+-- certain flags can be specified on a per-file basis, in an OPTIONS
+-- pragma at the beginning of the source file.  This means that when
+-- compiling mulitple files, we have to restore the global option
+-- settings before compiling a new file.  
+--
+-- The DriverState record contains the per-file-mutable state.
+
+data DriverState = DriverState {
+
+       -- are we runing cpp on this file?
+       cpp_flag                :: Bool,
+
+       -- heap/stack sizes
+       specific_heap_size      :: Integer,
+       specific_stack_size     :: Integer,
+  
+       -- misc
+       stolen_x86_regs         :: Int,
+       excess_precision        :: Bool,
+       warning_opt             :: WarningState,
+       cmdline_hc_includes     :: [String],
+
+       -- options for a particular phase
+       anti_opt_C              :: [String],
+       opt_dep                 :: [String],
+       opt_L                   :: [String],
+       opt_P                   :: [String],
+       opt_C                   :: [String],
+       opt_c                   :: [String],
+       opt_a                   :: [String],
+       opt_m                   :: [String],
+       opt_l                   :: [String],
+       opt_dll                 :: [String]
+   }
+
+initDriverState = DriverState {
+       cpp_flag                = False,
+       specific_heap_size      = 6 * 1000 * 1000,
+       specific_stack_size     = 1000 * 1000,
+       stolen_x86_regs         = 4,
+       excess_precision        = False,
+       warning_opt             = W_default,
+       cmdline_hc_includes     = [],
+       anti_opt_C              = [],
+       opt_dep                 = [],
+       opt_L                   = [],
+       opt_P                   = [],
+       opt_C                   = [],
+       opt_c                   = [],
+       opt_a                   = [],
+       opt_m                   = [],
+       opt_l                   = [],
+       opt_dll                 = []
+   }
+       
+GLOBAL_VAR(driver_state, initDriverState, DriverState)
+
+readState :: (DriverState -> a) -> IO a
+readState f = readIORef driver_state >>= return . f
+
+updateState :: (DriverState -> DriverState) -> IO ()
+updateState f = readIORef driver_state >>= writeIORef driver_state . f
+
+addAntiOpt_C a = updateState (\s -> s{anti_opt_C =  a : anti_opt_C s})
+addOpt_dep   a = updateState (\s -> s{opt_dep    =  a : opt_dep    s})
+addOpt_L     a = updateState (\s -> s{opt_L      =  a : opt_L      s})
+addOpt_P     a = updateState (\s -> s{opt_P      =  a : opt_P      s})
+addOpt_C     a = updateState (\s -> s{opt_C      =  a : opt_C      s})
+addOpt_c     a = updateState (\s -> s{opt_c      =  a : opt_c      s})
+addOpt_a     a = updateState (\s -> s{opt_a      =  a : opt_a      s})
+addOpt_m     a = updateState (\s -> s{opt_m      =  a : opt_m      s})
+addOpt_l     a = updateState (\s -> s{opt_l      =  a : opt_l      s})
+addOpt_dll   a = updateState (\s -> s{opt_dll    =  a : opt_dll    s})
+
+addCmdlineHCInclude a = 
+   updateState (\s -> s{cmdline_hc_includes =  a : cmdline_hc_includes s})
+
+       -- we add to the options from the front, so we need to reverse the list
+getOpts :: (DriverState -> [a]) -> IO [a]
+getOpts opts = readState opts >>= return . reverse
+
+newHeapSize :: Integer -> IO ()
+newHeapSize new = updateState 
+   (\s -> let current = specific_heap_size s in
+         s{ specific_heap_size = if new > current then new else current })
+
+newStackSize :: Integer -> IO ()
+newStackSize new = updateState 
+   (\s -> let current = specific_stack_size s in
+         s{ specific_stack_size = if new > current then new else current })
+
+-----------------------------------------------------------------------------
+-- Phases
+
+{-
+Phase of the           | Suffix saying | Flag saying   | (suffix of)
+compilation system     | ``start here''| ``stop after''| output file
+
+literate pre-processor | .lhs          | -             | -
+C pre-processor (opt.) | -             | -E            | -
+Haskell compiler       | .hs           | -C, -S        | .hc, .s
+C compiler (opt.)      | .hc or .c     | -S            | .s
+assembler              | .s  or .S     | -c            | .o
+linker                 | other         | -             | a.out
+-}
+
+data Phase 
+       = MkDependHS    -- haskell dependency generation
+       | Unlit
+       | Cpp
+       | Hsc
+       | Cc
+       | HCc           -- Haskellised C (as opposed to vanilla C) compilation
+       | Mangle        -- assembly mangling, now done by a separate script.
+       | SplitMangle   -- after mangler if splitting
+       | SplitAs
+       | As
+       | Ln 
+  deriving (Eq)
+
+-----------------------------------------------------------------------------
+-- Errors
+
+data BarfKind
+  = PhaseFailed String ExitCode
+  | Interrupted
+  | UsageError String                  -- prints the short usage msg after the error
+  | OtherError String                  -- just prints the error message
+  deriving Eq
+
+GLOBAL_VAR(prog_name, "ghc", String)
+
+get_prog_name = unsafePerformIO (readIORef prog_name) -- urk!
+
+instance Show BarfKind where
+  showsPrec _ e = showString get_prog_name . showString ": " . showBarf e
+
+showBarf (UsageError str) = showString str . showChar '\n' . showString short_usage
+showBarf (OtherError str) = showString str
+showBarf (PhaseFailed phase code) = 
+       showString phase . showString " failed, code = " . shows code
+showBarf (Interrupted) = showString "interrupted"
+
+unknownFlagErr f = throwDyn (UsageError ("unrecognised flag: " ++ f))
+
+barfKindTc = mkTyCon "BarfKind"
+instance Typeable BarfKind where
+  typeOf _ = mkAppTy barfKindTc []
+
+-----------------------------------------------------------------------------
+-- Global compilation flags
+
+
+       -- Cpp-related flags
+hs_source_cpp_opts = global
+       [ "-D__HASKELL1__="++cHaskell1Version
+       , "-D__GLASGOW_HASKELL__="++cProjectVersionInt                          
+       , "-D__HASKELL98__"
+       , "-D__CONCURRENT_HASKELL__"
+       ]
+
+       -- Verbose
+GLOBAL_VAR(verbose, False, Bool)
+is_verbose = do v <- readIORef verbose; if v then return "-v" else return ""
+
+       -- Keep output from intermediate phases
+GLOBAL_VAR(keep_hi_diffs,      False,          Bool)
+GLOBAL_VAR(keep_hc_files,      False,          Bool)
+GLOBAL_VAR(keep_s_files,       False,          Bool)
+GLOBAL_VAR(keep_raw_s_files,   False,          Bool)
+GLOBAL_VAR(keep_tmp_files,     False,          Bool)
+
+       -- Misc
+GLOBAL_VAR(scale_sizes_by,      1.0,           Double)
+GLOBAL_VAR(dry_run,            False,          Bool)
+#if !defined(HAVE_WIN32_DLL_SUPPORT) || defined(DONT_WANT_WIN32_DLL_SUPPORT)
+GLOBAL_VAR(static,             True,           Bool)
+#else
+GLOBAL_VAR(static,              False,          Bool)
+#endif
+GLOBAL_VAR(collect_ghc_timing,         False,          Bool)
+GLOBAL_VAR(do_asm_mangling,    True,           Bool)
+
+-----------------------------------------------------------------------------
+-- Splitting object files (for libraries)
+
+GLOBAL_VAR(split_object_files, False,          Bool)
+GLOBAL_VAR(split_prefix,       "",             String)
+GLOBAL_VAR(n_split_files,      0,              Int)
+       
+can_split :: Bool
+can_split =  prefixMatch "i386" cTARGETPLATFORM
+         || prefixMatch "alpha" cTARGETPLATFORM
+         || prefixMatch "hppa" cTARGETPLATFORM
+         || prefixMatch "m68k" cTARGETPLATFORM
+         || prefixMatch "mips" cTARGETPLATFORM
+         || prefixMatch "powerpc" cTARGETPLATFORM
+         || prefixMatch "rs6000" cTARGETPLATFORM
+         || prefixMatch "sparc" cTARGETPLATFORM
+
+-----------------------------------------------------------------------------
+-- Compiler output options
+
+data HscLang
+  = HscC
+  | HscAsm
+  | HscJava
+  deriving Eq
+
+GLOBAL_VAR(hsc_lang, if cGhcWithNativeCodeGen == "YES" && 
+                        (prefixMatch "i386" cTARGETPLATFORM ||
+                         prefixMatch "sparc" cTARGETPLATFORM)
+                       then  HscAsm
+                       else  HscC, 
+          HscLang)
+
+GLOBAL_VAR(output_dir,  Nothing, Maybe String)
+GLOBAL_VAR(output_suf,  Nothing, Maybe String)
+GLOBAL_VAR(output_file, Nothing, Maybe String)
+GLOBAL_VAR(output_hi,   Nothing, Maybe String)
+
+GLOBAL_VAR(ld_inputs,  [],      [String])
+
+odir_ify :: String -> IO String
+odir_ify f = do
+  odir_opt <- readIORef output_dir
+  case odir_opt of
+       Nothing -> return f
+       Just d  -> return (newdir d f)
+
+osuf_ify :: String -> IO String
+osuf_ify f = do
+  osuf_opt <- readIORef output_suf
+  case osuf_opt of
+       Nothing -> return f
+       Just s  -> return (newsuf s f)
+
+-----------------------------------------------------------------------------
+-- Hi Files
+
+GLOBAL_VAR(produceHi,          True,   Bool)
+GLOBAL_VAR(hi_on_stdout,       False,  Bool)
+GLOBAL_VAR(hi_with,            "",     String)
+GLOBAL_VAR(hi_suf,             "hi",   String)
+
+data HiDiffFlag = NormalHiDiffs | UsageHiDiffs | NoHiDiffs
+GLOBAL_VAR(hi_diffs, NoHiDiffs, HiDiffFlag)
+
+-----------------------------------------------------------------------------
+-- Warnings & sanity checking
+
+-- Warning packages that are controlled by -W and -Wall.  The 'standard'
+-- warnings that you get all the time are
+--        
+--        -fwarn-overlapping-patterns
+--        -fwarn-missing-methods
+--        -fwarn-missing-fields
+--        -fwarn-deprecations
+--        -fwarn-duplicate-exports
+-- 
+-- these are turned off by -Wnot.
+
+standardWarnings  = [ "-fwarn-overlapping-patterns"
+                   , "-fwarn-missing-methods"
+                   , "-fwarn-missing-fields"
+                   , "-fwarn-deprecations"
+                   , "-fwarn-duplicate-exports"
+                   ]
+minusWOpts       = standardWarnings ++ 
+                   [ "-fwarn-unused-binds"
+                   , "-fwarn-unused-matches"
+                   , "-fwarn-incomplete-patterns"
+                   , "-fwarn-unused-imports"
+                   ]
+minusWallOpts    = minusWOpts ++
+                   [ "-fwarn-type-defaults"
+                   , "-fwarn-name-shadowing"
+                   , "-fwarn-missing-signatures"
+                   , "-fwarn-hi-shadowing"
+                   ]
+
+data WarningState = W_default | W_ | W_all | W_not
+
+-----------------------------------------------------------------------------
+-- Compiler optimisation options
+
+GLOBAL_VAR(opt_level, 0, Int)
+
+setOptLevel :: String -> IO ()
+setOptLevel ""             = do { writeIORef opt_level 1; go_via_C }
+setOptLevel "not"          = writeIORef opt_level 0
+setOptLevel [c] | isDigit c = do
+   let level = ord c - ord '0'
+   writeIORef opt_level level
+   when (level >= 1) go_via_C
+setOptLevel s = unknownFlagErr ("-O"++s)
+
+go_via_C = do
+   l <- readIORef hsc_lang
+   case l of { HscAsm -> writeIORef hsc_lang HscC; 
+              _other -> return () }
+
+GLOBAL_VAR(opt_minus_o2_for_C, False, Bool)
+
+GLOBAL_VAR(opt_MaxSimplifierIterations, 4, Int)
+GLOBAL_VAR(opt_StgStats,    False, Bool)
+GLOBAL_VAR(opt_UsageSPInf,  False, Bool)  -- Off by default
+
+hsc_minusO2_flags = hsc_minusO_flags   -- for now
+
+hsc_minusNoO_flags = do
+  iter        <- readIORef opt_MaxSimplifierIterations
+  return [ 
+       "-fignore-interface-pragmas",
+       "-fomit-interface-pragmas",
+       "-fsimplify",
+           "[",
+               "-fmax-simplifier-iterations" ++ show iter,
+           "]"
+       ]
+
+hsc_minusO_flags = do
+  iter       <- readIORef opt_MaxSimplifierIterations
+  usageSP    <- readIORef opt_UsageSPInf
+  stgstats   <- readIORef opt_StgStats
+
+  return [ 
+       "-ffoldr-build-on",
+
+        "-fdo-eta-reduction",
+       "-fdo-lambda-eta-expansion",
+       "-fcase-of-case",
+       "-fcase-merge",
+       "-flet-to-case",
+
+       -- initial simplify: mk specialiser happy: minimum effort please
+
+       "-fsimplify",
+         "[", 
+               "-finline-phase0",
+                       -- Don't inline anything till full laziness has bitten
+                       -- In particular, inlining wrappers inhibits floating
+                       -- e.g. ...(case f x of ...)...
+                       --  ==> ...(case (case x of I# x# -> fw x#) of ...)...
+                       --  ==> ...(case x of I# x# -> case fw x# of ...)...
+                       -- and now the redex (f x) isn't floatable any more
+
+               "-fno-rules",
+                       -- Similarly, don't apply any rules until after full 
+                       -- laziness.  Notably, list fusion can prevent floating.
+
+               "-fno-case-of-case",
+                       -- Don't do case-of-case transformations.
+                       -- This makes full laziness work better
+
+               "-fmax-simplifier-iterations2",
+         "]",
+
+       -- Specialisation is best done before full laziness
+       -- so that overloaded functions have all their dictionary lambdas manifest
+       "-fspecialise",
+
+       "-ffloat-outwards",
+       "-ffloat-inwards",
+
+       "-fsimplify",
+         "[", 
+               "-finline-phase1",
+               -- Want to run with inline phase 1 after the specialiser to give
+               -- maximum chance for fusion to work before we inline build/augment
+               -- in phase 2.  This made a difference in 'ansi' where an 
+               -- overloaded function wasn't inlined till too late.
+               "-fmax-simplifier-iterations" ++ show iter,
+         "]",
+
+       -- infer usage information here in case we need it later.
+        -- (add more of these where you need them --KSW 1999-04)
+        if usageSP then "-fusagesp" else "",
+
+       "-fsimplify",
+         "[", 
+               -- Need inline-phase2 here so that build/augment get 
+               -- inlined.  I found that spectral/hartel/genfft lost some useful
+               -- strictness in the function sumcode' if augment is not inlined
+               -- before strictness analysis runs
+
+               "-finline-phase2",
+               "-fmax-simplifier-iterations2",
+         "]",
+
+       "-fsimplify",
+         "[", 
+               "-fmax-simplifier-iterations2",
+               -- No -finline-phase: allow all Ids to be inlined now
+               -- This gets foldr inlined before strictness analysis
+         "]",
+
+       "-fstrictness",
+       "-fcpr-analyse",
+       "-fworker-wrapper",
+       "-fglom-binds",
+
+       "-fsimplify",
+         "[", 
+               "-fmax-simplifier-iterations" ++ show iter,
+               -- No -finline-phase: allow all Ids to be inlined now
+         "]",
+
+       "-ffloat-outwards",
+               -- nofib/spectral/hartel/wang doubles in speed if you
+               -- do full laziness late in the day.  It only happens
+               -- after fusion and other stuff, so the early pass doesn't
+               -- catch it.  For the record, the redex is 
+               --        f_el22 (f_el21 r_midblock)
+
+-- Leave out lambda lifting for now
+--       "-fsimplify", -- Tidy up results of full laziness
+--         "[", 
+--               "-fmax-simplifier-iterations2",
+--         "]",
+--       "-ffloat-outwards-full",      
+
+       -- We want CSE to follow the final full-laziness pass, because it may
+       -- succeed in commoning up things floated out by full laziness.
+       --
+       -- CSE must immediately follow a simplification pass, because it relies
+       -- on the no-shadowing invariant.  See comments at the top of CSE.lhs
+       -- So it must NOT follow float-inwards, which can give rise to shadowing,
+       -- even if its input doesn't have shadows.  Hence putting it between
+       -- the two passes.
+       "-fcse",        
+                       
+
+       "-ffloat-inwards",
+
+-- Case-liberation for -O2.  This should be after
+-- strictness analysis and the simplification which follows it.
+
+--       ( ($OptLevel != 2)
+--       ? ""
+--       : "-fliberate-case -fsimplify [ $Oopt_FB_Support -ffloat-lets-exposing-whnf -ffloat-primops-ok -fcase-of-case -fdo-case-elim -fcase-merge -fdo-lambda-eta-expansion -freuse-con -flet-to-case $Oopt_PedanticBottoms $Oopt_MaxSimplifierIterations $Oopt_ShowSimplifierProgress ]" ),
+--
+--       "-fliberate-case",
+
+       -- Final clean-up simplification:
+       "-fsimplify",
+         "[", 
+               "-fmax-simplifier-iterations" ++ show iter,
+               -- No -finline-phase: allow all Ids to be inlined now
+         "]"
+
+       ]
+
+-----------------------------------------------------------------------------
+-- Paths & Libraries
+
+split_marker = ':'   -- not configurable (ToDo)
+
+import_paths, include_paths, library_paths :: IORef [String]
+GLOBAL_VAR(import_paths,  ["."], [String])
+GLOBAL_VAR(include_paths, ["."], [String])
+GLOBAL_VAR(library_paths, [],   [String])
+
+GLOBAL_VAR(cmdline_libraries,   [], [String])
+
+addToDirList :: IORef [String] -> String -> IO ()
+addToDirList ref path
+  = do paths <- readIORef ref
+       writeIORef ref (paths ++ split split_marker path)
+
+-----------------------------------------------------------------------------
+-- Packages
+
+GLOBAL_VAR(package_config, (findFile "package.conf" (cGHC_DRIVER_DIR++"/package.conf.inplace")), String)
+
+listPackages :: IO ()
+listPackages = do 
+  details <- readIORef package_details
+  hPutStr stdout (listPkgs details)
+  hPutChar stdout '\n'
+  exitWith ExitSuccess
+
+newPackage :: IO ()
+newPackage = do
+  checkConfigAccess
+  details <- readIORef package_details
+  hPutStr stdout "Reading package info from stdin... "
+  stuff <- getContents
+  let new_pkg = read stuff :: Package
+  catchAll new_pkg
+       (\_ -> throwDyn (OtherError "parse error in package info"))
+  hPutStrLn stdout "done."
+  if (name new_pkg `elem` map name details)
+       then throwDyn (OtherError ("package `" ++ name new_pkg ++ 
+                                       "' already installed"))
+       else do
+  conf_file <- readIORef package_config
+  savePackageConfig conf_file
+  maybeRestoreOldConfig conf_file $ do
+  writeNewConfig conf_file ( ++ [new_pkg])
+  exitWith ExitSuccess
+
+deletePackage :: String -> IO ()
+deletePackage pkg = do  
+  checkConfigAccess
+  details <- readIORef package_details
+  if (pkg `notElem` map name details)
+       then throwDyn (OtherError ("package `" ++ pkg ++ "' not installed"))
+       else do
+  conf_file <- readIORef package_config
+  savePackageConfig conf_file
+  maybeRestoreOldConfig conf_file $ do
+  writeNewConfig conf_file (filter ((/= pkg) . name))
+  exitWith ExitSuccess
+
+checkConfigAccess :: IO ()
+checkConfigAccess = do
+  conf_file <- readIORef package_config
+  access <- getPermissions conf_file
+  unless (writable access)
+       (throwDyn (OtherError "you don't have permission to modify the package configuration file"))
+
+maybeRestoreOldConfig :: String -> IO () -> IO ()
+maybeRestoreOldConfig conf_file io
+  = catchAllIO io (\e -> do
+        hPutStr stdout "\nWARNING: an error was encountered while the new \n\ 
+                      \configuration was being written.  Attempting to \n\ 
+                      \restore the old configuration... "
+        system ("cp " ++ conf_file ++ ".old " ++ conf_file)
+        hPutStrLn stdout "done."
+       throw e
+    )
+
+writeNewConfig :: String -> ([Package] -> [Package]) -> IO ()
+writeNewConfig conf_file fn = do
+  hPutStr stdout "Writing new package config file... "
+  old_details <- readIORef package_details
+  h <- openFile conf_file WriteMode
+  hPutStr h (dumpPackages (fn old_details))
+  hClose h
+  hPutStrLn stdout "done."
+
+savePackageConfig :: String -> IO ()
+savePackageConfig conf_file = do
+  hPutStr stdout "Saving old package config file... "
+    -- mv rather than cp because we've already done an hGetContents
+    -- on this file so we won't be able to open it for writing
+    -- unless we move the old one out of the way...
+  system ("mv " ++ conf_file ++ " " ++ conf_file ++ ".old")
+  hPutStrLn stdout "done."
+
+-- package list is maintained in dependency order
+packages = global ["std", "rts", "gmp"] :: IORef [String]
+-- comma in value, so can't use macro, grrr
+{-# NOINLINE packages #-}
+
+addPackage :: String -> IO ()
+addPackage package
+  = do pkg_details <- readIORef package_details
+       case lookupPkg package pkg_details of
+         Nothing -> throwDyn (OtherError ("unknown package name: " ++ package))
+         Just details -> do
+           ps <- readIORef packages
+           unless (package `elem` ps) $ do
+               mapM_ addPackage (package_deps details)
+               ps <- readIORef packages
+               writeIORef packages (package:ps)
+
+getPackageImportPath   :: IO [String]
+getPackageImportPath = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  return (nub (concat (map import_dirs ps')))
+
+getPackageIncludePath   :: IO [String]
+getPackageIncludePath = do
+  ps <- readIORef packages 
+  ps' <- getPackageDetails ps
+  return (nub (filter (not.null) (concatMap include_dirs ps')))
+
+       -- includes are in reverse dependency order (i.e. rts first)
+getPackageCIncludes   :: IO [String]
+getPackageCIncludes = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  return (reverse (nub (filter (not.null) (concatMap c_includes ps'))))
+
+getPackageLibraryPath  :: IO [String]
+getPackageLibraryPath = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  return (nub (concat (map library_dirs ps')))
+
+getPackageLibraries    :: IO [String]
+getPackageLibraries = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  tag <- readIORef build_tag
+  let suffix = if null tag then "" else '_':tag
+  return (concat (
+       map (\p -> map (++suffix) (hs_libraries p) ++ extra_libraries p) ps'
+     ))
+
+getPackageExtraGhcOpts :: IO [String]
+getPackageExtraGhcOpts = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  return (concatMap extra_ghc_opts ps')
+
+getPackageExtraCcOpts  :: IO [String]
+getPackageExtraCcOpts = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  return (concatMap extra_cc_opts ps')
+
+getPackageExtraLdOpts  :: IO [String]
+getPackageExtraLdOpts = do
+  ps <- readIORef packages
+  ps' <- getPackageDetails ps
+  return (concatMap extra_ld_opts ps')
+
+getPackageDetails :: [String] -> IO [Package]
+getPackageDetails ps = do
+  pkg_details <- readIORef package_details
+  return [ pkg | p <- ps, Just pkg <- [ lookupPkg p pkg_details ] ]
+
+GLOBAL_VAR(package_details, (error "package_details"), [Package])
+
+lookupPkg :: String -> [Package] -> Maybe Package
+lookupPkg nm ps
+   = case [p | p <- ps, name p == nm] of
+        []    -> Nothing
+        (p:_) -> Just p
+
+-----------------------------------------------------------------------------
+-- Ways
+
+-- The central concept of a "way" is that all objects in a given
+-- program must be compiled in the same "way".  Certain options change
+-- parameters of the virtual machine, eg. profiling adds an extra word
+-- to the object header, so profiling objects cannot be linked with
+-- non-profiling objects.
+
+-- After parsing the command-line options, we determine which "way" we
+-- are building - this might be a combination way, eg. profiling+ticky-ticky.
+
+-- We then find the "build-tag" associated with this way, and this
+-- becomes the suffix used to find .hi files and libraries used in
+-- this compilation.
+
+GLOBAL_VAR(build_tag, "", String)
+
+data WayName
+  = WayProf
+  | WayUnreg
+  | WayDll
+  | WayTicky
+  | WayPar
+  | WayGran
+  | WaySMP
+  | WayDebug
+  | WayUser_a
+  | WayUser_b
+  | WayUser_c
+  | WayUser_d
+  | WayUser_e
+  | WayUser_f
+  | WayUser_g
+  | WayUser_h
+  | WayUser_i
+  | WayUser_j
+  | WayUser_k
+  | WayUser_l
+  | WayUser_m
+  | WayUser_n
+  | WayUser_o
+  | WayUser_A
+  | WayUser_B
+  deriving (Eq,Ord)
+
+GLOBAL_VAR(ways, [] ,[WayName])
+
+-- ToDo: allow WayDll with any other allowed combination
+
+allowed_combinations = 
+   [  [WayProf,WayUnreg],
+      [WayProf,WaySMP]    -- works???
+   ]
+
+findBuildTag :: IO [String]  -- new options
+findBuildTag = do
+  way_names <- readIORef ways
+  case sort way_names of
+     []  -> do  writeIORef build_tag ""
+               return []
+
+     [w] -> do let details = lkupWay w
+              writeIORef build_tag (wayTag details)
+              return (wayOpts details)
+
+     ws  -> if  ws `notElem` allowed_combinations
+               then throwDyn (OtherError $
+                               "combination not supported: "  ++
+                               foldr1 (\a b -> a ++ '/':b) 
+                               (map (wayName . lkupWay) ws))
+               else let stuff = map lkupWay ws
+                        tag   = concat (map wayTag stuff)
+                        flags = map wayOpts stuff
+                    in do
+                    writeIORef build_tag tag
+                    return (concat flags)
+
+lkupWay w = 
+   case lookup w way_details of
+       Nothing -> error "findBuildTag"
+       Just details -> details
+
+data Way = Way {
+  wayTag   :: String,
+  wayName  :: String,
+  wayOpts  :: [String]
+  }
+
+way_details :: [ (WayName, Way) ]
+way_details =
+  [ (WayProf, Way  "p" "Profiling"  
+       [ "-fscc-profiling"
+       , "-DPROFILING"
+       , "-optc-DPROFILING"
+       , "-fvia-C" ]),
+
+    (WayTicky, Way  "t" "Ticky-ticky Profiling"  
+       [ "-fticky-ticky"
+       , "-DTICKY_TICKY"
+       , "-optc-DTICKY_TICKY"
+       , "-fvia-C" ]),
+
+    (WayUnreg, Way  "u" "Unregisterised" 
+       [ "-optc-DNO_REGS"
+       , "-optc-DUSE_MINIINTERPRETER"
+       , "-fno-asm-mangling"
+       , "-funregisterised"
+       , "-fvia-C" ]),
+
+    (WayDll, Way  "dll" "DLLized"
+        [ ]),
+
+    (WayPar, Way  "mp" "Parallel" 
+       [ "-fparallel"
+       , "-D__PARALLEL_HASKELL__"
+       , "-optc-DPAR"
+       , "-package concurrent"
+       , "-fvia-C" ]),
+
+    (WayGran, Way  "mg" "Gransim" 
+       [ "-fgransim"
+       , "-D__GRANSIM__"
+       , "-optc-DGRAN"
+       , "-package concurrent"
+       , "-fvia-C" ]),
+
+    (WaySMP, Way  "s" "SMP"
+       [ "-fsmp"
+       , "-optc-pthread"
+       , "-optl-pthread"
+       , "-optc-DSMP"
+       , "-fvia-C" ]),
+
+    (WayUser_a,  Way  "a"  "User way 'a'"  ["$WAY_a_REAL_OPTS"]),      
+    (WayUser_b,  Way  "b"  "User way 'b'"  ["$WAY_b_REAL_OPTS"]),      
+    (WayUser_c,  Way  "c"  "User way 'c'"  ["$WAY_c_REAL_OPTS"]),      
+    (WayUser_d,  Way  "d"  "User way 'd'"  ["$WAY_d_REAL_OPTS"]),      
+    (WayUser_e,  Way  "e"  "User way 'e'"  ["$WAY_e_REAL_OPTS"]),      
+    (WayUser_f,  Way  "f"  "User way 'f'"  ["$WAY_f_REAL_OPTS"]),      
+    (WayUser_g,  Way  "g"  "User way 'g'"  ["$WAY_g_REAL_OPTS"]),      
+    (WayUser_h,  Way  "h"  "User way 'h'"  ["$WAY_h_REAL_OPTS"]),      
+    (WayUser_i,  Way  "i"  "User way 'i'"  ["$WAY_i_REAL_OPTS"]),      
+    (WayUser_j,  Way  "j"  "User way 'j'"  ["$WAY_j_REAL_OPTS"]),      
+    (WayUser_k,  Way  "k"  "User way 'k'"  ["$WAY_k_REAL_OPTS"]),      
+    (WayUser_l,  Way  "l"  "User way 'l'"  ["$WAY_l_REAL_OPTS"]),      
+    (WayUser_m,  Way  "m"  "User way 'm'"  ["$WAY_m_REAL_OPTS"]),      
+    (WayUser_n,  Way  "n"  "User way 'n'"  ["$WAY_n_REAL_OPTS"]),      
+    (WayUser_o,  Way  "o"  "User way 'o'"  ["$WAY_o_REAL_OPTS"]),      
+    (WayUser_A,  Way  "A"  "User way 'A'"  ["$WAY_A_REAL_OPTS"]),      
+    (WayUser_B,  Way  "B"  "User way 'B'"  ["$WAY_B_REAL_OPTS"]) 
+  ]
+
+-----------------------------------------------------------------------------
+-- Programs for particular phases
+
+GLOBAL_VAR(pgm_L,   findFile "unlit"      cGHC_UNLIT,      String)
+GLOBAL_VAR(pgm_P,   cRAWCPP,                              String)
+GLOBAL_VAR(pgm_C,   findFile "hsc"        cGHC_HSC,        String)
+GLOBAL_VAR(pgm_c,   cGCC,                                 String)
+GLOBAL_VAR(pgm_m,   findFile "ghc-asm"    cGHC_MANGLER,    String)
+GLOBAL_VAR(pgm_s,   findFile "ghc-split"  cGHC_SPLIT,      String)
+GLOBAL_VAR(pgm_a,   cGCC,                                 String)
+GLOBAL_VAR(pgm_l,   cGCC,                                 String)
+
+-----------------------------------------------------------------------------
+-- Via-C compilation stuff
+
+-- flags returned are: ( all C compilations
+--                    , registerised HC compilations
+--                    )
+
+machdepCCOpts 
+   | prefixMatch "alpha"   cTARGETPLATFORM  
+       = return ( ["-static"], [] )
+
+   | prefixMatch "hppa"    cTARGETPLATFORM  
+        -- ___HPUX_SOURCE, not _HPUX_SOURCE, is #defined if -ansi!
+        -- (very nice, but too bad the HP /usr/include files don't agree.)
+       = return ( ["-static", "-D_HPUX_SOURCE"], [] )
+
+   | prefixMatch "m68k"    cTARGETPLATFORM
+      -- -fno-defer-pop : for the .hc files, we want all the pushing/
+      --    popping of args to routines to be explicit; if we let things
+      --    be deferred 'til after an STGJUMP, imminent death is certain!
+      --
+      -- -fomit-frame-pointer : *don't*
+      --     It's better to have a6 completely tied up being a frame pointer
+      --     rather than let GCC pick random things to do with it.
+      --     (If we want to steal a6, then we would try to do things
+      --     as on iX86, where we *do* steal the frame pointer [%ebp].)
+       = return ( [], ["-fno-defer-pop", "-fno-omit-frame-pointer"] )
+
+   | prefixMatch "i386"    cTARGETPLATFORM  
+      -- -fno-defer-pop : basically the same game as for m68k
+      --
+      -- -fomit-frame-pointer : *must* in .hc files; because we're stealing
+      --   the fp (%ebp) for our register maps.
+       = do n_regs <- readState stolen_x86_regs
+            sta    <- readIORef static
+            return ( [ if sta then "-DDONT_WANT_WIN32_DLL_SUPPORT" else "" ],
+                     [ "-fno-defer-pop", "-fomit-frame-pointer",
+                       "-DSTOLEN_X86_REGS="++show n_regs ]
+                   )
+
+   | prefixMatch "mips"    cTARGETPLATFORM
+       = return ( ["static"], [] )
+
+   | prefixMatch "powerpc" cTARGETPLATFORM || prefixMatch "rs6000" cTARGETPLATFORM
+       = return ( ["static"], ["-finhibit-size-directive"] )
+
+   | otherwise
+       = return ( [], [] )
+
+-----------------------------------------------------------------------------
+-- Build the Hsc command line
+
+build_hsc_opts :: IO [String]
+build_hsc_opts = do
+  opt_C_ <- getOpts opt_C              -- misc hsc opts
+
+       -- warnings
+  warn_level <- readState warning_opt
+  let warn_opts =  case warn_level of
+                       W_default -> standardWarnings
+                       W_        -> minusWOpts
+                       W_all     -> minusWallOpts
+                       W_not     -> []
+
+       -- optimisation
+  minus_o <- readIORef opt_level
+  optimisation_opts <-
+        case minus_o of
+           0 -> hsc_minusNoO_flags
+           1 -> hsc_minusO_flags
+           2 -> hsc_minusO2_flags
+           _ -> error "unknown opt level"
+           -- ToDo: -Ofile
+       -- STG passes
+  ways_ <- readIORef ways
+  let stg_massage | WayProf `elem` ways_ =  "-fmassage-stg-for-profiling"
+                 | otherwise            = ""
+
+  stg_stats <- readIORef opt_StgStats
+  let stg_stats_flag | stg_stats = "-dstg-stats"
+                    | otherwise = ""
+
+  let stg_opts = [ stg_massage, stg_stats_flag, "-flet-no-escape" ]
+       -- let-no-escape always on for now
+
+  verb <- is_verbose
+  let hi_vers = "-fhi-version="++cProjectVersionInt
+  static <- (do s <- readIORef static; if s then return "-static" else return "")
+
+  l <- readIORef hsc_lang
+  let lang = case l of
+               HscC    -> "-olang=C"
+               HscAsm  -> "-olang=asm"
+               HscJava -> "-olang=java"
+
+  -- get hi-file suffix
+  hisuf <- readIORef hi_suf
+
+  -- hi-suffix for packages depends on the build tag.
+  package_hisuf <-
+       do tag <- readIORef build_tag
+          if null tag
+               then return "hi"
+               else return (tag ++ "_hi")
+
+  import_dirs <- readIORef import_paths
+  package_import_dirs <- getPackageImportPath
+  
+  let hi_map = "-himap=" ++
+               makeHiMap import_dirs hisuf 
+                        package_import_dirs package_hisuf
+                        split_marker
+
+      hi_map_sep = "-himap-sep=" ++ [split_marker]
+
+  scale <- readIORef scale_sizes_by
+  heap  <- readState specific_heap_size
+  stack <- readState specific_stack_size
+
+  -- take into account -fno-* flags by removing the equivalent -f*
+  -- flag from our list.
+  anti_flags <- getOpts anti_opt_C
+  let basic_opts = opt_C_ ++ warn_opts ++ optimisation_opts ++ stg_opts
+      filtered_opts = filter (`notElem` anti_flags) basic_opts
+  
+  return 
+       (  
+       filtered_opts
+       -- ToDo: C stub files
+       ++ [ hi_vers, static, verb, lang, hi_map, hi_map_sep ]
+       ++ rts_opts
+       )
+
+makeHiMap 
+  (import_dirs         :: [String])
+  (hi_suffix           :: String)
+  (package_import_dirs :: [String])
+  (package_hi_suffix   :: String)   
+  (split_marker        :: Char)
+  = foldr (add_dir hi_suffix) 
+       (foldr (add_dir package_hi_suffix) "" package_import_dirs)
+       import_dirs
+  where
+     add_dir hisuf dir str = dir ++ "%." ++ hisuf ++ split_marker : str
+
+
+getOptionsFromSource 
+       :: String               -- input file
+       -> IO [String]          -- options, if any
+getOptionsFromSource file
+  = do h <- openFile file ReadMode
+       catchJust ioErrors (look h)
+         (\e -> if isEOFError e then return [] else ioError e)
+  where
+       look h = do
+           l <- hGetLine h
+           case () of
+               () | null l -> look h
+                  | prefixMatch "#" l -> look h
+                  | prefixMatch "{-# LINE" l -> look h   -- -}
+                  | Just (opts:_) <- matchRegex optionRegex l
+                       -> return (words opts)
+                  | otherwise -> return []
+
+optionRegex = mkRegex "\\{-#[ \t]+OPTIONS[ \t]+(.*)#-\\}"   -- -}
+
+-----------------------------------------------------------------------------
+-- Main loop
+
+get_source_files :: [String] -> ([String],[String])
+get_source_files = partition (('-' /=) . head)
+
+main =
+  -- all error messages are propagated as exceptions
+  my_catchDyn (\dyn -> case dyn of
+                         PhaseFailed _phase code -> exitWith code
+                         Interrupted -> exitWith (ExitFailure 1)
+                         _ -> do hPutStrLn stderr (show (dyn :: BarfKind))
+                                 exitWith (ExitFailure 1)
+             ) $
+
+  -- make sure we clean up after ourselves
+  later (do  forget_it <- readIORef keep_tmp_files
+            unless forget_it $ do
+            verb <- readIORef verbose
+            cleanTempFiles verb
+        )
+       -- exceptions will be blocked while we clean the temporary files,
+       -- so there shouldn't be any difficulty if we receive further
+       -- signals.
+
+  do
+       -- install signal handlers
+   main_thread <- myThreadId
+
+#ifndef mingw32_TARGET_OS
+   let sig_handler = Catch (raiseInThread main_thread 
+                               (DynException (toDyn Interrupted)))
+   installHandler sigQUIT sig_handler Nothing 
+   installHandler sigINT  sig_handler Nothing
+#endif
+
+   doIfSet opt_Verbose 
+       (hPutStr stderr "Glasgow Haskell Compiler, Version "    >>
+        hPutStr stderr compiler_version                        >>
+        hPutStr stderr ", for Haskell 98, compiled by GHC version " >>
+        hPutStr stderr booter_version                          >>
+        hPutStr stderr "\n")                                   >>
+
+   pgm    <- getProgName
+   writeIORef prog_name pgm
+
+   argv   <- getArgs
+
+       -- grab any -B options from the command line first
+   argv'  <- setTopDir argv
+
+       -- read the package configuration
+   conf_file <- readIORef package_config
+   contents <- readFile conf_file
+   writeIORef package_details (read contents)
+
+       -- find the phase to stop after (i.e. -E, -C, -c, -S flags)
+   (flags2, todo, stop_flag) <- getToDo argv'
+   writeIORef v_todo todo
+
+       -- process all the other arguments, and get the source files
+   srcs <- processArgs driver_opts flags2 []
+
+       -- find the build tag, and re-process the build-specific options
+   more_opts <- findBuildTag
+   _ <- processArgs driver_opts more_opts []
+
+       -- get the -v flag
+   verb <- readIORef verbose
+
+   when verb (hPutStrLn stderr ("Using package config file: " ++ conf_file))
+
+       -- mkdependHS is special
+   when (todo == DoMkDependHS) beginMkDependHS
+
+       -- for each source file, find which phases to run
+   pipelines <- mapM (genPipeline todo stop_flag) srcs
+   let src_pipelines = zip srcs pipelines
+
+   o_file <- readIORef output_file
+   if isJust o_file && todo /= DoLink && length srcs > 1
+       then throwDyn (UsageError "can't apply -o option to multiple source files")
+       else do
+
+   if null srcs then throwDyn (UsageError "no input files") else do
+
+       -- save the flag state, because this could be modified by OPTIONS pragmas
+       -- during the compilation, and we'll need to restore it before starting
+       -- the next compilation.
+   saved_driver_state <- readIORef driver_state
+
+   let compileFile (src, phases) = do
+         r <- run_pipeline phases src (todo==DoLink) True orig_base orig_suff
+         writeIORef driver_state saved_driver_state
+         return r
+         where (orig_base, orig_suff) = splitFilename src
+
+   o_files <- mapM compileFile src_pipelines
+
+   when (todo == DoMkDependHS) endMkDependHS
+
+   when (todo == DoLink) (do_link o_files)
+
+
+-----------------------------------------------------------------------------
+-- Which phase to stop at
+
+data ToDo = DoMkDependHS | DoMkDLL | StopBefore Phase | DoLink
+  deriving (Eq)
+
+GLOBAL_VAR(v_todo, error "todo", ToDo)
+
+todoFlag :: String -> Maybe ToDo
+todoFlag "-M" = Just $ DoMkDependHS
+todoFlag "-E" = Just $ StopBefore Hsc
+todoFlag "-C" = Just $ StopBefore HCc
+todoFlag "-S" = Just $ StopBefore As
+todoFlag "-c" = Just $ StopBefore Ln
+todoFlag _    = Nothing
+
+getToDo :: [String]
+        -> IO ( [String]   -- rest of command line
+              , ToDo       -- phase to stop at
+              , String     -- "stop at" flag
+              )
+getToDo flags 
+  = case my_partition todoFlag flags of
+       ([]   , rest) -> return (rest, DoLink,  "") -- default is to do linking
+       ([(flag,one)], rest) -> return (rest, one, flag)
+       (_    , _   ) -> 
+         throwDyn (OtherError "only one of the flags -M, -E, -C, -S, -c is allowed")
+
+-----------------------------------------------------------------------------
+-- genPipeline
+--
+-- Herein is all the magic about which phases to run in which order, whether
+-- the intermediate files should be in /tmp or in the current directory,
+-- what the suffix of the intermediate files should be, etc.
+
+-- The following compilation pipeline algorithm is fairly hacky.  A
+-- better way to do this would be to express the whole comilation as a
+-- data flow DAG, where the nodes are the intermediate files and the
+-- edges are the compilation phases.  This framework would also work
+-- nicely if a haskell dependency generator was included in the
+-- driver.
+
+-- It would also deal much more cleanly with compilation phases that
+-- generate multiple intermediates, (eg. hsc generates .hc, .hi, and
+-- possibly stub files), where some of the output files need to be
+-- processed further (eg. the stub files need to be compiled by the C
+-- compiler).
+
+-- A cool thing to do would then be to execute the data flow graph
+-- concurrently, automatically taking advantage of extra processors on
+-- the host machine.  For example, when compiling two Haskell files
+-- where one depends on the other, the data flow graph would determine
+-- that the C compiler from the first comilation can be overlapped
+-- with the hsc comilation for the second file.
+
+data IntermediateFileType
+  = Temporary
+  | Persistent
+  deriving (Eq)
+
+-- the first compilation phase for a given file is determined
+-- by its suffix.
+startPhase "lhs"   = Unlit
+startPhase "hs"    = Cpp
+startPhase "hc"    = HCc
+startPhase "c"     = Cc
+startPhase "raw_s" = Mangle
+startPhase "s"     = As
+startPhase "S"     = As
+startPhase "o"     = Ln     
+startPhase _       = Ln           -- all unknown file types
+
+genPipeline
+   :: ToDo             -- when to stop
+   -> String           -- "stop after" flag (for error messages)
+   -> String           -- original filename
+   -> IO [             -- list of phases to run for this file
+            (Phase,
+             IntermediateFileType,  -- keep the output from this phase?
+             String)                -- output file suffix
+         ]     
+
+genPipeline todo stop_flag filename
+ = do
+   split      <- readIORef split_object_files
+   mangle     <- readIORef do_asm_mangling
+   lang       <- readIORef hsc_lang
+   keep_hc    <- readIORef keep_hc_files
+   keep_raw_s <- readIORef keep_raw_s_files
+   keep_s     <- readIORef keep_s_files
+
+   let
+   ----------- -----  ----   ---   --   --  -  -  -
+    (_basename, suffix) = splitFilename filename
+
+    start_phase = startPhase suffix
+
+    haskell_ish_file = suffix `elem` [ "hs", "lhs", "hc" ]
+    c_ish_file       = suffix `elem` [ "c", "s", "S" ]  -- maybe .cc et al.??
+
+   -- for a .hc file, or if the -C flag is given, we need to force lang to HscC
+    real_lang 
+       | suffix == "hc"  = HscC
+       | todo == StopBefore HCc && lang /= HscC && haskell_ish_file = HscC
+       | otherwise = lang
+
+   let
+   ----------- -----  ----   ---   --   --  -  -  -
+    pipeline
+      | todo == DoMkDependHS = [ Unlit, Cpp, MkDependHS ]
+
+      | haskell_ish_file = 
+       case real_lang of
+       HscC    | split && mangle -> [ Unlit, Cpp, Hsc, HCc, Mangle, 
+                                       SplitMangle, SplitAs ]
+               | mangle          -> [ Unlit, Cpp, Hsc, HCc, Mangle, As ]
+               | split           -> not_valid
+               | otherwise       -> [ Unlit, Cpp, Hsc, HCc, As ]
+
+       HscAsm  | split           -> [ Unlit, Cpp, Hsc, SplitMangle, SplitAs ]
+               | otherwise       -> [ Unlit, Cpp, Hsc, As ]
+
+       HscJava | split           -> not_valid
+               | otherwise       -> error "not implemented: compiling via Java"
+
+      | c_ish_file      = [ Cc, As ]
+
+      | otherwise       = [ ]  -- just pass this file through to the linker
+
+       -- ToDo: this is somewhat cryptic
+    not_valid = throwDyn (OtherError ("invalid option combination"))
+   ----------- -----  ----   ---   --   --  -  -  -
+
+       -- this shouldn't happen.
+   if start_phase /= Ln && start_phase `notElem` pipeline
+       then throwDyn (OtherError ("can't find starting phase for "
+                                   ++ filename))
+       else do
+
+       -- if we can't find the phase we're supposed to stop before,
+       -- something has gone wrong.
+   case todo of
+       StopBefore phase -> 
+          when (phase /= Ln 
+                && phase `notElem` pipeline
+                && not (phase == As && SplitAs `elem` pipeline)) $
+             throwDyn (OtherError 
+               ("flag " ++ stop_flag
+                ++ " is incompatible with source file `" ++ filename ++ "'"))
+       _ -> return ()
+
+   let
+   ----------- -----  ----   ---   --   --  -  -  -
+      annotatePipeline
+        :: [Phase]             -- raw pipeline
+        -> Phase               -- phase to stop before
+        -> [(Phase, IntermediateFileType, String{-file extension-})]
+      annotatePipeline []     _    = []
+      annotatePipeline (Ln:_) _    = []
+      annotatePipeline (phase:next_phase:ps) stop = 
+         (phase, keep_this_output, phase_input_ext next_phase)
+            : annotatePipeline (next_phase:ps) stop
+         where
+               keep_this_output
+                    | next_phase == stop = Persistent
+                    | otherwise =
+                       case next_phase of
+                            Ln -> Persistent
+                            Mangle | keep_raw_s -> Persistent
+                            As     | keep_s     -> Persistent
+                            HCc    | keep_hc    -> Persistent
+                            _other              -> Temporary
+
+       -- add information about output files to the pipeline
+       -- the suffix on an output file is determined by the next phase
+       -- in the pipeline, so we add linking to the end of the pipeline
+       -- to force the output from the final phase to be a .o file.
+      stop_phase = case todo of StopBefore phase -> phase
+                               DoMkDependHS     -> Ln
+                               DoLink           -> Ln
+      annotated_pipeline = annotatePipeline (pipeline ++ [ Ln ]) stop_phase
+
+      phase_ne p (p1,_,_) = (p1 /= p)
+   ----------- -----  ----   ---   --   --  -  -  -
+
+   return $
+     dropWhile (phase_ne start_phase) . 
+       foldr (\p ps -> if phase_ne stop_phase p then p:ps else [])  []
+               $ annotated_pipeline
+
+
+
+-- the output suffix for a given phase is uniquely determined by
+-- the input requirements of the next phase.
+phase_input_ext Unlit       = "lhs"
+phase_input_ext        Cpp         = "lpp"
+phase_input_ext        Hsc         = "cpp"
+phase_input_ext        HCc         = "hc"
+phase_input_ext Cc          = "c"
+phase_input_ext        Mangle      = "raw_s"
+phase_input_ext        SplitMangle = "split_s" -- not really generated
+phase_input_ext        As          = "s"
+phase_input_ext        SplitAs     = "split_s" -- not really generated
+phase_input_ext        Ln          = "o"
+phase_input_ext MkDependHS  = "dep"
+
+run_pipeline
+  :: [ (Phase, IntermediateFileType, String) ] -- phases to run
+  -> String                    -- input file
+  -> Bool                      -- doing linking afterward?
+  -> Bool                      -- take into account -o when generating output?
+  -> String                    -- original basename (eg. Main)
+  -> String                    -- original suffix   (eg. hs)
+  -> IO String                 -- return final filename
+
+run_pipeline [] input_fn _ _ _ _ = return input_fn
+run_pipeline ((phase, keep, o_suffix):phases) 
+       input_fn do_linking use_ofile orig_basename orig_suffix
+  = do
+
+     output_fn <- outputFileName (null phases) keep o_suffix
+
+     carry_on <- run_phase phase orig_basename orig_suffix input_fn output_fn
+       -- sometimes we bail out early, eg. when the compiler's recompilation
+       -- checker has determined that recompilation isn't necessary.
+     if not carry_on 
+       then do let (_,keep,final_suffix) = last phases
+               ofile <- outputFileName True keep final_suffix
+               return ofile
+       else do -- carry on ...
+
+       -- sadly, ghc -E is supposed to write the file to stdout.  We
+       -- generate <file>.cpp, so we also have to cat the file here.
+     when (null phases && phase == Cpp) $
+       run_something "Dump pre-processed file to stdout"
+                     ("cat " ++ output_fn)
+
+     run_pipeline phases output_fn do_linking use_ofile orig_basename orig_suffix
+
+  where
+     outputFileName last_phase keep suffix
+       = do o_file <- readIORef output_file
+            if last_phase && not do_linking && use_ofile && isJust o_file
+              then case o_file of 
+                      Just s  -> return s
+                      Nothing -> error "outputFileName"
+              else if keep == Persistent
+                          then do f <- odir_ify (orig_basename ++ '.':suffix)
+                                  osuf_ify f
+                          else do filename <- newTempName suffix
+                                  add files_to_clean filename
+                                  return filename
+
+-------------------------------------------------------------------------------
+-- mkdependHS
+
+       -- flags
+GLOBAL_VAR(dep_makefile,       "Makefile", String);
+GLOBAL_VAR(dep_include_prelude, False, Bool);
+GLOBAL_VAR(dep_ignore_dirs,    [], [String]);
+GLOBAL_VAR(dep_suffixes,       [], [String]);
+GLOBAL_VAR(dep_warnings,       True, Bool);
+
+       -- global vars
+GLOBAL_VAR(dep_makefile_hdl,           error "dep_makefile_hdl", Maybe Handle);
+GLOBAL_VAR(dep_tmp_file,               error "dep_tmp_file", String);
+GLOBAL_VAR(dep_tmp_hdl,                error "dep_tmp_hdl", Handle);
+GLOBAL_VAR(dep_dir_contents,           error "dep_dir_contents", [(String,[String])]);
+
+depStartMarker = "# DO NOT DELETE: Beginning of Haskell dependencies"
+depEndMarker   = "# DO NOT DELETE: End of Haskell dependencies"
+
+-- for compatibility with the old mkDependHS, we accept options of the form
+-- -optdep-f -optdep.depend, etc.
+dep_opts = [
+   (  "s",                     SepArg (add dep_suffixes) ),
+   (  "f",                     SepArg (writeIORef dep_makefile) ),
+   (  "w",                     NoArg (writeIORef dep_warnings False) ),
+   (  "-include-prelude",      NoArg (writeIORef dep_include_prelude True) ),
+   (  "X",                     Prefix (addToDirList dep_ignore_dirs) ),
+   (  "-exclude-directory=",   Prefix (addToDirList dep_ignore_dirs) )
+ ]
+
+beginMkDependHS :: IO ()
+beginMkDependHS = do
+
+       -- slurp in the mkdependHS-style options
+  flags <- getOpts opt_dep
+  _ <- processArgs dep_opts flags []
+
+       -- open a new temp file in which to stuff the dependency info
+       -- as we go along.
+  dep_file <- newTempName "dep"
+  add files_to_clean dep_file
+  writeIORef dep_tmp_file dep_file
+  tmp_hdl <- openFile dep_file WriteMode
+  writeIORef dep_tmp_hdl tmp_hdl
+
+       -- open the makefile
+  makefile <- readIORef dep_makefile
+  exists <- doesFileExist makefile
+  if not exists
+       then do 
+          writeIORef dep_makefile_hdl Nothing
+          return ()
+
+       else do
+          makefile_hdl <- openFile makefile ReadMode
+          writeIORef dep_makefile_hdl (Just makefile_hdl)
+
+               -- slurp through until we get the magic start string,
+               -- copying the contents into dep_makefile
+          let slurp = do
+               l <- hGetLine makefile_hdl
+               if (l == depStartMarker)
+                       then return ()
+                       else do hPutStrLn tmp_hdl l; slurp
+        
+               -- slurp through until we get the magic end marker,
+               -- throwing away the contents
+          let chuck = do
+               l <- hGetLine makefile_hdl
+               if (l == depEndMarker)
+                       then return ()
+                       else chuck
+        
+          catchJust ioErrors slurp 
+               (\e -> if isEOFError e then return () else ioError e)
+          catchJust ioErrors chuck
+               (\e -> if isEOFError e then return () else ioError e)
+
+
+       -- write the magic marker into the tmp file
+  hPutStrLn tmp_hdl depStartMarker
+
+       -- cache the contents of all the import directories, for future
+       -- reference.
+  import_dirs <- readIORef import_paths
+  pkg_import_dirs <- getPackageImportPath
+  import_dir_contents <- mapM getDirectoryContents import_dirs
+  pkg_import_dir_contents <- mapM getDirectoryContents pkg_import_dirs
+  writeIORef dep_dir_contents 
+       (zip import_dirs import_dir_contents ++
+        zip pkg_import_dirs pkg_import_dir_contents)
+
+       -- ignore packages unless --include-prelude is on
+  include_prelude <- readIORef dep_include_prelude
+  when (not include_prelude) $
+    mapM_ (add dep_ignore_dirs) pkg_import_dirs
+
+  return ()
+
+
+endMkDependHS :: IO ()
+endMkDependHS = do
+  makefile     <- readIORef dep_makefile
+  makefile_hdl <- readIORef dep_makefile_hdl
+  tmp_file     <- readIORef dep_tmp_file
+  tmp_hdl      <- readIORef dep_tmp_hdl
+
+       -- write the magic marker into the tmp file
+  hPutStrLn tmp_hdl depEndMarker
+
+  case makefile_hdl of
+     Nothing  -> return ()
+     Just hdl -> do
+
+         -- slurp the rest of the orignal makefile and copy it into the output
+       let slurp = do
+               l <- hGetLine hdl
+               hPutStrLn tmp_hdl l
+               slurp
+        
+       catchJust ioErrors slurp 
+               (\e -> if isEOFError e then return () else ioError e)
+
+       hClose hdl
+
+  hClose tmp_hdl  -- make sure it's flushed
+
+       -- create a backup of the original makefile
+  when (isJust makefile_hdl) $
+     run_something ("Backing up " ++ makefile)
+       (unwords [ "cp", makefile, makefile++".bak" ])
+
+       -- copy the new makefile in place
+  run_something "Installing new makefile"
+       (unwords [ "cp", tmp_file, makefile ])
+
+
+findDependency :: String -> Import -> IO (Maybe (String, Bool))
+findDependency mod imp = do
+   dir_contents <- readIORef dep_dir_contents
+   ignore_dirs  <- readIORef dep_ignore_dirs
+   hisuf <- readIORef hi_suf
+
+   let
+     (imp_mod, is_source) = 
+       case imp of
+          Normal str -> (str, False)
+          Source str -> (str, True )   
+
+     imp_hi = imp_mod ++ '.':hisuf
+     imp_hiboot = imp_mod ++ ".hi-boot"
+     imp_hiboot_v = imp_mod ++ ".hi-boot-" ++ cHscIfaceFileVersion
+     imp_hs = imp_mod ++ ".hs"
+     imp_lhs = imp_mod ++ ".lhs"
+
+     deps | is_source = [ imp_hiboot_v, imp_hiboot, imp_hs, imp_lhs ]
+         | otherwise = [ imp_hi, imp_hs, imp_lhs ]
+
+     search [] = throwDyn (OtherError ("can't find one of the following: " ++
+                                     unwords (map (\d -> '`': d ++ "'") deps) ++
+                                     " (imported from `" ++ mod ++ "')"))
+     search ((dir, contents) : dirs)
+          | null present = search dirs
+          | otherwise = 
+               if dir `elem` ignore_dirs 
+                       then return Nothing
+                       else if is_source
+                               then if dep /= imp_hiboot_v 
+                                       then return (Just (dir++'/':imp_hiboot, False)) 
+                                       else return (Just (dir++'/':dep, False))        
+                               else return (Just (dir++'/':imp_hi, not is_source))
+          where
+               present = filter (`elem` contents) deps
+               dep     = head present
+   -- in
+   search dir_contents
+
+
+-------------------------------------------------------------------------------
+-- Unlit phase 
+
+run_phase Unlit _basename _suff input_fn output_fn
+  = do unlit <- readIORef pgm_L
+       unlit_flags <- getOpts opt_L
+       run_something "Literate pre-processor"
+         ("echo '# 1 \"" ++input_fn++"\"' > "++output_fn++" && "
+          ++ unlit ++ ' ':input_fn ++ " - >> " ++ output_fn)
+       return True
+
+-------------------------------------------------------------------------------
+-- Cpp phase 
+
+run_phase Cpp _basename _suff input_fn output_fn
+  = do src_opts <- getOptionsFromSource input_fn
+       -- ToDo: this is *wrong* if we're processing more than one file:
+       -- the OPTIONS will persist through the subsequent compilations.
+       _ <- processArgs driver_opts src_opts []
+
+       do_cpp <- readState cpp_flag
+       if do_cpp
+          then do
+                   cpp <- readIORef pgm_P
+           hscpp_opts <- getOpts opt_P
+                   hs_src_cpp_opts <- readIORef hs_source_cpp_opts
+
+           cmdline_include_paths <- readIORef include_paths
+           pkg_include_dirs <- getPackageIncludePath
+           let include_paths = map (\p -> "-I"++p) (cmdline_include_paths
+                                                       ++ pkg_include_dirs)
+
+           verb <- is_verbose
+           run_something "C pre-processor" 
+               (unwords
+                          (["echo '{-# LINE 1 \"" ++ input_fn ++ "\" -}'", ">", output_fn, "&&",
+                    cpp, verb] 
+                   ++ include_paths
+                   ++ hs_src_cpp_opts
+                   ++ hscpp_opts
+                   ++ [ "-x", "c", input_fn, ">>", output_fn ]
+                  ))
+         else do
+           run_something "Ineffective C pre-processor"
+                  ("echo '{-# LINE 1 \""  ++ input_fn ++ "\" -}' > " 
+                   ++ output_fn ++ " && cat " ++ input_fn
+                   ++ " >> " ++ output_fn)
+       return True
+
+-----------------------------------------------------------------------------
+-- MkDependHS phase
+
+run_phase MkDependHS basename suff input_fn _output_fn = do 
+   src <- readFile input_fn
+   let imports = getImports src
+
+   deps <- mapM (findDependency basename) imports
+
+   osuf_opt <- readIORef output_suf
+   let osuf = case osuf_opt of
+                       Nothing -> "o"
+                       Just s  -> s
+
+   extra_suffixes <- readIORef dep_suffixes
+   let suffixes = osuf : map (++ ('_':osuf)) extra_suffixes
+       ofiles = map (\suf -> basename ++ '.':suf) suffixes
+          
+   objs <- mapM odir_ify ofiles
+   
+   hdl <- readIORef dep_tmp_hdl
+
+       -- std dependeny of the object(s) on the source file
+   hPutStrLn hdl (unwords objs ++ " : " ++ basename ++ '.':suff)
+
+   let genDep (dep, False {- not an hi file -}) = 
+         hPutStrLn hdl (unwords objs ++ " : " ++ dep)
+       genDep (dep, True  {- is an hi file -}) = do
+         hisuf <- readIORef hi_suf
+         let dep_base = remove_suffix '.' dep
+             deps = (dep_base ++ hisuf)
+                    : map (\suf -> dep_base ++ suf ++ '_':hisuf) extra_suffixes
+                 -- length objs should be == length deps
+         sequence_ (zipWith (\o d -> hPutStrLn hdl (o ++ " : " ++ d)) objs deps)
+
+   mapM genDep [ d | Just d <- deps ]
+
+   return True
+
+-- add the lines to dep_makefile:
+          -- always:
+                  -- this.o : this.hs
+
+          -- if the dependency is on something other than a .hi file:
+                  -- this.o this.p_o ... : dep
+          -- otherwise
+                  -- if the import is {-# SOURCE #-}
+                          -- this.o this.p_o ... : dep.hi-boot[-$vers]
+                          
+                  -- else
+                          -- this.o ...   : dep.hi
+                          -- this.p_o ... : dep.p_hi
+                          -- ...
+   
+          -- (where .o is $osuf, and the other suffixes come from
+          -- the cmdline -s options).
+   
+-----------------------------------------------------------------------------
+-- Hsc phase
+
+run_phase Hsc  basename suff input_fn output_fn
+  = do  hsc <- readIORef pgm_C
+       
+  -- we add the current directory (i.e. the directory in which
+  -- the .hs files resides) to the import path, since this is
+  -- what gcc does, and it's probably what you want.
+       let current_dir = getdir basename
+       
+       paths <- readIORef include_paths
+       writeIORef include_paths (current_dir : paths)
+       
+  -- build the hsc command line
+       hsc_opts <- build_hsc_opts
+       
+       doing_hi <- readIORef produceHi
+       tmp_hi_file <- if doing_hi      
+                         then do fn <- newTempName "hi"
+                                 add files_to_clean fn
+                                 return fn
+                         else return ""
+       
+  -- tmp files for foreign export stub code
+       tmp_stub_h <- newTempName "stub_h"
+       tmp_stub_c <- newTempName "stub_c"
+       add files_to_clean tmp_stub_h
+       add files_to_clean tmp_stub_c
+       
+  -- figure out where to put the .hi file
+       ohi    <- readIORef output_hi
+       hisuf  <- readIORef hi_suf
+       let hi_flags = case ohi of
+                          Nothing -> [ "-hidir="++current_dir, "-hisuf="++hisuf ]
+                          Just fn -> [ "-hifile="++fn ]
+
+  -- figure out if the source has changed, for recompilation avoidance.
+  -- only do this if we're eventually going to generate a .o file.
+  -- (ToDo: do when generating .hc files too?)
+  --
+  -- Setting source_unchanged to "-fsource_unchanged" means that M.o seems
+  -- to be up to date wrt M.hs; so no need to recompile unless imports have
+  -- changed (which the compiler itself figures out).
+  -- Setting source_unchanged to "" tells the compiler that M.o is out of
+  -- date wrt M.hs (or M.o doesn't exist) so we must recompile regardless.
+       do_recomp <- readIORef recomp
+       todo <- readIORef v_todo
+        o_file <- odir_ify (basename ++ '.':phase_input_ext Ln)
+       source_unchanged <- 
+          if not (do_recomp && ( todo == DoLink || todo == StopBefore Ln ))
+            then return ""
+            else do t1 <- getModificationTime (basename ++ '.':suff)
+                    o_file_exists <- doesFileExist o_file
+                    if not o_file_exists
+                       then return ""  -- Need to recompile
+                       else do t2 <- getModificationTime o_file
+                               if t2 > t1
+                                 then return "-fsource-unchanged"
+                                 else return ""
+
+  -- run the compiler!
+       run_something "Haskell Compiler" 
+                (unwords (hsc : input_fn : (
+                   hsc_opts
+                   ++ hi_flags
+                   ++ [ 
+                         source_unchanged,
+                         "-ofile="++output_fn, 
+                         "-F="++tmp_stub_c, 
+                         "-FH="++tmp_stub_h 
+                      ]
+                   ++ stat_opts
+                )))
+
+  -- check whether compilation was performed, bail out if not
+       b <- doesFileExist output_fn
+       if not b && not (null source_unchanged) -- sanity
+               then do run_something "Touching object file"
+                           ("touch " ++ o_file)
+                       return False
+               else do -- carry on...
+
+  -- Deal with stubs
+       let stub_h = basename ++ "_stub.h"
+       let stub_c = basename ++ "_stub.c"
+       
+               -- copy .h_stub file into current dir if present
+       b <- doesFileExist tmp_stub_h
+       when b (do
+               run_something "Copy stub .h file"
+                               ("cp " ++ tmp_stub_h ++ ' ':stub_h)
+       
+                       -- #include <..._stub.h> in .hc file
+               addCmdlineHCInclude tmp_stub_h  -- hack
+
+                       -- copy the _stub.c file into the current dir
+               run_something "Copy stub .c file" 
+                   (unwords [ 
+                       "rm -f", stub_c, "&&",
+                       "echo \'#include \""++stub_h++"\"\' >"++stub_c, " &&",
+                       "cat", tmp_stub_c, ">> ", stub_c
+                       ])
+
+                       -- compile the _stub.c file w/ gcc
+               pipeline <- genPipeline (StopBefore Ln) "" stub_c
+               run_pipeline pipeline stub_c False{-no linking-} 
+                               False{-no -o option-}
+                               (basename++"_stub") "c"
+
+               add ld_inputs (basename++"_stub.o")
+        )
+       return True
+
+-----------------------------------------------------------------------------
+-- Cc phase
+
+-- we don't support preprocessing .c files (with -E) now.  Doing so introduces
+-- way too many hacks, and I can't say I've ever used it anyway.
+
+run_phase cc_phase _basename _suff input_fn output_fn
+   | cc_phase == Cc || cc_phase == HCc
+   = do        cc <- readIORef pgm_c
+               cc_opts <- (getOpts opt_c)
+               cmdline_include_dirs <- readIORef include_paths
+
+        let hcc = cc_phase == HCc
+
+               -- add package include paths even if we're just compiling
+               -- .c files; this is the Value Add(TM) that using
+               -- ghc instead of gcc gives you :)
+        pkg_include_dirs <- getPackageIncludePath
+       let include_paths = map (\p -> "-I"++p) (cmdline_include_dirs 
+                                                       ++ pkg_include_dirs)
+
+       c_includes <- getPackageCIncludes
+       cmdline_includes <- readState cmdline_hc_includes -- -#include options
+
+       let cc_injects | hcc = unlines (map mk_include 
+                                       (c_includes ++ reverse cmdline_includes))
+                      | otherwise = ""
+           mk_include h_file = 
+               case h_file of 
+                  '"':_{-"-} -> "#include "++h_file
+                  '<':_      -> "#include "++h_file
+                  _          -> "#include \""++h_file++"\""
+
+       cc_help <- newTempName "c"
+       add files_to_clean cc_help
+       h <- openFile cc_help WriteMode
+       hPutStr h cc_injects
+       hPutStrLn h ("#include \"" ++ input_fn ++ "\"\n")
+       hClose h
+
+       ccout <- newTempName "ccout"
+       add files_to_clean ccout
+
+       mangle <- readIORef do_asm_mangling
+       (md_c_flags, md_regd_c_flags) <- machdepCCOpts
+
+        verb <- is_verbose
+
+       o2 <- readIORef opt_minus_o2_for_C
+       let opt_flag | o2        = "-O2"
+                    | otherwise = "-O"
+
+       pkg_extra_cc_opts <- getPackageExtraCcOpts
+
+       excessPrecision <- readState excess_precision
+
+       run_something "C Compiler"
+        (unwords ([ cc, "-x", "c", cc_help, "-o", output_fn ]
+                  ++ md_c_flags
+                  ++ (if cc_phase == HCc && mangle
+                        then md_regd_c_flags
+                        else [])
+                  ++ [ verb, "-S", "-Wimplicit", opt_flag ]
+                  ++ [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt ]
+                  ++ cc_opts
+#ifdef mingw32_TARGET_OS
+                   ++ [" -mno-cygwin"]
+#endif
+                  ++ (if excessPrecision then [] else [ "-ffloat-store" ])
+                  ++ include_paths
+                  ++ pkg_extra_cc_opts
+--                ++ [">", ccout]
+                  ))
+       return True
+
+       -- ToDo: postprocess the output from gcc
+
+-----------------------------------------------------------------------------
+-- Mangle phase
+
+run_phase Mangle _basename _suff input_fn output_fn
+  = do mangler <- readIORef pgm_m
+       mangler_opts <- getOpts opt_m
+       machdep_opts <-
+        if (prefixMatch "i386" cTARGETPLATFORM)
+           then do n_regs <- readState stolen_x86_regs
+                   return [ show n_regs ]
+           else return []
+       run_something "Assembly Mangler"
+       (unwords (mangler : 
+                    mangler_opts
+                 ++ [ input_fn, output_fn ]
+                 ++ machdep_opts
+               ))
+       return True
+
+-----------------------------------------------------------------------------
+-- Splitting phase
+
+run_phase SplitMangle _basename _suff input_fn _output_fn
+  = do  splitter <- readIORef pgm_s
+
+       -- this is the prefix used for the split .s files
+       tmp_pfx <- readIORef tmpdir
+       x <- getProcessID
+       let split_s_prefix = tmp_pfx ++ "/ghc" ++ show x
+       writeIORef split_prefix split_s_prefix
+       add files_to_clean (split_s_prefix ++ "__*") -- d:-)
+
+       -- allocate a tmp file to put the no. of split .s files in (sigh)
+       n_files <- newTempName "n_files"
+       add files_to_clean n_files
+
+       run_something "Split Assembly File"
+        (unwords [ splitter
+                 , input_fn
+                 , split_s_prefix
+                 , n_files ]
+        )
+
+       -- save the number of split files for future references
+       s <- readFile n_files
+       let n = read s :: Int
+       writeIORef n_split_files n
+       return True
+
+-----------------------------------------------------------------------------
+-- As phase
+
+run_phase As _basename _suff input_fn output_fn
+  = do         as <- readIORef pgm_a
+        as_opts <- getOpts opt_a
+
+        cmdline_include_paths <- readIORef include_paths
+        let cmdline_include_flags = map (\p -> "-I"++p) cmdline_include_paths
+        run_something "Assembler"
+          (unwords (as : as_opts
+                      ++ cmdline_include_flags
+                      ++ [ "-c", input_fn, "-o",  output_fn ]
+                   ))
+       return True
+
+run_phase SplitAs basename _suff _input_fn _output_fn
+  = do  as <- readIORef pgm_a
+        as_opts <- getOpts opt_a
+
+       split_s_prefix <- readIORef split_prefix
+       n <- readIORef n_split_files
+
+       odir <- readIORef output_dir
+       let real_odir = case odir of
+                               Nothing -> basename
+                               Just d  -> d
+
+       let assemble_file n = do
+                   let input_s  = split_s_prefix ++ "__" ++ show n ++ ".s"
+                   let output_o = newdir real_odir 
+                                       (basename ++ "__" ++ show n ++ ".o")
+                   real_o <- osuf_ify output_o
+                   run_something "Assembler" 
+                           (unwords (as : as_opts
+                                     ++ [ "-c", "-o", real_o, input_s ]
+                           ))
+       
+       mapM_ assemble_file [1..n]
+       return True
+
+-----------------------------------------------------------------------------
+-- Linking
+
+do_link :: [String] -> IO ()
+do_link o_files = do
+    ln <- readIORef pgm_l
+    verb <- is_verbose
+    o_file <- readIORef output_file
+    let output_fn = case o_file of { Just s -> s; Nothing -> "a.out"; }
+
+    pkg_lib_paths <- getPackageLibraryPath
+    let pkg_lib_path_opts = map ("-L"++) pkg_lib_paths
+
+    lib_paths <- readIORef library_paths
+    let lib_path_opts = map ("-L"++) lib_paths
+
+    pkg_libs <- getPackageLibraries
+    let pkg_lib_opts = map (\lib -> "-l"++lib) pkg_libs
+
+    libs <- readIORef cmdline_libraries
+    let lib_opts = map ("-l"++) (reverse libs)
+        -- reverse because they're added in reverse order from the cmd line
+
+    pkg_extra_ld_opts <- getPackageExtraLdOpts
+
+       -- probably _stub.o files
+    extra_ld_inputs <- readIORef ld_inputs
+
+       -- opts from -optl-<blah>
+    extra_ld_opts <- getOpts opt_l
+
+    run_something "Linker"
+       (unwords 
+        ([ ln, verb, "-o", output_fn ]
+        ++ o_files
+        ++ extra_ld_inputs
+        ++ lib_path_opts
+        ++ lib_opts
+        ++ pkg_lib_path_opts
+        ++ pkg_lib_opts
+        ++ pkg_extra_ld_opts
+        ++ extra_ld_opts
+       )
+       )
+
+-----------------------------------------------------------------------------
+-- Running an external program
+
+run_something phase_name cmd
+ = do
+   verb <- readIORef verbose
+   when verb $ do
+       putStr phase_name
+       putStrLn ":"
+       putStrLn cmd
+       hFlush stdout
+
+   -- test for -n flag
+   n <- readIORef dry_run
+   unless n $ do 
+
+   -- and run it!
+#ifndef mingw32_TARGET_OS
+   exit_code <- system cmd `catchAllIO` 
+                  (\_ -> throwDyn (PhaseFailed phase_name (ExitFailure 1)))
+#else
+   tmp <- newTempName "sh"
+   h <- openFile tmp WriteMode
+   hPutStrLn h cmd
+   hClose h
+   exit_code <- system ("sh - " ++ tmp) `catchAllIO` 
+                  (\e -> throwDyn (PhaseFailed phase_name (ExitFailure 1)))
+   removeFile tmp
+#endif
+
+   if exit_code /= ExitSuccess
+       then throwDyn (PhaseFailed phase_name exit_code)
+       else do when verb (putStr "\n")
+               return ()
+
+-----------------------------------------------------------------------------
+-- Flags
+
+data OptKind 
+       = NoArg (IO ())                 -- flag with no argument
+       | HasArg (String -> IO ())      -- flag has an argument (maybe prefix)
+       | SepArg (String -> IO ())      -- flag has a separate argument
+       | Prefix (String -> IO ())      -- flag is a prefix only
+       | OptPrefix (String -> IO ())   -- flag may be a prefix
+       | AnySuffix (String -> IO ())   -- flag is a prefix, pass whole arg to fn
+       | PassFlag  (String -> IO ())   -- flag with no arg, pass flag to fn
+
+-- note that ordering is important in the following list: any flag which
+-- is a prefix flag (i.e. HasArg, Prefix, OptPrefix, AnySuffix) will override
+-- flags further down the list with the same prefix.
+
+driver_opts = 
+  [  ------- help -------------------------------------------------------
+     ( "?"             , NoArg long_usage)
+  ,  ( "-help"         , NoArg long_usage)
+  
+
+      ------- version ----------------------------------------------------
+  ,  ( "-version"       , NoArg (do hPutStrLn stdout (cProjectName
+                                     ++ ", version " ++ version_str)
+                                    exitWith ExitSuccess))
+  ,  ( "-numeric-version", NoArg (do hPutStrLn stdout version_str
+                                    exitWith ExitSuccess))
+
+      ------- verbosity ----------------------------------------------------
+  ,  ( "v"             , NoArg (writeIORef verbose True) )
+  ,  ( "n"              , NoArg (writeIORef dry_run True) )
+
+       ------- recompilation checker --------------------------------------
+  ,  ( "recomp"                , NoArg (writeIORef recomp True) )
+  ,  ( "no-recomp"     , NoArg (writeIORef recomp False) )
+
+       ------- ways --------------------------------------------------------
+  ,  ( "prof"          , NoArg (addNoDups ways WayProf) )
+  ,  ( "unreg"         , NoArg (addNoDups ways WayUnreg) )
+  ,  ( "dll"            , NoArg (addNoDups ways WayDll) )
+  ,  ( "ticky"         , NoArg (addNoDups ways WayTicky) )
+  ,  ( "parallel"      , NoArg (addNoDups ways WayPar) )
+  ,  ( "gransim"       , NoArg (addNoDups ways WayGran) )
+  ,  ( "smp"           , NoArg (addNoDups ways WaySMP) )
+  ,  ( "debug"         , NoArg (addNoDups ways WayDebug) )
+       -- ToDo: user ways
+
+       ------- Interface files ---------------------------------------------
+  ,  ( "hi"            , NoArg (writeIORef produceHi True) )
+  ,  ( "nohi"          , NoArg (writeIORef produceHi False) )
+  ,  ( "hi-diffs"      , NoArg (writeIORef hi_diffs  NormalHiDiffs) )
+  ,  ( "no-hi-diffs"   , NoArg (writeIORef hi_diffs  NoHiDiffs) )
+  ,  ( "hi-diffs-with-usages" , NoArg (writeIORef hi_diffs UsageHiDiffs) )
+  ,  ( "keep-hi-diffs" , NoArg (writeIORef keep_hi_diffs True) )
+       --"hi-with-*"    -> hiw <- readIORef hi_with  (ToDo)
+
+       --------- Profiling --------------------------------------------------
+  ,  ( "auto-dicts"    , NoArg (addOpt_C "-fauto-sccs-on-dicts") )
+  ,  ( "auto-all"      , NoArg (addOpt_C "-fauto-sccs-on-all-toplevs") )
+  ,  ( "auto"          , NoArg (addOpt_C "-fauto-sccs-on-exported-toplevs") )
+  ,  ( "caf-all"       , NoArg (addOpt_C "-fauto-sccs-on-individual-cafs") )
+         -- "ignore-sccs"  doesn't work  (ToDo)
+
+  ,  ( "no-auto-dicts" , NoArg (addAntiOpt_C "-fauto-sccs-on-dicts") )
+  ,  ( "no-auto-all"   , NoArg (addAntiOpt_C "-fauto-sccs-on-all-toplevs") )
+  ,  ( "no-auto"       , NoArg (addAntiOpt_C "-fauto-sccs-on-exported-toplevs") )
+  ,  ( "no-caf-all"    , NoArg (addAntiOpt_C "-fauto-sccs-on-individual-cafs") )
+
+       ------- Miscellaneous -----------------------------------------------
+  ,  ( "cpp"           , NoArg (updateState (\s -> s{ cpp_flag = True })) )
+  ,  ( "#include"      , HasArg (addCmdlineHCInclude) )
+  ,  ( "no-link-chk"    , NoArg (return ()) ) -- ignored for backwards compat
+
+       ------- Output Redirection ------------------------------------------
+  ,  ( "odir"          , HasArg (writeIORef output_dir  . Just) )
+  ,  ( "o"             , SepArg (writeIORef output_file . Just) )
+  ,  ( "osuf"          , HasArg (writeIORef output_suf  . Just) )
+  ,  ( "hisuf"         , HasArg (writeIORef hi_suf) )
+  ,  ( "tmpdir"                , HasArg (writeIORef tmpdir . (++ "/")) )
+  ,  ( "ohi"           , HasArg (\s -> case s of 
+                                         "-" -> writeIORef hi_on_stdout True
+                                         _   -> writeIORef output_hi (Just s)) )
+       -- -odump?
+
+  ,  ( "keep-hc-file"   , AnySuffix (\_ -> writeIORef keep_hc_files True) )
+  ,  ( "keep-s-file"    , AnySuffix (\_ -> writeIORef keep_s_files  True) )
+  ,  ( "keep-raw-s-file", AnySuffix (\_ -> writeIORef keep_raw_s_files  True) )
+  ,  ( "keep-tmp-files" , AnySuffix (\_ -> writeIORef keep_tmp_files True) )
+
+  ,  ( "split-objs"    , NoArg (if can_split
+                                   then do writeIORef split_object_files True
+                                           addOpt_C "-fglobalise-toplev-names"
+                                           addOpt_c "-DUSE_SPLIT_MARKERS"
+                                   else hPutStrLn stderr
+                                           "warning: don't know how to  split \
+                                           \object files on this architecture"
+                               ) )
+  
+       ------- Include/Import Paths ----------------------------------------
+  ,  ( "i"             , OptPrefix (addToDirList import_paths) )
+  ,  ( "I"             , Prefix    (addToDirList include_paths) )
+
+       ------- Libraries ---------------------------------------------------
+  ,  ( "L"             , Prefix (addToDirList library_paths) )
+  ,  ( "l"             , Prefix (add cmdline_libraries) )
+
+        ------- Packages ----------------------------------------------------
+  ,  ( "package-name"   , HasArg (\s -> addOpt_C ("-inpackage="++s)) )
+
+  ,  ( "package"        , HasArg (addPackage) )
+  ,  ( "syslib"         , HasArg (addPackage) )        -- for compatibility w/ old vsns
+
+  ,  ( "-list-packages"  , NoArg (listPackages) )
+  ,  ( "-add-package"    , NoArg (newPackage) )
+  ,  ( "-delete-package" , SepArg (deletePackage) )
+
+        ------- Specific phases  --------------------------------------------
+  ,  ( "pgmL"           , HasArg (writeIORef pgm_L) )
+  ,  ( "pgmP"           , HasArg (writeIORef pgm_P) )
+  ,  ( "pgmC"           , HasArg (writeIORef pgm_C) )
+  ,  ( "pgmc"           , HasArg (writeIORef pgm_c) )
+  ,  ( "pgmm"           , HasArg (writeIORef pgm_m) )
+  ,  ( "pgms"           , HasArg (writeIORef pgm_s) )
+  ,  ( "pgma"           , HasArg (writeIORef pgm_a) )
+  ,  ( "pgml"           , HasArg (writeIORef pgm_l) )
+
+  ,  ( "optdep"                , HasArg (addOpt_dep) )
+  ,  ( "optL"          , HasArg (addOpt_L) )
+  ,  ( "optP"          , HasArg (addOpt_P) )
+  ,  ( "optC"          , HasArg (addOpt_C) )
+  ,  ( "optc"          , HasArg (addOpt_c) )
+  ,  ( "optm"          , HasArg (addOpt_m) )
+  ,  ( "opta"          , HasArg (addOpt_a) )
+  ,  ( "optl"          , HasArg (addOpt_l) )
+  ,  ( "optdll"                , HasArg (addOpt_dll) )
+
+       ------ HsCpp opts ---------------------------------------------------
+  ,  ( "D"             , Prefix (\s -> addOpt_P ("-D'"++s++"'") ) )
+  ,  ( "U"             , Prefix (\s -> addOpt_P ("-U'"++s++"'") ) )
+
+       ------ Warning opts -------------------------------------------------
+  ,  ( "W"             , NoArg (updateState (\s -> s{ warning_opt = W_ })))
+  ,  ( "Wall"          , NoArg (updateState (\s -> s{ warning_opt = W_all })))
+  ,  ( "Wnot"          , NoArg (updateState (\s -> s{ warning_opt = W_not })))
+  ,  ( "w"             , NoArg (updateState (\s -> s{ warning_opt = W_not })))
+
+       ----- Linker --------------------------------------------------------
+  ,  ( "static"        , NoArg (writeIORef static True) )
+
+        ------ Compiler RTS options -----------------------------------------
+  ,  ( "H"                 , HasArg (newHeapSize  . decodeSize) )
+  ,  ( "K"                 , HasArg (newStackSize . decodeSize) )
+  ,  ( "Rscale-sizes"     , HasArg (floatOpt scale_sizes_by) )
+
+       ------ Debugging ----------------------------------------------------
+  ,  ( "dstg-stats"       , NoArg (writeIORef opt_StgStats True) )
+
+  ,  ( "dno-"             , Prefix (\s -> addAntiOpt_C ("-d"++s)) )
+  ,  ( "d"                , AnySuffix (addOpt_C) )
+
+       ------ Machine dependant (-m<blah>) stuff ---------------------------
+
+  ,  ( "monly-2-regs",                 NoArg (updateState (\s -> s{stolen_x86_regs = 2}) ))
+  ,  ( "monly-3-regs",                 NoArg (updateState (\s -> s{stolen_x86_regs = 3}) ))
+  ,  ( "monly-4-regs",                 NoArg (updateState (\s -> s{stolen_x86_regs = 4}) ))
+
+        ------ Compiler flags -----------------------------------------------
+  ,  ( "O2-for-C"         , NoArg (writeIORef opt_minus_o2_for_C True) )
+  ,  ( "O"                , OptPrefix (setOptLevel) )
+
+  ,  ( "fglasgow-exts-no-lang", NoArg ( do addOpt_C "-fglasgow-exts") )
+
+  ,  ( "fglasgow-exts"     , NoArg (do addOpt_C "-fglasgow-exts"
+                                      addPackage "lang"))
+
+  ,  ( "fasm"             , OptPrefix (\_ -> writeIORef hsc_lang HscAsm) )
+
+  ,  ( "fvia-c"                   , NoArg (writeIORef hsc_lang HscC) )
+  ,  ( "fvia-C"                   , NoArg (writeIORef hsc_lang HscC) )
+
+  ,  ( "fno-asm-mangling"  , NoArg (writeIORef do_asm_mangling False) )
+
+  ,  ( "fmax-simplifier-iterations", 
+               Prefix (writeIORef opt_MaxSimplifierIterations . read) )
+
+  ,  ( "fusagesp"         , NoArg (do writeIORef opt_UsageSPInf True
+                                      addOpt_C "-fusagesp-on") )
+
+  ,  ( "fexcess-precision" , NoArg (do updateState 
+                                          (\s -> s{ excess_precision = True })
+                                      addOpt_C "-fexcess-precision"))
+
+       -- flags that are "active negatives"
+  ,  ( "fno-implicit-prelude"  , PassFlag (addOpt_C) )
+  ,  ( "fno-prune-tydecls"     , PassFlag (addOpt_C) )
+  ,  ( "fno-prune-instdecls"   , PassFlag (addOpt_C) )
+  ,  ( "fno-pre-inlining"      , PassFlag (addOpt_C) )
+
+       -- All other "-fno-<blah>" options cancel out "-f<blah>" on the hsc cmdline
+  ,  ( "fno-",                 Prefix (\s -> addAntiOpt_C ("-f"++s)) )
+
+       -- Pass all remaining "-f<blah>" options to hsc
+  ,  ( "f",                    AnySuffix (addOpt_C) )
+  ]
+
+-----------------------------------------------------------------------------
+-- Process command-line  
+
+processArgs :: [(String,OptKind)] -> [String] -> [String]
+   -> IO [String]  -- returns spare args
+processArgs _spec [] spare = return (reverse spare)
+processArgs spec args@(('-':_):_) spare = do
+  args' <- processOneArg spec args
+  processArgs spec args' spare
+processArgs spec (arg:args) spare = 
+  processArgs spec args (arg:spare)
+
+processOneArg :: [(String,OptKind)] -> [String] -> IO [String]
+processOneArg spec (('-':arg):args) = do
+  let (rest,action) = findArg spec arg
+      dash_arg = '-':arg
+  case action of
+
+       NoArg  io -> 
+               if rest == ""
+                       then io >> return args
+                       else unknownFlagErr dash_arg
+
+       HasArg fio -> 
+               if rest /= "" 
+                       then fio rest >> return args
+                       else case args of
+                               [] -> unknownFlagErr dash_arg
+                               (arg1:args1) -> fio arg1 >> return args1
+
+       SepArg fio -> 
+               case args of
+                       [] -> unknownFlagErr dash_arg
+                       (arg1:args1) -> fio arg1 >> return args1
+
+       Prefix fio -> 
+               if rest /= ""
+                       then fio rest >> return args
+                       else unknownFlagErr dash_arg
+       
+       OptPrefix fio -> fio rest >> return args
+
+       AnySuffix fio -> fio ('-':arg) >> return args
+
+       PassFlag fio  -> 
+               if rest /= ""
+                       then unknownFlagErr dash_arg
+                       else fio ('-':arg) >> return args
+
+findArg :: [(String,OptKind)] -> String -> (String,OptKind)
+findArg spec arg
+  = case [ (remove_spaces rest, k) | (pat,k) <- spec,
+                                    Just rest <- [my_prefix_match pat arg],
+                                    is_prefix k || null rest ] of
+       [] -> unknownFlagErr ('-':arg)
+       (one:_) -> one
+
+is_prefix (NoArg _) = False
+is_prefix (SepArg _) = False
+is_prefix (PassFlag _) = False
+is_prefix _ = True
+
+-----------------------------------------------------------------------------
+-- convert sizes like "3.5M" into integers
+
+decodeSize :: String -> Integer
+decodeSize str
+  | c == ""             = truncate n
+  | c == "K" || c == "k" = truncate (n * 1000)
+  | c == "M" || c == "m" = truncate (n * 1000 * 1000)
+  | c == "G" || c == "g" = truncate (n * 1000 * 1000 * 1000)
+  | otherwise            = throwDyn (OtherError ("can't decode size: " ++ str))
+  where (m, c) = span pred str
+        n      = read m  :: Double
+       pred c = isDigit c || c == '.'
+
+floatOpt :: IORef Double -> String -> IO ()
+floatOpt ref str
+  = writeIORef ref (read str :: Double)
+
+-----------------------------------------------------------------------------
+-- Finding files in the installation
+
+GLOBAL_VAR(topDir, clibdir, String)
+
+       -- grab the last -B option on the command line, and
+       -- set topDir to its value.
+setTopDir :: [String] -> IO [String]
+setTopDir args = do
+  let (minusbs, others) = partition (prefixMatch "-B") args
+  (case minusbs of
+    []   -> writeIORef topDir clibdir
+    some -> writeIORef topDir (drop 2 (last some)))
+  return others
+
+findFile name alt_path = unsafePerformIO (do
+  top_dir <- readIORef topDir
+  let installed_file = top_dir ++ '/':name
+  let inplace_file   = top_dir ++ '/':cCURRENT_DIR ++ '/':alt_path
+  b <- doesFileExist inplace_file
+  if b  then return inplace_file
+       else return installed_file
+ )
+
+-----------------------------------------------------------------------------
+-- Utils
+
+my_partition :: (a -> Maybe b) -> [a] -> ([(a,b)],[a])
+my_partition _ [] = ([],[])
+my_partition p (a:as)
+  = let (bs,cs) = my_partition p as in
+    case p a of
+       Nothing -> (bs,a:cs)
+       Just b  -> ((a,b):bs,cs)
+
+my_prefix_match :: String -> String -> Maybe String
+my_prefix_match [] rest = Just rest
+my_prefix_match (_:_) [] = Nothing
+my_prefix_match (p:pat) (r:rest)
+  | p == r    = my_prefix_match pat rest
+  | otherwise = Nothing
+
+prefixMatch :: Eq a => [a] -> [a] -> Bool
+prefixMatch [] _str = True
+prefixMatch _pat [] = False
+prefixMatch (p:ps) (s:ss) | p == s    = prefixMatch ps ss
+                         | otherwise = False
+
+postfixMatch :: String -> String -> Bool
+postfixMatch pat str = prefixMatch (reverse pat) (reverse str)
+
+later = flip finally
+
+my_catchDyn = flip catchDyn
+
+splitFilename :: String -> (String,String)
+splitFilename f = (reverse (stripDot rev_basename), reverse rev_ext)
+  where (rev_ext, rev_basename) = span ('.' /=) (reverse f)
+        stripDot ('.':xs) = xs
+        stripDot xs       = xs
+
+suffixOf :: String -> String
+suffixOf s = drop_longest_prefix s '.'
+
+split :: Char -> String -> [String]
+split c s = case rest of
+               []     -> [chunk] 
+               _:rest -> chunk : split c rest
+  where (chunk, rest) = break (==c) s
+
+add :: IORef [a] -> a -> IO ()
+add var x = do
+  xs <- readIORef var
+  writeIORef var (x:xs)
+
+addNoDups :: Eq a => IORef [a] -> a -> IO ()
+addNoDups var x = do
+  xs <- readIORef var
+  unless (x `elem` xs) $ writeIORef var (x:xs)
+
+remove_suffix :: Char -> String -> String
+remove_suffix c s
+  | null pre  = reverse suf
+  | otherwise = reverse pre
+  where (suf,pre) = break (==c) (reverse s)
+
+drop_longest_prefix :: String -> Char -> String
+drop_longest_prefix s c = reverse suf
+  where (suf,_pre) = break (==c) (reverse s)
+
+take_longest_prefix :: String -> Char -> String
+take_longest_prefix s c = reverse pre
+  where (_suf,pre) = break (==c) (reverse s)
+
+newsuf :: String -> String -> String
+newsuf suf s = remove_suffix '.' s ++ suf
+
+-- getdir strips the filename off the input string, returning the directory.
+getdir :: String -> String
+getdir s = if null dir then "." else init dir
+  where dir = take_longest_prefix s '/'
+
+newdir :: String -> String -> String
+newdir dir s = dir ++ '/':drop_longest_prefix s '/'
+
+remove_spaces :: String -> String
+remove_spaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
+
+-----------------------------------------------------------------------------
+-- compatibility code
+
+#if __GLASGOW_HASKELL__ <= 408
+catchJust = catchIO
+ioErrors  = justIoErrors
+#endif
+
+#ifdef mingw32_TARGET_OS
+foreign import "_getpid" getProcessID :: IO Int 
+#endif