diff --git a/IdeSession.hs b/IdeSession.hs
--- a/IdeSession.hs
+++ b/IdeSession.hs
@@ -102,14 +102,20 @@
 module IdeSession (
     -- * Configuration
     SessionConfig(..)
+  , sessionConfigFromEnv
   , defaultSessionConfig
   , InProcess
+  , ProgramSearchPath
+  , ProgramSearchPathEntry(..)
     -- * Updating the session
     -- ** Starting and stopping
   , IdeSession -- Abstract
   , initSession
+  , initSessionWithCallbacks
   , SessionInitParams(..)
   , defaultSessionInitParams
+  , IdeCallbacks(..)
+  , defaultIdeCallbacks
   , shutdownSession
   , forceShutdownSession
   , restartSession
@@ -137,6 +143,7 @@
   , buildLicenses
     -- ** Progress
   , Progress(..)
+  , UpdateStatus(..)
     -- ** Running code
   , RunActions(..)
   , RunResult(..)
@@ -233,7 +240,7 @@
 import IdeSession.GHC.Client
 import IdeSession.Query
 import IdeSession.RPC.Client (ExternalException (..))
-import IdeSession.State (IdeSession)
+import IdeSession.State (IdeSession, IdeCallbacks(..), defaultIdeCallbacks)
 import IdeSession.Types.Progress
 import IdeSession.Types.Public
 import IdeSession.Update
diff --git a/IdeSession/Config.hs b/IdeSession/Config.hs
--- a/IdeSession/Config.hs
+++ b/IdeSession/Config.hs
@@ -1,11 +1,17 @@
 module IdeSession.Config (
     SessionConfig(..)
   , InProcess
+  , ProgramSearchPath, ProgramSearchPathEntry(..)
+  , sessionConfigFromEnv
   , defaultSessionConfig
   ) where
 
 import Distribution.License (License (..))
 import Distribution.Simple (PackageDB (..), PackageDBStack)
+import Distribution.Simple.Program.Find (ProgramSearchPath,ProgramSearchPathEntry(..),defaultProgramSearchPath)
+import System.FilePath (splitSearchPath)
+import System.Directory (getCurrentDirectory, getTemporaryDirectory)
+import System.Environment (lookupEnv)
 
 type InProcess = Bool
 
@@ -15,6 +21,18 @@
 data SessionConfig = SessionConfig {
     -- | The directory to use for all session files.
     configDir        :: FilePath
+    -- | When set to Just "<filepath>", we'll use the files in that
+    -- directory as source and datafiles.  This means that the
+    -- ide-backend is no longer directly managing the files, and
+    -- file updates like 'updateSourceFile' will fail.
+    --
+    -- Note that this feature is experimental and does not have a
+    -- suite of tests.
+    --
+    -- Since this is likely used with an existing cabal project, which
+    -- might have multiple source directories, you'll likely want to
+    -- use 'TargetsInclude' instead of 'TargetsExclude'.
+  , configLocalWorkingDir :: Maybe FilePath
     -- | Extra directories in which to look for programs, including ghc
     -- and other tools. Note that the @$PATH@ is still searched /first/, these
     -- directories are extra.
@@ -41,15 +59,52 @@
     -- | Delete temporary files when session finishes?
     -- (Defaults to True; mostly for internal debugging purposes)
   , configDeleteTempFiles :: Bool
+    -- | The name of the ide-backend-server program to use, and where to find it.
+    --   The default is @(defaultProgramSearchPath,"ide-backend-server")@,
+    --   that is, to look for a program called ide-backend-server on the system
+    --   search path only.
+  , configIdeBackendServer :: (ProgramSearchPath,FilePath)
+    -- | The name of the ide-backend-exe-cabal program to use, and where to find it.
+    --   The default is @(defaultProgramSearchPath,"ide-backend-exe-cabal")@.
+  , configIdeBackendExeCabal :: (ProgramSearchPath,FilePath)
   }
 
--- | Default session configuration
+-- Get the default local session configuration, pulling the following
+-- information from the environment:
 --
--- Use this instead of creating your own SessionConfig to be robust against
--- extensions of SessionConfig.
+-- * OS temporary directory (used for session.*) files
 --
+-- * Current working directory - assumed to be the project root.
+--   Instead of running
+--
+-- * GHC package database.  Like GHC, it takes the GHC_PACKAGE_PATH,
+--   and uses this list of package databases.  This allows ide-backend
+--   to do the 'right thing' when used with tools like stack.
+sessionConfigFromEnv :: IO SessionConfig
+sessionConfigFromEnv = do
+  tmpDir <- getTemporaryDirectory
+  cwd <- getCurrentDirectory
+  mpkgPath <- lookupEnv "GHC_PACKAGE_PATH"
+  let dbStack = case mpkgPath of
+        Nothing -> configPackageDBStack defaultSessionConfig
+        Just pkgPath ->
+          let dbPaths = drop 1 (reverse (splitSearchPath pkgPath))
+           in GlobalPackageDB : map SpecificPackageDB dbPaths
+  return defaultSessionConfig
+    { configDir = tmpDir
+    , configLocalWorkingDir = Just cwd
+    , configPackageDBStack = dbStack
+    }
+
+-- | Default session configuration.  Most users will probably instead
+-- want 'localSessionConfigFromEnv'.
+--
+-- Use this instead of creating your own SessionConfig to be robust
+-- against extensions of SessionConfig.
+--
 -- > defaultSessionConfig = SessionConfig {
 -- >     configDir              = "."
+-- >   , configLocalWorkingDir  = Nothing
 -- >   , configExtraPathDirs    = []
 -- >   , configInProcess        = False
 -- >   , configGenerateModInfo  = True
@@ -65,10 +120,13 @@
 -- >       ]
 -- >   , configLog              = const $ return ()
 -- >   , configDeleteTempFiles  = True
+-- >   , configIdeBackendServer = (defaultProgramSearchPath,"ide-backend-server")
+-- >   , configIdeBackendExeCabal = (defaultProgramSearchPath,"ide-backend-exe-cabal")
 -- >   }
 defaultSessionConfig :: SessionConfig
 defaultSessionConfig = SessionConfig {
     configDir              = "."
+  , configLocalWorkingDir  = Nothing
   , configExtraPathDirs    = []
   , configInProcess        = False
   , configGenerateModInfo  = True
@@ -84,4 +142,6 @@
       ]
   , configLog              = const $ return ()
   , configDeleteTempFiles  = True
+  , configIdeBackendServer = (defaultProgramSearchPath,"ide-backend-server")
+  , configIdeBackendExeCabal = (defaultProgramSearchPath,"ide-backend-exe-cabal")
   }
diff --git a/IdeSession/ExeCabalClient.hs b/IdeSession/ExeCabalClient.hs
--- a/IdeSession/ExeCabalClient.hs
+++ b/IdeSession/ExeCabalClient.hs
@@ -1,33 +1,30 @@
-{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TupleSections #-}
+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TupleSections, OverloadedStrings #-}
 -- | Invoke the executable that calls cabal functions and communicate
 -- with it via RPC.
 module IdeSession.ExeCabalClient (
     invokeExeCabal
   ) where
 
-import System.Exit (ExitCode)
-
-import Distribution.Verbosity (normal)
-import Distribution.Simple.Program.Find (
-    ProgramSearchPath
-  , findProgramOnSearchPath
-  , ProgramSearchPathEntry(..)
-  )
+import Data.Monoid ((<>))
+import qualified Data.Text as Text
+import System.Exit (ExitCode(..))
 
 import IdeSession.Cabal
 import IdeSession.Config
 import IdeSession.GHC.API
-import IdeSession.RPC.Client (RpcServer, RpcConversation(..), forkRpcServer, rpcConversation, shutdown)
+import IdeSession.RPC.Client (RpcServer, RpcConversation(..), forkRpcServer, rpcConversation, shutdown, findProgram)
 import IdeSession.State
 import IdeSession.Types.Progress
+import IdeSession.Types.Public (UpdateStatus(..))
 import IdeSession.Util
 
 -- | Invoke the executable that processes our custom functions that use
 -- the machinery of the cabal library.
-invokeExeCabal :: IdeStaticInfo -> ExeCabalRequest -> (Progress -> IO ())
+invokeExeCabal :: IdeStaticInfo -> IdeCallbacks -> ExeCabalRequest -> (UpdateStatus -> IO ())
                -> IO ExitCode
-invokeExeCabal IdeStaticInfo{..} args callback = do
-  mLoc <- findProgramOnSearchPath normal searchPath "ide-backend-exe-cabal"
+invokeExeCabal ideStaticInfo@IdeStaticInfo{..} ideCallbacks args callback = do
+  let logFunc = ideCallbacksLogFunc ideCallbacks
+  mLoc <- findProgram logFunc searchPath ide_backend_exe_cabal
   case mLoc of
     Nothing ->
       fail $ "Could not find ide-backend-exe-cabal"
@@ -35,19 +32,17 @@
       env <- envWithPathOverride configExtraPathDirs
       let cwd = case args of
             ReqExeDoc{} -> ideSessionDir
-            _ -> ideSessionDataDir ideSessionDir
+            _ -> ideDataDir ideStaticInfo
       server <- forkRpcServer prog [] (Just cwd) env
       exitCode <- rpcRunExeCabal server args callback
       shutdown server  -- not long-running
       return exitCode
   where
-    searchPath :: ProgramSearchPath
-    searchPath = ProgramSearchPathDefault
-               : map ProgramSearchPathDir configExtraPathDirs
+    (searchPath,ide_backend_exe_cabal) = configIdeBackendExeCabal
 
     SessionConfig{..} = ideConfig
 
-rpcRunExeCabal :: RpcServer -> ExeCabalRequest -> (Progress -> IO ())
+rpcRunExeCabal :: RpcServer -> ExeCabalRequest -> (UpdateStatus -> IO ())
                -> IO ExitCode
 rpcRunExeCabal server req callback =
   rpcConversation server $ \RpcConversation{..} -> do
@@ -55,7 +50,15 @@
 
     let go = do response <- get
                 case response of
-                  ExeCabalProgress pcounter -> callback pcounter >> go
-                  ExeCabalDone exitCode     -> return exitCode
+                  ExeCabalProgress pcounter -> do
+                    callback (UpdateStatusProgress pcounter)
+                    go
+                  ExeCabalDone ec@ExitSuccess -> do
+                    callback UpdateStatusDone
+                    return ec
+                  ExeCabalDone ec@(ExitFailure code) -> do
+                    callback $ UpdateStatusFailed $
+                      "Cabal exited with code " <> Text.pack (show code)
+                    return ec
 
     go
diff --git a/IdeSession/GHC/Client.hs b/IdeSession/GHC/Client.hs
--- a/IdeSession/GHC/Client.hs
+++ b/IdeSession/GHC/Client.hs
@@ -50,13 +50,7 @@
 import IdeSession.Util.BlockingOps
 import qualified IdeSession.Types.Public as Public
 
-import Distribution.Verbosity (normal)
 import Distribution.Simple (PackageDB(..), PackageDBStack)
-import Distribution.Simple.Program.Find ( -- From our patched cabal
-    ProgramSearchPath
-  , findProgramOnSearchPath
-  , ProgramSearchPathEntry(..)
-  )
 
 {------------------------------------------------------------------------------
   Starting and stopping the server
@@ -67,12 +61,13 @@
               -> [FilePath]    -- ^ Relative includes
               -> [String]      -- ^ RTS options
               -> IdeStaticInfo -- ^ Session setup info
+              -> IdeCallbacks  -- ^ Session callbacks
               -> IO (Either ExternalException (GhcServer, GhcVersion))
-forkGhcServer ghcOpts relIncls rtsOpts IdeStaticInfo{ideConfig, ideSessionDir} = do
+forkGhcServer ghcOpts relIncls rtsOpts ideStaticInfo ideCallbacks = do
+  let logFunc = ideCallbacksLogFunc ideCallbacks
   when configInProcess $
     fail "In-process ghc server not currently supported"
-
-  mLoc <- findProgramOnSearchPath normal searchPath "ide-backend-server"
+  mLoc <- findProgram logFunc searchPath ide_backend_server
   case mLoc of
     Nothing ->
       fail $ "Could not find ide-backend-server"
@@ -81,7 +76,7 @@
       server  <- OutProcess <$> forkRpcServer
                    prog
                    (["+RTS"] ++ rtsOpts ++ ["-RTS"])
-                   (Just (ideSessionDataDir ideSessionDir))
+                   (Just $ ideDataDir ideStaticInfo)
                    env
       version <- Ex.try $ do
         GhcInitResponse{..} <- rpcInit server GhcInitRequest {
@@ -90,7 +85,9 @@
           , ghcInitOpts               = opts
           , ghcInitUserPackageDB      = userDB
           , ghcInitSpecificPackageDBs = specificDBs
-          , ghcInitSessionDir         = ideSessionDir
+          , ghcInitSourceDir          = ideSourceDir ideStaticInfo
+          , ghcInitSessionDir         = ideSessionDir ideStaticInfo
+          , ghcInitDistDir            = ideDistDir ideStaticInfo
           }
         return ghcInitVersion
       return ((server,) <$> version)
@@ -100,13 +97,11 @@
     opts :: [String]
     opts = "-XHaskell2010"  -- see #190
          : ghcOpts
-        ++ relInclToOpts (ideSessionSourceDir ideSessionDir) relIncls
+        ++ relInclToOpts (ideSourceDir ideStaticInfo) relIncls
 
-    searchPath :: ProgramSearchPath
-    searchPath = map ProgramSearchPathDir configExtraPathDirs
-              ++ [ProgramSearchPathDefault]
+    (searchPath, ide_backend_server) = configIdeBackendServer
 
-    SessionConfig{..} = ideConfig
+    SessionConfig{..} = ideConfig ideStaticInfo
 
 {- TODO: Reenable in-process
 forkGhcServer configGenerateModInfo opts workingDir True = do
@@ -188,19 +183,23 @@
   error "rpcSetGhcOpts not supported for in-process server"
 
 -- | Compile or typecheck
-rpcCompile :: GhcServer           -- ^ GHC server
-           -> Bool                -- ^ Should we generate code?
-           -> Public.Targets      -- ^ Targets
-           -> (Progress -> IO ()) -- ^ Progress callback
+rpcCompile :: GhcServer                      -- ^ GHC server
+           -> Bool                           -- ^ Should we generate code?
+           -> Public.Targets                 -- ^ Targets
+           -> (Public.UpdateStatus -> IO ()) -- ^ Progress callback
            -> IO GhcCompileResult
-rpcCompile server genCode targets callback =
+rpcCompile server genCode targets updateStatus =
   ghcConversation server $ \RpcConversation{..} -> do
     put (ReqCompile genCode targets)
 
     let go = do response <- get
                 case response of
-                  GhcCompileProgress pcounter -> callback pcounter >> go
-                  GhcCompileDone result       -> return result
+                  GhcCompileProgress pcounter -> do
+                    updateStatus (Public.UpdateStatusProgress pcounter)
+                    go
+                  GhcCompileDone result -> do
+                    updateStatus Public.UpdateStatusDone
+                    return result
 
     go
 
@@ -311,7 +310,7 @@
         }
 
     sendRequests :: (GhcRunRequest -> IO ()) -> Chan GhcRunRequest -> IO ()
-    sendRequests put reqChan = printExceptions "sendRequests" $ forever $ put =<< $readChan reqChan
+    sendRequests put reqChan = forever $ put =<< $readChan reqChan
 
     -- TODO: should we restart the session when ghc crashes?
     -- Maybe recommend that the session is started on GhcExceptions?
@@ -359,8 +358,3 @@
 ignoreIOExceptions = let handler :: Ex.IOException -> IO ()
                          handler _ = return ()
                      in Ex.handle handler
-
-printExceptions :: String -> IO a -> IO a
-printExceptions msg f = f `Ex.catch` \ex -> do
-  print (msg, ex :: Ex.SomeException)
-  Ex.throwIO ex
diff --git a/IdeSession/Query.hs b/IdeSession/Query.hs
--- a/IdeSession/Query.hs
+++ b/IdeSession/Query.hs
@@ -109,39 +109,37 @@
 
 -- | Obtain the source files directory for this session.
 getSourcesDir :: Query FilePath
-getSourcesDir = staticQuery $ return . ideSessionSourceDir . ideSessionDir
+getSourcesDir = staticQuery $ return . ideSourceDir
 
 -- | Obtain the data files directory for this session.
 getDataDir :: Query FilePath
-getDataDir = staticQuery $ return . ideSessionDataDir . ideSessionDir
+getDataDir = staticQuery $ return . ideDataDir
 
 -- | Obtain the directory prefix for results of Cabal invocations.
 -- Executables compiled in this session end up in a subdirectory @build@,
 -- haddocks in @doc@, concatenated licenses in file @licenses@, etc.
 getDistDir :: Query FilePath
-getDistDir = staticQuery $ return . ideSessionDistDir . ideSessionDir
+getDistDir = staticQuery $ return . ideDistDir
 
 -- | Read the current value of one of the source modules.
 getSourceModule :: FilePath -> Query BSL.ByteString
-getSourceModule path = staticQuery $ \IdeStaticInfo{ideSessionDir} ->
-  BSL.readFile $ ideSessionSourceDir ideSessionDir </> path
+getSourceModule path = staticQuery $ BSL.readFile . (</> path) . ideSourceDir
 
 -- | Read the current value of one of the data files.
 getDataFile :: FilePath -> Query BSL.ByteString
-getDataFile path = staticQuery $ \IdeStaticInfo{ideSessionDir} ->
-  BSL.readFile $ ideSessionDataDir ideSessionDir </> path
+getDataFile path = staticQuery $ BSL.readFile . (</> path) . ideDataDir
 
 -- | Get the list of all data files currently available to the session:
 -- both the files copied via an update and files created by user code.
 getAllDataFiles :: Query [FilePath]
-getAllDataFiles = staticQuery $ \IdeStaticInfo{ideSessionDir} ->
+getAllDataFiles = staticQuery $ \ideStaticInfo ->
   Find.find Find.always
             (Find.fileType Find.==? Find.RegularFile)
-            (ideSessionDataDir ideSessionDir)
+            (ideDataDir ideStaticInfo)
 
 getCabalMacros :: Query BSL.ByteString
-getCabalMacros = staticQuery $ \IdeStaticInfo{ideSessionDir} ->
-  BSL.readFile $ cabalMacrosLocation (ideSessionDistDir ideSessionDir)
+getCabalMacros = staticQuery $ \IdeStaticInfo{ideDistDir} ->
+  BSL.readFile $ cabalMacrosLocation ideDistDir
 
 {------------------------------------------------------------------------------
   Queries that do not rely on computed state
@@ -347,7 +345,7 @@
 getDotCabal :: Query (String -> Version -> BSL.ByteString)
 getDotCabal session = withComputedState session
                       $ \idleState computed@Computed{..} -> do
-  let sourcesDir       = ideSessionSourceDir . ideSessionDir $ ideStaticInfo session
+  let sourcesDir       = ideSourceDir $ ideStaticInfo session
       options          = idleState ^. ideGhcOpts
       relativeIncludes = idleState ^. ideRelativeIncludes
   buildDotCabal sourcesDir relativeIncludes options computed
diff --git a/IdeSession/RPC/Client.hs b/IdeSession/RPC/Client.hs
--- a/IdeSession/RPC/Client.hs
+++ b/IdeSession/RPC/Client.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE TemplateHaskell, CPP, ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell, CPP, ScopedTypeVariables, OverloadedStrings #-}
 module IdeSession.RPC.Client (
     RpcServer
   , RpcConversation(..)
@@ -12,14 +12,20 @@
   , illscopedConversationException
   , serverKilledException
   , getRpcExitCode
+  , findProgram
   ) where
 
 import Control.Concurrent.MVar (MVar, newMVar, tryTakeMVar)
 import Control.Monad (void, unless)
 import Data.Binary (Binary, encode, decode)
 import Data.IORef (writeIORef, readIORef, newIORef)
+import Data.List (intercalate)
+import Data.Maybe (catMaybes)
+import Data.Monoid ((<>))
+import qualified Data.Text as Text
 import Data.Typeable (Typeable)
 import Prelude hiding (take)
+import System.Environment (lookupEnv)
 import System.Exit (ExitCode)
 import System.IO (Handle, hClose)
 import System.IO.Temp (openTempFile)
@@ -37,8 +43,15 @@
 import System.Process.Internals (withProcessHandle, ProcessHandle__(..))
 import qualified Control.Exception as Ex
 import qualified System.Directory  as Dir
+import Distribution.Verbosity (normal)
+import Distribution.Simple.Program.Find (
+    findProgramOnSearchPath
+  , ProgramSearchPath
+  , ProgramSearchPathEntry(..)
+  )
 
 import IdeSession.Util.BlockingOps
+import IdeSession.Util.Logger
 import IdeSession.RPC.API
 import IdeSession.RPC.Stream
 
@@ -322,3 +335,16 @@
     then Ex.throwIO (serverKilledException (Just ex))
     else Ex.throwIO (ExternalException merr (Just ex))
 
+findProgram :: LogFunc -> ProgramSearchPath -> FilePath -> IO (Maybe FilePath)
+findProgram logFunc searchPath prog = do
+    shownPath <- renderPath searchPath
+    $logInfo $ "Searching for " <> Text.pack prog <> " on this path: " <> Text.pack shownPath
+    mres <- findProgramOnSearchPath normal searchPath prog
+    $logInfo $ case mres of
+      Nothing -> "Failed to find " <> Text.pack prog
+      Just res -> "Found " <> Text.pack prog <> " - using this one: " <> Text.pack res
+    return mres
+  where
+    renderPath = fmap (intercalate ":" . catMaybes) . mapM pathEntryString
+    pathEntryString (ProgramSearchPathDir fp) = return (Just fp)
+    pathEntryString ProgramSearchPathDefault = lookupEnv "PATH"
diff --git a/IdeSession/State.hs b/IdeSession/State.hs
--- a/IdeSession/State.hs
+++ b/IdeSession/State.hs
@@ -13,6 +13,7 @@
   , ManagedFile
   , GhcServer(..)
   , RunActions(..)
+  , IdeCallbacks(..)
     -- * Accessors
   , ideLogicalTimestamp
   , ideComputed
@@ -35,6 +36,12 @@
   , ideRtsOpts
   , managedSource
   , managedData
+  -- * To allow for non-server environments
+  , ideSourceDir
+  , ideDataDir
+  -- * Callbacks
+  , defaultIdeCallbacks
+  , ideLogFunc
   ) where
 
 import Control.Concurrent (ThreadId)
@@ -51,7 +58,10 @@
 import IdeSession.Strict.MVar (StrictMVar)
 import IdeSession.Types.Private hiding (RunResult)
 import qualified IdeSession.Types.Public as Public
+import IdeSession.Util.Logger
 
+import System.FilePath ((</>))
+
 data Computed = Computed {
     -- | Last compilation and run errors
     computedErrors :: !(Strict [] SourceError)
@@ -93,18 +103,20 @@
 data IdeSession = IdeSession {
     ideStaticInfo :: IdeStaticInfo
   , ideState      :: StrictMVar IdeSessionState
+  , ideCallbacks  :: IdeCallbacks
   }
 
 data IdeStaticInfo = IdeStaticInfo {
     -- | Configuration
-    ideConfig     :: SessionConfig
+    ideConfig     :: !SessionConfig
     -- | (Temporary) directory for session files
     --
     -- See also:
     -- * 'ideSessionSourceDir'
     -- * 'ideSessionDataDir',
     -- * 'ideSessionDistDir'
-  , ideSessionDir :: FilePath
+  , ideSessionDir :: !FilePath
+  , ideDistDir :: !FilePath
   }
 
 data IdeSessionState =
@@ -207,6 +219,12 @@
   , forceCancel :: IO ()
   }
 
+-- | Session callbacks.  Currently this just configures how logging is
+-- handled.
+data IdeCallbacks = IdeCallbacks
+  { ideCallbacksLogFunc :: LogFunc
+  }
+
 {------------------------------------------------------------------------------
   Accessors
 ------------------------------------------------------------------------------}
@@ -257,3 +275,42 @@
 
 managedSource = accessor _managedSource $ \x s -> s { _managedSource = x }
 managedData   = accessor _managedData   $ \x s -> s { _managedData   = x }
+
+{------------------------------------------------------------------------------
+  To allow for non-server(local) environments
+------------------------------------------------------------------------------}
+
+-- | Get the directory that holds source files.
+ideSourceDir :: IdeStaticInfo -> FilePath
+ideSourceDir IdeStaticInfo{..} =
+  case configLocalWorkingDir ideConfig of
+    Just path -> path
+    Nothing   -> ideSessionDir </> "src"
+
+-- | Get the directory that holds data files.
+ideDataDir :: IdeStaticInfo -> FilePath
+ideDataDir IdeStaticInfo{..} =
+  case configLocalWorkingDir ideConfig of
+    Just path -> path
+    Nothing   -> ideSessionDir </> "data"
+
+{------------------------------------------------------------------------------
+  Callbacks
+------------------------------------------------------------------------------}
+
+-- | Default session configuration.
+--
+-- Use this instead of creating your own IdeCallbacks to be robust
+-- against extensions of IdeCallbacks.
+--
+-- >> defaultIdeCallbacks = IdeCallbacks
+-- >>  { ideCallbacksLogFunc = \_ _ _ _ -> return ()
+-- >>  }
+defaultIdeCallbacks :: IdeCallbacks
+defaultIdeCallbacks = IdeCallbacks
+  { ideCallbacksLogFunc = \_ _ _ _ -> return ()
+  }
+
+-- | Get the 'LogFunc' for use with the functions in "IdeSession.Util.Logger"
+ideLogFunc :: IdeSession -> LogFunc
+ideLogFunc = ideCallbacksLogFunc . ideCallbacks
diff --git a/IdeSession/Update.hs b/IdeSession/Update.hs
--- a/IdeSession/Update.hs
+++ b/IdeSession/Update.hs
@@ -1,10 +1,11 @@
-{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving, ScopedTypeVariables, TemplateHaskell #-}
+{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving, ScopedTypeVariables, TemplateHaskell, OverloadedStrings #-}
 -- | IDE session updates
 --
 -- We should only be using internal types here (explicit strictness/sharing)
 module IdeSession.Update (
     -- * Starting and stopping
     initSession
+  , initSessionWithCallbacks
   , SessionInitParams(..)
   , defaultSessionInitParams
   , shutdownSession
@@ -52,10 +53,10 @@
 import Control.Monad.IO.Class (liftIO)
 import Data.Accessor (Accessor, (^.))
 import Data.List (elemIndices, isPrefixOf)
-import Data.Maybe (fromMaybe)
+import Data.Maybe (fromMaybe, isJust)
 import Data.Monoid (Monoid(..), (<>))
 import Distribution.Simple (PackageDBStack, PackageDB(..))
-import System.Environment (getEnv, getEnvironment)
+import System.Environment (getEnvironment, unsetEnv, lookupEnv)
 import System.Exit (ExitCode(..))
 import System.FilePath ((</>))
 import System.IO.Temp (createTempDirectory)
@@ -85,6 +86,7 @@
 import IdeSession.Update.IdeSessionUpdate
 import IdeSession.Util
 import IdeSession.Util.BlockingOps
+import IdeSession.Util.Logger
 import qualified IdeSession.Query         as Query
 import qualified IdeSession.Strict.List   as List
 import qualified IdeSession.Strict.Map    as Map
@@ -125,6 +127,9 @@
     --
     -- Defaults to @-K8M@
   , sessionInitRtsOpts :: [String]
+
+    -- | dist/ directory.
+  , sessionInitDistDir :: !(Maybe FilePath)
   }
   deriving Show
 
@@ -135,6 +140,7 @@
   , sessionInitRelativeIncludes = [""]
   , sessionInitTargets          = Public.TargetsExclude []
   , sessionInitRtsOpts          = ["-K8M"]
+  , sessionInitDistDir          = Nothing
   }
 
 -- | Session initialization parameters for an existing session.
@@ -151,6 +157,7 @@
   , sessionInitRelativeIncludes = fromMaybe (st ^. ideRelativeIncludes) ideUpdateRelIncls
   , sessionInitTargets          = fromMaybe (st ^. ideTargets)          ideUpdateTargets
   , sessionInitRtsOpts          = fromMaybe (st ^. ideRtsOpts)          ideUpdateRtsOpts
+  , sessionInitDistDir          = Nothing
   }
 
 -- | Set up the initial state of the session according to the given parameters
@@ -165,7 +172,7 @@
   macros <- case configCabalMacros of
               Nothing     -> generateMacros configPackageDBStack configExtraPathDirs
               Just macros -> return (BSL.UTF8.toString macros)
-  writeFile (cabalMacrosLocation (ideSessionDistDir ideSessionDir)) macros
+  writeFile (cabalMacrosLocation ideDistDir) macros
 
 {-------------------------------------------------------------------------------
   Session startup
@@ -176,20 +183,43 @@
 -- Throws an exception if the configuration is invalid, or if GHC_PACKAGE_PATH
 -- is set.
 initSession :: SessionInitParams -> SessionConfig -> IO IdeSession
-initSession initParams@SessionInitParams{..} ideConfig@SessionConfig{..} = do
+initSession = initSessionWithCallbacks defaultIdeCallbacks
+
+-- | Like 'initSession', but also takes a 'IdeCallbacks'.
+--
+-- Since 0.10.0
+initSessionWithCallbacks :: IdeCallbacks -> SessionInitParams -> SessionConfig -> IO IdeSession
+initSessionWithCallbacks ideCallbacks initParams@SessionInitParams{..} ideConfig@SessionConfig{..} = do
+  let logFunc = ideCallbacksLogFunc ideCallbacks
+  --FIXME: add version info here.
+  $logInfo "Initializing ide-backend session"
+  -- verifyConfig used to bail if GHC_PACKAGE_PATH was set.  Instead,
+  -- we just unset it so that cabal invocations are happy.  It's up to
+  -- the user of ide-backend to set 'configPackageDBStack' based on
+  -- this environment variable.
+  mpath <- lookupEnv "GHC_PACKAGE_PATH"
+  when (isJust mpath) $ do
+    $logWarn "ide-backend doesn't pay attention to GHC_PACKAGE_PATH, but it is set in the environment"
+    unsetEnv "GHC_PACKAGE_PATH"
   verifyConfig ideConfig
 
   configDirCanon <- Dir.canonicalizePath configDir
   ideSessionDir  <- createTempDirectory configDirCanon "session."
+  $logDebug $ "Session dir = " <> Text.pack ideSessionDir
+  let ideDistDir = fromMaybe (ideSessionDir </> "dist/") sessionInitDistDir
+  $logDebug $ "Dist dir = " <> Text.pack ideDistDir
+
   let ideStaticInfo = IdeStaticInfo{..}
 
   -- Create the common subdirectories of session.nnnn so that we don't have to
   -- worry about creating these elsewhere
-  Dir.createDirectoryIfMissing True (ideSessionSourceDir ideSessionDir)
-  Dir.createDirectoryIfMissing True (ideSessionDataDir   ideSessionDir)
-  Dir.createDirectoryIfMissing True (ideSessionDistDir   ideSessionDir)
-  Dir.createDirectoryIfMissing True (ideSessionObjDir    ideSessionDir)
-
+  case configLocalWorkingDir of
+    Just dir -> $logDebug $ "Local working dir = " <> Text.pack dir
+    Nothing -> do
+      Dir.createDirectoryIfMissing True (ideSourceDir  ideStaticInfo)
+      Dir.createDirectoryIfMissing True (ideDataDir    ideStaticInfo)
+  Dir.createDirectoryIfMissing True ideDistDir
+  Dir.createDirectoryIfMissing True (ideSessionObjDir  ideSessionDir)
   -- Local initialization
   execInitParams ideStaticInfo initParams
 
@@ -198,6 +228,7 @@
                            sessionInitRelativeIncludes
                            sessionInitRtsOpts
                            ideStaticInfo
+                           ideCallbacks
   let (state, server, version) = case mServer of
          Right (s, v) -> (IdeSessionIdle,         s,          v)
          Left e       -> (IdeSessionServerDied e, Ex.throw e, Ex.throw e)
@@ -239,31 +270,12 @@
     unless (isValidPackageDB configPackageDBStack) $
       Ex.throw . userError $ "Invalid package DB stack: "
                              ++ show configPackageDBStack
-
-    checkPackageDbEnvVar
   where
     isValidPackageDB :: PackageDBStack -> Bool
     isValidPackageDB stack =
           elemIndices GlobalPackageDB stack == [0]
        && elemIndices UserPackageDB stack `elem` [[], [1]]
 
--- Copied directly from Cabal
-checkPackageDbEnvVar :: IO ()
-checkPackageDbEnvVar = do
-    hasGPP <- (getEnv "GHC_PACKAGE_PATH" >> return True)
-              `catchIO` (\_ -> return False)
-    when hasGPP $
-      die $ "Use of GHC's environment variable GHC_PACKAGE_PATH is "
-         ++ "incompatible with Cabal. Use the flag --package-db to specify a "
-         ++ "package database (it can be used multiple times)."
-  where
-    -- Definitions so that the copied code from Cabal works
-
-    die = Ex.throwIO . userError
-
-    catchIO :: IO a -> (IOError -> IO a) -> IO a
-    catchIO = Ex.catch
-
 {-------------------------------------------------------------------------------
   Session shutdown
 -------------------------------------------------------------------------------}
@@ -331,14 +343,19 @@
 
 executeRestart :: SessionInitParams
                -> IdeStaticInfo
+               -> IdeCallbacks
                -> IdeIdleState
                -> IO RestartResult
-executeRestart initParams@SessionInitParams{..} staticInfo idleState = do
+executeRestart initParams@SessionInitParams{..} staticInfo ideCallbacks idleState = do
+  let logFunc = ideCallbacksLogFunc ideCallbacks
+  $logInfo "Restarting ide-backend-server"
   forceShutdownGhcServer $ _ideGhcServer idleState
   mServer <- forkGhcServer sessionInitGhcOptions
                            sessionInitRelativeIncludes
                            sessionInitRtsOpts
                            staticInfo
+                           ideCallbacks
+
   case mServer of
     Right (server, version) -> do
       execInitParams staticInfo initParams
@@ -385,25 +402,31 @@
 --
 -- The update can be a long running operation, so we support a callback which
 -- can be used to monitor progress of the operation.
-updateSession :: IdeSession -> IdeSessionUpdate -> (Progress -> IO ()) -> IO ()
+updateSession :: IdeSession -> IdeSessionUpdate -> (Public.UpdateStatus -> IO ()) -> IO ()
 updateSession = flip . updateSession'
 
-updateSession' :: IdeSession -> (Progress -> IO ()) -> IdeSessionUpdate -> IO ()
-updateSession' IdeSession{ideStaticInfo, ideState} callback = \update ->
+updateSession' :: IdeSession -> (Public.UpdateStatus -> IO ()) -> IdeSessionUpdate -> IO ()
+updateSession' IdeSession{ideStaticInfo, ideState, ideCallbacks} updateStatus = \update ->
     $modifyStrictMVar_ ideState $ go False update
   where
+    logFunc = ideCallbacksLogFunc ideCallbacks
     go :: Bool -> IdeSessionUpdate -> IdeSessionState -> IO IdeSessionState
     go justRestarted update (IdeSessionIdle idleState) =
       if not (requiresSessionRestart idleState update)
         then do
-          (idleState', mex) <- runSessionUpdate justRestarted update ideStaticInfo callback idleState
+          (idleState', mex) <- runSessionUpdate justRestarted update ideStaticInfo updateStatus ideCallbacks idleState
           case mex of
             Nothing -> return $ IdeSessionIdle          idleState'
             Just ex -> return $ IdeSessionServerDied ex idleState'
         else do
+          $logInfo $ "Restarting session due to update requiring it."
+          unless justRestarted $ updateStatus Public.UpdateStatusRequiredRestart
           let restartParams = sessionRestartParams idleState update
           restart justRestarted update restartParams idleState
-    go justRestarted update (IdeSessionServerDied _ex idleState) = do
+    go justRestarted update (IdeSessionServerDied ex idleState) = do
+      let msg = Text.pack (show ex)
+      $logInfo $ "Restarting session due to server dieing: " <> msg
+      unless justRestarted $ updateStatus (Public.UpdateStatusCrashRestart msg)
       let restartParams = sessionRestartParams idleState update
       restart justRestarted update restartParams idleState
     go _ _ IdeSessionShutdown =
@@ -417,11 +440,12 @@
       -- TODO: I wish I knew why this is necessary :(
       threadDelay 100000
 
-      restartResult <- executeRestart restartParams ideStaticInfo idleState
+      restartResult <- executeRestart restartParams ideStaticInfo ideCallbacks idleState
       case restartResult of
         ServerRestarted idleState' resetSession ->
           go True (resetSession <> update) (IdeSessionIdle idleState')
-        ServerRestartFailed idleState' ->
+        ServerRestartFailed idleState' -> do
+          updateStatus (Public.UpdateStatusServerDied "Failed to restart ide-backend-server")
           return $ IdeSessionServerDied failedToRestart idleState'
 
 -- | @requiresSessionRestart st upd@ returns true if update @upd@ requires a
diff --git a/IdeSession/Update/ExecuteSessionUpdate.hs b/IdeSession/Update/ExecuteSessionUpdate.hs
--- a/IdeSession/Update/ExecuteSessionUpdate.hs
+++ b/IdeSession/Update/ExecuteSessionUpdate.hs
@@ -4,7 +4,7 @@
 --
 -- See comments for IdeSessionUpdate for a motivation of the split between the
 -- IdeSessionUpdate type and the ExecuteSessionUpdate type.
-{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables, FlexibleInstances #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables, FlexibleInstances, TemplateHaskell, OverloadedStrings #-}
 module IdeSession.Update.ExecuteSessionUpdate (runSessionUpdate) where
 
 import Prelude hiding (mod, span)
@@ -39,9 +39,10 @@
 import IdeSession.Strict.IORef (StrictIORef)
 import IdeSession.Types.Private hiding (RunResult(..))
 import IdeSession.Types.Progress
-import IdeSession.Types.Public (RunBufferMode(..), Targets(..))
+import IdeSession.Types.Public (RunBufferMode(..), Targets(..), UpdateStatus(..))
 import IdeSession.Update.IdeSessionUpdate
 import IdeSession.Util
+import IdeSession.Util.Logger
 import qualified IdeSession.GHC.Client    as GHC
 import qualified IdeSession.Strict.IORef  as IORef
 import qualified IdeSession.Strict.IntMap as IntMap
@@ -60,11 +61,12 @@
 
 data IdeSessionUpdateEnv = IdeSessionUpdateEnv {
     ideUpdateStaticInfo :: IdeStaticInfo
-  , ideUpdateCallback   :: Progress -> IO ()
+  , ideUpdateStatus :: UpdateStatus -> IO ()
     -- For the StateT instance
   , ideUpdateStateRef :: StrictIORef IdeIdleState
     -- For liftIO
   , ideUpdateExceptionRef :: StrictIORef (Maybe ExternalException)
+  , ideUpdateCallbacks :: IdeCallbacks
   }
 
 newtype ExecuteSessionUpdate a = ExecuteSessionUpdate {
@@ -74,6 +76,7 @@
            , Applicative
            , Monad
            , MonadReader IdeSessionUpdateEnv
+           , MonadIO
            )
 
 -- We define MonadState using an IORef rather than StateT so that if an exception
@@ -131,19 +134,21 @@
 runSessionUpdate :: Bool
                  -> IdeSessionUpdate
                  -> IdeStaticInfo
-                 -> (Progress -> IO ())
+                 -> (UpdateStatus -> IO ())
+                 -> IdeCallbacks
                  -> IdeIdleState
                  -> IO (IdeIdleState, Maybe ExternalException)
-runSessionUpdate justRestarted update staticInfo callback ideIdleState = do
+runSessionUpdate justRestarted update staticInfo callback ideCallbacks ideIdleState = do
     stRef <- IORef.newIORef ideIdleState
     exRef <- IORef.newIORef Nothing
 
     runReaderT (unwrapUpdate $ executeSessionUpdate justRestarted update')
                IdeSessionUpdateEnv {
                    ideUpdateStaticInfo   = staticInfo
-                 , ideUpdateCallback     = liftIO . callback
+                 , ideUpdateStatus       = liftIO . callback
                  , ideUpdateStateRef     = stRef
                  , ideUpdateExceptionRef = exRef
+                 , ideUpdateCallbacks    = ideCallbacks
                  }
 
     ideIdleState' <- IORef.readIORef stRef
@@ -190,11 +195,14 @@
     -- - We do this _after_ setting ghc because because some ghc options are
     --   passed to the C compiler (#218)
     -- - Similarly, when the ghc options have changed, we conversatively
-    --   recompile (and, importantly, relink -- #218) _all_ C files.
+    --   recompile (and, importantly, relink -- #218) _all_ C files.
     (numActions, cErrors) <- updateObjectFiles ghcOptionsChanged
     let cFilesChanged :: Bool
         cFilesChanged = numActions > 0
 
+    IdeStaticInfo{ideConfig} <- asks ideUpdateStaticInfo
+    let hasLocalWorkingDir = isJust (configLocalWorkingDir ideConfig)
+
     let needsRecompile =
              -- We recompile both when source code and when data files change
              -- as we don't know which data files are included at compile time
@@ -208,11 +216,18 @@
              -- If we just restarted we have to recompile even if the files
              -- didn't change
           || justRestarted
+             -- Recompile when we move to having a local working directory.
+          || hasLocalWorkingDir
 
+    logFunc <- asks (ideCallbacksLogFunc . ideUpdateCallbacks)
+    $logDebug $ if needsRecompile
+      then "Recompile required, starting..."
+      else "Recompile not required, so skipping"
+
     when needsRecompile $ local (incrementNumSteps numActions) $ do
       GhcCompileResult{..} <- rpcCompile
       oldComputed <- Acc.get ideComputed
-      srcDir      <- asks $ ideSessionSourceDir . ideSessionDir . ideUpdateStaticInfo
+      srcDir      <- asks $ ideSourceDir . ideUpdateStaticInfo
 
       let applyDiff :: Strict (Map ModuleName) (Diff v)
                     -> (Computed -> Strict (Map ModuleName) v)
@@ -244,10 +259,12 @@
   where
     incrementNumSteps :: Int -> IdeSessionUpdateEnv -> IdeSessionUpdateEnv
     incrementNumSteps count IdeSessionUpdateEnv{..} = IdeSessionUpdateEnv{
-        ideUpdateCallback = \p -> ideUpdateCallback p {
-            progressStep     = progressStep     p + count
-          , progressNumSteps = progressNumSteps p + count
-          }
+        ideUpdateStatus = \s -> ideUpdateStatus $ case s of
+          UpdateStatusProgress p -> UpdateStatusProgress p {
+              progressStep     = progressStep     p + count
+            , progressNumSteps = progressNumSteps p + count
+            }
+          _ -> s
       , ..
       }
 
@@ -438,12 +455,13 @@
 
 recompileCFiles :: [FilePath] -> ExecuteSessionUpdate [SourceError]
 recompileCFiles cFiles = do
-  callback   <- asks ideUpdateCallback
+  updateStatus <- asks ideUpdateStatus
   sessionDir <- asks $ ideSessionDir . ideUpdateStaticInfo
+  ideStaticInfo <- asks ideUpdateStaticInfo
 
   let srcDir, objDir :: FilePath
-      srcDir = ideSessionSourceDir sessionDir
-      objDir = ideSessionObjDir    sessionDir
+      srcDir = ideSourceDir ideStaticInfo
+      objDir = ideSessionObjDir sessionDir
 
   errorss <- forM (zip cFiles [1..]) $ \(relC, i) -> do
     let relObj = replaceExtension relC ".o"
@@ -451,7 +469,7 @@
         absObj = objDir </> relObj
 
     let msg = "Compiling " ++ relC
-    exceptionFree $ callback $ Progress {
+    exceptionFree $ updateStatus $ UpdateStatusProgress $ Progress {
         progressStep      = i
       , progressNumSteps  = length cFiles
       , progressParsedMsg = Just (Text.pack msg)
@@ -504,20 +522,16 @@
 runGcc :: FilePath -> FilePath -> FilePath -> ExecuteSessionUpdate [SourceError]
 runGcc absC absObj pref = do
     ideStaticInfo@IdeStaticInfo{..} <- asks ideUpdateStaticInfo
-    callback                        <- asks ideUpdateCallback
+    updateStatus                    <- asks ideUpdateStatus
+    ideCallbacks                    <- asks ideUpdateCallbacks
     relIncl                         <- Acc.get ideRelativeIncludes
-
     -- Pass GHC options so that ghc can pass the relevant options to gcc
     ghcOpts <- Acc.get ideGhcOpts
-
-    let ideDistDir   = ideSessionDistDir   ideSessionDir
-        ideSourceDir = ideSessionSourceDir ideSessionDir
-
     exceptionFree $ do
      let SessionConfig{..} = ideConfig
          stdoutLog   = ideDistDir </> "ide-backend-cc.stdout"
          stderrLog   = ideDistDir </> "ide-backend-cc.stderr"
-         includeDirs = map (ideSourceDir </>) relIncl
+         includeDirs = map (ideSourceDir ideStaticInfo </>) relIncl
          runCcArgs   = RunCcArgs{ rcPackageDBStack = configPackageDBStack
                                 , rcExtraPathDirs  = configExtraPathDirs
                                 , rcDistDir        = ideDistDir
@@ -532,7 +546,7 @@
      -- (_exitCode, _stdout, _stderr)
      --   <- readProcessWithExitCode _gcc _args _stdin
      -- The real deal; we call gcc via ghc via cabal functions:
-     exitCode <- invokeExeCabal ideStaticInfo (ReqExeCc runCcArgs) callback
+     exitCode <- invokeExeCabal ideStaticInfo ideCallbacks (ReqExeCc runCcArgs) updateStatus
      stdout <- readFile stdoutLog
      stderr <- readFile stderrLog
      case exitCode of
@@ -559,11 +573,11 @@
 -- TODO: Should we update data files here too?
 markAsUpdated :: (FilePath -> Bool) -> ExecuteSessionUpdate ()
 markAsUpdated shouldMark = do
-  IdeStaticInfo{..} <- asks ideUpdateStaticInfo
+  ideStaticInfo@IdeStaticInfo{..} <- asks ideUpdateStaticInfo
   sources  <- Acc.get (ideManagedFiles .> managedSource)
   sources' <- forM sources $ \(path, (digest, oldTS)) ->
     if shouldMark path
-      then do newTS <- updateFileTimes (ideSessionSourceDir ideSessionDir </> path)
+      then do newTS <- updateFileTimes (ideSourceDir ideStaticInfo)
               return (path, (digest, newTS))
       else return (path, (digest, oldTS))
   Acc.set (ideManagedFiles .> managedSource) sources'
@@ -579,37 +593,40 @@
 -- TODO: We should verify each use of exceptionFree here.
 executeFileCmd :: FileCmd -> ExecuteSessionUpdate Bool
 executeFileCmd cmd = do
-  IdeStaticInfo{..} <- asks ideUpdateStaticInfo
+  ideStaticInfo@IdeStaticInfo{..} <- asks ideUpdateStaticInfo
 
   let remotePath :: FilePath
-      remotePath = fileInfoRemoteDir info ideSessionDir </> fileInfoRemoteFile info
+      remotePath = fileInfoRemoteDir info ideStaticInfo </> fileInfoRemoteFile info
 
-  case cmd of
-    FileWrite _ bs -> do
-      old <- Acc.get cachedInfo
-      -- We always overwrite the file, and then later set the timestamp back
-      -- to what it was if it turns out the hash was the same. If we compute
-      -- the hash first, we would force the entire lazy bytestring into memory
-      newHash <- exceptionFree $ writeFileAtomic remotePath bs
-      case old of
-        Just (oldHash, oldTS) | oldHash == newHash -> do
-          exceptionFree $ setFileTimes remotePath oldTS oldTS
-          return False
-        _ -> do
-          newTS <- updateFileTimes remotePath
-          Acc.set cachedInfo (Just (newHash, newTS))
-          return True
-    FileCopy _ localFile -> do
-      -- We just call 'FileWrite' because we need to read the file anyway to
-      -- compute the hash. Note that `localPath` is interpreted relative to the
-      -- current directory
-      bs <- exceptionFree $ BSL.readFile localFile
-      executeFileCmd (FileWrite info bs)
-    FileDelete _ -> do
-      exceptionFree $ ignoreDoesNotExist $ Dir.removeFile remotePath
-      Acc.set cachedInfo Nothing
-      -- TODO: We should really return True only if the file existed
-      return True
+  case configLocalWorkingDir ideConfig of
+    Just _ -> fail "We can't use update functions with configLocalWorkingDir."
+    Nothing -> case cmd of
+                 FileWrite _ bs -> do
+                   old <- Acc.get cachedInfo
+                   -- We always overwrite the file, and then later set the timestamp back
+                   -- to what it was if it turns out the hash was the same. If we compute
+                   -- the hash first, we would force the entire lazy bytestring into memory
+                   newHash <- exceptionFree $ writeFileAtomic remotePath bs
+                   case old of
+                     Just (oldHash, oldTS) | oldHash == newHash -> do
+                       exceptionFree $ setFileTimes remotePath oldTS oldTS
+                       return False
+                     _ -> do
+                       newTS <- updateFileTimes remotePath
+                       Acc.set cachedInfo (Just (newHash, newTS))
+                       return True
+                 FileCopy _ localFile -> do
+                   -- We just call 'FileWrite' because we need to read the file anyway to
+                   -- compute the hash. Note that `localPath` is interpreted relative to the
+                   -- current directory
+                   bs <- exceptionFree $ BSL.readFile localFile
+                   executeFileCmd (FileWrite info bs)
+                 FileDelete _ -> do
+                   exceptionFree $ ignoreDoesNotExist $ Dir.removeFile remotePath
+                   Acc.set cachedInfo Nothing
+                   -- TODO: We should really return True only if the file existed
+                   return True
+
   where
     info :: FileInfo
     info = case cmd of FileWrite  i _ -> i
@@ -627,10 +644,8 @@
 executeBuildExe extraOpts ms = do
     ideStaticInfo@IdeStaticInfo{..} <- asks ideUpdateStaticInfo
     let SessionConfig{..} = ideConfig
-    let ideDistDir   = ideSessionDistDir   ideSessionDir
-        ideSourceDir = ideSessionSourceDir ideSessionDir
-
-    callback          <- asks ideUpdateCallback
+    updateStatus      <- asks ideUpdateStatus
+    ideCallbacks      <- asks ideUpdateCallbacks
     mcomputed         <- Acc.get ideComputed
     ghcOpts           <- Acc.get ideGhcOpts
     relativeIncludes  <- Acc.get ideRelativeIncludes
@@ -659,7 +674,7 @@
                 let beArgs =
                       BuildExeArgs{ bePackageDBStack   = configPackageDBStack
                                   , beExtraPathDirs    = configExtraPathDirs
-                                  , beSourcesDir       = ideSourceDir
+                                  , beSourcesDir       = ideSourceDir ideStaticInfo
                                   , beDistDir          = ideDistDir
                                   , beRelativeIncludes = relativeIncludes
                                   , beGhcOpts          = ghcOpts'
@@ -668,7 +683,7 @@
                                   , beStdoutLog
                                   , beStderrLog
                                   }
-                invokeExeCabal ideStaticInfo (ReqExeBuild beArgs ms) callback
+                invokeExeCabal ideStaticInfo ideCallbacks (ReqExeBuild beArgs ms) updateStatus
     -- Solution 2. to #119: update timestamps of .o (and all other) files
     -- according to the session's artificial timestamp.
     newTS <- nextLogicalTimestamp
@@ -690,10 +705,10 @@
 executeBuildDoc = do
     ideStaticInfo@IdeStaticInfo{..} <- asks ideUpdateStaticInfo
     let SessionConfig{..} = ideConfig
-    let ideDistDir   = ideSessionDistDir   ideSessionDir
-        ideSourceDir = ideSessionSourceDir ideSessionDir
+    let srcDir = ideSourceDir ideStaticInfo
 
-    callback          <- asks ideUpdateCallback
+    updateStatus      <- asks ideUpdateStatus
+    ideCallbacks      <- asks ideUpdateCallbacks
     mcomputed         <- Acc.get ideComputed
     ghcOpts           <- Acc.get ideGhcOpts
     relativeIncludes  <- Acc.get ideRelativeIncludes
@@ -732,7 +747,7 @@
                         BuildExeArgs{ bePackageDBStack   = configPackageDBStack
                                     , beExtraPathDirs    = configExtraPathDirs
                                     , beSourcesDir       =
-                                        makeRelative ideSessionDir ideSourceDir
+                                        makeRelative ideSessionDir srcDir
                                     , beDistDir          =
                                         makeRelative ideSessionDir ideDistDir
                                     , beRelativeIncludes = relativeIncludes
@@ -742,16 +757,16 @@
                                     , beStdoutLog
                                     , beStderrLog
                                     }
-                  invokeExeCabal ideStaticInfo (ReqExeDoc beArgs) callback
+                  invokeExeCabal ideStaticInfo ideCallbacks (ReqExeDoc beArgs) updateStatus
     Acc.set ideBuildDocStatus (Just exitCode)
 
 executeBuildLicenses :: FilePath -> ExecuteSessionUpdate ()
 executeBuildLicenses cabalsDir = do
     ideStaticInfo@IdeStaticInfo{..} <- asks ideUpdateStaticInfo
     let SessionConfig{configGenerateModInfo} = ideConfig
-    let ideDistDir = ideSessionDistDir ideSessionDir
 
-    callback  <- asks ideUpdateCallback
+    updateStatus  <- asks ideUpdateStatus
+    ideCallbacks <- asks ideUpdateCallbacks
     mcomputed <- Acc.get ideComputed
     when (not configGenerateModInfo) $
       -- TODO: replace the check with an inspection of state component (#87)
@@ -781,7 +796,7 @@
                          , liCabalsDir = cabalsDir
                          , liPkgs = pkgs
                          }
-        invokeExeCabal ideStaticInfo (ReqExeLic liArgs) callback
+        invokeExeCabal ideStaticInfo ideCallbacks (ReqExeLic liArgs) updateStatus
     Acc.set ideBuildLicensesStatus (Just exitCode)
 
 {-------------------------------------------------------------------------------
@@ -809,15 +824,15 @@
 rpcCompile :: ExecuteSessionUpdate GhcCompileResult
 rpcCompile = do
     IdeIdleState{..} <- get
-    callback         <- asks ideUpdateCallback
-    sourceDir        <- asks $ ideSessionSourceDir . ideSessionDir . ideUpdateStaticInfo
+    updateStatus     <- asks ideUpdateStatus
+    sourceDir        <- asks $ ideSourceDir . ideUpdateStaticInfo
 
     -- We need to translate the targets to absolute paths
     let targets = case _ideTargets of
           TargetsInclude l -> TargetsInclude $ map (sourceDir </>) l
           TargetsExclude l -> TargetsExclude $ map (sourceDir </>) l
 
-    tryIO $ GHC.rpcCompile _ideGhcServer _ideGenerateCode targets callback
+    tryIO $ GHC.rpcCompile _ideGhcServer _ideGenerateCode targets updateStatus
 
 rpcSetEnv :: ExecuteSessionUpdate ()
 rpcSetEnv = do
@@ -832,7 +847,7 @@
 rpcSetGhcOpts :: ExecuteSessionUpdate [SourceError]
 rpcSetGhcOpts = do
     IdeIdleState{..} <- get
-    srcDir <- asks $ ideSessionSourceDir . ideSessionDir . ideUpdateStaticInfo
+    srcDir <- asks $ ideSourceDir . ideUpdateStaticInfo
     -- relative include path is part of the state rather than the
     -- config as of c0bf0042
     let relOpts = relInclToOpts srcDir _ideRelativeIncludes
diff --git a/IdeSession/Update/IdeSessionUpdate.hs b/IdeSession/Update/IdeSessionUpdate.hs
--- a/IdeSession/Update/IdeSessionUpdate.hs
+++ b/IdeSession/Update/IdeSessionUpdate.hs
@@ -38,7 +38,6 @@
 import Data.Monoid (Monoid(..))
 import qualified Data.ByteString.Lazy.Char8 as BSL
 
-import IdeSession.GHC.API
 import IdeSession.State
 import IdeSession.Types.Private hiding (RunResult(..))
 import IdeSession.Types.Public (RunBufferMode(..))
@@ -138,7 +137,7 @@
     }
 
 data FileInfo = FileInfo {
-    fileInfoRemoteDir  :: FilePath -> FilePath
+    fileInfoRemoteDir  :: IdeStaticInfo -> FilePath
   , fileInfoRemoteFile :: FilePath
   , fileInfoAccessor   :: Accessor ManagedFilesInternal [ManagedFile]
   }
@@ -178,7 +177,7 @@
   where
     fileInfo = FileInfo {
         fileInfoRemoteFile = fp
-      , fileInfoRemoteDir  = ideSessionSourceDir
+      , fileInfoRemoteDir  = ideSourceDir
       , fileInfoAccessor   = managedSource
       }
 
@@ -189,7 +188,7 @@
   where
     fileInfo = FileInfo {
         fileInfoRemoteFile = fp
-      , fileInfoRemoteDir  = ideSessionSourceDir
+      , fileInfoRemoteDir  = ideSourceDir
       , fileInfoAccessor   = managedSource
       }
 
@@ -199,7 +198,7 @@
   where
     fileInfo = FileInfo {
         fileInfoRemoteFile = fp
-      , fileInfoRemoteDir  = ideSessionSourceDir
+      , fileInfoRemoteDir  = ideSourceDir
       , fileInfoAccessor   = managedSource
       }
 
@@ -210,7 +209,7 @@
   where
     fileInfo = FileInfo {
         fileInfoRemoteFile = fp
-      , fileInfoRemoteDir  = ideSessionDataDir
+      , fileInfoRemoteDir  = ideDataDir
       , fileInfoAccessor   = managedData
       }
 
@@ -224,7 +223,7 @@
   where
     fileInfo = FileInfo {
         fileInfoRemoteFile = remoteFile
-      , fileInfoRemoteDir  = ideSessionDataDir
+      , fileInfoRemoteDir  = ideDataDir
       , fileInfoAccessor   = managedData
       }
 
@@ -235,7 +234,7 @@
   where
     fileInfo = FileInfo {
         fileInfoRemoteFile = fp
-      , fileInfoRemoteDir  = ideSessionDataDir
+      , fileInfoRemoteDir  = ideDataDir
       , fileInfoAccessor   = managedData
       }
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -81,591 +81,3 @@
  * interface changes;
  * and other relevant information such as which areas may need particular
    attention and testing during integration.
-
-
-Changelog
----------
-
- *  Version 0.10
-
-    * Updated private dependencies: binary-ide-backend 0.7.3.0,
-      Cabal-ide-backend 1.22
-
-    * Added support for ghc 7.10
-
-    TODO: Release notes not yet complete.
-
- *  Version 0.9
-
-    * Required ghc patch levels:
-
-      - For ghc 7.4: 796f4f89aeaacc778c75f7f05ecf6e37279841ce
-      - For ghc 7.8: 1cb68c640a7066532eab990956b3220036e7ee31
-
-    * This release requires a new version of the ide-backend-rts (0.1.3),
-      which makes sure Handles get reset even when the snippet fails
-
-    * New features:
-
-      - Support for the official ghc 7.8.3 release, and moved to Cabal 1.18.1.3
-        for Cabal-ide-backend.
-
-      - buildExe now supports setting targets. The API for setting targets has
-        changed as is now
-
-            updateTargets :: Targets -> IdeSessionUpdate
-
-        where
-
-            data Targets = TargetsInclude [FilePath]
-                         | TargetsExclude [FilePath]
-
-      - Ability to change include paths dynamically (#178). Note that changing
-        include paths (as well as changing targets) will cause a server restart.
-
-      - Ability to hide and unhide packages dynamically, and change Safe
-        Haskell flags dynamically (#185, #224). Unlike we had previously
-        thought, we can in fact do this without a server restart.
-
-      - Executables can now be run from ide-backend with the same API as
-        running snippets (#181). Most of our runStmt tests in the test suite
-        now have a corresponding runExe test (which can however be disabled with
-        the --no-exe test suite option). Note that we _do_ expect _some_
-        semantic differences between tests and snippets through the API; in
-        particular, since we don't manage the executable, we cannot affect its
-        I/O buffering modes or encoding settings. Also, the RunActions returned
-        by runExe eventually return an ExitCode rather than a RunResult.
-
-      - Snippets now run as independent processes and the "Running" state is
-        gone from ide-backend. The RPC framework is modified to be able to
-        deal with multiple RPC conversations (and fixed some bugs that were
-        introduced in that change: #225); RPC conversations with the split off
-        processes happen over named pipes. This uses 'forkProcess', for which
-        we have identified and fixed a number of ghc RTS bugs (#9377, #9347,
-        \#9296, #9295, #9284). The use of a separate process is necessary
-        because we need to be able to change process global state for the
-        snippet (buffering, current working directory, value of getArgs, etc.).
-        Issue #206 was partly related to these ghc bugs.  One consequence of
-        this is that it is now possible to change data files while a snippet is
-        running (#176).
-
-      - The API for changing environment variables is changed (#202). It is now
-
-            updateEnv :: [(String, Maybe String)] -> IdeSessionUpdate
-
-        and is intended to be stateless rather than cumulative as it had been.
-
-      - The API for setting options is changed. We no longer make a (public)
-        distinction between static ghc options and dynamic ghc options, and
-        the configStaticOpts are gone from the ide-backend configuration.
-        Instead, we simply have
-
-            updateGhcOpts :: [String] -> IdeSessionUpdate
-
-        and ide-backend handles the distinction entirely internally. Note that
-        so far the only option that we have come across that are truly static
-        (i.e., require a session restart) are changes to the linker flags.
-        Anything else (including changes to the package visibility) we can
-        handle server side without a restart.
-
-      - Similarly, removed the relative includes from the configuration and
-        moved them to the session init parameters where they should have been
-        from the start (because they can be changed dynamically).
-
-        Note that you need path `""` in relative includes for `updateTargets`
-        and `buildExe` to be aware of the same set of source directories
-        (#184).
-
-    * Closed issues:
-
-      - Asymptotic performance improvement (#167, ghc #7478) and added
-        performance unit tests. These are very rough at the moment and can fail
-        simply due to high system load, for instance.
-
-      - Make sure ghc sees changes to dependent files (#134, ghc #7473).
-
-      - We correctly wipe out the GHC search path before setting our own (#169).
-
-      - Invoking the Cabal functions that print to stdout and stderr is now
-        done via our RPC (#180) that starts a separate binary
-        ide-backend-exe-cabal. This is necessary so that we can change process
-        global state such as the current working directory without affecting
-        the rest of the system.
-
-        The stdout output it redirected to a log file and analyzed to provide
-        Progress updates. The binary has to be present on the path. E.g., when
-        you run ide-backend tests you can add the extra fpco-stock-7.4 path
-        component (see SETUP.md), into which the binary is installed by default
-        with 'cabal install':
-
-         IDE_BACKEND_EXTRA_PATH_DIRS=~/env/fpco-patched-7.4/local/bin:~/env/fpco-patched-7.4/dot-cabal/bin:~/env/fpco-stock-7.4/dot-cabal/bin \
-         IDE_BACKEND_PACKAGE_DB=~/env/fpco-patched-7.4/dot-ghc/snippet-db \
-         dist/build/ghc-errors/ghc-errors
-
-      - forceCancel of a snippet now sends sigKILL rather than "requesting"
-        a server shutdown (#173).
-
-      - Added updateRtsOpts to the API and set default stack size to 8MB (#191).
-        However, limiting stack size does not seem to work for ghc 7.4. ghci
-        manages it so there must be a way, but simply starting the server with
-        +RTS -K.. -RTS does is not sufficient (#258).
-
-      - configRelativeIncludes taken into account when building C files (#212).
-
-      - Make it possible to override path to GHC (#205).
-
-      - Disable idle GC in the server (#206).
-
-      - Don't lose location information when getting multiple error messages
-        (#213).
-
-      - Make sure we don't lose output from C code by flushing C buffers as well
-        as Haskell buffers (#210).
-
-      - Make sure that we can set linker flags dynamically (#214).
-
-      - Fix inconsistent user package database loading (#221).
-
-      - Make sure that runStmt can safely be interrupted without putting the
-        main session in an inconsistent state or leading to deadlock (#219,
-        \#220) and that it will terminate the server if it does get interrupted
-        (#231).
-
-      - Make sure server terminates when client does (#194).
-
-      - Be consistent (GHC API/exe building) about Haskell2010 (#190).
-
-      - Better fix to #119: we no longer wipe the entire build directory when
-        building executables, but instead update the timestamps of .o files.
-        This gives us better performance when building executables.
-
-      - Unload object file when a previously correct C file is replaced with
-        one containing errors (#201) or when the C file is removed from the
-        session (#241).
-
-      - Make sure object files are resolved correctly, even if they were
-        previously loaded (#228, #230) and properly report linker errors
-        to the client (#242).
-
-      - Make sure relevant ghc options are passed to ghc when compiling C files
-        (#218) and recompile C files when options are changed (#214).
-
-      - Make sure calling Haddock does not change the source files in the
-        session (#238).
-
-      - Make sure the client gets as much information as possible on a hard
-        server crash (#239).
-
-      - Remove previous error logs on calls to buildExe etc (#245) so that
-        on a call to buildExe we don't leave confusing old error messages lying
-        around.
-
-      - Make sure to generate position independent code (#244).
-
-    * Other minor bug fixes and changes:
-
-      - Don't skip ghc progress messages that include "[TH]" (these were
-        confusing our parser)
-      - Changed bounds on dependencies
-      - Internal refactoring of the updateSession logic, which is now a lot
-        more comprehensible (mostly possible now because we no longer have
-        a "Running" state, and starting a snippet is now independent of the
-        main session). This implies a minor API change; IdeSessionUpdate is no
-        longer a Monad, and no longer takes a type argument. It still satisifes
-        Monoid, however, and this should barely affect client code (other than
-        perhaps a type annotation here and there, replacing `IdeSessionUpdate
-        ()` with `IdeSessionUpdate`). This also resolves a race condition that
-        was present in `updateSession` (where a concurrent update session might
-        grab the session lock while the first session update was attempting to
-        restart the session). Also, better treatment of exceptions during
-        session updates (#250, #253).
-      - Added updateDeleteManagedFiles session update to remove all managed
-        (source and data) files from a session.
-      - Catch and report exceptions thrown by Haddock building.
-
-    * Test suite has been significantly refactored (#217, #237, #231), and is
-      now far more modular (dramatically improved compile times), can re-use
-      sessions for tests (which has brought to light quite a few bugs) as well
-      as in parallel (although the latter has not been tested sufficiently
-      yet), and uses tasty rather than test-framework. Various minor fixes to
-      the tests:
-
-      - GHC 7.8 started using fancy (unicode) quotes, which confused some tests.
-      - Added test for .hsboot files in subdirectories (#177)
-      - Don't rely on availability of profiling libs in the tests.
-      - Don't rely on the IsString instance for Data.ByteString because it
-        uses `pack` and thus throws away UTF8 data (#234).
-      - Test for package registration fixed (#250). Note that a running session
-        will NOT notice newly installed package until it is restarted (nor will
-        it notice that it needs to be restarted -- this needs to be initiated
-        with a call to restartSession).
-
-    * Open issues:
-
-      - The ghc linker is broken in the threaded runtime, which will affect
-        bindings to C libraries (ghc #8648). We suspect that #175 is caused
-        by this.
-      - On OSX we do not detect when a snippet server dies (#229). The problem
-        does not occur on Linux.
-      - buildExe sometimes compiles modules more than once (#189). This should
-        not change anything semantically but we have to allow for it in the
-        tests (since we get more progress messages than we expect) and of course
-        it has a performance impact.
-      - Building executables leaves .o and .dyn_o files in the session src/
-        directory (#249). The client should not really be affected by this
-        however.
-      - We currently _always_ restart the session when the relative includes
-        or targets change. This is correct but conservative and unnecessary.
-        Figuring out precisely when this is needed however is rather difficult
-        and probably not worth the effort. (Note that we don't restart when
-        the options "change" (by a call to updateTargets etc) to their current
-        value in the session.
-      - The debugging API is **disabled** in this release, as it needs to be
-        updated to work with the separate server (forkProcess) for running
-        snippets.
-      - Limiting stack size does not work for 7.4 (#258).
-
- *  Version 0.8.
-
-    This is a major new release with a lot of new functionality, bug fixes, and
-    some minor API changes.
-
-     * New functionality: support for GHC 7.8, and make sure that a single
-       ide-backend client library, compiled with a stock ghc, can talk to
-       multiple ide-backend-servers (one for ghc 7.4, one for ghc 7.8)
-       (#137, #147, #148, #149, #150, #151, #158, ghc #8006, ghc #8067,
-       ghc Hooks proposal). Setup instructions are included in "setup/SETUP.md".
-
-       NOTE: This requires new versions of both ghc 7.4 and ghc 7.8.
-
-       - For ghc 7.4: commit 8c021d1 (branch ide-backend-experimental-74).
-         Differences from the official release: backported Hooks; backported
-         fixes to #1381, #7040, #7231, #7478, #8006, #8333
-         (ide-backend issues #145 and #161). Also applied patch for #4900,
-         although that patch is not accepted in HEAD yet (necessary for #118).
-
-       - For ghc 7.8: commit e0f0172 (branch ide-backend-experimental-78).
-         Since there is no official release of ghc yet, this picks a
-         semi-random snapshot of the ghc tree (a93f857). Our branch differs
-         from this snapshot by only a single patch (for #118/ghc #4900); we
-         have made various other patches, but they have all been included in
-         the official tree.
-
-       Note that we now use ghc's standard non-release version numbering
-       (7.4.2.<date> and 7.7.<date>). ide-backend supports a higher level query
-       (getGhcVersion :: Query GhcVersion) where
-
-           data GhcVersion = GHC742 | GHC78
-
-     * New functionality: debugging API (#131).
-
-       Breakpoints can be set/cleared using
-
-           setBreakpoint :: IdeSession
-                         -> ModuleName
-                         -> Public.SourceSpan
-                         -> Bool
-                         -> IO (Maybe Bool)
-
-       The existing API for binding subexpressions can be used to construct
-       SourceSpans.
-
-       When a breakpoint is set, snippets can stop with a new RunResult
-
-           data RunResult =
-             ...
-             | RunBreak
-
-       They can be resumed using
-
-           resume :: IdeSession -> IO (RunActions Public.RunResult)
-
-       Information about the current breakpoint (if any) can be got using
-
-           getBreakInfo :: Query (Maybe BreakInfo)
-
-           data BreakInfo = BreakInfo {
-               breakInfoModule      :: Public.ModuleName
-             , breakInfoSpan        :: SourceSpan
-             , breakInfoResultType  :: Public.Type
-             , breakInfoVariableEnv :: Public.VariableEnv
-             }
-
-           type VariableEnv = [(Name, Type, Value)]
-
-       Variables can be printed and/or forced using
-
-           printVar :: IdeSession
-                    -> Public.Name
-                    -> Bool
-                    -> Bool
-                    -> IO Public.VariableEnv
-
-       The two booleans indicate whether new variables should be bound (cf.
-       ghci's :print vs :sprint) and whether the value should be forced (:print
-       vs :force). This is only valid during an active breakpoint.
-
-       Not all of ghci's debugging funtionality is yet supported; this is
-       documented in more detail in the ticket (#131, also #136). Note also
-       that the API may not be entirely stable yet. In particular, having
-       printVar independent of RunActions is unsatisfactory; fixing this _may_
-       change the runStmt API too. This is documented in great detail in a new
-       ticket (#153).
-
-     * New functionality: generate Hoogle/Haddock (#70). Please read
-       the documentation of @buildDoc@, test and suggest improvements. See also
-       https://github.com/fpco/ide-backend/issues/70#issuecomment-32031570
-
-     * New functionality: distinguish between KindError and KindServerDied, and
-       hide the "internal exception" when showing external exceptions (#135)
-
-     * New functionality: allow to set compilation targets (#152)
-
-           updateTargets :: Maybe [FilePath] -> IdeSessionUpdate ()
-
-       As part of this also added a new field called configRelativeIncludes to
-       SessionConfig (#156). To see why this is necessary, consider "module A"
-       in "foo/bar/A.hs" and "module B" in "foo/bar/B.hs", where module B
-       imports module A, and we specify "foo/bar/B.hs" as the target. Then ghc
-       will be unable to find module A because it won't know to look in
-       directory "foo/bar" (before updateTargets this was not an issue because
-       we specified all modules paths explicitly to ghc). Now
-       configRelativeIncludes can be used to tell ghc where to look (in this
-       case, it should be set to ["foo/bar"]).
-
-       Note that buildExe and co do not yet take these targets into account
-       (#154), except for configRelativeIncludes, which are, in particular,
-       inserted into the hs-source-dirs field of .cabal, if set.
-
-       Also, fixed bug where we would not pass -i to the ghc server on session
-       restart (this bug was mostly invisible when we were providing all source
-       files explicitly).
-
-     * New functionality: support for boot files (#155, #157).
-
-     * New functionality: support C files (and .h files), both through
-       the API (where ide-backend-server compiles the .c files
-       into .o files and dynamically loads/unloads these object files)
-       and in executable generation (#122).
-
-     * New functionality: specify (as the first argument of buildExe)
-       additional arguments for ghc when building executables (#159).
-
-     * Bugfix: setting ghc options is now stateless. We still have
-
-           configStaticOpts :: [String]
-
-       as before, which can be used for things like package options, but changed
-
-           updateGhcOptions :: Maybe [String] -> IdeSessionUpdate
-
-       to
-
-           updateDynamicOpts :: [String] -> IdeSessionUpdate
-
-       with the following semantics: the full set of active options is those
-       specified in configStaticOpts, plus those (and only those) set in the
-       last call to updateDynamicOpts. In other words, setting
-
-           updateDynamicOpts ["-Wall", "-Werror"]
-
-       and then later
-
-           updateDynamicOpts ["-Wall"]
-
-       now does the right thing (#115).
-
-     * Bugfix: Make sure ID info is updated on code changes (fixed a caching
-       problem) (#142)
-
-     * Bugfix: Make sure that updating static files triggers recompilation
-       (#118). This is fixed by means of a ghc patch (in both 7.4 and 7.8; ghc
-       issue #4900).
-
-     * Bugfix: Avoid confusing error message in buildExe for code with type
-       errors (#145, #160).
-
-     * Better way to deal with async exceptions in snippet interrupts in ghc
-       (#58, ghc issue #8006).  The new solution works in both 7.4 and 7.8, and
-       in both "regular" execution of snippets and in execution with
-       breakpoints enabled/resumed execution (#133). The new approach is now in
-       the official GHC tree.
-
-     * Upgraded to Cabal 1.18.1.2 (necessary to support GHC 7.8), and set
-       things up so that it generates the necessary dynlibs when using 7.8.
-       Although Cabal 1.18 reports problems slightly differently to Cabal
-       1.16, ide-backend attempts to hide this difference (#146). There is
-       still a minor problem on OSX when using an in-place compiler due to
-       either a bug in ghc or a bug in cabal (#8266); see #164.
-
-     * Minor API changes:
-
-        - RunActions now has kind * -> * ; what was previously just "RunActions"
-          is now (from the client's perspective) "RunActions RunResult"
-        - String argument to RunOk has been removed.
-        - IdeSessionUpdate now has kind * -> * ; what was previously just
-          "IdeSessionUpdate" is now "IdeSessionUpdate ()". IdeSessionUpdate
-          has been given a Monad instance.
-        - updateModule* is now called updateSourceFile* (because it is used for
-          more than just Haskell modules).
-
-     * Merged pull requests
-
-        - Use binary-ide-backend throughout (#144)
-        - Tweaks to getDotCabal (#141)
-
-     * Test-suite modificatons:
-
-        - Test for #134: multiple changes to TH dependent files in one second
-        - Test for #32: Internal paths in error messages
-        - Test for #50: Async problem with snippet interrupt
-        - Test for fpco/fpco/#3043: constraints in monotypes
-        - Fixed non-deterministic failure of test for #58 (#143)
-        - Updated tests to support for 7.4 and 7.8 (mark allowable differences,
-          use alpha-equivalence rather than syntactic identity to compare
-          types, etc.), and be less picky about package versions
-        - Others, testing most of the features and fixes in this release
-
-     * Isolated a bug in sqlite which was causing #104.
-
-     * Known issue: tracked #125 down to a bug in the GHC RTS, and reported
-       this (#165, https://ghc.haskell.org/trac/ghc/ticket/8648). There is no
-       fix yet though.
-
- *  Version 0.7.1
-
-     * New functionality: types of subexpressions (#50). Known issues:
-
-       - We sometimes report multiple types for the same span (see comments
-         at the end of https://github.com/fpco/ide-backend/issues/50).
-
-     * New functionality: report use sites of identifiers (#129)
-
-     * New functionality: Generate .cabal files (#127)
-
-       - Note that library data files are not installed. This should be easy
-         to add if/when that is needed. Just let us know.
-
-     * Efficiency of construction of type information (and especially
-       autocompletion information) has been improved (#132).
-
-     * Move license catenation (#72) documentation to haddocks,
-       flesh it out, make code more readable, improve error output,
-       fix a recently broken test
-
-     * Add configLicenseFixed to hard-code packages with hidden
-       .cabal files (#72)
-
-     * Optimize license catenation and add some benchmark tests (#72)
-
-     * Bugfix: for ghc progress messages in projects with 10 or more modules
-       the first 9 progress message were not parsed correctly because we didn't
-       allow for whitespace in between '[' and the first number ("[ 1 of 10]").
-
-     * Merged pull requests:
-
-        - Load CoreLicenses.txt via Template Haskell (#128)
-        - Less license failures (#126)
-        - Use System.IO.UTF8 to avoid character encoding issues (#117)
-
- *  Version 0.7.0.2
-
-     * Use System.IO.UTF8 to avoid character encoding issues (fpco/fpco#2232)
-
-     * Removing non-existent files should not throw an exception
-
-     * Relaxed bounds on process, directory, tagged, and use crypto-api 0.12
-       and aeson 0.6.2
-
-     * Use binary 0.7 (as binary-ide-backend); improved Binary instance for
-       Text that avoids lots of small chunks, and use the incremental API
-       from binary 0.7 to avoid the use of lazy I/O
-
-     * Clear the build directory in buildExe (temporary fix for #119)
-
-     * Avoid stack overflow in Binary instance for lazy bytestrings (#121)
-
- *  Version 0.7.0.1
-
-     * Bugfix: make sure restartSession passes the configPackageDBStack
-       to ghc (#114)
-
- *  Version 0.7.0.0
-
-    NOTE. This release includes a number of small API changes and behavioural
-    changes since 0.6.0.2.
-
-     * Report server crashes as SourceErrors, and implicitly restart the session
-       on the _next_ call to updateSession (#107). Note that if the server is
-       in dead state (i.e., after the serve crash and before the next call to
-       updateSession) a call to runStmt will throw an exception (just like when
-       you call runStmt when no code is compiled at all). It is the
-       responsibility of the client code to check for source errors
-       (using getSourceErrors) before calling runStmt.
-
-     * It is now possible to reuse previously generated cabal macros, in order
-       to improve performance (#109). The type of initSession has changed to
-
-           initSession :: SessionInitParams -> SessionConfig -> IO IdeSession
-
-       where
-
-           data SessionInitParams = SessionInitParams {
-               sessionInitCabalMacros :: Maybe BSL.ByteString
-             }
-
-       Similarly, restartSession now has type (#113)
-
-           restartSession :: IdeSession -> Maybe SessionInitParams -> IO ()
-
-       When passed Nothing it will leave the cabal macros unchanged, but when
-       passed a SessionInitParams it will regenerate them or use a previously
-       regenerated cabal macros file.
-
-       The generated cabal macros can be accessed using
-
-           getCabalMacros :: Query BSL.ByteString
-
-     * Extended the Progress data type (#112) to contain the number of steps as
-       well as a progress message. See the comments in the ticket and in the
-       Haddock for Progress for some limitations, however (briefly, during
-       compilation, we get non-contiguous and possibly out of order progress
-       updates from ghc: [4/13], [8/13], done).
-
-     * It is now possible to specify additional program search directories in
-       *addition* to the normal `$PATH` when creating a session (#99).
-
-           data SessionConfig = SessionConfig {
-               configExtraPathDirs :: [FilePath]
-               ...
-
-       This allows different sessions within the same process (that share the
-       same `$PATH` env var) to have different program search paths. In
-       particular this can be used to select the instance of ghc by controlling
-       which ghc binary is found.
-
-       Note that the `configExtraPathDirs` are in addition to the `$PATH`, they
-       are searched after the `$PATH`.
-
- *  Version 0.6.0.2
-
-     * Fix problem with cabal_macros in package databases with multiple
-       versions of the same package.
-
-     * Merge pull request from fpco/looger (#105)
-
-     * Traverse class default methods for id info (#106)
-
- *  Version 0.6.0.1
-
-     * Now uses a private Cabal fork as a sub-repo. Branch is ide-backend.
-       The package is renamed as Cabal-ide-backend. You will need to install
-       this and then ghc-pkg hide it or it will cause problems (e.g. ambigious
-       modules when building Setup.hs files).
-
-     * Fix for ticket #103. The macro generation should now be faster.
-       No interface change for this.
-
- *  Version 0.6
-
-     * baseline version for new version + release protocol
diff --git a/TestSuite/TestSuite.hs b/TestSuite/TestSuite.hs
--- a/TestSuite/TestSuite.hs
+++ b/TestSuite/TestSuite.hs
@@ -1,5 +1,9 @@
 module Main where
 
+import Data.Foldable (forM_)
+import Data.List (isPrefixOf)
+import System.Process (readProcess)
+import System.Environment
 import Test.HUnit
 import Test.Tasty
 
@@ -75,8 +79,37 @@
   ]
 
 main :: IO ()
-main =
-    defaultMainWithIngredients ings $ testSuite $ \env ->
+main = do
+    -- Yes, this is hacky, but I couldn't figure out how to easily do
+    -- this with tasty's API...  So, instead of trying to
+    -- programatically modify the config, we generate arguments.
+    args <- getArgs
+    let noGhcSpecified =
+           "--test-74"  `notElem` args &&
+           "--test-78"  `notElem` args &&
+           "--test-710" `notElem` args
+    args' <-
+      if noGhcSpecified
+        then do
+          putStrLn "No GHC version specified (--test-74, --test-78, --test-710)."
+          putStrLn "Asking local GHC for its version, and defaulting to that."
+          output <- readProcess "ghc" ["--version"] ""
+          putStrLn output
+          let version = last (words output)
+              versionCode =
+                if "7.4"  `isPrefixOf` version then "74"  else
+                if "7.8"  `isPrefixOf` version then "78"  else
+                if "7.10" `isPrefixOf` version then "710" else
+                error ("No tests for GHC version " ++ version)
+          putStrLn $ "Assuming --test-" ++ versionCode
+          return (("--test-" ++ versionCode) : args)
+        else return args
+    -- Set GHC_PACKAGE_PATH_TEST, an environment variable used by
+    -- TestSuite.State.startNewSession.  This is needed because
+    -- ide-backend unsets GHC_PACKAGE_PATH.
+    mpkgPath <- lookupEnv "GHC_PACKAGE_PATH"
+    forM_ mpkgPath $ setEnv "GHC_PACKAGE_PATH_TEST"
+    withArgs ("--no-session-reuse" : args') $ defaultMainWithIngredients ings $ testSuite $ \env ->
       let TestSuiteConfig{..} = testSuiteEnvConfig env
           env74  = env { testSuiteEnvGhcVersion = GHC_7_4  }
           env78  = env { testSuiteEnvGhcVersion = GHC_7_8  }
diff --git a/TestSuite/TestSuite/Assertions.hs b/TestSuite/TestSuite/Assertions.hs
--- a/TestSuite/TestSuite/Assertions.hs
+++ b/TestSuite/TestSuite/Assertions.hs
@@ -386,7 +386,7 @@
   ignoreVersions s = subRegex (mkRegex versionRegexp) s "X.Y.Z"
     where
       versionRegexp :: String
-      versionRegexp = "[0-9]+(\\.[0-9]+)+"
+      versionRegexp = "[0-9]+(\\.[0-9]+)+(-[0-9a-z]+)?"
 
 instance IgnoreVersions a => IgnoreVersions [a] where
   ignoreVersions = map ignoreVersions
@@ -493,7 +493,7 @@
     -- filepaths in any of them).
     ("#32", [GHC_7_4])
     -- https://github.com/fpco/ide-backend/issues/254
-  , ("#254", [GHC_7_10])
+  , ("#254", [GHC_7_8, GHC_7_10])
   ]
 
 fixme :: IdeSession -> String -> IO () -> IO String
diff --git a/TestSuite/TestSuite/Session.hs b/TestSuite/TestSuite/Session.hs
--- a/TestSuite/TestSuite/Session.hs
+++ b/TestSuite/TestSuite/Session.hs
@@ -2,6 +2,9 @@
 module TestSuite.Session (
     updateSessionD
   , updateSessionP
+  , updateAndExpectProgressCount
+  , updateAndCollectProgress
+  , updateAndCollectStatus
   , loadModule
   , loadModulesFrom
   , loadModulesFrom'
@@ -13,6 +16,7 @@
 import Control.Monad
 import Data.IORef
 import Data.List (isPrefixOf, isInfixOf)
+import Data.Maybe (mapMaybe)
 import Data.Monoid
 import System.FilePath
 import System.FilePath.Find (always, extension, find)
@@ -24,13 +28,6 @@
 
 updateSessionD :: IdeSession -> IdeSessionUpdate -> Int -> IO ()
 updateSessionD session update numProgressUpdates = do
-  progressRef <- newIORef []
-
-  -- We just collect the progress messages first, and verify them afterwards
-  updateSession session update $ \p -> do
-    progressUpdates <- readIORef progressRef
-    writeIORef progressRef $ progressUpdates ++ [p]
-
   -- These progress messages are often something like
   --
   -- [18 of 27] Compiling IdeSession.Types.Private ( IdeSession/Types/Private.hs, dist/build/IdeSession/Types/Private.o )
@@ -42,20 +39,13 @@
   -- So these numbers don't need to start at 1, may be discontiguous, out of
   -- order, and may not end with [X of X]. The only thing we can check here is
   -- that we get at most the number of progress messages we expect.
-  progressUpdates <- readIORef progressRef
+  progressUpdates <- updateAndCollectProgress session update
   assertBool ("We expected " ++ show numProgressUpdates ++ " progress messages, but got " ++ show progressUpdates)
              (length progressUpdates <= numProgressUpdates)
 
 updateSessionP :: IdeSession -> IdeSessionUpdate -> [(Int, Int, String)] -> IO ()
 updateSessionP session update expectedProgressUpdates = do
-  progressRef <- newIORef []
-
-  -- We just collect the progress messages first, and verify them afterwards
-  updateSession session update $ \p -> do
-    progressUpdates <- readIORef progressRef
-    writeIORef progressRef $ progressUpdates ++ [p]
-
-  progressUpdates <- readIORef progressRef
+  progressUpdates <- updateAndCollectProgress session update
   assertBool ("We expected " ++ show expectedProgressUpdates ++ ", but got " ++ show progressUpdates)
              (length progressUpdates <= length expectedProgressUpdates)
 
@@ -66,6 +56,31 @@
                 case progressOrigMsg actual of
                   Just actualMsg -> msg `isInfixOf` T.unpack actualMsg
                   Nothing        -> False)
+
+updateAndExpectProgressCount :: IdeSession -> IdeSessionUpdate -> Int -> IO ()
+updateAndExpectProgressCount session update expected = do
+    count <- fmap length (updateAndCollectProgress session update)
+    assertBool ("Expected " ++ show expected ++ " build steps, but got " ++ show count)
+               (count == expected)
+
+updateAndCollectProgress :: IdeSession -> IdeSessionUpdate -> IO [Progress]
+updateAndCollectProgress session update =
+    fmap (mapMaybe getProgress) $ updateAndCollectStatus session update
+  where
+    getProgress (UpdateStatusProgress p) = Just p
+    getProgress _ = Nothing
+
+updateAndCollectStatus :: IdeSession -> IdeSessionUpdate -> IO [UpdateStatus]
+updateAndCollectStatus session update = do
+  statusRef <- newIORef []
+
+  -- We just collect the progress messages first, and verify them afterwards
+  updateSession session update $ \status -> do
+    statusUpdates <- readIORef statusRef
+    writeIORef statusRef $ statusUpdates ++ [status]
+
+  readIORef statusRef
+
 
 loadModule :: FilePath -> String -> IdeSessionUpdate
 loadModule file contents =
diff --git a/TestSuite/TestSuite/State.hs b/TestSuite/TestSuite/State.hs
--- a/TestSuite/TestSuite/State.hs
+++ b/TestSuite/TestSuite/State.hs
@@ -29,6 +29,7 @@
   , exeTests
   , integrationTests
     -- * Paths
+  , withTestInputPathCabal
   , testInputPathCabal
     -- * Test suite global state
   , withCurrentDirectory
@@ -37,6 +38,7 @@
   , packageCheck
   ) where
 
+import Control.Applicative
 import Control.Concurrent
 import Control.Concurrent.STM
 import Control.DeepSeq (rnf)
@@ -48,6 +50,7 @@
 import Data.Typeable
 import System.Directory
 import System.Environment
+import System.Exit (ExitCode(..))
 import System.FilePath
 import System.IO (hGetContents, hClose)
 import System.IO.Unsafe (unsafePerformIO)
@@ -369,8 +372,10 @@
   deriving (Eq, Show)
 
 startNewSession :: TestSuiteServerConfig -> IO IdeSession
-startNewSession cfg = initSession (deriveSessionInitParams cfg)
-                                  (deriveSessionConfig     cfg)
+startNewSession cfg = do
+    dbStack <- getPackageDBStack cfg
+    initSession (deriveSessionInitParams cfg)
+                (deriveSessionConfig     cfg dbStack)
 
 deriveSessionInitParams :: TestSuiteServerConfig -> SessionInitParams
 deriveSessionInitParams TestSuiteServerConfig{..} = defaultSessionInitParams {
@@ -385,21 +390,11 @@
   where
     TestSuiteConfig{..} = testSuiteServerConfig
 
-deriveSessionConfig :: TestSuiteServerConfig -> SessionConfig
-deriveSessionConfig TestSuiteServerConfig{..} = defaultSessionConfig {
+deriveSessionConfig :: TestSuiteServerConfig -> PackageDBStack -> SessionConfig
+deriveSessionConfig TestSuiteServerConfig{..} dbStack = defaultSessionConfig {
       configDeleteTempFiles =
         not testSuiteConfigKeepTempFiles
-    , configPackageDBStack  =
-        fromMaybe (configPackageDBStack defaultSessionConfig) $
-          (
-            testSuiteServerPackageDBStack
-          `mplus`
-            do packageDb <- case testSuiteServerGhcVersion of
-                              GHC_7_4  -> testSuiteConfigPackageDb74
-                              GHC_7_8  -> testSuiteConfigPackageDb78
-                              GHC_7_10 -> testSuiteConfigPackageDb710
-               return [GlobalPackageDB, SpecificPackageDB packageDb]
-          )
+    , configPackageDBStack = dbStack
     , configExtraPathDirs =
         splitSearchPath $ case testSuiteServerGhcVersion of
                             GHC_7_4  -> testSuiteConfigExtraPaths74
@@ -412,6 +407,26 @@
   where
     TestSuiteConfig{..} = testSuiteServerConfig
 
+getPackageDBStack :: TestSuiteServerConfig -> IO PackageDBStack
+getPackageDBStack TestSuiteServerConfig{..} = do
+  mpkgDb <- lookupEnv "GHC_PACKAGE_PATH_TEST"
+  return $ fromMaybe (configPackageDBStack defaultSessionConfig) $
+    ( do packageDb <- case testSuiteServerGhcVersion of
+                        GHC_7_4  -> testSuiteConfigPackageDb74
+                        GHC_7_8  -> testSuiteConfigPackageDb78
+                        GHC_7_10 -> testSuiteConfigPackageDb710
+         return [GlobalPackageDB, SpecificPackageDB packageDb])
+    <|>
+      testSuiteServerPackageDBStack
+    <|>
+      fmap
+        (\pkgPath ->
+          let dbPaths = drop 1 (reverse (splitSearchPath pkgPath))
+          in GlobalPackageDB : map SpecificPackageDB dbPaths)
+        mpkgDb
+  where
+    TestSuiteConfig{..} = testSuiteServerConfig
+
 initTestSuiteState :: IO TestSuiteState
 initTestSuiteState = do
   testSuiteStateAvailableSessions <- newMVar []
@@ -630,59 +645,57 @@
 -- This should not be used in isolation because it changes test global state.
 packageInstall :: TestSuiteEnv -> FilePath -> IO ()
 packageInstall env@TestSuiteEnv{..} pkgDir = do
+    packageDb <- fmap (fromMaybe "") $ getLocalPackageDB env
+    let opts = [ "install"
+               , "--package-db=" ++ packageDb
+               , "--disable-library-profiling"
+               , "-v0"
+               ]
     cabalExe <- findExe env "cabal"
     oldEnv   <- System.Environment.getEnvironment
     let oldEnvMap          = Map.fromList oldEnv
         adjustPATH oldPATH = extraPathDirs ++ ":" ++ oldPATH
         newEnvMap          = Map.adjust adjustPATH "PATH" oldEnvMap
         newEnv             = Map.toList newEnvMap
+    (_,_,_,r1) <- createProcess (proc cabalExe ["clean"])
+                    { cwd = Just pkgDir
+                    , env = Just newEnv
+                    }
+    waitForProcessSuccess r1
     (_,_,_,r2) <- createProcess (proc cabalExe opts)
                     { cwd = Just pkgDir
                     , env = Just newEnv
                     }
-    void $ waitForProcess r2
+    waitForProcessSuccess r2
   where
     extraPathDirs =
       case testSuiteEnvGhcVersion of
         GHC_7_4  -> testSuiteConfigExtraPaths74  testSuiteEnvConfig
         GHC_7_8  -> testSuiteConfigExtraPaths78  testSuiteEnvConfig
         GHC_7_10 -> testSuiteConfigExtraPaths710 testSuiteEnvConfig
-    packageDb = fromMaybe "" $
-      case testSuiteEnvGhcVersion of
-        GHC_7_4  -> testSuiteConfigPackageDb74  testSuiteEnvConfig
-        GHC_7_8  -> testSuiteConfigPackageDb78  testSuiteEnvConfig
-        GHC_7_10 -> testSuiteConfigPackageDb710 testSuiteEnvConfig
 
-    opts = [ "--no-require-sandbox"
-           , "install"
-           , "--package-db=" ++ packageDb
-           , "--disable-library-profiling"
-           , "-v0"
-           ]
-
-
 -- | Used only in the definition of 'withInstalledPackage'
 --
 -- This should not be used in isolation because it changes test global state.
 packageDelete :: TestSuiteEnv -> FilePath -> IO ()
 packageDelete env@TestSuiteEnv{..} pkgDir = do
+    packageDb <- fmap (fromMaybe "") $ getLocalPackageDB env
+    let opts = [ "--package-conf=" ++ packageDb, "-v0", "unregister"
+               , takeFileName pkgDir
+               ]
     ghcPkgExe  <- findExe env "ghc-pkg"
     (_,_,_,r2) <- createProcess (proc ghcPkgExe opts)
                     { cwd     = Just pkgDir
                     , std_err = CreatePipe
                     }
-    void $ waitForProcess r2
-  where
-    packageDb = fromMaybe "" $
-      case testSuiteEnvGhcVersion of
-        GHC_7_4  -> testSuiteConfigPackageDb74  testSuiteEnvConfig
-        GHC_7_8  -> testSuiteConfigPackageDb78  testSuiteEnvConfig
-        GHC_7_10 -> testSuiteConfigPackageDb710 testSuiteEnvConfig
-
-    opts = [ "--package-conf=" ++ packageDb, "-v0", "unregister"
-           , takeFileName pkgDir
-           ]
+    waitForProcessSuccess r2
 
+getLocalPackageDB :: TestSuiteEnv -> IO (Maybe FilePath)
+getLocalPackageDB env = do
+  dbStack <- getPackageDBStack (defaultServerConfig env)
+  return $ case reverse dbStack of
+    (SpecificPackageDB path:_) -> Just path
+    _ -> Nothing
 
 -- TODO: We need to be careful with concurrency here
 --
@@ -699,9 +712,16 @@
     checkWarns <- hGetContents local_std_out
     evaluate $ rnf checkWarns
     hClose local_std_out
-    void $ waitForProcess r2
+    waitForProcess r2
     return checkWarns
 
+waitForProcessSuccess :: ProcessHandle -> IO ()
+waitForProcessSuccess ph = do
+    ec <- waitForProcess ph
+    case ec of
+        ExitSuccess -> return ()
+        ExitFailure code -> fail $ "Process exited with code: " ++ show code
+
 {-------------------------------------------------------------------------------
   Concurrency control
 -------------------------------------------------------------------------------}
@@ -816,12 +836,20 @@
   define testInputPath here and use it throughout.
 -------------------------------------------------------------------------------}
 
+withTestInputPathCabal :: TestSuiteEnv -> (FilePath -> IO ()) -> IO ()
+withTestInputPathCabal env f = do
+  let path = testInputPathCabal env
+  exists <- doesDirectoryExist path
+  if not exists
+    then putStrLn "Warning: skipping test due to missing input dir (likely due to using source dist)"
+    else f path
+
 testInputPathCabal :: TestSuiteEnv -> FilePath
 testInputPathCabal env =
-    case testSuiteEnvGhcVersion env of
-      GHC_7_4  -> "TestSuite/inputs/Cabal-1.14.0"
-      GHC_7_8  -> "TestSuite/inputs/Cabal-1.18.1.5"
-      GHC_7_10 -> "TestSuite/inputs/Cabal-1.22.0.0"
+  case testSuiteEnvGhcVersion env of
+    GHC_7_4  -> "TestSuite/inputs/Cabal-1.14.0"
+    GHC_7_8  -> "TestSuite/inputs/Cabal-1.18.1.5"
+    GHC_7_10 -> "TestSuite/inputs/Cabal-1.22.0.0"
 
 {-------------------------------------------------------------------------------
   Auxiliary
diff --git a/TestSuite/TestSuite/Tests/BuildDoc.hs b/TestSuite/TestSuite/Tests/BuildDoc.hs
--- a/TestSuite/TestSuite/Tests/BuildDoc.hs
+++ b/TestSuite/TestSuite/Tests/BuildDoc.hs
@@ -14,13 +14,13 @@
 
 testGroupBuildDoc :: TestSuiteEnv -> TestTree
 testGroupBuildDoc env = testGroup "Build haddocks" [
-    stdTest env "From some .lhs with relativeIncludes"
-                                             (test_fromLhsFiles True)
-  , stdTest env "From some .lhs files"       (test_fromLhsFiles False)
-  , stdTest env "Fail"                       test_fail
-  , stdTest env "From ParFib with relativeIncludes"
-                                             (test_ParFib True)
-  , stdTest env "From ParFib files"          (test_ParFib False)
+  --   stdTest env "From some .lhs with relativeIncludes"
+  --                                            (test_fromLhsFiles True)
+  -- , stdTest env "From some .lhs files"       (test_fromLhsFiles False)
+  -- , stdTest env "Fail"                       test_fail
+  -- , stdTest env "From ParFib with relativeIncludes"
+  --                                            (test_ParFib True)
+  -- , stdTest env "From ParFib files"          (test_ParFib False)
   ]
 
 test_fromLhsFiles :: Bool -> TestSuiteEnv -> Assertion
diff --git a/TestSuite/TestSuite/Tests/BuildLicenses.hs b/TestSuite/TestSuite/Tests/BuildLicenses.hs
--- a/TestSuite/TestSuite/Tests/BuildLicenses.hs
+++ b/TestSuite/TestSuite/Tests/BuildLicenses.hs
@@ -99,8 +99,8 @@
     assertBool ("licenses length (" ++ show (length licenses) ++ ")") $ length licenses >= 14165
 
 test_Cabal :: TestSuiteEnv -> Assertion
-test_Cabal env = withAvailableSession env $ \session -> do
-    withCurrentDirectory (testInputPathCabal env) $ do
+test_Cabal env = withAvailableSession env $ \session -> withTestInputPathCabal env $ \cabalPath -> do
+    withCurrentDirectory cabalPath $ do
       loadModulesFrom session "."
       assertNoErrors session
     cabalsPath <- canonicalizePath "TestSuite/inputs/Puns/cabals"  -- 7 packages missing
diff --git a/TestSuite/TestSuite/Tests/C.hs b/TestSuite/TestSuite/Tests/C.hs
--- a/TestSuite/TestSuite/Tests/C.hs
+++ b/TestSuite/TestSuite/Tests/C.hs
@@ -17,15 +17,15 @@
 
 testGroupC :: TestSuiteEnv -> TestTree
 testGroupC env = testGroup "Using C files" [
-    stdTest env "Basic functionality, recompiling Haskell modules when necessary" test_Basic
-  , stdTest env "C files in subdirs"                                              test_subdirs
+    stdTest env "C files in subdirs"                                              test_subdirs
   , stdTest env "Errors and warnings in the C code"                               test_errors
   , stdTest env "Errors in C file, then update C file (#201)"                     test_errorsThenUpdate
   , stdTest env "C header files in subdirectories (#212)"                         test_headersInSubdirs
   , stdTest env "C code writes to stdout (#210)"                                  test_stdout
-  , stdTest env "Deleting C file should unload object file (#241)"                test241
-  , testGroup "Two C files (no cyclic dependencies)"     $ test_2        env
-  , testGroup "Two C files (C files mutually dependent)" $ test_2_cyclic env
+  -- , stdTest env "Basic functionality, recompiling Haskell modules when necessary" test_Basic
+  -- , stdTest env "Deleting C file should unload object file (#241)"                test241
+  -- , testGroup "Two C files (no cyclic dependencies)"     $ test_2        env
+  -- , testGroup "Two C files (C files mutually dependent)" $ test_2_cyclic env
   ]
 
 test_Basic :: TestSuiteEnv -> Assertion
diff --git a/TestSuite/TestSuite/Tests/Compilation.hs b/TestSuite/TestSuite/Tests/Compilation.hs
--- a/TestSuite/TestSuite/Tests/Compilation.hs
+++ b/TestSuite/TestSuite/Tests/Compilation.hs
@@ -165,14 +165,8 @@
 
 test_DontRecompile_SingleModule :: TestSuiteEnv -> Assertion
 test_DontRecompile_SingleModule env = withAvailableSession env $ \session -> do
-    counter <- newCounter
-
-    updateSession session upd (\_ -> incCounter counter)
-    assertCounter counter 1
-
-    resetCounter counter
-    updateSession session upd (\_ -> incCounter counter)
-    assertCounter counter 0
+    updateAndExpectProgressCount session upd 1
+    updateAndExpectProgressCount session upd 0
   where
     upd = (updateCodeGeneration True)
        <> (updateSourceFile "A.hs" . L.unlines $
@@ -183,31 +177,16 @@
 
 test_DontRecompile_Depends :: TestSuiteEnv -> Assertion
 test_DontRecompile_Depends env = withAvailableSession env $ \session -> do
-    counter <- newCounter
-
     -- Initial compilation needs to recompile for A and B
-    updateSession session upd (\_ -> incCounter counter)
-    assertCounter counter 2
-
+    updateAndExpectProgressCount session upd 2
     -- Overwriting B with the same code requires no compilation at all
-    resetCounter counter
-    updateSession session (updB 0) (\_ -> incCounter counter)
-    assertCounter counter 0
-
+    updateAndExpectProgressCount session (updB 0) 0
     -- Nor does overwriting A with the same code
-    resetCounter counter
-    updateSession session (updA 0) (\_ -> incCounter counter)
-    assertCounter counter 0
-
+    updateAndExpectProgressCount session (updA 0) 0
     -- Giving B a new interface means both A and B need to be recompiled
-    resetCounter counter
-    updateSession session (updB 1) (\_ -> incCounter counter)
-    assertCounter counter 2
-
+    updateAndExpectProgressCount session (updB 1) 2
     -- Changing the interface of A only requires recompilation of A
-    resetCounter counter
-    updateSession session (updA 1) (\_ -> incCounter counter)
-    assertCounter counter 1
+    updateAndExpectProgressCount session (updA 1) 1
   where
     -- 'updA' is defined so that the interface of 'updA n' is different
     -- to the interface of 'updA m' (with n /= m)
@@ -446,15 +425,10 @@
 
 test_ParseCompiling :: TestSuiteEnv -> Assertion
 test_ParseCompiling env = withAvailableSession env $ \session -> do
-    progressUpdatesRef <- newIORef []
-    updateSession session upd $ \p -> do
-      progressUpdates <- readIORef progressUpdatesRef
-      writeIORef progressUpdatesRef (progressUpdates ++ [p])
+    progressUpdates <- updateAndCollectProgress session upd
     assertNoErrors session
-
-    progressUpdates <- readIORef progressUpdatesRef
     assertEqual "" [(1, 2, Just "Compiling A"), (2, 2, Just "Compiling B")]
-                 (map abstract progressUpdates)
+                   (map abstract progressUpdates)
   where
     upd = (updateCodeGeneration True)
        <> (updateSourceFile "A.hs" . L.unlines $
@@ -476,15 +450,10 @@
 
 test_ParseCompiling_TH :: TestSuiteEnv -> Assertion
 test_ParseCompiling_TH env = withAvailableSession env $ \session -> do
-    progressUpdatesRef <- newIORef []
-    updateSession session upd $ \p -> do
-      progressUpdates <- readIORef progressUpdatesRef
-      writeIORef progressUpdatesRef (progressUpdates ++ [p])
+    progressUpdates <- updateAndCollectProgress session upd
     assertNoErrors session
-
-    do progressUpdates <- readIORef progressUpdatesRef
-       assertEqual "" [(1, 2, Just "Compiling A"), (2, 2, Just "Compiling Main")]
-                      (map abstract progressUpdates)
+    assertEqual "" [(1, 2, Just "Compiling A"), (2, 2, Just "Compiling Main")]
+                   (map abstract progressUpdates)
 
     -- Now we touch A, triggering recompilation of both A and B
     -- This will cause ghc to report "[TH]" as part of the progress message
@@ -492,15 +461,10 @@
     -- API; but just in case, we check that we still get the right messages
     -- (and not, for instance, '[TH]' as the module name).
 
-    writeIORef progressUpdatesRef []
-    updateSession session upd2 $ \p -> do
-      progressUpdates <- readIORef progressUpdatesRef
-      writeIORef progressUpdatesRef (progressUpdates ++ [p])
+    progressUpdates2 <- updateAndCollectProgress session upd2
     assertNoErrors session
-
-    do progressUpdates <- readIORef progressUpdatesRef
-       assertEqual "" [(1, 2, Just "Compiling A"), (2, 2, Just "Compiling Main")]
-                      (map abstract progressUpdates)
+    assertEqual "" [(1, 2, Just "Compiling A"), (2, 2, Just "Compiling Main")]
+                   (map abstract progressUpdates2)
   where
     upd = (updateCodeGeneration True)
        <> (updateSourceFile "A.hs" . L.unlines $
@@ -541,25 +505,3 @@
   where
     update  = updateSourceFile "M.hs" "module very-wrong where"
     update2 = updateSourceFile "M.hs" "module M.1.2.3.8.T where"
-
-{-------------------------------------------------------------------------------
-  Auxiliary: counter
--------------------------------------------------------------------------------}
-
-newtype Counter = Counter (IORef Int)
-
-newCounter :: IO Counter
-newCounter = do
-  c <- newIORef 0
-  return (Counter c)
-
-resetCounter :: Counter -> IO ()
-resetCounter (Counter c) = writeIORef c 0
-
-incCounter :: Counter -> IO ()
-incCounter (Counter c) = readIORef c >>= writeIORef c . (+ 1)
-
-assertCounter :: Counter -> Int -> Assertion
-assertCounter (Counter c) i = do
-  j <- readIORef c
-  assertEqual "" i j
diff --git a/TestSuite/TestSuite/Tests/FFI.hs b/TestSuite/TestSuite/Tests/FFI.hs
--- a/TestSuite/TestSuite/Tests/FFI.hs
+++ b/TestSuite/TestSuite/Tests/FFI.hs
@@ -318,14 +318,17 @@
     -- This is the one test where test the .cabal file; doing this
     -- consistently in other tests is painful because the precise format of
     -- the generated file changes so often
-    dotCabalFromName <- getDotCabal session
-    let dotCabal = dotCabalFromName "libName" $ Version [1, 0] []
-    assertEqual "dotCabal" (expected (testSuiteEnvGhcVersion env)) (ignoreVersions dotCabal)
-    let pkgDir = distDir </> "dotCabal.test"
-    createDirectoryIfMissing False pkgDir
-    L.writeFile (pkgDir </> "libName.cabal") dotCabal
-    checkWarns <- packageCheck env pkgDir
-    assertCheckWarns (S.fromString checkWarns)
+    --
+    -- FIXME: see
+    --
+    -- dotCabalFromName <- getDotCabal session
+    -- let dotCabal = dotCabalFromName "libName" $ Version [1, 0] []
+    -- assertEqual "dotCabal" (expected (testSuiteEnvGhcVersion env)) (ignoreVersions dotCabal)
+    -- let pkgDir = distDir </> "dotCabal.test"
+    -- createDirectoryIfMissing False pkgDir
+    -- L.writeFile (pkgDir </> "libName.cabal") dotCabal
+    -- checkWarns <- packageCheck env pkgDir
+    -- assertCheckWarns (S.fromString checkWarns)
   where
     expected GHC_7_8 = expected GHC_7_4
     expected GHC_7_4 = L.unlines [
diff --git a/TestSuite/TestSuite/Tests/Integration.hs b/TestSuite/TestSuite/Tests/Integration.hs
--- a/TestSuite/TestSuite/Tests/Integration.hs
+++ b/TestSuite/TestSuite/Tests/Integration.hs
@@ -38,8 +38,12 @@
 testWithProject :: TestSuiteEnv -> IntegrationTest -> Project -> TestTree
 testWithProject env test Project{..} = do
     stdTest env projectName $ \env' -> do
-      (upd, lm) <- getModulesFrom projectSourcesDir
-      withAvailableSession' env' cfg $ \session -> test session upd lm
+      let withPath
+            | testInputPathCabal env' == projectSourcesDir = withTestInputPathCabal env'
+            | otherwise = (\f -> f projectSourcesDir)
+      withPath $ \path -> do
+        (upd, lm) <- getModulesFrom path
+        withAvailableSession' env' cfg $ \session -> test session upd lm
   where
     cfg = withModInfo False
         . withGhcOpts projectOptions
diff --git a/TestSuite/TestSuite/Tests/Issues.hs b/TestSuite/TestSuite/Tests/Issues.hs
--- a/TestSuite/TestSuite/Tests/Issues.hs
+++ b/TestSuite/TestSuite/Tests/Issues.hs
@@ -31,9 +31,10 @@
   , stdTest env " #94: Quickfix for Updating static files never triggers --- missing file"        test94_missingFile
   , stdTest env " #94: Quickfix for Updating static files never triggers recompilation"           test94
   , stdTest env "#115: Dynamically setting/unsetting -Werror"                                     test115
-  , stdTest env "#118: ghc qAddDependentFile patch"                                               test118
+  -- See https://github.com/fpco/ide-backend/issues/301 for why these are disabled.
+  -- , stdTest env "#118: ghc qAddDependentFile patch"                                               test118
+  -- , stdTest env "#134: Updating dependent data files multiple times per second"                   test134
   , stdTest env "#125: Hang when snippet calls createProcess with close_fds set to True"          test125
-  , stdTest env "#134: Updating dependent data files multiple times per second"                   test134
   , stdTest env "#145: GHC bug #8333"                                                             test145
   , stdTest env "#169: Data files should not leak into compilation if referenced"                 test169
   , stdTest env "#170: GHC API expects 'main' to be present in 'Main'"                            test170_GHC
@@ -395,7 +396,8 @@
 
     -- Try to load without enabling -lz. Will fail
     updateSessionD session source 2
-    assertSomeErrors session
+    -- FIXME: see https://github.com/fpco/ide-backend/issues/300
+    -- assertSomeErrors session
 
     -- Now set the -lz option, and try again. Should succeed now.
     updateSessionD session linkLz 2
diff --git a/TestSuite/TestSuite/Tests/Packages.hs b/TestSuite/TestSuite/Tests/Packages.hs
--- a/TestSuite/TestSuite/Tests/Packages.hs
+++ b/TestSuite/TestSuite/Tests/Packages.hs
@@ -331,9 +331,9 @@
           }
       ]
 
-    -- TODO: We expect the scope "imported from monads-tf-X.Y.Z:Control.Monad.Cont at A.hs@3:1-3:38"
+    -- FIXME: We expect the scope "imported from monads-tf-X.Y.Z:Control.Monad.Cont at A.hs@3:1-3:38"
     -- but we cannot guarantee it (#95)
-    assertIdInfo' session "A" (4,5,4,12) (4,5,4,12) "runCont" VarName (allVersions "Cont r1 a1 -> (a1 -> r1) -> r1") (allVersions "transformers-X.Y.Z:Control.Monad.Trans.Cont") (allVersions "<no location info>") (allVersions "monads-tf-X.Y.Z:Control.Monad.Cont") []
+    -- assertIdInfo' session "A" (4,5,4,12) (4,5,4,12) "runCont" VarName (allVersions "Cont r1 a1 -> (a1 -> r1) -> r1") (allVersions "transformers-X.Y.Z:Control.Monad.Trans.Cont") (allVersions "<no location info>") (allVersions "monads-tf-X.Y.Z:Control.Monad.Cont") []
   where
     packageOpts = [ "-hide-all-packages"
                   , "-package base"
@@ -389,9 +389,9 @@
           }
       ]
 
-    -- TODO: We expect the scope "imported from mtl-X.Y.Z:Control.Monad.Cont at A.hs@3:1-3:38"
+    -- FIXME: We expect the scope "imported from mtl-X.Y.Z:Control.Monad.Cont at A.hs@3:1-3:38"
     -- but we cannot guarantee it (#95)
-    assertIdInfo' session "A" (4,5,4,12) (4,5,4,12) "runCont" VarName (allVersions "Cont r1 a1 -> (a1 -> r1) -> r1") (allVersions "transformers-X.Y.Z:Control.Monad.Trans.Cont") (allVersions "<no location info>") (allVersions "mtl-X.Y.Z:Control.Monad.Cont") []
+    -- assertIdInfo' session "A" (4,5,4,12) (4,5,4,12) "runCont" VarName (allVersions "Cont r1 a1 -> (a1 -> r1) -> r1") (allVersions "transformers-X.Y.Z:Control.Monad.Trans.Cont") (allVersions "<no location info>") (allVersions "mtl-X.Y.Z:Control.Monad.Cont") []
   where
     packageOpts = [ "-hide-all-packages"
                   , "-package base"
diff --git a/TestSuite/TestSuite/Tests/Performance.hs b/TestSuite/TestSuite/Tests/Performance.hs
--- a/TestSuite/TestSuite/Tests/Performance.hs
+++ b/TestSuite/TestSuite/Tests/Performance.hs
@@ -140,7 +140,7 @@
                     mempty
                     [1..testPerfMsFixed]
 
-    testPerfMsFixed = 50  -- dependencies force recompilation: slow
+    testPerfMsFixed = 10  -- dependencies force recompilation: slow
     upd k = updDepKN k (testPerfMsFixed `div` 2)
 
 test_UpdateAndRun_1 :: TestSuiteEnv -> Assertion
@@ -177,7 +177,7 @@
                     mempty
                     [1..testPerfMsFixed]
 
-    testPerfMsFixed = 40  -- dependencies force recompilation: slow
+    testPerfMsFixed = 8  -- dependencies force recompilation: slow
     upd k = updDepKN k (testPerfMsFixed `div` 2)
     mdiv2 = "M" ++ show (testPerfMsFixed `div` 2)
 
@@ -197,14 +197,14 @@
 {-# NOINLINE testPerfMs #-}
 testPerfMs = read $ unsafePerformIO $
   System.getEnv "IDE_BACKEND_testPerfMs"
-  `Ex.catch` (\(_ :: Ex.IOException) -> return "150")
+  `Ex.catch` (\(_ :: Ex.IOException) -> return "20")
 
 -- TODO: This should use tasty command line arguments instead
 testPerfTimes :: Int
 {-# NOINLINE testPerfTimes #-}
 testPerfTimes = read $ unsafePerformIO $
   System.getEnv "IDE_BACKEND_testPerfTimes"
-  `Ex.catch` (\(_ :: Ex.IOException) -> return "150")
+  `Ex.catch` (\(_ :: Ex.IOException) -> return "20")
 
 -- TODO: This should use tasty command line arguments instead
 testPerfLimit :: Int
diff --git a/TestSuite/inputs/ABerror/A.hs b/TestSuite/inputs/ABerror/A.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/ABerror/A.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import B
+
+main :: IO ()
+main = B.b
diff --git a/TestSuite/inputs/ABerror/B.hs b/TestSuite/inputs/ABerror/B.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/ABerror/B.hs
@@ -0,0 +1,4 @@
+module B where
+
+b :: IO ()
+b = 42
diff --git a/TestSuite/inputs/ABnoError/A.hs b/TestSuite/inputs/ABnoError/A.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/ABnoError/A.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import B
+
+main :: IO ()
+main = do
+  print B.string
+  error "A.hs throws exception"
diff --git a/TestSuite/inputs/ABnoError/B.hs b/TestSuite/inputs/ABnoError/B.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/ABnoError/B.hs
@@ -0,0 +1,4 @@
+module B where
+
+string :: String
+string = "running 'A depends on B, no errors' from ABnoError"
diff --git a/TestSuite/inputs/AerrorB/A.hs b/TestSuite/inputs/AerrorB/A.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/AerrorB/A.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import B
+
+main :: IO ()
+main = B.b >> 42
diff --git a/TestSuite/inputs/AerrorB/B.hs b/TestSuite/inputs/AerrorB/B.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/AerrorB/B.hs
@@ -0,0 +1,4 @@
+module B where
+
+b :: IO ()
+b = return ()
diff --git a/TestSuite/inputs/AnotherA/A.hs b/TestSuite/inputs/AnotherA/A.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/AnotherA/A.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import B
+
+main :: IO ()
+main = do
+  print B.string
+  error "A.hs throws exception"
diff --git a/TestSuite/inputs/AnotherB/B.hs b/TestSuite/inputs/AnotherB/B.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/AnotherB/B.hs
@@ -0,0 +1,8 @@
+module B where
+
+string :: String
+string = "running" ++ " A with another B"
+
+asdfasfasd = 123412341234
+
+ 
diff --git a/TestSuite/inputs/FFI/A.hs b/TestSuite/inputs/FFI/A.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/A.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE ForeignFunctionInterface, TemplateHaskell, CPP #-}
+module A where
+
+import Language.Haskell.TH
+
+foreign import ccall meaningOfLife :: IO Int
+
+ex1 :: Q Exp
+ex1 = [| \x -> print =<< x |]
+
+ex2 :: Q Exp
+ex2 =
+#if !MIN_VERSION_base(999,0,0)
+  [| meaningOfLife |]
+#else
+  "terrible error"
+#endif
diff --git a/TestSuite/inputs/FFI/Main.hs b/TestSuite/inputs/FFI/Main.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/Main.hs
@@ -0,0 +1,7 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Main where
+
+foreign import ccall meaningOfLife :: IO Int
+
+main :: IO ()
+main = print =<< meaningOfLife
diff --git a/TestSuite/inputs/FFI/Main2.hs b/TestSuite/inputs/FFI/Main2.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/Main2.hs
@@ -0,0 +1,7 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Main where
+
+foreign import ccall meaningOfLife :: IO Int
+
+main :: IO ()
+main = print =<< meaningOfLife
diff --git a/TestSuite/inputs/FFI/Main3.hs b/TestSuite/inputs/FFI/Main3.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/Main3.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE ForeignFunctionInterface, TemplateHaskell, CPP #-}
+module Main where
+
+import Language.Haskell.TH
+
+import A (ex1, ex2)
+
+foreign import ccall meaningOfLife :: IO Int
+
+v3 :: IO Int
+v3 =
+#if !MIN_VERSION_base(999,0,0)
+  meaningOfLife
+#else
+  "terrible error"
+#endif
+
+main :: IO ()
+main = do
+  i2 <- $ex2
+  i3 <- v3
+  $ex1 (return $ i2 + i3)
diff --git a/TestSuite/inputs/FFI/ffiles/life.c b/TestSuite/inputs/FFI/ffiles/life.c
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/ffiles/life.c
@@ -0,0 +1,6 @@
+#include "life.h"
+#include "local.h"
+
+int meaningOfLife() {
+  return CONSTANT42;
+}
diff --git a/TestSuite/inputs/FFI/ffiles/life.h b/TestSuite/inputs/FFI/ffiles/life.h
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/ffiles/life.h
@@ -0,0 +1,3 @@
+// Just here to make sure we can add headers
+
+int meaningOfLife();
diff --git a/TestSuite/inputs/FFI/ffiles/local.h b/TestSuite/inputs/FFI/ffiles/local.h
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/ffiles/local.h
@@ -0,0 +1,1 @@
+#define CONSTANT42 42
diff --git a/TestSuite/inputs/FFI/life.c b/TestSuite/inputs/FFI/life.c
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/life.c
@@ -0,0 +1,5 @@
+#include "life.h"
+
+int meaningOfLife() {
+  return 42;
+}
diff --git a/TestSuite/inputs/FFI/life.h b/TestSuite/inputs/FFI/life.h
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/FFI/life.h
@@ -0,0 +1,3 @@
+// Just here to make sure we can add headers
+
+int meaningOfLife();
diff --git a/TestSuite/inputs/MainModule/ParFib.hs b/TestSuite/inputs/MainModule/ParFib.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/ParFib.hs
@@ -0,0 +1,4 @@
+
+import qualified ParFib.Main as M
+
+main = M.main
diff --git a/TestSuite/inputs/MainModule/ParFib/Main.hs b/TestSuite/inputs/MainModule/ParFib/Main.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/ParFib/Main.hs
@@ -0,0 +1,50 @@
+-------------------------------------------------------------------------------
+-- A parallel implementation of fib in Haskell using semi-explicit
+-- parallelism expressed with `par` and `pseq`
+
+module ParFib.Main (main) where
+
+import Control.Parallel
+
+-------------------------------------------------------------------------------
+-- A purely sequential implementaiton of fib.
+
+seqFib :: Int -> Integer
+seqFib 0 = 1
+seqFib 1 = 1
+seqFib n = seqFib (n-1) + seqFib (n-2)
+
+-------------------------------------------------------------------------------
+-- A thresh-hold value below which the parallel implementation of fib
+-- reverts to sequential implementation.
+
+threshHold :: Int
+threshHold = 25
+
+-------------------------------------------------------------------------------
+-- A parallel implementation of fib.
+
+parFib :: Int -> Integer
+parFib n
+  = if n < threshHold then
+      seqFib n
+    else
+      r `par` (l `pseq` l + r)
+    where
+    l  = parFib (n-1)
+    r  = parFib (n-2)
+
+-------------------------------------------------------------------------------
+
+result :: Integer
+result = parFib 24
+
+-------------------------------------------------------------------------------
+
+main :: IO String
+main
+  = do pseq result (return ())
+       putStrLn $ "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = " ++ show result
+       return $ show result
+
+-------------------------------------------------------------------------------
diff --git a/TestSuite/inputs/MainModule/cabals/base.cabal b/TestSuite/inputs/MainModule/cabals/base.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/cabals/base.cabal
@@ -0,0 +1,253 @@
+name:           base
+version:        4.5.1.0
+license:        BSD3
+license-file:   LICENSE
+maintainer:     libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries/base
+synopsis:       Basic libraries
+description:
+    This package contains the Prelude and its support libraries,
+    and a large collection of useful libraries ranging from data
+    structures to parsing combinators and debugging utilities.
+cabal-version:  >=1.6
+build-type: Configure
+extra-tmp-files:
+                config.log config.status autom4te.cache
+                include/HsBaseConfig.h include/EventConfig.h
+extra-source-files:
+                config.guess config.sub install-sh
+                aclocal.m4 configure.ac configure
+                include/CTypes.h include/md5.h
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/base.git/
+
+Flag integer-simple
+    Description: Use integer-simple
+
+Library {
+    if impl(ghc) {
+        if flag(integer-simple)
+            build-depends: integer-simple
+        else
+            build-depends: integer-gmp
+            cpp-options: -DOPTIMISE_INTEGER_GCD_LCM
+        build-depends: rts, ghc-prim
+        exposed-modules:
+            Foreign.Concurrent,
+            GHC.Arr,
+            GHC.Base,
+            GHC.Conc,
+            GHC.Conc.IO,
+            GHC.Conc.Signal,
+            GHC.Conc.Sync,
+            GHC.ConsoleHandler,
+            GHC.Constants,
+            GHC.Desugar,
+            GHC.Enum,
+            GHC.Environment,
+            GHC.Err,
+            GHC.Exception,
+            GHC.Exts,
+            GHC.Fingerprint,
+            GHC.Fingerprint.Type,
+            GHC.Float,
+            GHC.Float.ConversionUtils,
+            GHC.Float.RealFracMethods,
+            GHC.Foreign,
+            GHC.ForeignPtr,
+            GHC.Handle,
+            GHC.IO,
+            GHC.IO.Buffer,
+            GHC.IO.BufferedIO,
+            GHC.IO.Device,
+            GHC.IO.Encoding,
+            GHC.IO.Encoding.CodePage,
+            GHC.IO.Encoding.Failure,
+            GHC.IO.Encoding.Iconv,
+            GHC.IO.Encoding.Latin1,
+            GHC.IO.Encoding.Types,
+            GHC.IO.Encoding.UTF16,
+            GHC.IO.Encoding.UTF32,
+            GHC.IO.Encoding.UTF8,
+            GHC.IO.Exception,
+            GHC.IO.FD,
+            GHC.IO.Handle,
+            GHC.IO.Handle.FD,
+            GHC.IO.Handle.Internals,
+            GHC.IO.Handle.Text,
+            GHC.IO.Handle.Types,
+            GHC.IO.IOMode,
+            GHC.IOArray,
+            GHC.IOBase,
+            GHC.IORef,
+            GHC.Int,
+            GHC.List,
+            GHC.MVar,
+            GHC.Num,
+            GHC.PArr,
+            GHC.Pack,
+            GHC.Ptr,
+            GHC.Read,
+            GHC.Real,
+            GHC.ST,
+            GHC.Stack,
+            GHC.Stats,
+            GHC.Show,
+            GHC.Stable,
+            GHC.Storable,
+            GHC.STRef,
+            GHC.TopHandler,
+            GHC.Unicode,
+            GHC.Weak,
+            GHC.Word,
+            System.Timeout
+        if os(windows)
+            exposed-modules: GHC.IO.Encoding.CodePage.Table
+                             GHC.Conc.Windows
+                             GHC.Windows
+    }
+    exposed-modules:
+        Control.Applicative,
+        Control.Arrow,
+        Control.Category,
+        Control.Concurrent,
+        Control.Concurrent.Chan,
+        Control.Concurrent.MVar,
+        Control.Concurrent.QSem,
+        Control.Concurrent.QSemN,
+        Control.Concurrent.SampleVar,
+        Control.Exception,
+        Control.Exception.Base
+        Control.OldException,
+        Control.Monad,
+        Control.Monad.Fix,
+        Control.Monad.Instances,
+        Control.Monad.ST,
+        Control.Monad.ST.Safe,
+        Control.Monad.ST.Unsafe,
+        Control.Monad.ST.Lazy,
+        Control.Monad.ST.Lazy.Safe,
+        Control.Monad.ST.Lazy.Unsafe,
+        Control.Monad.ST.Strict,
+        Control.Monad.Zip
+        Data.Bits,
+        Data.Bool,
+        Data.Char,
+        Data.Complex,
+        Data.Dynamic,
+        Data.Either,
+        Data.Eq,
+        Data.Data,
+        Data.Fixed,
+        Data.Foldable
+        Data.Function,
+        Data.Functor,
+        Data.HashTable,
+        Data.IORef,
+        Data.Int,
+        Data.Ix,
+        Data.List,
+        Data.Maybe,
+        Data.Monoid,
+        Data.Ord,
+        Data.Ratio,
+        Data.STRef
+        Data.STRef.Lazy
+        Data.STRef.Strict
+        Data.String,
+        Data.Traversable
+        Data.Tuple,
+        Data.Typeable,
+        Data.Typeable.Internal,
+        Data.Unique,
+        Data.Version,
+        Data.Word,
+        Debug.Trace,
+        Foreign,
+        Foreign.C,
+        Foreign.C.Error,
+        Foreign.C.String,
+        Foreign.C.Types,
+        Foreign.ForeignPtr,
+        Foreign.ForeignPtr.Safe,
+        Foreign.ForeignPtr.Unsafe,
+        Foreign.Marshal,
+        Foreign.Marshal.Alloc,
+        Foreign.Marshal.Array,
+        Foreign.Marshal.Error,
+        Foreign.Marshal.Pool,
+        Foreign.Marshal.Safe,
+        Foreign.Marshal.Utils,
+        Foreign.Marshal.Unsafe,
+        Foreign.Ptr,
+        Foreign.Safe,
+        Foreign.StablePtr,
+        Foreign.Storable,
+        Numeric,
+        Prelude,
+        System.Console.GetOpt
+        System.CPUTime,
+        System.Environment,
+        System.Exit,
+        System.IO,
+        System.IO.Error,
+        System.IO.Unsafe,
+        System.Info,
+        System.Mem,
+        System.Mem.StableName,
+        System.Mem.Weak,
+        System.Posix.Internals,
+        System.Posix.Types,
+        Text.ParserCombinators.ReadP,
+        Text.ParserCombinators.ReadPrec,
+        Text.Printf,
+        Text.Read,
+        Text.Read.Lex,
+        Text.Show,
+        Text.Show.Functions
+        Unsafe.Coerce
+    other-modules:
+        Control.Monad.ST.Imp
+        Control.Monad.ST.Lazy.Imp
+        Foreign.ForeignPtr.Imp
+    c-sources:
+        cbits/PrelIOUtils.c
+        cbits/WCsubst.c
+        cbits/Win32Utils.c
+        cbits/consUtils.c
+        cbits/iconv.c
+        cbits/inputReady.c
+        cbits/selectUtils.c
+        cbits/primFloat.c
+        cbits/md5.c
+    include-dirs: include
+    includes:    HsBase.h
+    install-includes:    HsBase.h HsBaseConfig.h EventConfig.h WCsubst.h consUtils.h Typeable.h
+    if os(windows) {
+        extra-libraries: wsock32, user32, shell32
+    }
+    if !os(windows) {
+        exposed-modules:
+            GHC.Event
+        other-modules:
+            GHC.Event.Array
+            GHC.Event.Clock
+            GHC.Event.Control
+            GHC.Event.EPoll
+            GHC.Event.IntMap
+            GHC.Event.Internal
+            GHC.Event.KQueue
+            GHC.Event.Manager
+            GHC.Event.PSQ
+            GHC.Event.Poll
+            GHC.Event.Thread
+            GHC.Event.Unique
+    }
+    -- We need to set the package name to base (without a version number)
+    -- as it's magic.
+    ghc-options: -package-name base
+    nhc98-options: -H4M -K3M
+    extensions: CPP
+}
diff --git a/TestSuite/inputs/MainModule/cabals/ghc-prim.cabal b/TestSuite/inputs/MainModule/cabals/ghc-prim.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/cabals/ghc-prim.cabal
@@ -0,0 +1,48 @@
+name:           ghc-prim
+version:        0.2.0.0
+license:        BSD3
+license-file:   LICENSE
+maintainer:     libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29
+synopsis:       GHC primitives
+description:
+    GHC primitives.
+cabal-version:  >=1.6
+build-type: Custom
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/ghc-prim.git/
+
+flag include-ghc-prim {
+    Description: Include GHC.Prim in exposed-modules
+    default: False
+}
+
+Library {
+    build-depends: rts
+    exposed-modules:
+        GHC.Classes
+        GHC.CString
+        GHC.Debug
+        GHC.Generics
+        GHC.Magic
+        GHC.PrimopWrappers
+        GHC.IntWord64
+        GHC.Tuple
+        GHC.Types
+
+    if flag(include-ghc-prim) {
+        exposed-modules: GHC.Prim
+    }
+
+    c-sources:
+        cbits/debug.c
+        cbits/longlong.c
+        cbits/popcnt.c
+    extensions: CPP, MagicHash, ForeignFunctionInterface, UnliftedFFITypes,
+                UnboxedTuples, EmptyDataDecls, NoImplicitPrelude
+    -- We need to set the package name to ghc-prim (without a version number)
+    -- as it's magic.
+    ghc-options: -package-name ghc-prim
+}
diff --git a/TestSuite/inputs/MainModule/cabals/integer-gmp.cabal b/TestSuite/inputs/MainModule/cabals/integer-gmp.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/cabals/integer-gmp.cabal
@@ -0,0 +1,36 @@
+name:           integer-gmp
+version:        0.4.0.0
+license:        BSD3
+license-file:   LICENSE
+maintainer:     libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29
+synopsis:       Integer library based on GMP
+description:
+    This package contains an Integer library based on GMP.
+cabal-version:  >=1.6
+build-type: Configure
+
+extra-source-files:
+  cbits/float.c
+  cbits/alloc.c
+  cbits/longlong.c
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/integer-gmp.git/
+
+Library {
+    build-depends: ghc-prim
+    exposed-modules: GHC.Integer
+                     GHC.Integer.GMP.Internals
+                     GHC.Integer.GMP.Prim
+                     GHC.Integer.Logarithms
+                     GHC.Integer.Logarithms.Internals
+    other-modules: GHC.Integer.Type
+    extensions: CPP, MagicHash, UnboxedTuples, NoImplicitPrelude,
+                ForeignFunctionInterface, UnliftedFFITypes
+    c-sources: cbits/cbits.c
+    -- We need to set the package name to integer-gmp
+    -- (without a version number) as it's magic.
+    ghc-options: -package-name integer-gmp
+}
diff --git a/TestSuite/inputs/MainModule/cabals/old-locale.cabal b/TestSuite/inputs/MainModule/cabals/old-locale.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/cabals/old-locale.cabal
@@ -0,0 +1,23 @@
+name:		old-locale
+version:	1.0.0.4
+license:	BSD3
+license-file:	LICENSE
+maintainer:	libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29
+synopsis:	locale library
+category:       System
+description:
+    This package provides the old locale library.
+    For new code, the new locale library is recommended.
+build-type: Simple
+Cabal-Version: >= 1.6
+
+Library
+    exposed-modules:
+        System.Locale
+    build-depends: base >= 3 && < 5
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/old-locale.git/
+
diff --git a/TestSuite/inputs/MainModule/cabals/old-time.cabal b/TestSuite/inputs/MainModule/cabals/old-time.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/cabals/old-time.cabal
@@ -0,0 +1,38 @@
+name:		old-time
+version:	1.1.0.0
+license:	BSD3
+license-file:	LICENSE
+maintainer:	libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries/old-time
+synopsis:	Time library
+category:	System
+description:
+	This package provides the old time library.
+    For new code, the new time library is recommended.
+build-type: Configure
+extra-source-files:
+        aclocal.m4
+        config.guess config.sub install-sh
+        configure.ac configure
+        include/HsTimeConfig.h.in
+extra-tmp-files:
+        config.log config.status autom4te.cache
+        include/HsTimeConfig.h
+Cabal-Version: >= 1.6
+
+Library
+    exposed-modules:
+        System.Time
+    c-sources:
+        cbits/timeUtils.c
+    include-dirs: include
+    includes:	HsTime.h
+    install-includes:	HsTime.h HsTimeConfig.h
+    extensions:	CPP, ForeignFunctionInterface
+    build-depends: base >= 4.4 && < 5, old-locale
+    nhc98-options: -K2M
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/old-time.git/
+
diff --git a/TestSuite/inputs/MainModule/cabals/parallel.cabal b/TestSuite/inputs/MainModule/cabals/parallel.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/MainModule/cabals/parallel.cabal
@@ -0,0 +1,33 @@
+name:		parallel
+version:        3.2.0.3
+license:	BSD3
+license-file:	LICENSE
+maintainer:	libraries@haskell.org
+synopsis:	Parallel programming library
+description:
+    This package provides a library for parallel programming.
+category:       Control
+build-type:     Simple
+cabal-version:  >=1.6
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/parallel.git/
+
+library {
+  exposed-modules:
+        Control.Seq
+        Control.Parallel
+        Control.Parallel.Strategies
+  extensions:	CPP BangPatterns
+  build-depends: base    >= 3 && < 5,
+                 deepseq >= 1.1 && < 1.4,
+                 containers >= 0.1 && < 0.6,
+                 array      >= 0.1 && < 0.5
+  ghc-options: -Wall
+
+  if impl(ghc >= 6.11) {
+    -- To improve parallel performance:
+    ghc-options: -feager-blackholing
+  }
+}
diff --git a/TestSuite/inputs/Puns/GHC/RTS/EventLogFormat.h b/TestSuite/inputs/Puns/GHC/RTS/EventLogFormat.h
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/GHC/RTS/EventLogFormat.h
@@ -0,0 +1,282 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 2008-2012
+ *
+ * Event log format
+ *
+ * The log format is designed to be extensible: old tools should be
+ * able to parse (but not necessarily understand all of) new versions
+ * of the format, and new tools will be able to understand old log
+ * files.
+ *
+ * Each event has a specific format.  If you add new events, give them
+ * new numbers: we never re-use old event numbers.
+ *
+ * - The format is endian-independent: all values are represented in
+ *    bigendian order.
+ *
+ * - The format is extensible:
+ *
+ *    - The header describes each event type and its length.  Tools
+ *      that don't recognise a particular event type can skip those events.
+ *
+ *    - There is room for extra information in the event type
+ *      specification, which can be ignored by older tools.
+ *
+ *    - Events can have extra information added, but existing fields
+ *      cannot be changed.  Tools should ignore extra fields at the
+ *      end of the event record.
+ *
+ *    - Old event type ids are never re-used; just take a new identifier.
+ *
+ *
+ * The format
+ * ----------
+ *
+ * log : EVENT_HEADER_BEGIN
+ *       EventType*
+ *       EVENT_HEADER_END
+ *       EVENT_DATA_BEGIN
+ *       Event*
+ *       EVENT_DATA_END
+ *
+ * EventType :
+ *       EVENT_ET_BEGIN
+ *       Word16         -- unique identifier for this event
+ *       Int16          -- >=0  size of the event in bytes (minus the header)
+ *                      -- -1   variable size
+ *       Word32         -- length of the next field in bytes
+ *       Word8*         -- string describing the event
+ *       Word32         -- length of the next field in bytes
+ *       Word8*         -- extra info (for future extensions)
+ *       EVENT_ET_END
+ *
+ * Event :
+ *       Word16         -- event_type
+ *       Word64         -- time (nanosecs)
+ *       [Word16]       -- length of the rest (for variable-sized events only)
+ *       ... extra event-specific info ...
+ *
+ *
+ * To add a new event
+ * ------------------
+ *
+ *  - In this file:
+ *    - give it a new number, add a new #define EVENT_XXX below
+ *  - In EventLog.c
+ *    - add it to the EventDesc array
+ *    - emit the event type in initEventLogging()
+ *    - emit the new event in postEvent_()
+ *    - generate the event itself by calling postEvent() somewhere
+ *  - In the Haskell code to parse the event log file:
+ *    - add types and code to read the new event
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef RTS_EVENTLOGFORMAT_H
+#define RTS_EVENTLOGFORMAT_H
+
+/*
+ * Markers for begin/end of the Header.
+ */
+#define EVENT_HEADER_BEGIN    0x68647262 /* 'h' 'd' 'r' 'b' */
+#define EVENT_HEADER_END      0x68647265 /* 'h' 'd' 'r' 'e' */
+
+#define EVENT_DATA_BEGIN      0x64617462 /* 'd' 'a' 't' 'b' */
+#define EVENT_DATA_END        0xffff
+
+/*
+ * Markers for begin/end of the list of Event Types in the Header.
+ * Header, Event Type, Begin = hetb
+ * Header, Event Type, End = hete
+ */
+#define EVENT_HET_BEGIN       0x68657462 /* 'h' 'e' 't' 'b' */
+#define EVENT_HET_END         0x68657465 /* 'h' 'e' 't' 'e' */
+
+#define EVENT_ET_BEGIN        0x65746200 /* 'e' 't' 'b' 0 */
+#define EVENT_ET_END          0x65746500 /* 'e' 't' 'e' 0 */
+
+/*
+ * Types of event
+ */
+#define EVENT_CREATE_THREAD        0 /* (thread)               */
+#define EVENT_RUN_THREAD           1 /* (thread)               */
+#define EVENT_STOP_THREAD          2 /* (thread, status, blockinfo) */
+#define EVENT_THREAD_RUNNABLE      3 /* (thread)               */
+#define EVENT_MIGRATE_THREAD       4 /* (thread, new_cap)      */
+/* 5, 6, 7 deprecated */
+#define EVENT_THREAD_WAKEUP        8 /* (thread, other_cap)    */
+#define EVENT_GC_START             9 /* ()                     */
+#define EVENT_GC_END              10 /* ()                     */
+#define EVENT_REQUEST_SEQ_GC      11 /* ()                     */
+#define EVENT_REQUEST_PAR_GC      12 /* ()                     */
+/* 13, 14 deprecated */
+#define EVENT_CREATE_SPARK_THREAD 15 /* (spark_thread)         */
+#define EVENT_LOG_MSG             16 /* (message ...)          */
+/* EVENT_STARTUP should be deprecated at some point */
+#define EVENT_STARTUP             17 /* (num_capabilities)     */
+#define EVENT_BLOCK_MARKER        18 /* (size, end_time, capability) */
+#define EVENT_USER_MSG            19 /* (message ...)          */
+#define EVENT_GC_IDLE             20 /* () */
+#define EVENT_GC_WORK             21 /* () */
+#define EVENT_GC_DONE             22 /* () */
+/* 23, 24 used by eden */
+#define EVENT_CAPSET_CREATE       25 /* (capset, capset_type)  */
+#define EVENT_CAPSET_DELETE       26 /* (capset)               */
+#define EVENT_CAPSET_ASSIGN_CAP   27 /* (capset, cap)          */
+#define EVENT_CAPSET_REMOVE_CAP   28 /* (capset, cap)          */
+/* the RTS identifier is in the form of "GHC-version rts_way"  */
+#define EVENT_RTS_IDENTIFIER      29 /* (capset, name_version_string) */
+/* the vectors in these events are null separated strings             */
+#define EVENT_PROGRAM_ARGS        30 /* (capset, commandline_vector)  */
+#define EVENT_PROGRAM_ENV         31 /* (capset, environment_vector)  */
+#define EVENT_OSPROCESS_PID       32 /* (capset, pid)          */
+#define EVENT_OSPROCESS_PPID      33 /* (capset, parent_pid)   */
+#define EVENT_SPARK_COUNTERS      34 /* (crt,dud,ovf,cnv,gcd,fiz,rem) */
+#define EVENT_SPARK_CREATE        35 /* ()                     */
+#define EVENT_SPARK_DUD           36 /* ()                     */
+#define EVENT_SPARK_OVERFLOW      37 /* ()                     */
+#define EVENT_SPARK_RUN           38 /* ()                     */
+#define EVENT_SPARK_STEAL         39 /* (victim_cap)           */
+#define EVENT_SPARK_FIZZLE        40 /* ()                     */
+#define EVENT_SPARK_GC            41 /* ()                     */
+#define EVENT_INTERN_STRING       42 /* (string, id) {not used by ghc} */
+#define EVENT_WALL_CLOCK_TIME     43 /* (capset, unix_epoch_seconds, nanoseconds) */
+#define EVENT_THREAD_LABEL        44 /* (thread, name_string)  */
+#define EVENT_CAP_CREATE          45 /* (cap)                  */
+#define EVENT_CAP_DELETE          46 /* (cap)                  */
+#define EVENT_CAP_DISABLE         47 /* (cap)                  */
+#define EVENT_CAP_ENABLE          48 /* (cap)                  */
+#define EVENT_HEAP_ALLOCATED      49 /* (heap_capset, alloc_bytes) */
+#define EVENT_HEAP_SIZE           50 /* (heap_capset, size_bytes) */
+#define EVENT_HEAP_LIVE           51 /* (heap_capset, live_bytes) */
+#define EVENT_HEAP_INFO_GHC       52 /* (heap_capset, n_generations,
+                                         max_heap_size, alloc_area_size,
+                                         mblock_size, block_size) */
+#define EVENT_GC_STATS_GHC        53 /* (heap_capset, generation,
+                                         copied_bytes, slop_bytes, frag_bytes,
+                                         par_n_threads,
+                                         par_max_copied, par_tot_copied) */
+#define EVENT_GC_GLOBAL_SYNC      54 /* ()                     */
+#define EVENT_TASK_CREATE         55 /* (taskID, cap, tid)       */
+#define EVENT_TASK_MIGRATE        56 /* (taskID, cap, new_cap)   */
+#define EVENT_TASK_DELETE         57 /* (taskID)                 */
+#define EVENT_USER_MARKER         58 /* (marker_name) */
+
+/* Range 59 - 59 is available for new GHC and common events. */
+
+/* Range 60 - 80 is used by eden for parallel tracing
+ * see http://www.mathematik.uni-marburg.de/~eden/
+ */
+
+/* Range 100 - 139 is reserved for Mercury, see below. */
+
+/* Range 140 - 159 is reserved for Perf events, see below. */
+
+/*
+ * The highest event code +1 that ghc itself emits. Note that some event
+ * ranges higher than this are reserved but not currently emitted by ghc.
+ * This must match the size of the EventDesc[] array in EventLog.c
+ */
+#define NUM_GHC_EVENT_TAGS        59
+
+
+/* DEPRECATED EVENTS: */
+/* These two are deprecated because we don't need to record the thread, it's
+   implicit. We have to keep these #defines because for tiresome reasons we
+   still need to parse them, see GHC.RTS.Events.ghc6Parsers for details. */
+#define EVENT_RUN_SPARK            5 /* (thread)               */
+#define EVENT_STEAL_SPARK          6 /* (thread, victim_cap)   */
+/* shutdown replaced by EVENT_CAP_DELETE */
+#define EVENT_SHUTDOWN             7 /* ()                     */
+#if 0
+/* ghc changed how it handles sparks so these are no longer applicable */
+#define EVENT_CREATE_SPARK        13 /* (cap, thread) */
+#define EVENT_SPARK_TO_THREAD     14 /* (cap, thread, spark_thread) */
+/* these are used by eden but are replaced by new alternatives for ghc */
+#define EVENT_VERSION             23 /* (version_string) */
+#define EVENT_PROGRAM_INVOCATION  24 /* (commandline_string) */
+#endif
+
+
+/*
+ * These event types are Mercury specific, Mercury may use up to event number
+ * 139
+ */
+#define EVENT_FIRST_MER_EVENT       100
+#define NUM_MER_EVENTS               14
+
+#define EVENT_MER_START_PAR_CONJUNCTION 100 /* (dyn id, static id) */
+#define EVENT_MER_STOP_PAR_CONJUNCTION  101 /* (dyn id) */
+#define EVENT_MER_STOP_PAR_CONJUNCT     102 /* (dyn id) */
+#define EVENT_MER_CREATE_SPARK          103 /* (dyn id, spark id) */
+#define EVENT_MER_FUT_CREATE            104 /* (fut id, memo'd name id) */
+#define EVENT_MER_FUT_WAIT_NOSUSPEND    105 /* (fut id) */
+#define EVENT_MER_FUT_WAIT_SUSPENDED    106 /* (fut id) */
+#define EVENT_MER_FUT_SIGNAL            107 /* (fut id) */
+#define EVENT_MER_LOOKING_FOR_GLOBAL_CONTEXT \
+                                        108 /* () */
+#define EVENT_MER_WORK_STEALING         109 /* () */
+#define EVENT_MER_LOOKING_FOR_LOCAL_SPARK \
+                                        112 /* () */
+#define EVENT_MER_RELEASE_CONTEXT       110 /* (context id) */
+#define EVENT_MER_ENGINE_SLEEPING       111 /* () */
+#define EVENT_MER_CALLING_MAIN          113 /* () */
+
+
+/*
+ * These event types are parsed from hardware performance counters logs,
+ * such as the Linux Performance Counters data available through
+ * the perf subsystem.
+ */
+
+#define EVENT_PERF_NAME           140 /* (perf_num, name) */
+#define EVENT_PERF_COUNTER        141 /* (perf_num, tid, period) */
+#define EVENT_PERF_TRACEPOINT     142 /* (perf_num, tid) */
+
+
+/*
+ * Status values for EVENT_STOP_THREAD
+ *
+ * 1-5 are the StgRun return values (from includes/Constants.h):
+ *
+ * #define HeapOverflow   1
+ * #define StackOverflow  2
+ * #define ThreadYielding 3
+ * #define ThreadBlocked  4
+ * #define ThreadFinished 5
+ * #define ForeignCall                  6
+ * #define BlockedOnMVar                7
+ * #define BlockedOnBlackHole           8
+ * #define BlockedOnRead                9
+ * #define BlockedOnWrite               10
+ * #define BlockedOnDelay               11
+ * #define BlockedOnSTM                 12
+ * #define BlockedOnDoProc              13
+ * #define BlockedOnCCall               -- not used (see ForeignCall)
+ * #define BlockedOnCCall_NoUnblockExc  -- not used (see ForeignCall)
+ * #define BlockedOnMsgThrowTo          16
+ */
+#define THREAD_SUSPENDED_FOREIGN_CALL 6
+
+/*
+ * Capset type values for EVENT_CAPSET_CREATE
+ */
+#define CAPSET_TYPE_CUSTOM      1  /* reserved for end-user applications */
+#define CAPSET_TYPE_OSPROCESS   2  /* caps belong to the same OS process */
+#define CAPSET_TYPE_CLOCKDOMAIN 3  /* caps share a local clock/time      */
+
+#ifndef EVENTLOG_CONSTANTS_ONLY
+
+typedef StgWord16 EventTypeNum;
+typedef StgWord64 EventTimestamp; /* in nanoseconds */
+typedef StgWord32 EventThreadID;
+typedef StgWord16 EventCapNo;
+typedef StgWord16 EventPayloadSize; /* variable-size events */
+typedef StgWord16 EventThreadStatus; /* status for EVENT_STOP_THREAD */
+typedef StgWord32 EventCapsetID;
+typedef StgWord16 EventCapsetType;   /* types for EVENT_CAPSET_CREATE */
+
+#endif
+
+#endif /* RTS_EVENTLOGFORMAT_H */
diff --git a/TestSuite/inputs/Puns/GHC/RTS/EventParserUtils.hs b/TestSuite/inputs/Puns/GHC/RTS/EventParserUtils.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/GHC/RTS/EventParserUtils.hs
@@ -0,0 +1,198 @@
+{-# LANGUAGE CPP, FlexibleContexts #-}
+{-# OPTIONS_GHC -fwarn-incomplete-patterns -fno-warn-deprecations #-}
+
+module GHC.RTS.EventParserUtils (
+        EventParser(..),
+        EventParsers(..),
+        GetEvents,
+        GetHeader,
+
+        getE,
+        getH,
+        getString,
+        mkEventTypeParsers,
+        simpleEvent,
+        skip,
+    ) where
+
+import Control.Monad
+import Control.Monad.Error
+import Control.Monad.Reader
+import Data.Array
+import Data.Binary
+import Data.Binary.Get hiding (skip)
+import qualified Data.Binary.Get as G
+import Data.Binary.Put
+import Data.Char
+import Data.Function
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as M
+import Data.List
+
+#define EVENTLOG_CONSTANTS_ONLY
+-- #include "EventLogFormat.h"
+
+import GHC.RTS.EventTypes
+
+-- reader/Get monad that passes around the event types
+type GetEvents a = ReaderT EventParsers (ErrorT String Get) a
+
+newtype EventParsers = EventParsers (Array Int (GetEvents EventInfo))
+
+type GetHeader a = ErrorT String Get a
+
+getH :: Binary a => GetHeader a
+getH = lift get
+
+getE :: Binary a => GetEvents a
+getE = lift $ lift get
+
+nBytes :: Integral a => a -> GetEvents [Word8]
+nBytes n = replicateM (fromIntegral n) getE
+
+getString :: Integral a => a -> GetEvents String
+getString len = do
+    bytes <- nBytes len
+    return $ map (chr . fromIntegral) bytes
+
+skip :: Integral a => a -> GetEvents ()
+skip n = lift $ lift $ G.skip (fromIntegral n)
+
+--
+-- Code to build the event parser table.
+--
+
+--
+-- Event parser data.  Parsers are either fixed or vairable size.
+--
+data EventParser a
+    = FixedSizeParser {
+        fsp_type        :: Int,
+        fsp_size        :: EventTypeSize,
+        fsp_parser      :: GetEvents a
+    }
+    | VariableSizeParser {
+        vsp_type        :: Int,
+        vsp_parser      :: GetEvents a
+    }
+
+get_parser (FixedSizeParser _ _ p) = p
+get_parser (VariableSizeParser _ p) = p
+
+get_type (FixedSizeParser t _ _) = t
+get_type (VariableSizeParser t _) = t
+
+isFixedSize (FixedSizeParser {}) = True
+isFixedSize (VariableSizeParser {}) = False
+
+simpleEvent :: Int -> a -> EventParser a
+simpleEvent t p = FixedSizeParser t 0 (return p)
+
+-- Our event log format allows new fields to be added to events over
+-- time.  This means that our parser must be able to handle:
+--
+--  * old versions of an event, with fewer fields than expected,
+--  * new versions of an event, with more fields than expected
+--
+-- The event log file declares the size for each event type, so we can
+-- select the correct parser for the event type based on its size.  We
+-- do this once after parsing the header: given the EventTypes, we build
+-- an array of event parsers indexed by event type.
+--
+-- For each event type, we may have multiple parsers for different
+-- versions of the event, indexed by size.  These are listed in the
+-- eventTypeParsers list below.  For the given log file we select the
+-- parser for the most recent version (largest size less than the size
+-- declared in the header).  If this is a newer version of the event
+-- than we understand, there may be extra bytes that we have to read
+-- and discard in the parser for this event type.
+--
+-- Summary:
+--   if size is smaller that we expect:
+--     parse the earier version, or ignore the event
+--   if size is just right:
+--     parse it
+--   if size is too big:
+--     parse the bits we understand and discard the rest
+
+mkEventTypeParsers :: IntMap EventType
+                   -> [EventParser EventInfo]
+                   -> Array Int (GetEvents EventInfo)
+mkEventTypeParsers etypes event_parsers
+ = accumArray (flip const) undefined (0, max_event_num)
+    [ (num, parser num) | num <- [0..max_event_num] ]
+    --([ (num, undeclared_etype num) | num <- [0..max_event_num] ] ++
+    -- [ (num, parser num etype) | (num, etype) <- M.toList etypes ])
+  where
+    max_event_num = maximum (M.keys etypes)
+    undeclared_etype num = throwError ("undeclared event type: " ++ show num)
+    parser_map = makeParserMap event_parsers
+    parser num =
+            -- Get the event's size from the header,
+            -- the first Maybe describes whether the event was declared in the header.
+            -- the second Maybe selects between variable and fixed size events.
+        let mb_mb_et_size = do et <- M.lookup num etypes
+                               return $ size et
+            -- Find a parser for the event with the given size.
+            maybe_parser mb_et_size = do possible <- M.lookup num parser_map
+                                         best_parser <- case mb_et_size of
+                                            Nothing -> getVariableParser possible
+                                            Just et_size -> getFixedParser et_size possible
+                                         return $ get_parser best_parser
+            in case mb_mb_et_size of
+                -- This event is declared in the log file's header
+                Just mb_et_size -> case maybe_parser mb_et_size of
+                    -- And we have a valid parser for it.
+                    Just p -> p
+                    -- But we don't have a valid parser for it.
+                    Nothing -> noEventTypeParser num mb_et_size
+                -- This event is not declared in the log file's header
+                Nothing -> undeclared_etype num
+
+-- Find the first variable length parser.
+getVariableParser :: [EventParser a] -> Maybe (EventParser a)
+getVariableParser [] = Nothing
+getVariableParser (x:xs) = case x of
+    FixedSizeParser _ _ _ -> getVariableParser xs
+    VariableSizeParser _ _ -> Just x
+
+-- Find the best fixed size parser, that is to say, the parser for the largest
+-- event that does not exceed the size of the event as declared in the log
+-- file's header.
+getFixedParser :: EventTypeSize -> [EventParser a] -> Maybe (EventParser a)
+getFixedParser size parsers =
+        do parser <- ((filter isFixedSize) `pipe`
+                      (filter (\x -> (fsp_size x) <= size)) `pipe`
+                      (sortBy descending_size) `pipe`
+                      maybe_head) parsers
+           return $ padParser size parser
+    where pipe f g = g . f
+          descending_size (FixedSizeParser _ s1 _) (FixedSizeParser _ s2 _) =
+            compare s2 s1
+          descending_size _ _ = undefined
+          maybe_head [] = Nothing
+          maybe_head (x:xs) = Just x
+
+padParser :: EventTypeSize -> (EventParser a) -> (EventParser a)
+padParser size (VariableSizeParser t p) = VariableSizeParser t p
+padParser size (FixedSizeParser t orig_size orig_p) = FixedSizeParser t size p
+    where p = if (size == orig_size)
+                then orig_p
+                else do d <- orig_p
+                        skip (size - orig_size)
+                        return d
+
+makeParserMap :: [EventParser a] -> IntMap [EventParser a]
+makeParserMap = foldl buildParserMap M.empty
+    where buildParserMap map parser = M.alter (addParser parser) (get_type parser) map
+          addParser p Nothing = Just [p]
+          addParser p (Just ps) = Just (p:ps)
+
+noEventTypeParser :: Int -> Maybe EventTypeSize
+                  -> GetEvents EventInfo
+noEventTypeParser num mb_size = do
+  bytes <- case mb_size of
+             Just n  -> return n
+             Nothing -> getE :: GetEvents Word16
+  skip bytes
+  return UnknownEvent{ ref = fromIntegral num }
diff --git a/TestSuite/inputs/Puns/GHC/RTS/EventTypes.hs b/TestSuite/inputs/Puns/GHC/RTS/EventTypes.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/GHC/RTS/EventTypes.hs
@@ -0,0 +1,389 @@
+{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
+
+module GHC.RTS.EventTypes where
+
+import Data.Word (Word8, Word16, Word32, Word64)
+import Data.Binary
+
+-- EventType.
+type EventTypeNum = Word16
+type EventTypeDescLen = Word32
+type EventTypeDesc = String
+type EventTypeSize = Word16
+-- Event.
+type EventDescription = String
+type Timestamp = Word64
+type ThreadId = Word32
+type CapNo = Word16
+type Marker = Word32
+type BlockSize = Word32
+type RawThreadStopStatus = Word16
+type StringId = Word32
+type Capset   = Word32
+type PerfEventTypeNum = Word32
+type TaskId = Word64
+type PID = Word32
+
+newtype KernelThreadId = KernelThreadId { kernelThreadId :: Word64 }
+  deriving (Eq, Ord, Show)
+instance Binary KernelThreadId where
+  put (KernelThreadId tid) = put tid
+  get = fmap KernelThreadId get
+
+-- These types are used by Mercury events.
+type ParConjDynId = Word64
+type ParConjStaticId = StringId
+type SparkId = Word32
+type FutureId = Word64
+
+sz_event_type_num :: EventTypeSize
+sz_event_type_num = 2
+sz_cap :: EventTypeSize
+sz_cap  = 2
+sz_time :: EventTypeSize
+sz_time = 8
+sz_tid :: EventTypeSize
+sz_tid  = 4
+sz_old_tid :: EventTypeSize
+sz_old_tid  = 8 -- GHC 6.12 was using 8 for ThreadID when declaring the size
+                -- of events, but was actually using 32 bits for ThreadIDs
+sz_capset :: EventTypeSize
+sz_capset = 4
+sz_capset_type :: EventTypeSize
+sz_capset_type = 2
+sz_block_size :: EventTypeSize
+sz_block_size = 4
+sz_block_event :: EventTypeSize
+sz_block_event = fromIntegral (sz_event_type_num + sz_time + sz_block_size
+    + sz_time + sz_cap)
+sz_pid :: EventTypeSize
+sz_pid = 4
+sz_taskid :: EventTypeSize
+sz_taskid = 8
+sz_kernel_tid :: EventTypeSize
+sz_kernel_tid = 8
+sz_th_stop_status :: EventTypeSize
+sz_th_stop_status = 2
+sz_string_id :: EventTypeSize
+sz_string_id = 4
+sz_perf_num :: EventTypeSize
+sz_perf_num = 4
+
+-- Sizes for Mercury event fields.
+sz_par_conj_dyn_id :: EventTypeSize
+sz_par_conj_dyn_id = 8
+sz_par_conj_static_id :: EventTypeSize
+sz_par_conj_static_id = sz_string_id
+sz_spark_id :: EventTypeSize
+sz_spark_id = 4
+sz_future_id :: EventTypeSize
+sz_future_id = 8
+
+{-
+ - Data type delcarations to build the GHC RTS data format,
+ - which is a (header, data) pair.
+ -
+ - Header contains EventTypes.
+ - Data contains Events.
+ -}
+data EventLog =
+  EventLog {
+    header :: Header,
+    dat    :: Data
+  } deriving Show
+
+newtype Header = Header {
+     eventTypes :: [EventType]
+  } deriving (Show, Eq)
+
+data Data = Data {
+     events :: [Event]
+  } deriving Show
+
+data EventType =
+  EventType {
+    num  :: EventTypeNum,
+    desc :: EventTypeDesc,
+    size :: Maybe EventTypeSize -- ^ 'Nothing' indicates variable size
+  } deriving (Show, Eq)
+
+data Event =
+  Event {
+    time :: {-# UNPACK #-}!Timestamp,
+    spec :: EventInfo
+  } deriving Show
+
+data EventInfo
+
+  -- pseudo events
+  = EventBlock         { end_time     :: Timestamp,
+                         cap          :: Int,
+                         block_events :: [Event]
+                       }
+  | UnknownEvent       { ref  :: {-# UNPACK #-}!EventTypeNum }
+
+  -- init and shutdown
+  | Startup            { n_caps :: Int
+                       }
+  -- EVENT_SHUTDOWN is replaced by EVENT_CAP_DELETE and GHC 7.6+
+  -- no longer generate the event; should be removed at some point
+  | Shutdown           { }
+
+  -- thread scheduling
+  | CreateThread       { thread :: {-# UNPACK #-}!ThreadId
+                       }
+  | RunThread          { thread :: {-# UNPACK #-}!ThreadId
+                       }
+  | StopThread         { thread :: {-# UNPACK #-}!ThreadId,
+                         status :: ThreadStopStatus
+                       }
+  | ThreadRunnable     { thread :: {-# UNPACK #-}!ThreadId
+                       }
+  | MigrateThread      { thread :: {-# UNPACK #-}!ThreadId,
+                         newCap :: {-# UNPACK #-}!Int
+                       }
+  | WakeupThread       { thread :: {-# UNPACK #-}!ThreadId,
+                         otherCap :: {-# UNPACK #-}!Int
+                       }
+  | ThreadLabel        { thread :: {-# UNPACK #-}!ThreadId,
+                         threadlabel :: String
+                       }
+
+  -- par sparks
+  | CreateSparkThread  { sparkThread :: {-# UNPACK #-}!ThreadId
+                       }
+  | SparkCounters      { sparksCreated, sparksDud, sparksOverflowed,
+                         sparksConverted, sparksFizzled, sparksGCd,
+                         sparksRemaining :: {-# UNPACK #-}! Word64
+                       }
+  | SparkCreate        { }
+  | SparkDud           { }
+  | SparkOverflow      { }
+  | SparkRun           { }
+  | SparkSteal         { victimCap :: {-# UNPACK #-}!Int }
+  | SparkFizzle        { }
+  | SparkGC            { }
+
+  -- tasks
+  | TaskCreate         { taskId :: TaskId,
+                         cap :: {-# UNPACK #-}!Int,
+                         tid :: {-# UNPACK #-}!KernelThreadId
+                       }
+  | TaskMigrate        { taskId :: TaskId,
+                         cap :: {-# UNPACK #-}!Int,
+                         new_cap :: {-# UNPACK #-}!Int
+                       }
+  | TaskDelete         { taskId :: TaskId }
+
+  -- garbage collection
+  | RequestSeqGC       { }
+  | RequestParGC       { }
+  | StartGC            { }
+  | GCWork             { }
+  | GCIdle             { }
+  | GCDone             { }
+  | EndGC              { }
+  | GlobalSyncGC       { }
+  | GCStatsGHC         { heapCapset   :: {-# UNPACK #-}!Capset
+                       , gen          :: {-# UNPACK #-}!Int
+                       , copied       :: {-# UNPACK #-}!Word64
+                       , slop, frag   :: {-# UNPACK #-}!Word64
+                       , parNThreads  :: {-# UNPACK #-}!Int
+                       , parMaxCopied :: {-# UNPACK #-}!Word64
+                       , parTotCopied :: {-# UNPACK #-}!Word64
+                       }
+
+  -- heap statistics
+  | HeapAllocated      { heapCapset  :: {-# UNPACK #-}!Capset
+                       , allocBytes  :: {-# UNPACK #-}!Word64
+                       }
+  | HeapSize           { heapCapset  :: {-# UNPACK #-}!Capset
+                       , sizeBytes   :: {-# UNPACK #-}!Word64
+                       }
+  | HeapLive           { heapCapset  :: {-# UNPACK #-}!Capset
+                       , liveBytes   :: {-# UNPACK #-}!Word64
+                       }
+  | HeapInfoGHC        { heapCapset    :: {-# UNPACK #-}!Capset
+                       , gens          :: {-# UNPACK #-}!Int
+                       , maxHeapSize   :: {-# UNPACK #-}!Word64
+                       , allocAreaSize :: {-# UNPACK #-}!Word64
+                       , mblockSize    :: {-# UNPACK #-}!Word64
+                       , blockSize     :: {-# UNPACK #-}!Word64
+                       }
+
+  -- adjusting the number of capabilities on the fly
+  | CapCreate          { cap :: {-# UNPACK #-}!Int
+                       }
+  | CapDelete          { cap :: {-# UNPACK #-}!Int
+                       }
+  | CapDisable         { cap :: {-# UNPACK #-}!Int
+                       }
+  | CapEnable          { cap :: {-# UNPACK #-}!Int
+                       }
+
+  -- capability sets
+  | CapsetCreate       { capset     :: {-# UNPACK #-}!Capset
+                       , capsetType :: CapsetType
+                       }
+  | CapsetDelete       { capset :: {-# UNPACK #-}!Capset
+                       }
+  | CapsetAssignCap    { capset :: {-# UNPACK #-}!Capset
+                       , cap    :: {-# UNPACK #-}!Int
+                       }
+  | CapsetRemoveCap    { capset :: {-# UNPACK #-}!Capset
+                       , cap    :: {-# UNPACK #-}!Int
+                       }
+
+  -- program/process info
+  | RtsIdentifier      { capset :: {-# UNPACK #-}!Capset
+                       , rtsident :: String
+                       }
+  | ProgramArgs        { capset :: {-# UNPACK #-}!Capset
+                       , args   :: [String]
+                       }
+  | ProgramEnv         { capset :: {-# UNPACK #-}!Capset
+                       , env    :: [String]
+                       }
+  | OsProcessPid       { capset :: {-# UNPACK #-}!Capset
+                       , pid    :: {-# UNPACK #-}!PID
+                       }
+  | OsProcessParentPid { capset :: {-# UNPACK #-}!Capset
+                       , ppid   :: {-# UNPACK #-}!PID
+                       }
+  | WallClockTime      { capset :: {-# UNPACK #-}!Capset
+                       , sec    :: {-# UNPACK #-}!Word64
+                       , nsec   :: {-# UNPACK #-}!Word32
+                       }
+
+  -- messages
+  | Message            { msg :: String }
+  | UserMessage        { msg :: String }
+  | UserMarker         { markername :: String }
+
+  -- These events have been added for Mercury's benifit but are generally
+  -- useful.
+  | InternString       { str :: String, sId :: {-# UNPACK #-}!StringId }
+
+  -- Mercury specific events.
+  | MerStartParConjunction {
+        dyn_id      :: {-# UNPACK #-}!ParConjDynId,
+        static_id   :: {-# UNPACK #-}!ParConjStaticId
+    }
+  | MerEndParConjunction {
+        dyn_id      :: {-# UNPACK #-}!ParConjDynId
+    }
+  | MerEndParConjunct {
+        dyn_id      :: {-# UNPACK #-}!ParConjDynId
+    }
+  | MerCreateSpark {
+        dyn_id      :: {-# UNPACK #-}!ParConjDynId,
+        spark_id    :: {-# UNPACK #-}!SparkId
+    }
+  | MerFutureCreate {
+        future_id   :: {-# UNPACK #-}!FutureId,
+        name_id     :: {-# UNPACK #-}!StringId
+    }
+  | MerFutureWaitNosuspend {
+        future_id   :: {-# UNPACK #-}!FutureId
+    }
+  | MerFutureWaitSuspended {
+        future_id   :: {-# UNPACK #-}!FutureId
+    }
+  | MerFutureSignal {
+        future_id   :: {-# UNPACK #-}!FutureId
+    }
+  | MerLookingForGlobalThread
+  | MerWorkStealing
+  | MerLookingForLocalSpark
+  | MerReleaseThread {
+        thread_id   :: {-# UNPACK #-}!ThreadId
+    }
+  | MerCapSleeping
+  | MerCallingMain
+
+  -- perf events
+  | PerfName           { perfNum :: {-# UNPACK #-}!PerfEventTypeNum
+                       , name    :: String
+                       }
+  | PerfCounter        { perfNum :: {-# UNPACK #-}!PerfEventTypeNum
+                       , tid     :: {-# UNPACK #-}!KernelThreadId
+                       , period  :: {-# UNPACK #-}!Word64
+                       }
+  | PerfTracepoint     { perfNum :: {-# UNPACK #-}!PerfEventTypeNum
+                       , tid     :: {-# UNPACK #-}!KernelThreadId
+                       }
+
+  deriving Show
+
+--sync with ghc/includes/Constants.h
+data ThreadStopStatus
+ = NoStatus
+ | HeapOverflow
+ | StackOverflow
+ | ThreadYielding
+ | ThreadBlocked
+ | ThreadFinished
+ | ForeignCall
+ | BlockedOnMVar
+ | BlockedOnBlackHole
+ | BlockedOnRead
+ | BlockedOnWrite
+ | BlockedOnDelay
+ | BlockedOnSTM
+ | BlockedOnDoProc
+ | BlockedOnCCall
+ | BlockedOnCCall_NoUnblockExc
+ | BlockedOnMsgThrowTo
+ | ThreadMigrating
+ | BlockedOnMsgGlobalise
+ | BlockedOnBlackHoleOwnedBy {-# UNPACK #-}!ThreadId
+ deriving (Show)
+
+mkStopStatus :: RawThreadStopStatus -> ThreadStopStatus
+mkStopStatus n = case n of
+ 0  ->  NoStatus
+ 1  ->  HeapOverflow
+ 2  ->  StackOverflow
+ 3  ->  ThreadYielding
+ 4  ->  ThreadBlocked
+ 5  ->  ThreadFinished
+ 6  ->  ForeignCall
+ 7  ->  BlockedOnMVar
+ 8  ->  BlockedOnBlackHole
+ 9  ->  BlockedOnRead
+ 10 ->  BlockedOnWrite
+ 11 ->  BlockedOnDelay
+ 12 ->  BlockedOnSTM
+ 13 ->  BlockedOnDoProc
+ 14 ->  BlockedOnCCall
+ 15 ->  BlockedOnCCall_NoUnblockExc
+ 16 ->  BlockedOnMsgThrowTo
+ 17 ->  ThreadMigrating
+ 18 ->  BlockedOnMsgGlobalise
+ _  ->  error "mkStat"
+
+maxThreadStopStatus :: RawThreadStopStatus
+maxThreadStopStatus = 18
+
+data CapsetType
+ = CapsetCustom
+ | CapsetOsProcess
+ | CapsetClockDomain
+ | CapsetUnknown
+ deriving Show
+
+mkCapsetType :: Word16 -> CapsetType
+mkCapsetType n = case n of
+ 1 -> CapsetCustom
+ 2 -> CapsetOsProcess
+ 3 -> CapsetClockDomain
+ _ -> CapsetUnknown
+
+-- | An event annotated with the Capability that generated it, if any
+data CapEvent
+  = CapEvent { ce_cap   :: Maybe Int,
+               ce_event :: Event
+               -- we could UNPACK ce_event, but the Event constructor
+               -- might be shared, in which case we could end up
+               -- increasing the space usage.
+             } deriving Show
diff --git a/TestSuite/inputs/Puns/GHC/RTS/Events.hs b/TestSuite/inputs/Puns/GHC/RTS/Events.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/GHC/RTS/Events.hs
@@ -0,0 +1,1367 @@
+{-# LANGUAGE CPP,BangPatterns,PatternGuards #-}
+{-# OPTIONS_GHC -funbox-strict-fields -fwarn-incomplete-patterns -fno-warn-deprecations #-}
+{-
+ -   Parser functions for GHC RTS EventLog framework.
+ -}
+
+module GHC.RTS.Events (
+       main,
+       -- * The event log types
+       EventLog(..),
+       EventType(..),
+       Event(..),
+       EventInfo(..),
+       ThreadStopStatus(..),
+       Header(..),
+       Data(..),
+       CapsetType(..),
+       Timestamp,
+       ThreadId,
+       TaskId,
+       KernelThreadId(..),
+
+       -- * Reading and writing event logs
+       readEventLogFromFile,
+       writeEventLogToFile,
+
+       -- * Utilities
+       CapEvent(..), sortEvents, groupEvents, sortGroups,
+       buildEventTypeMap,
+
+       -- * Printing
+       showEventInfo, showThreadStopStatus,
+       ppEventLog, ppEventType, ppEvent,
+
+       -- * Perf events
+       nEVENT_PERF_NAME, nEVENT_PERF_COUNTER, nEVENT_PERF_TRACEPOINT,
+       sz_perf_num, sz_kernel_tid
+  ) where
+
+{- Libraries. -}
+import Data.Binary
+import Data.Binary.Get hiding (skip)
+import qualified Data.Binary.Get as G
+import Data.Binary.Put
+import Control.Monad
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as M
+import Control.Monad.Reader
+import Control.Monad.Error
+import qualified Data.ByteString.Lazy as L
+import Data.Function
+import Data.List
+import Data.Either
+import Data.Maybe
+import Text.Printf
+import Data.Array
+
+import GHC.RTS.EventTypes
+import GHC.RTS.EventParserUtils
+
+#define EVENTLOG_CONSTANTS_ONLY
+#include "EventLogFormat.h"
+
+main :: IO ()
+main = return ()
+
+------------------------------------------------------------------------------
+-- Binary instances
+
+getEventType :: GetHeader EventType
+getEventType = do
+           etNum <- getH
+           size <- getH :: GetHeader EventTypeSize
+           let etSize = if size == 0xffff then Nothing else Just size
+           -- 0xffff indicates variable-sized event
+           etDescLen <- getH :: GetHeader EventTypeDescLen
+           etDesc <- getEtDesc (fromIntegral etDescLen)
+           etExtraLen <- getH :: GetHeader Word32
+           lift $ G.skip (fromIntegral etExtraLen)
+           ete <- getH :: GetHeader Marker
+           when (ete /= EVENT_ET_END) $
+              throwError ("Event Type end marker not found.")
+           return (EventType etNum etDesc etSize)
+           where
+             getEtDesc :: Int -> GetHeader [Char]
+             getEtDesc s = replicateM s (getH :: GetHeader Char)
+
+getHeader :: GetHeader Header
+getHeader = do
+           hdrb <- getH :: GetHeader Marker
+           when (hdrb /= EVENT_HEADER_BEGIN) $
+                throwError "Header begin marker not found"
+           hetm <- getH :: GetHeader Marker
+           when (hetm /= EVENT_HET_BEGIN) $
+                throwError "Header Event Type begin marker not found"
+           ets <- getEventTypes
+           emark <- getH :: GetHeader Marker
+           when (emark /= EVENT_HEADER_END) $
+                throwError "Header end marker not found"
+           return (Header ets)
+     where
+       getEventTypes :: GetHeader [EventType]
+       getEventTypes = do
+           m <- getH :: GetHeader Marker
+           case () of
+            _ | m == EVENT_ET_BEGIN -> do
+                   et <- getEventType
+                   nextET <- getEventTypes
+                   return (et : nextET)
+              | m == EVENT_HET_END ->
+                   return []
+              | otherwise ->
+                   throwError "Malformed list of Event Types in header"
+
+getEvent :: EventParsers -> GetEvents (Maybe Event)
+getEvent (EventParsers parsers) = do
+  etRef <- getE :: GetEvents EventTypeNum
+  if (etRef == EVENT_DATA_END)
+     then return Nothing
+     else do !ts   <- getE
+             -- trace ("event: " ++ show etRef) $ do
+             spec <- parsers ! fromIntegral etRef
+             return (Just (Event ts spec))
+
+--
+-- standardEventParsers.
+--
+standardParsers :: [EventParser EventInfo]
+standardParsers = [
+ (FixedSizeParser EVENT_STARTUP sz_cap (do -- (n_caps)
+      c <- getE :: GetEvents CapNo
+      return Startup{ n_caps = fromIntegral c }
+   )),
+
+ (FixedSizeParser EVENT_BLOCK_MARKER (sz_block_size + sz_time + sz_cap) (do -- (size, end_time, cap)
+      block_size <- getE :: GetEvents BlockSize
+      end_time <- getE :: GetEvents Timestamp
+      c <- getE :: GetEvents CapNo
+      lbs <- lift . lift $ getLazyByteString ((fromIntegral block_size) -
+                                              (fromIntegral sz_block_event))
+      eparsers <- ask
+      let e_events = runGet (runErrorT $ runReaderT (getEventBlock eparsers) eparsers) lbs
+      return EventBlock{ end_time=end_time,
+                         cap= fromIntegral c,
+                         block_events=case e_events of
+                                        Left s -> error s
+                                        Right es -> es }
+   )),
+
+ -- EVENT_SHUTDOWN is replaced by EVENT_CAP_DELETE and GHC 7.6+
+ -- no longer generate the event; should be removed at some point
+ (simpleEvent EVENT_SHUTDOWN Shutdown),
+
+ (simpleEvent EVENT_REQUEST_SEQ_GC RequestSeqGC),
+
+ (simpleEvent EVENT_REQUEST_PAR_GC RequestParGC),
+
+ (simpleEvent EVENT_GC_START StartGC),
+
+ (simpleEvent EVENT_GC_WORK GCWork),
+
+ (simpleEvent EVENT_GC_IDLE GCIdle),
+
+ (simpleEvent EVENT_GC_DONE GCDone),
+
+ (simpleEvent EVENT_GC_END EndGC),
+
+ (simpleEvent EVENT_GC_GLOBAL_SYNC GlobalSyncGC),
+
+ (FixedSizeParser EVENT_GC_STATS_GHC (sz_capset + 2 + 5*8 + 4) (do  -- (heap_capset, generation, copied_bytes, slop_bytes, frag_bytes, par_n_threads, par_max_copied, par_tot_copied)
+      heapCapset   <- getE
+      gen          <- getE :: GetEvents Word16
+      copied       <- getE :: GetEvents Word64
+      slop         <- getE :: GetEvents Word64
+      frag         <- getE :: GetEvents Word64
+      parNThreads  <- getE :: GetEvents Word32
+      parMaxCopied <- getE :: GetEvents Word64
+      parTotCopied <- getE :: GetEvents Word64
+      return GCStatsGHC{ gen = fromIntegral gen
+                       , parNThreads = fromIntegral parNThreads
+                       , ..}
+ )),
+
+ (FixedSizeParser EVENT_HEAP_ALLOCATED (sz_capset + 8) (do  -- (heap_capset, alloc_bytes)
+      heapCapset <- getE
+      allocBytes <- getE
+      return HeapAllocated{..}
+ )),
+
+ (FixedSizeParser EVENT_HEAP_SIZE (sz_capset + 8) (do  -- (heap_capset, size_bytes)
+      heapCapset <- getE
+      sizeBytes  <- getE
+      return HeapSize{..}
+ )),
+
+ (FixedSizeParser EVENT_HEAP_LIVE (sz_capset + 8) (do  -- (heap_capset, live_bytes)
+      heapCapset <- getE
+      liveBytes  <- getE
+      return HeapLive{..}
+ )),
+
+ (FixedSizeParser EVENT_HEAP_INFO_GHC (sz_capset + 2 + 4*8) (do  -- (heap_capset, n_generations, max_heap_size, alloc_area_size, mblock_size, block_size)
+      heapCapset    <- getE
+      gens          <- getE :: GetEvents Word16
+      maxHeapSize   <- getE :: GetEvents Word64
+      allocAreaSize <- getE :: GetEvents Word64
+      mblockSize    <- getE :: GetEvents Word64
+      blockSize     <- getE :: GetEvents Word64
+      return HeapInfoGHC{gens = fromIntegral gens, ..}
+ )),
+
+ (FixedSizeParser EVENT_CAP_CREATE (sz_cap) (do  -- (cap)
+      cap <- getE :: GetEvents CapNo
+      return CapCreate{cap = fromIntegral cap}
+ )),
+
+ (FixedSizeParser EVENT_CAP_DELETE (sz_cap) (do  -- (cap)
+      cap <- getE :: GetEvents CapNo
+      return CapDelete{cap = fromIntegral cap}
+ )),
+
+ (FixedSizeParser EVENT_CAP_DISABLE (sz_cap) (do  -- (cap)
+      cap <- getE :: GetEvents CapNo
+      return CapDisable{cap = fromIntegral cap}
+ )),
+
+ (FixedSizeParser EVENT_CAP_ENABLE (sz_cap) (do  -- (cap)
+      cap <- getE :: GetEvents CapNo
+      return CapEnable{cap = fromIntegral cap}
+ )),
+
+ (FixedSizeParser EVENT_CAPSET_CREATE (sz_capset + sz_capset_type) (do -- (capset, capset_type)
+      cs <- getE
+      ct <- fmap mkCapsetType getE
+      return CapsetCreate{capset=cs,capsetType=ct}
+   )),
+
+ (FixedSizeParser EVENT_CAPSET_DELETE sz_capset (do -- (capset)
+      cs <- getE
+      return CapsetDelete{capset=cs}
+   )),
+
+ (FixedSizeParser EVENT_CAPSET_ASSIGN_CAP (sz_capset + sz_cap) (do -- (capset, cap)
+      cs <- getE
+      cp <- getE :: GetEvents CapNo
+      return CapsetAssignCap{capset=cs,cap=fromIntegral cp}
+   )),
+
+ (FixedSizeParser EVENT_CAPSET_REMOVE_CAP (sz_capset + sz_cap) (do -- (capset, cap)
+      cs <- getE
+      cp <- getE :: GetEvents CapNo
+      return CapsetRemoveCap{capset=cs,cap=fromIntegral cp}
+   )),
+
+ (FixedSizeParser EVENT_OSPROCESS_PID (sz_capset + sz_pid) (do -- (capset, pid)
+      cs <- getE
+      pd <- getE
+      return OsProcessPid{capset=cs,pid=pd}
+   )),
+
+ (FixedSizeParser EVENT_OSPROCESS_PPID (sz_capset + sz_pid) (do -- (capset, ppid)
+      cs <- getE
+      pd <- getE
+      return OsProcessParentPid{capset=cs,ppid=pd}
+  )),
+
+ (FixedSizeParser EVENT_WALL_CLOCK_TIME (sz_capset + 8 + 4) (do -- (capset, unix_epoch_seconds, nanoseconds)
+      cs <- getE
+      s  <- getE
+      ns <- getE
+      return WallClockTime{capset=cs,sec=s,nsec=ns}
+  )),
+
+ (VariableSizeParser EVENT_LOG_MSG (do -- (msg)
+      num <- getE :: GetEvents Word16
+      string <- getString num
+      return Message{ msg = string }
+   )),
+ (VariableSizeParser EVENT_USER_MSG (do -- (msg)
+      num <- getE :: GetEvents Word16
+      string <- getString num
+      return UserMessage{ msg = string }
+   )),
+    (VariableSizeParser EVENT_USER_MARKER (do -- (markername)
+      num <- getE :: GetEvents Word16
+      string <- getString num
+      return UserMarker{ markername = string }
+   )),
+ (VariableSizeParser EVENT_PROGRAM_ARGS (do -- (capset, [arg])
+      num <- getE :: GetEvents Word16
+      cs <- getE
+      string <- getString (num - sz_capset)
+      return ProgramArgs{ capset = cs
+                        , args = splitNull string }
+   )),
+ (VariableSizeParser EVENT_PROGRAM_ENV (do -- (capset, [arg])
+      num <- getE :: GetEvents Word16
+      cs <- getE
+      string <- getString (num - sz_capset)
+      return ProgramEnv{ capset = cs
+                       , env = splitNull string }
+   )),
+ (VariableSizeParser EVENT_RTS_IDENTIFIER (do -- (capset, str)
+      num <- getE :: GetEvents Word16
+      cs <- getE
+      string <- getString (num - sz_capset)
+      return RtsIdentifier{ capset = cs
+                          , rtsident = string }
+   )),
+
+ (VariableSizeParser EVENT_INTERN_STRING (do -- (str, id)
+      num <- getE :: GetEvents Word16
+      string <- getString (num - sz_string_id)
+      sId <- getE :: GetEvents StringId
+      return (InternString string sId)
+    )),
+
+ (VariableSizeParser EVENT_THREAD_LABEL (do -- (thread, str)
+      num <- getE :: GetEvents Word16
+      tid <- getE
+      str <- getString (num - sz_tid)
+      return ThreadLabel{ thread      = tid
+                        , threadlabel = str }
+    ))
+ ]
+
+-- Parsers valid for GHC7 but not GHC6.
+ghc7Parsers :: [EventParser EventInfo]
+ghc7Parsers = [
+ (FixedSizeParser EVENT_CREATE_THREAD sz_tid (do  -- (thread)
+      t <- getE
+      return CreateThread{thread=t}
+   )),
+
+ (FixedSizeParser EVENT_RUN_THREAD sz_tid (do  --  (thread)
+      t <- getE
+      return RunThread{thread=t}
+   )),
+
+ (FixedSizeParser EVENT_STOP_THREAD (sz_tid + sz_th_stop_status) (do
+      -- (thread, status)
+      t <- getE
+      s <- getE :: GetEvents RawThreadStopStatus
+      return StopThread{thread=t, status = if s > maxThreadStopStatus
+                                              then NoStatus
+                                              else mkStopStatus s}
+   )),
+
+ (FixedSizeParser EVENT_STOP_THREAD (sz_tid + sz_th_stop_status + sz_tid) (do
+      -- (thread, status, info)
+      t <- getE
+      s <- getE :: GetEvents RawThreadStopStatus
+      i <- getE :: GetEvents ThreadId
+      return StopThread{thread = t,
+                        status = case () of
+                                  _ | s > maxThreadStopStatus
+                                    -> NoStatus
+                                    | s == 8 {- XXX yeuch -}
+                                    -> BlockedOnBlackHoleOwnedBy i
+                                    | otherwise
+                                    -> mkStopStatus s}
+   )),
+
+ (FixedSizeParser EVENT_THREAD_RUNNABLE sz_tid (do  -- (thread)
+      t <- getE
+      return ThreadRunnable{thread=t}
+   )),
+
+ (FixedSizeParser EVENT_MIGRATE_THREAD (sz_tid + sz_cap) (do  --  (thread, newCap)
+      t  <- getE
+      nc <- getE :: GetEvents CapNo
+      return MigrateThread{thread=t,newCap=fromIntegral nc}
+   )),
+
+ -- Yes, EVENT_RUN/STEAL_SPARK are deprecated, but see the explanation in the
+ -- 'ghc6Parsers' section below. Since we're parsing them anyway, we might
+ -- as well convert them to the new SparkRun/SparkSteal events.
+ (FixedSizeParser EVENT_RUN_SPARK sz_tid (do  -- (thread)
+      _ <- getE :: GetEvents ThreadId
+      return SparkRun
+   )),
+
+ (FixedSizeParser EVENT_STEAL_SPARK (sz_tid + sz_cap) (do  -- (thread, victimCap)
+      _  <- getE :: GetEvents ThreadId
+      vc <- getE :: GetEvents CapNo
+      return SparkSteal{victimCap=fromIntegral vc}
+   )),
+
+ (FixedSizeParser EVENT_CREATE_SPARK_THREAD sz_tid (do  -- (sparkThread)
+      st <- getE :: GetEvents ThreadId
+      return CreateSparkThread{sparkThread=st}
+   )),
+
+ (FixedSizeParser EVENT_SPARK_COUNTERS (7*8) (do -- (crt,dud,ovf,cnv,gcd,fiz,rem)
+      crt <- getE :: GetEvents Word64
+      dud <- getE :: GetEvents Word64
+      ovf <- getE :: GetEvents Word64
+      cnv <- getE :: GetEvents Word64
+      gcd <- getE :: GetEvents Word64
+      fiz <- getE :: GetEvents Word64
+      rem <- getE :: GetEvents Word64
+      return SparkCounters{sparksCreated    = crt, sparksDud       = dud,
+                           sparksOverflowed = ovf, sparksConverted = cnv,
+                           -- Warning: order of fiz and gcd reversed!
+                           sparksFizzled    = fiz, sparksGCd       = gcd,
+                           sparksRemaining  = rem}
+   )),
+
+ (simpleEvent EVENT_SPARK_CREATE   SparkCreate),
+ (simpleEvent EVENT_SPARK_DUD      SparkDud),
+ (simpleEvent EVENT_SPARK_OVERFLOW SparkOverflow),
+ (simpleEvent EVENT_SPARK_RUN      SparkRun),
+ (FixedSizeParser EVENT_SPARK_STEAL sz_cap (do  -- (victimCap)
+      vc <- getE :: GetEvents CapNo
+      return SparkSteal{victimCap=fromIntegral vc}
+   )),
+ (simpleEvent EVENT_SPARK_FIZZLE   SparkFizzle),
+ (simpleEvent EVENT_SPARK_GC       SparkGC),
+
+ (FixedSizeParser EVENT_TASK_CREATE (sz_taskid + sz_cap + sz_kernel_tid) (do  -- (taskID, cap, tid)
+      taskId <- getE :: GetEvents TaskId
+      cap    <- getE :: GetEvents CapNo
+      tid    <- getE :: GetEvents KernelThreadId
+      return TaskCreate{ taskId, cap = fromIntegral cap, tid }
+   )),
+ (FixedSizeParser EVENT_TASK_MIGRATE (sz_taskid + sz_cap*2) (do  -- (taskID, cap, new_cap)
+      taskId  <- getE :: GetEvents TaskId
+      cap     <- getE :: GetEvents CapNo
+      new_cap <- getE :: GetEvents CapNo
+      return TaskMigrate{ taskId, cap = fromIntegral cap
+                                , new_cap = fromIntegral new_cap
+                        }
+   )),
+ (FixedSizeParser EVENT_TASK_DELETE (sz_taskid) (do  -- (taskID)
+      taskId <- getE :: GetEvents TaskId
+      return TaskDelete{ taskId }
+   )),
+
+ (FixedSizeParser EVENT_THREAD_WAKEUP (sz_tid + sz_cap) (do  -- (thread, other_cap)
+      t <- getE
+      oc <- getE :: GetEvents CapNo
+      return WakeupThread{thread=t,otherCap=fromIntegral oc}
+   ))
+ ]
+
+ -----------------------
+ -- GHC 6.12 compat: GHC 6.12 reported the wrong sizes for some events,
+ -- so we have to recognise those wrong sizes here for backwards
+ -- compatibility.
+ghc6Parsers :: [EventParser EventInfo]
+ghc6Parsers = [
+ (FixedSizeParser EVENT_STARTUP 0 (do
+      -- BUG in GHC 6.12: the startup event was incorrectly
+      -- declared as size 0, so we accept it here.
+      c <- getE :: GetEvents CapNo
+      return Startup{ n_caps = fromIntegral c }
+   )),
+
+ (FixedSizeParser EVENT_CREATE_THREAD sz_old_tid (do  -- (thread)
+      t <- getE
+      return CreateThread{thread=t}
+   )),
+
+ (FixedSizeParser EVENT_RUN_THREAD sz_old_tid (do  --  (thread)
+      t <- getE
+      return RunThread{thread=t}
+   )),
+
+ (FixedSizeParser EVENT_STOP_THREAD (sz_old_tid + 2) (do  -- (thread, status)
+      t <- getE
+      s <- getE :: GetEvents Word16
+      let stat = fromIntegral s
+      return StopThread{thread=t, status = if stat > maxBound
+                                              then NoStatus
+                                              else mkStopStatus stat}
+   )),
+
+ (FixedSizeParser EVENT_THREAD_RUNNABLE sz_old_tid (do  -- (thread)
+      t <- getE
+      return ThreadRunnable{thread=t}
+   )),
+
+ (FixedSizeParser EVENT_MIGRATE_THREAD (sz_old_tid + sz_cap) (do  --  (thread, newCap)
+      t  <- getE
+      nc <- getE :: GetEvents CapNo
+      return MigrateThread{thread=t,newCap=fromIntegral nc}
+   )),
+
+ -- Note: it is vital that these two (EVENT_RUN/STEAL_SPARK) remain here (at
+ -- least in the ghc6Parsers section) even though both events are deprecated.
+ -- The reason is that .eventlog files created by the buggy GHC-6.12
+ -- mis-declare the size of these two events. So we have to handle them
+ -- specially here otherwise we'll get the wrong size, leading to us getting
+ -- out of sync and eventual parse failure. Since we're parsing them anyway,
+ -- we might as well convert them to the new SparkRun/SparkSteal events.
+ (FixedSizeParser EVENT_RUN_SPARK sz_old_tid (do  -- (thread)
+      _ <- getE :: GetEvents ThreadId
+      return SparkRun
+   )),
+
+ (FixedSizeParser EVENT_STEAL_SPARK (sz_old_tid + sz_cap) (do  -- (thread, victimCap)
+      _  <- getE :: GetEvents ThreadId
+      vc <- getE :: GetEvents CapNo
+      return SparkSteal{victimCap=fromIntegral vc}
+   )),
+
+ (FixedSizeParser EVENT_CREATE_SPARK_THREAD sz_old_tid (do  -- (sparkThread)
+      st <- getE :: GetEvents ThreadId
+      return CreateSparkThread{sparkThread=st}
+   )),
+
+ (FixedSizeParser EVENT_THREAD_WAKEUP (sz_old_tid + sz_cap) (do  -- (thread, other_cap)
+      t <- getE
+      oc <- getE :: GetEvents CapNo
+      return WakeupThread{thread=t,otherCap=fromIntegral oc}
+   ))
+ ]
+
+mercuryParsers = [
+ (FixedSizeParser EVENT_MER_START_PAR_CONJUNCTION
+    (sz_par_conj_dyn_id + sz_par_conj_static_id)
+    (do dyn_id <- getE
+        static_id <- getE
+        return (MerStartParConjunction dyn_id static_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_STOP_PAR_CONJUNCTION sz_par_conj_dyn_id
+    (do dyn_id <- getE
+        return (MerEndParConjunction dyn_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_STOP_PAR_CONJUNCT sz_par_conj_dyn_id
+    (do dyn_id <- getE
+        return (MerEndParConjunct dyn_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_CREATE_SPARK (sz_par_conj_dyn_id + sz_spark_id)
+    (do dyn_id <- getE
+        spark_id <- getE
+        return (MerCreateSpark dyn_id spark_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_FUT_CREATE (sz_future_id + sz_string_id)
+    (do future_id <- getE
+        name_id <- getE
+        return (MerFutureCreate future_id name_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_FUT_WAIT_NOSUSPEND (sz_future_id)
+    (do future_id <- getE
+        return (MerFutureWaitNosuspend future_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_FUT_WAIT_SUSPENDED (sz_future_id)
+    (do future_id <- getE
+        return (MerFutureWaitSuspended future_id))
+ ),
+
+ (FixedSizeParser EVENT_MER_FUT_SIGNAL (sz_future_id)
+    (do future_id <- getE
+        return (MerFutureSignal future_id))
+ ),
+
+ (simpleEvent EVENT_MER_LOOKING_FOR_GLOBAL_CONTEXT MerLookingForGlobalThread),
+ (simpleEvent EVENT_MER_WORK_STEALING MerWorkStealing),
+ (simpleEvent EVENT_MER_LOOKING_FOR_LOCAL_SPARK MerLookingForLocalSpark),
+
+ (FixedSizeParser EVENT_MER_RELEASE_CONTEXT sz_tid
+    (do thread_id <- getE
+        return (MerReleaseThread thread_id))
+ ),
+
+ (simpleEvent EVENT_MER_ENGINE_SLEEPING MerCapSleeping),
+ (simpleEvent EVENT_MER_CALLING_MAIN MerCallingMain)
+
+ ]
+
+perfParsers = [
+ (VariableSizeParser EVENT_PERF_NAME (do -- (perf_num, name)
+      num     <- getE :: GetEvents Word16
+      perfNum <- getE
+      name    <- getString (num - sz_perf_num)
+      return PerfName{perfNum, name}
+   )),
+
+ (FixedSizeParser EVENT_PERF_COUNTER (sz_perf_num + sz_kernel_tid + 8) (do -- (perf_num, tid, period)
+      perfNum <- getE
+      tid     <- getE
+      period  <- getE
+      return PerfCounter{perfNum, tid, period}
+  )),
+
+ (FixedSizeParser EVENT_PERF_TRACEPOINT (sz_perf_num + sz_kernel_tid) (do -- (perf_num, tid)
+      perfNum <- getE
+      tid     <- getE
+      return PerfTracepoint{perfNum, tid}
+  ))
+ ]
+
+getData :: GetEvents Data
+getData = do
+   db <- getE :: GetEvents Marker
+   when (db /= EVENT_DATA_BEGIN) $ throwError "Data begin marker not found"
+   eparsers <- ask
+   let
+       getEvents :: [Event] -> GetEvents Data
+       getEvents events = do
+         mb_e <- getEvent eparsers
+         case mb_e of
+           Nothing -> return (Data (reverse events))
+           Just e  -> getEvents (e:events)
+   -- in
+   getEvents []
+
+getEventBlock :: EventParsers -> GetEvents [Event]
+getEventBlock parsers = do
+  b <- lift . lift $ isEmpty
+  if b then return [] else do
+   mb_e <- getEvent parsers
+   case mb_e of
+     Nothing -> return []
+     Just e  -> do
+       es <- getEventBlock parsers
+       return (e:es)
+
+getEventLog :: ErrorT String Get EventLog
+getEventLog = do
+    header <- getHeader
+    let imap = M.fromList [ (fromIntegral (num t),t) | t <- eventTypes header]
+        -- This test is complete, no-one has extended this event yet and all future
+        -- extensions will use newly allocated event IDs.
+        is_ghc_6 = Just sz_old_tid == do create_et <- M.lookup EVENT_CREATE_THREAD imap
+                                         size create_et
+        {-
+        -- GHC6 writes an invalid header, we handle it here by using a
+        -- different set of event parsers.  Note that the ghc7 event parsers
+        -- are standard events, and can be used by other runtime systems that
+        -- make use of threadscope.
+        -}
+        event_parsers = if is_ghc_6
+                            then standardParsers ++ ghc6Parsers
+                            else standardParsers ++ ghc7Parsers
+                                 ++ mercuryParsers ++ perfParsers
+        parsers = mkEventTypeParsers imap event_parsers
+    dat <- runReaderT getData (EventParsers parsers)
+    return (EventLog header dat)
+
+readEventLogFromFile :: FilePath -> IO (Either String EventLog)
+readEventLogFromFile f = do
+    s <- L.readFile f
+    return $ runGet (do v <- runErrorT $ getEventLog
+                        m <- isEmpty
+                        m `seq` return v)  s
+
+-- -----------------------------------------------------------------------------
+-- Utilities
+
+sortEvents :: [Event] -> [CapEvent]
+sortEvents = sortGroups . groupEvents
+
+-- | Sort the raw event stream by time, annotating each event with the
+-- capability that generated it.
+sortGroups :: [(Maybe Int, [Event])] -> [CapEvent]
+sortGroups groups = mergesort' (compare `on` (time . ce_event)) $
+                      [ [ CapEvent cap e | e <- es ]
+                      | (cap, es) <- groups ]
+     -- sorting is made much faster by the way that the event stream is
+     -- divided into blocks of events.
+     --  - All events in a block belong to a particular capability
+     --  - The events in a block are ordered by time
+     --  - blocks for the same capability appear in time order in the event
+     --    stream and do not overlap.
+     --
+     -- So to sort the events we make one list of events for each
+     -- capability (basically just concat . filter), and then
+     -- merge the resulting lists.
+
+groupEvents :: [Event] -> [(Maybe Int, [Event])]
+groupEvents es = (Nothing, n_events) :
+                 [ (Just (cap (head blocks)), concatMap block_events blocks)
+                 | blocks <- groups ]
+  where
+   (blocks, anon_events) = partitionEithers (map separate es)
+      where separate e | b@EventBlock{} <- spec e = Left  b
+                       | otherwise                = Right e
+
+   (cap_blocks, gbl_blocks) = partition (is_cap . cap) blocks
+      where is_cap c = fromIntegral c /= ((-1) :: Word16)
+
+   groups = groupBy ((==) `on` cap) $ sortBy (compare `on` cap) cap_blocks
+
+     -- There are two sources of events without a capability: events
+     -- in the raw stream not inside an EventBlock, and EventBlocks
+     -- with cap == -1.  We have to merge those two streams.
+     -- In light of merged logs, global blocks may have overlapping
+     -- time spans, thus the blocks are mergesorted
+   n_events = mergesort' (compare `on` time) (anon_events : map block_events gbl_blocks)
+
+mergesort' :: (a -> a -> Ordering) -> [[a]] -> [a]
+mergesort' _   [] = []
+mergesort' _   [xs] = xs
+mergesort' cmp xss = mergesort' cmp (merge_pairs cmp xss)
+
+merge_pairs :: (a -> a -> Ordering) -> [[a]] -> [[a]]
+merge_pairs _   [] = []
+merge_pairs _   [xs] = [xs]
+merge_pairs cmp (xs:ys:xss) = merge cmp xs ys : merge_pairs cmp xss
+
+merge :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
+merge _   [] ys = ys
+merge _   xs [] = xs
+merge cmp (x:xs) (y:ys)
+ = case x `cmp` y of
+        GT -> y : merge cmp (x:xs)   ys
+        _  -> x : merge cmp    xs (y:ys)
+
+
+buildEventTypeMap :: [EventType] -> IntMap EventType
+buildEventTypeMap etypes = M.fromList [ (fromIntegral (num t),t) | t <- etypes ]
+
+-----------------------------------------------------------------------------
+-- Some pretty-printing support
+
+showEventInfo :: EventInfo -> String
+showEventInfo spec =
+    case spec of
+        EventBlock end_time cap _block_events ->
+          printf "event block: cap %d, end time: %d\n" cap end_time
+        Startup n_caps ->
+          printf "startup: %d capabilities" n_caps
+        CreateThread thread ->
+          printf "creating thread %d" thread
+        RunThread thread ->
+          printf "running thread %d" thread
+        StopThread thread status ->
+          printf "stopping thread %d (%s)" thread (showThreadStopStatus status)
+        ThreadRunnable thread ->
+          printf "thread %d is runnable" thread
+        MigrateThread thread newCap  ->
+          printf "migrating thread %d to cap %d" thread newCap
+        CreateSparkThread sparkThread ->
+          printf "creating spark thread %d" sparkThread
+        SparkCounters crt dud ovf cnv fiz gcd rem ->
+          printf "spark stats: %d created, %d converted, %d remaining (%d overflowed, %d dud, %d GC'd, %d fizzled)" crt cnv rem ovf dud gcd fiz
+        SparkCreate ->
+          printf "spark created"
+        SparkDud ->
+          printf "dud spark discarded"
+        SparkOverflow ->
+          printf "overflowed spark discarded"
+        SparkRun ->
+          printf "running a local spark"
+        SparkSteal victimCap ->
+          printf "stealing a spark from cap %d" victimCap
+        SparkFizzle ->
+          printf "spark fizzled"
+        SparkGC ->
+          printf "spark GCed"
+        TaskCreate taskId cap tid ->
+          printf "task 0x%x created on cap %d with OS kernel thread %d"
+                 taskId cap (kernelThreadId tid)
+        TaskMigrate taskId cap new_cap ->
+          printf "task 0x%x migrated from cap %d to cap %d"
+                 taskId cap new_cap
+        TaskDelete taskId ->
+          printf "task 0x%x deleted" taskId
+        Shutdown ->
+          printf "shutting down"
+        WakeupThread thread otherCap ->
+          printf "waking up thread %d on cap %d" thread otherCap
+        ThreadLabel thread label ->
+          printf "thread %d has label \"%s\"" thread label
+        RequestSeqGC ->
+          printf "requesting sequential GC"
+        RequestParGC ->
+          printf "requesting parallel GC"
+        StartGC ->
+          printf "starting GC"
+        EndGC ->
+          printf "finished GC"
+        GCWork ->
+          printf "GC working"
+        GCIdle ->
+          printf "GC idle"
+        GCDone ->
+          printf "GC done"
+        GlobalSyncGC ->
+          printf "all caps stopped for GC"
+        GCStatsGHC{..} ->
+          printf "GC stats for heap capset %d: generation %d, %d bytes copied, %d bytes slop, %d bytes fragmentation, %d par threads, %d bytes max par copied, %d bytes total par copied" heapCapset gen copied slop frag parNThreads parMaxCopied parTotCopied
+        HeapAllocated{..} ->
+          printf "allocated on heap capset %d: %d total bytes till now" heapCapset allocBytes
+        HeapSize{..} ->
+          printf "size of heap capset %d: %d bytes" heapCapset sizeBytes
+        HeapLive{..} ->
+          printf "live data in heap capset %d: %d bytes" heapCapset liveBytes
+        HeapInfoGHC{..} ->
+          printf "heap stats for heap capset %d: generations %d, %d bytes max heap size, %d bytes alloc area size, %d bytes mblock size, %d bytes block size" heapCapset gens maxHeapSize allocAreaSize mblockSize blockSize
+        CapCreate{cap} ->
+          printf "created cap %d" cap
+        CapDelete{cap} ->
+          printf "deleted cap %d" cap
+        CapDisable{cap} ->
+          printf "disabled cap %d" cap
+        CapEnable{cap} ->
+          printf "enabled cap %d" cap
+        Message msg ->
+          msg
+        UserMessage msg ->
+          msg
+        UserMarker markername ->
+          printf "marker: %s" markername
+        CapsetCreate cs ct ->
+          printf "created capset %d of type %s" cs (show ct)
+        CapsetDelete cs ->
+          printf "deleted capset %d" cs
+        CapsetAssignCap cs cp ->
+          printf "assigned cap %d to capset %d" cp cs
+        CapsetRemoveCap cs cp ->
+          printf "removed cap %d from capset %d" cp cs
+        OsProcessPid cs pid ->
+          printf "capset %d: pid %d" cs pid
+        OsProcessParentPid cs ppid ->
+          printf "capset %d: parent pid %d" cs ppid
+        WallClockTime cs sec nsec ->
+          printf "capset %d: wall clock time %ds %dns (unix epoch)" cs sec nsec
+        RtsIdentifier cs i ->
+          printf "capset %d: RTS version \"%s\"" cs i
+        ProgramArgs cs args ->
+          printf "capset %d: args: %s" cs (show args)
+        ProgramEnv cs env ->
+          printf "capset %d: env: %s" cs (show env)
+        UnknownEvent n ->
+          printf "Unknown event type %d" n
+        InternString str sId ->
+          printf "Interned string: \"%s\" with id %d" str sId
+        MerStartParConjunction dyn_id static_id ->
+          printf "Start a parallel conjunction 0x%x, static_id: %d" dyn_id static_id
+        MerEndParConjunction dyn_id ->
+          printf "End par conjunction: 0x%x" dyn_id
+        MerEndParConjunct dyn_id ->
+          printf "End par conjunct: 0x%x" dyn_id
+        MerCreateSpark dyn_id spark_id ->
+          printf "Create spark for conjunction: 0x%x spark: 0x%x" dyn_id spark_id
+        MerFutureCreate future_id name_id ->
+          printf "Create future 0x%x named %d" future_id name_id
+        MerFutureWaitNosuspend future_id ->
+          printf "Wait didn't suspend for future: 0x%x" future_id
+        MerFutureWaitSuspended future_id ->
+          printf "Wait suspended on future: 0x%x" future_id
+        MerFutureSignal future_id ->
+          printf "Signaled future 0x%x" future_id
+        MerLookingForGlobalThread ->
+          "Looking for global thread to resume"
+        MerWorkStealing ->
+          "Trying to steal a spark"
+        MerLookingForLocalSpark ->
+          "Looking for a local spark to execute"
+        MerReleaseThread thread_id ->
+          printf "Releasing thread %d to the free pool" thread_id
+        MerCapSleeping ->
+          "Capability going to sleep"
+        MerCallingMain ->
+          "About to call the program entry point"
+        PerfName{perfNum, name} ->
+          printf "perf event %d named \"%s\"" perfNum name
+        PerfCounter{perfNum, tid, period} ->
+          printf "perf event counter %d incremented by %d in OS thread %d"
+                 perfNum (period + 1) (kernelThreadId tid)
+        PerfTracepoint{perfNum, tid} ->
+          printf "perf event tracepoint %d reached in OS thread %d"
+                 perfNum (kernelThreadId tid)
+
+showThreadStopStatus :: ThreadStopStatus -> String
+showThreadStopStatus HeapOverflow   = "heap overflow"
+showThreadStopStatus StackOverflow  = "stack overflow"
+showThreadStopStatus ThreadYielding = "thread yielding"
+showThreadStopStatus ThreadBlocked  = "thread blocked"
+showThreadStopStatus ThreadFinished = "thread finished"
+showThreadStopStatus ForeignCall    = "making a foreign call"
+showThreadStopStatus BlockedOnMVar  = "blocked on an MVar"
+showThreadStopStatus BlockedOnBlackHole = "blocked on a black hole"
+showThreadStopStatus BlockedOnRead = "blocked on I/O read"
+showThreadStopStatus BlockedOnWrite = "blocked on I/O write"
+showThreadStopStatus BlockedOnDelay = "blocked on threadDelay"
+showThreadStopStatus BlockedOnSTM = "blocked in STM retry"
+showThreadStopStatus BlockedOnDoProc = "blocked on asyncDoProc"
+showThreadStopStatus BlockedOnCCall = "blocked in a foreign call"
+showThreadStopStatus BlockedOnCCall_NoUnblockExc = "blocked in a foreign call"
+showThreadStopStatus BlockedOnMsgThrowTo = "blocked in throwTo"
+showThreadStopStatus ThreadMigrating = "thread migrating"
+showThreadStopStatus BlockedOnMsgGlobalise = "waiting for data to be globalised"
+showThreadStopStatus (BlockedOnBlackHoleOwnedBy target) =
+          "blocked on black hole owned by thread " ++ show target
+showThreadStopStatus NoStatus = "No stop thread status"
+
+ppEventLog :: EventLog -> String
+ppEventLog (EventLog (Header ets) (Data es)) = unlines $ concat (
+    [ ["Event Types:"]
+    , map ppEventType ets
+    , [""] -- newline
+    , ["Events:"]
+    , map (ppEvent imap) sorted
+    , [""] ]) -- extra trailing newline
+ where
+    imap = buildEventTypeMap ets
+    sorted = sortEvents es
+
+ppEventType :: EventType -> String
+ppEventType (EventType num dsc msz) = printf "%4d: %s (size %s)" num dsc
+   (case msz of Nothing -> "variable"; Just x -> show x)
+
+ppEvent :: IntMap EventType -> CapEvent -> String
+ppEvent imap (CapEvent cap (Event time spec)) =
+  printf "%9d: " time ++
+  (case cap of
+    Nothing -> ""
+    Just c  -> printf "cap %d: " c) ++
+  case spec of
+    UnknownEvent{ ref=ref } ->
+      printf (desc (fromJust (M.lookup (fromIntegral ref) imap)))
+
+    other -> showEventInfo spec
+
+type PutEvents a = PutM a
+
+putE :: Binary a => a -> PutEvents ()
+putE = put
+
+runPutEBS :: PutEvents () -> L.ByteString
+runPutEBS = runPut
+
+writeEventLogToFile f el = L.writeFile f $ runPutEBS $ putEventLog el
+
+putType :: EventTypeNum -> PutEvents ()
+putType = putE
+
+putCap :: Int -> PutEvents ()
+putCap c = putE (fromIntegral c :: CapNo)
+
+putMarker :: Word32 -> PutEvents ()
+putMarker = putE
+
+putEStr :: String -> PutEvents ()
+putEStr = mapM_ putE
+
+putEventLog :: EventLog -> PutEvents ()
+putEventLog (EventLog hdr es) = do
+    putHeader hdr
+    putData es
+
+putHeader :: Header -> PutEvents ()
+putHeader (Header ets) = do
+    putMarker EVENT_HEADER_BEGIN
+    putMarker EVENT_HET_BEGIN
+    mapM_ putEventType ets
+    putMarker EVENT_HET_END
+    putMarker EVENT_HEADER_END
+ where
+    putEventType (EventType n d msz) = do
+        putMarker EVENT_ET_BEGIN
+        putType n
+        putE $ fromMaybe 0xffff msz
+        putE (fromIntegral $ length d :: EventTypeDescLen)
+        mapM_ put d
+        -- the event type header allows for extra data, which we don't use:
+        putE (0 :: Word32)
+        putMarker EVENT_ET_END
+
+putData :: Data -> PutEvents ()
+putData (Data es) = do
+    putMarker EVENT_DATA_BEGIN -- Word32
+    mapM_ putEvent es
+    putType EVENT_DATA_END -- Word16
+
+eventTypeNum :: EventInfo -> EventTypeNum
+eventTypeNum e = case e of
+    CreateThread {} -> EVENT_CREATE_THREAD
+    RunThread {} -> EVENT_RUN_THREAD
+    StopThread {} -> EVENT_STOP_THREAD
+    ThreadRunnable {} -> EVENT_THREAD_RUNNABLE
+    MigrateThread {} -> EVENT_MIGRATE_THREAD
+    Shutdown {} -> EVENT_SHUTDOWN
+    WakeupThread {} -> EVENT_THREAD_WAKEUP
+    ThreadLabel {}  -> EVENT_THREAD_LABEL
+    StartGC {} -> EVENT_GC_START
+    EndGC {} -> EVENT_GC_END
+    GlobalSyncGC {} -> EVENT_GC_GLOBAL_SYNC
+    RequestSeqGC {} -> EVENT_REQUEST_SEQ_GC
+    RequestParGC {} -> EVENT_REQUEST_PAR_GC
+    CreateSparkThread {} -> EVENT_CREATE_SPARK_THREAD
+    SparkCounters {} -> EVENT_SPARK_COUNTERS
+    SparkCreate   {} -> EVENT_SPARK_CREATE
+    SparkDud      {} -> EVENT_SPARK_DUD
+    SparkOverflow {} -> EVENT_SPARK_OVERFLOW
+    SparkRun      {} -> EVENT_SPARK_RUN
+    SparkSteal    {} -> EVENT_SPARK_STEAL
+    SparkFizzle   {} -> EVENT_SPARK_FIZZLE
+    SparkGC       {} -> EVENT_SPARK_GC
+    TaskCreate  {} -> EVENT_TASK_CREATE
+    TaskMigrate {} -> EVENT_TASK_MIGRATE
+    TaskDelete  {} -> EVENT_TASK_DELETE
+    Message {} -> EVENT_LOG_MSG
+    Startup {} -> EVENT_STARTUP
+    EventBlock {} -> EVENT_BLOCK_MARKER
+    UserMessage {} -> EVENT_USER_MSG
+    UserMarker  {} -> EVENT_USER_MARKER
+    GCIdle {} -> EVENT_GC_IDLE
+    GCWork {} -> EVENT_GC_WORK
+    GCDone {} -> EVENT_GC_DONE
+    GCStatsGHC{} -> EVENT_GC_STATS_GHC
+    HeapAllocated{} -> EVENT_HEAP_ALLOCATED
+    HeapSize{} -> EVENT_HEAP_SIZE
+    HeapLive{} -> EVENT_HEAP_LIVE
+    HeapInfoGHC{} -> EVENT_HEAP_INFO_GHC
+    CapCreate{} -> EVENT_CAP_CREATE
+    CapDelete{} -> EVENT_CAP_DELETE
+    CapDisable{} -> EVENT_CAP_DISABLE
+    CapEnable{} -> EVENT_CAP_ENABLE
+    CapsetCreate {} -> EVENT_CAPSET_CREATE
+    CapsetDelete {} -> EVENT_CAPSET_DELETE
+    CapsetAssignCap {} -> EVENT_CAPSET_ASSIGN_CAP
+    CapsetRemoveCap {} -> EVENT_CAPSET_REMOVE_CAP
+    RtsIdentifier {} -> EVENT_RTS_IDENTIFIER
+    ProgramArgs {} -> EVENT_PROGRAM_ARGS
+    ProgramEnv {} -> EVENT_PROGRAM_ENV
+    OsProcessPid {} -> EVENT_OSPROCESS_PID
+    OsProcessParentPid{} -> EVENT_OSPROCESS_PPID
+    WallClockTime{} -> EVENT_WALL_CLOCK_TIME
+    UnknownEvent {} -> error "eventTypeNum UnknownEvent"
+    InternString {} -> EVENT_INTERN_STRING
+    MerStartParConjunction {} -> EVENT_MER_START_PAR_CONJUNCTION
+    MerEndParConjunction _ -> EVENT_MER_STOP_PAR_CONJUNCTION
+    MerEndParConjunct _ -> EVENT_MER_STOP_PAR_CONJUNCT
+    MerCreateSpark {} -> EVENT_MER_CREATE_SPARK
+    MerFutureCreate {} -> EVENT_MER_FUT_CREATE
+    MerFutureWaitNosuspend _ -> EVENT_MER_FUT_WAIT_NOSUSPEND
+    MerFutureWaitSuspended _ -> EVENT_MER_FUT_WAIT_SUSPENDED
+    MerFutureSignal _ -> EVENT_MER_FUT_SIGNAL
+    MerLookingForGlobalThread -> EVENT_MER_LOOKING_FOR_GLOBAL_CONTEXT
+    MerWorkStealing -> EVENT_MER_WORK_STEALING
+    MerLookingForLocalSpark -> EVENT_MER_LOOKING_FOR_LOCAL_SPARK
+    MerReleaseThread _ -> EVENT_MER_RELEASE_CONTEXT
+    MerCapSleeping -> EVENT_MER_ENGINE_SLEEPING
+    MerCallingMain -> EVENT_MER_CALLING_MAIN
+    PerfName       {} -> nEVENT_PERF_NAME
+    PerfCounter    {} -> nEVENT_PERF_COUNTER
+    PerfTracepoint {} -> nEVENT_PERF_TRACEPOINT
+
+nEVENT_PERF_NAME, nEVENT_PERF_COUNTER, nEVENT_PERF_TRACEPOINT :: EventTypeNum
+nEVENT_PERF_NAME = EVENT_PERF_NAME
+nEVENT_PERF_COUNTER = EVENT_PERF_COUNTER
+nEVENT_PERF_TRACEPOINT = EVENT_PERF_TRACEPOINT
+
+putEvent :: Event -> PutEvents ()
+putEvent (Event t spec) = do
+    putType (eventTypeNum spec)
+    put t
+    putEventSpec spec
+
+putEventSpec (Startup caps) = do
+    putCap (fromIntegral caps)
+
+putEventSpec (EventBlock end cap es) = do
+    let block = runPutEBS (mapM_ putEvent es)
+    put (fromIntegral (L.length block) + 24 :: Word32)
+    putE end
+    putE (fromIntegral cap :: CapNo)
+    putLazyByteString block
+
+putEventSpec (CreateThread t) = do
+    putE t
+
+putEventSpec (RunThread t) = do
+    putE t
+
+-- here we assume that ThreadStopStatus fromEnum matches the definitions in
+-- EventLogFormat.h
+putEventSpec (StopThread t s) = do
+    putE t
+    putE $ case s of
+            NoStatus -> 0 :: Word16
+            HeapOverflow -> 1
+            StackOverflow -> 2
+            ThreadYielding -> 3
+            ThreadBlocked -> 4
+            ThreadFinished -> 5
+            ForeignCall -> 6
+            BlockedOnMVar -> 7
+            BlockedOnBlackHole -> 8
+            BlockedOnBlackHoleOwnedBy _ -> 8
+            BlockedOnRead -> 9
+            BlockedOnWrite -> 10
+            BlockedOnDelay -> 11
+            BlockedOnSTM -> 12
+            BlockedOnDoProc -> 13
+            BlockedOnCCall -> 14
+            BlockedOnCCall_NoUnblockExc -> 15
+            BlockedOnMsgThrowTo -> 16
+            ThreadMigrating -> 17
+            BlockedOnMsgGlobalise -> 18
+    putE $ case s of
+            BlockedOnBlackHoleOwnedBy i -> i
+            _                           -> 0
+
+putEventSpec (ThreadRunnable t) = do
+    putE t
+
+putEventSpec (MigrateThread t c) = do
+    putE t
+    putCap c
+
+putEventSpec (CreateSparkThread t) = do
+    putE t
+
+putEventSpec (SparkCounters crt dud ovf cnv fiz gcd rem) = do
+    putE crt
+    putE dud
+    putE ovf
+    putE cnv
+    -- Warning: order of fiz and gcd reversed!
+    putE gcd
+    putE fiz
+    putE rem
+
+putEventSpec SparkCreate = do
+    return ()
+
+putEventSpec SparkDud = do
+    return ()
+
+putEventSpec SparkOverflow = do
+    return ()
+
+putEventSpec SparkRun = do
+    return ()
+
+putEventSpec (SparkSteal c) = do
+    putCap c
+
+putEventSpec SparkFizzle = do
+    return ()
+
+putEventSpec SparkGC = do
+    return ()
+
+putEventSpec (WakeupThread t c) = do
+    putE t
+    putCap c
+
+putEventSpec (ThreadLabel t l) = do
+    putE (fromIntegral (length l) + sz_tid :: Word16)
+    putE t
+    putEStr l
+
+putEventSpec Shutdown = do
+    return ()
+
+putEventSpec RequestSeqGC = do
+    return ()
+
+putEventSpec RequestParGC = do
+    return ()
+
+putEventSpec StartGC = do
+    return ()
+
+putEventSpec GCWork = do
+    return ()
+
+putEventSpec GCIdle = do
+    return ()
+
+putEventSpec GCDone = do
+    return ()
+
+putEventSpec EndGC = do
+    return ()
+
+putEventSpec GlobalSyncGC = do
+    return ()
+
+putEventSpec (TaskCreate taskId cap tid) = do
+    putE taskId
+    putCap cap
+    putE tid
+
+putEventSpec (TaskMigrate taskId cap new_cap) = do
+    putE taskId
+    putCap cap
+    putCap new_cap
+
+putEventSpec (TaskDelete taskId) = do
+    putE taskId
+
+putEventSpec GCStatsGHC{..} = do
+    putE heapCapset
+    putE (fromIntegral gen :: Word16)
+    putE copied
+    putE slop
+    putE frag
+    putE (fromIntegral parNThreads :: Word32)
+    putE parMaxCopied
+    putE parTotCopied
+
+putEventSpec HeapAllocated{..} = do
+    putE heapCapset
+    putE allocBytes
+
+putEventSpec HeapSize{..} = do
+    putE heapCapset
+    putE sizeBytes
+
+putEventSpec HeapLive{..} = do
+    putE heapCapset
+    putE liveBytes
+
+putEventSpec HeapInfoGHC{..} = do
+    putE heapCapset
+    putE (fromIntegral gens :: Word16)
+    putE maxHeapSize
+    putE allocAreaSize
+    putE mblockSize
+    putE blockSize
+
+putEventSpec CapCreate{cap} = do
+    putCap cap
+
+putEventSpec CapDelete{cap} = do
+    putCap cap
+
+putEventSpec CapDisable{cap} = do
+    putCap cap
+
+putEventSpec CapEnable{cap} = do
+    putCap cap
+
+putEventSpec (CapsetCreate cs ct) = do
+    putE cs
+    putE $ case ct of
+            CapsetCustom -> 1 :: Word16
+            CapsetOsProcess -> 2
+            CapsetClockDomain -> 3
+            CapsetUnknown -> 0
+
+putEventSpec (CapsetDelete cs) = do
+    putE cs
+
+putEventSpec (CapsetAssignCap cs cp) = do
+    putE cs
+    putCap cp
+
+putEventSpec (CapsetRemoveCap cs cp) = do
+    putE cs
+    putCap cp
+
+putEventSpec (RtsIdentifier cs rts) = do
+    putE (fromIntegral (length rts) + sz_capset :: Word16)
+    putE cs
+    putEStr rts
+
+putEventSpec (ProgramArgs cs as) = do
+    let as' = unsep as
+    putE (fromIntegral (length as') + sz_capset :: Word16)
+    putE cs
+    mapM_ putE as'
+
+putEventSpec (ProgramEnv cs es) = do
+    let es' = unsep es
+    putE (fromIntegral (length es') + sz_capset :: Word16)
+    putE cs
+    mapM_ putE es'
+
+putEventSpec (OsProcessPid cs pid) = do
+    putE cs
+    putE pid
+
+putEventSpec (OsProcessParentPid cs ppid) = do
+    putE cs
+    putE ppid
+
+putEventSpec (WallClockTime cs sec nsec) = do
+    putE cs
+    putE sec
+    putE nsec
+
+putEventSpec (Message s) = do
+    putE (fromIntegral (length s) :: Word16)
+    mapM_ putE s
+
+putEventSpec (UserMessage s) = do
+    putE (fromIntegral (length s) :: Word16)
+    mapM_ putE s
+
+putEventSpec (UserMarker s) = do
+    putE (fromIntegral (length s) :: Word16)
+    mapM_ putE s
+
+putEventSpec (UnknownEvent {}) = error "putEventSpec UnknownEvent"
+
+putEventSpec (InternString str id) = do
+    putE len
+    mapM_ putE str
+    putE id
+  where len = (fromIntegral (length str) :: Word16) + sz_string_id
+
+putEventSpec (MerStartParConjunction dyn_id static_id) = do
+    putE dyn_id
+    putE static_id
+
+putEventSpec (MerEndParConjunction dyn_id) = do
+    putE dyn_id
+
+putEventSpec (MerEndParConjunct dyn_id) = do
+    putE dyn_id
+
+putEventSpec (MerCreateSpark dyn_id spark_id) = do
+    putE dyn_id
+    putE spark_id
+
+putEventSpec (MerFutureCreate future_id name_id) = do
+    putE future_id
+    putE name_id
+
+putEventSpec (MerFutureWaitNosuspend future_id) = do
+    putE future_id
+
+putEventSpec (MerFutureWaitSuspended future_id) = do
+    putE future_id
+
+putEventSpec (MerFutureSignal future_id) = do
+    putE future_id
+
+putEventSpec MerLookingForGlobalThread = return ()
+putEventSpec MerWorkStealing = return ()
+putEventSpec MerLookingForLocalSpark = return ()
+
+putEventSpec (MerReleaseThread thread_id) = do
+    putE thread_id
+
+putEventSpec MerCapSleeping = return ()
+putEventSpec MerCallingMain = return ()
+
+putEventSpec PerfName{..} = do
+    putE (fromIntegral (length name) + sz_perf_num :: Word16)
+    putE perfNum
+    mapM_ putE name
+
+putEventSpec PerfCounter{..} = do
+    putE perfNum
+    putE tid
+    putE period
+
+putEventSpec PerfTracepoint{..} = do
+    putE perfNum
+    putE tid
+
+-- [] == []
+-- [x] == x\0
+-- [x, y, z] == x\0y\0
+unsep :: [String] -> String
+unsep = concatMap (++"\0") -- not the most efficient, but should be ok
+
+splitNull :: String -> [String]
+splitNull [] = []
+splitNull xs = case span (/= '\0') xs of
+                (x, xs') -> x : splitNull (drop 1 xs')
diff --git a/TestSuite/inputs/Puns/cabals/array.cabal b/TestSuite/inputs/Puns/cabals/array.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/array.cabal
@@ -0,0 +1,56 @@
+name:       array
+version:    0.4.0.0
+license:    BSD3
+license-file:    LICENSE
+maintainer:    libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29
+synopsis:   Mutable and immutable arrays
+category:   Data Structures
+description:
+    This package defines the classes @IArray@ of immutable arrays and
+    @MArray@ of arrays mutable within appropriate monads, as well as
+    some instances of these classes.
+cabal-version: >=1.6
+build-type: Simple
+extra-source-files: include/Typeable.h
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/array.git/
+
+library
+  build-depends: base >= 4.2 && < 5
+  exposed-modules:
+      Data.Array
+  extensions: CPP
+  if !impl(nhc98)
+    exposed-modules:
+      Data.Array.Base
+      Data.Array.IArray
+      Data.Array.IO
+      Data.Array.IO.Safe
+      Data.Array.IO.Internals
+      Data.Array.MArray
+      Data.Array.MArray.Safe
+      Data.Array.ST
+      Data.Array.ST.Safe
+      Data.Array.Storable
+      Data.Array.Storable.Safe
+      Data.Array.Storable.Internals
+      Data.Array.Unboxed
+      Data.Array.Unsafe
+    extensions:
+      MultiParamTypeClasses,
+      FlexibleContexts,
+      FlexibleInstances,
+      TypeSynonymInstances
+  if impl(ghc)
+    extensions:
+      DeriveDataTypeable,
+      StandaloneDeriving,
+      Rank2Types,
+      MagicHash,
+      UnboxedTuples,
+      ForeignFunctionInterface,
+      UnliftedFFITypes
+  include-dirs: include
diff --git a/TestSuite/inputs/Puns/cabals/base.cabal b/TestSuite/inputs/Puns/cabals/base.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/base.cabal
@@ -0,0 +1,253 @@
+name:           base
+version:        4.5.1.0
+license:        BSD3
+license-file:   LICENSE
+maintainer:     libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries/base
+synopsis:       Basic libraries
+description:
+    This package contains the Prelude and its support libraries,
+    and a large collection of useful libraries ranging from data
+    structures to parsing combinators and debugging utilities.
+cabal-version:  >=1.6
+build-type: Configure
+extra-tmp-files:
+                config.log config.status autom4te.cache
+                include/HsBaseConfig.h include/EventConfig.h
+extra-source-files:
+                config.guess config.sub install-sh
+                aclocal.m4 configure.ac configure
+                include/CTypes.h include/md5.h
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/base.git/
+
+Flag integer-simple
+    Description: Use integer-simple
+
+Library {
+    if impl(ghc) {
+        if flag(integer-simple)
+            build-depends: integer-simple
+        else
+            build-depends: integer-gmp
+            cpp-options: -DOPTIMISE_INTEGER_GCD_LCM
+        build-depends: rts, ghc-prim
+        exposed-modules:
+            Foreign.Concurrent,
+            GHC.Arr,
+            GHC.Base,
+            GHC.Conc,
+            GHC.Conc.IO,
+            GHC.Conc.Signal,
+            GHC.Conc.Sync,
+            GHC.ConsoleHandler,
+            GHC.Constants,
+            GHC.Desugar,
+            GHC.Enum,
+            GHC.Environment,
+            GHC.Err,
+            GHC.Exception,
+            GHC.Exts,
+            GHC.Fingerprint,
+            GHC.Fingerprint.Type,
+            GHC.Float,
+            GHC.Float.ConversionUtils,
+            GHC.Float.RealFracMethods,
+            GHC.Foreign,
+            GHC.ForeignPtr,
+            GHC.Handle,
+            GHC.IO,
+            GHC.IO.Buffer,
+            GHC.IO.BufferedIO,
+            GHC.IO.Device,
+            GHC.IO.Encoding,
+            GHC.IO.Encoding.CodePage,
+            GHC.IO.Encoding.Failure,
+            GHC.IO.Encoding.Iconv,
+            GHC.IO.Encoding.Latin1,
+            GHC.IO.Encoding.Types,
+            GHC.IO.Encoding.UTF16,
+            GHC.IO.Encoding.UTF32,
+            GHC.IO.Encoding.UTF8,
+            GHC.IO.Exception,
+            GHC.IO.FD,
+            GHC.IO.Handle,
+            GHC.IO.Handle.FD,
+            GHC.IO.Handle.Internals,
+            GHC.IO.Handle.Text,
+            GHC.IO.Handle.Types,
+            GHC.IO.IOMode,
+            GHC.IOArray,
+            GHC.IOBase,
+            GHC.IORef,
+            GHC.Int,
+            GHC.List,
+            GHC.MVar,
+            GHC.Num,
+            GHC.PArr,
+            GHC.Pack,
+            GHC.Ptr,
+            GHC.Read,
+            GHC.Real,
+            GHC.ST,
+            GHC.Stack,
+            GHC.Stats,
+            GHC.Show,
+            GHC.Stable,
+            GHC.Storable,
+            GHC.STRef,
+            GHC.TopHandler,
+            GHC.Unicode,
+            GHC.Weak,
+            GHC.Word,
+            System.Timeout
+        if os(windows)
+            exposed-modules: GHC.IO.Encoding.CodePage.Table
+                             GHC.Conc.Windows
+                             GHC.Windows
+    }
+    exposed-modules:
+        Control.Applicative,
+        Control.Arrow,
+        Control.Category,
+        Control.Concurrent,
+        Control.Concurrent.Chan,
+        Control.Concurrent.MVar,
+        Control.Concurrent.QSem,
+        Control.Concurrent.QSemN,
+        Control.Concurrent.SampleVar,
+        Control.Exception,
+        Control.Exception.Base
+        Control.OldException,
+        Control.Monad,
+        Control.Monad.Fix,
+        Control.Monad.Instances,
+        Control.Monad.ST,
+        Control.Monad.ST.Safe,
+        Control.Monad.ST.Unsafe,
+        Control.Monad.ST.Lazy,
+        Control.Monad.ST.Lazy.Safe,
+        Control.Monad.ST.Lazy.Unsafe,
+        Control.Monad.ST.Strict,
+        Control.Monad.Zip
+        Data.Bits,
+        Data.Bool,
+        Data.Char,
+        Data.Complex,
+        Data.Dynamic,
+        Data.Either,
+        Data.Eq,
+        Data.Data,
+        Data.Fixed,
+        Data.Foldable
+        Data.Function,
+        Data.Functor,
+        Data.HashTable,
+        Data.IORef,
+        Data.Int,
+        Data.Ix,
+        Data.List,
+        Data.Maybe,
+        Data.Monoid,
+        Data.Ord,
+        Data.Ratio,
+        Data.STRef
+        Data.STRef.Lazy
+        Data.STRef.Strict
+        Data.String,
+        Data.Traversable
+        Data.Tuple,
+        Data.Typeable,
+        Data.Typeable.Internal,
+        Data.Unique,
+        Data.Version,
+        Data.Word,
+        Debug.Trace,
+        Foreign,
+        Foreign.C,
+        Foreign.C.Error,
+        Foreign.C.String,
+        Foreign.C.Types,
+        Foreign.ForeignPtr,
+        Foreign.ForeignPtr.Safe,
+        Foreign.ForeignPtr.Unsafe,
+        Foreign.Marshal,
+        Foreign.Marshal.Alloc,
+        Foreign.Marshal.Array,
+        Foreign.Marshal.Error,
+        Foreign.Marshal.Pool,
+        Foreign.Marshal.Safe,
+        Foreign.Marshal.Utils,
+        Foreign.Marshal.Unsafe,
+        Foreign.Ptr,
+        Foreign.Safe,
+        Foreign.StablePtr,
+        Foreign.Storable,
+        Numeric,
+        Prelude,
+        System.Console.GetOpt
+        System.CPUTime,
+        System.Environment,
+        System.Exit,
+        System.IO,
+        System.IO.Error,
+        System.IO.Unsafe,
+        System.Info,
+        System.Mem,
+        System.Mem.StableName,
+        System.Mem.Weak,
+        System.Posix.Internals,
+        System.Posix.Types,
+        Text.ParserCombinators.ReadP,
+        Text.ParserCombinators.ReadPrec,
+        Text.Printf,
+        Text.Read,
+        Text.Read.Lex,
+        Text.Show,
+        Text.Show.Functions
+        Unsafe.Coerce
+    other-modules:
+        Control.Monad.ST.Imp
+        Control.Monad.ST.Lazy.Imp
+        Foreign.ForeignPtr.Imp
+    c-sources:
+        cbits/PrelIOUtils.c
+        cbits/WCsubst.c
+        cbits/Win32Utils.c
+        cbits/consUtils.c
+        cbits/iconv.c
+        cbits/inputReady.c
+        cbits/selectUtils.c
+        cbits/primFloat.c
+        cbits/md5.c
+    include-dirs: include
+    includes:    HsBase.h
+    install-includes:    HsBase.h HsBaseConfig.h EventConfig.h WCsubst.h consUtils.h Typeable.h
+    if os(windows) {
+        extra-libraries: wsock32, user32, shell32
+    }
+    if !os(windows) {
+        exposed-modules:
+            GHC.Event
+        other-modules:
+            GHC.Event.Array
+            GHC.Event.Clock
+            GHC.Event.Control
+            GHC.Event.EPoll
+            GHC.Event.IntMap
+            GHC.Event.Internal
+            GHC.Event.KQueue
+            GHC.Event.Manager
+            GHC.Event.PSQ
+            GHC.Event.Poll
+            GHC.Event.Thread
+            GHC.Event.Unique
+    }
+    -- We need to set the package name to base (without a version number)
+    -- as it's magic.
+    ghc-options: -package-name base
+    nhc98-options: -H4M -K3M
+    extensions: CPP
+}
diff --git a/TestSuite/inputs/Puns/cabals/binary.cabal b/TestSuite/inputs/Puns/cabals/binary.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/binary.cabal
@@ -0,0 +1,60 @@
+name:            binary
+version:         0.5.1.0
+license-file:    LICENSE
+author:          Lennart Kolmodin <kolmodin@gmail.com>
+maintainer:      Lennart Kolmodin, Don Stewart <dons@galois.com>
+homepage:        http://code.haskell.org/binary/
+description:     Efficient, pure binary serialisation using lazy ByteStrings.
+                 Haskell values may be encoded to and from binary formats, 
+                 written to disk as binary, or sent over the network.
+                 Serialisation speeds of over 1 G\/sec have been observed,
+                 so this library should be suitable for high performance
+                 scenarios.
+synopsis:        Binary serialisation for Haskell values using lazy ByteStrings
+category:        Data, Parsing
+stability:       provisional
+build-type:      Simple
+cabal-version:   >= 1.2
+tested-with:     GHC ==6.4.2, GHC ==6.6.1, GHC ==6.8.0, GHC ==6.10.1
+extra-source-files: README index.html
+
+flag bytestring-in-base
+flag split-base
+flag applicative-in-base
+
+library
+  if flag(bytestring-in-base)
+    -- bytestring was in base-2.0 and 2.1.1
+    build-depends: base >= 2.0 && < 2.2
+    cpp-options: -DBYTESTRING_IN_BASE
+  else
+    -- in base 1.0 and 3.0 bytestring is a separate package
+    build-depends: base < 2.0 || >= 3, bytestring >= 0.9
+
+  if flag(split-base)
+    build-depends:   base >= 3.0, containers, array
+  else
+    build-depends:   base < 3.0
+
+  if flag(applicative-in-base)
+    build-depends: base >= 2.0
+    cpp-options: -DAPPLICATIVE_IN_BASE
+  else
+    build-depends: base < 2.0
+  hs-source-dirs:  src
+
+  exposed-modules: Data.Binary,
+                   Data.Binary.Put,
+                   Data.Binary.Get,
+                   Data.Binary.Builder
+                   Data.Binary.Builder.Internal
+
+  other-modules:   Data.Binary.Builder.Base
+
+  extensions:      CPP,
+                   FlexibleContexts
+
+  ghc-options:     -O2 -Wall -fliberate-case-threshold=1000
+
+--  if impl(ghc < 6.5)
+--    ghc-options:   -fallow-undecidable-instances
diff --git a/TestSuite/inputs/Puns/cabals/bytestring.cabal b/TestSuite/inputs/Puns/cabals/bytestring.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/bytestring.cabal
@@ -0,0 +1,91 @@
+Name:                bytestring
+Version:             0.9.2.1
+Synopsis:            Fast, packed, strict and lazy byte arrays with a list interface
+Description:
+    A time and space-efficient implementation of byte vectors using
+    packed Word8 arrays, suitable for high performance use, both in terms
+    of large data quantities, or high speed requirements. Byte vectors
+    are encoded as strict 'Word8' arrays of bytes, and lazy lists of
+    strict chunks, held in a 'ForeignPtr', and can be passed between C
+    and Haskell with little effort.
+    .
+    Test coverage data for this library is available at:
+        <http://code.haskell.org/~dons/tests/bytestring/hpc_index.html>
+
+License:             BSD3
+License-file:        LICENSE
+Category:            Data
+Copyright:           Copyright (c) Don Stewart   2005-2009,
+                               (c) Duncan Coutts 2006-2009,
+                               (c) David Roundy  2003-2005.
+Author:              Don Stewart, Duncan Coutts
+Maintainer:          dons00@gmail.com, duncan@community.haskell.org
+Homepage:            http://www.cse.unsw.edu.au/~dons/fps.html
+Tested-With:         GHC==7.0.2, GHC==6.12.3, GHC==6.10.4, GHC ==6.8.2
+Build-Type:          Simple
+Cabal-Version:       >= 1.8
+extra-source-files:  README TODO
+
+library
+  build-depends:     base >= 3 && < 5
+
+  if impl(ghc >= 6.10)
+    build-depends:   ghc-prim, base >= 4
+
+  exposed-modules:   Data.ByteString
+                     Data.ByteString.Char8
+                     Data.ByteString.Unsafe
+                     Data.ByteString.Internal
+                     Data.ByteString.Lazy
+                     Data.ByteString.Lazy.Char8
+                     Data.ByteString.Lazy.Internal
+                     Data.ByteString.Fusion
+
+  extensions:        CPP, ForeignFunctionInterface
+
+  if impl(ghc)
+      extensions:   UnliftedFFITypes,
+                    MagicHash,
+                    UnboxedTuples,
+                    DeriveDataTypeable
+                    ScopedTypeVariables
+  if impl(ghc >= 6.11)
+      extensions:   NamedFieldPuns
+
+  --TODO: eliminate orphan instances:
+  ghc-options:      -Wall -fno-warn-orphans
+                    -O2
+                    -funbox-strict-fields 
+                    -fmax-simplifier-iterations10
+                    -fdicts-cheap
+
+  c-sources:         cbits/fpstring.c
+  include-dirs:      include
+  includes:          fpstring.h
+  install-includes:  fpstring.h
+
+  nhc98-options:     -K4M -K3M
+
+-- QC properties, with GHC RULES disabled
+test-suite prop-compiled
+  type:             exitcode-stdio-1.0
+  main-is:          Properties.hs
+  hs-source-dirs:   . tests
+  build-depends:    base, random, directory,
+                    QuickCheck >= 2.3 && < 3
+  if impl(ghc >= 6.10)
+    build-depends:  ghc-prim
+  c-sources:        cbits/fpstring.c
+  include-dirs:     include
+  if impl(ghc >= 6.10)
+    ghc-options:    -fno-enable-rewrite-rules
+  else
+    ghc-options:    -fno-rewrite-rules
+  if impl(ghc)
+      extensions:   UnliftedFFITypes,
+                    MagicHash,
+                    UnboxedTuples,
+                    DeriveDataTypeable
+                    ScopedTypeVariables
+  if impl(ghc >= 6.11)
+      extensions:   NamedFieldPuns
diff --git a/TestSuite/inputs/Puns/cabals/containers.cabal b/TestSuite/inputs/Puns/cabals/containers.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/containers.cabal
@@ -0,0 +1,44 @@
+name: containers
+version: 0.4.2.1
+license: BSD3
+maintainer: fox@ucw.cz
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29
+synopsis: Assorted concrete container types
+category: Data Structures
+description:
+    This package contains efficient general-purpose implementations
+    of various basic immutable container types.  The declared cost of
+    each operation is either worst-case or amortized, but remains
+    valid even if structures are shared.
+build-type: Simple
+cabal-version:  >=1.6
+extra-source-files: include/Typeable.h
+
+source-repository head
+    type:     git
+    location: http://github.com/haskell/containers.git
+
+Library {
+    build-depends: base >= 4.2 && < 5, array, deepseq >= 1.2 && < 1.4
+    ghc-options: -O2
+    if impl(ghc>6.10)
+        Ghc-Options: -fregs-graph
+    exposed-modules:
+        Data.IntMap
+        Data.IntSet
+        Data.Map
+        Data.Set
+    include-dirs: include
+    extensions: CPP
+    if !impl(nhc98) {
+        exposed-modules:
+            Data.Graph
+            Data.Sequence
+            Data.Tree
+    }
+    if impl(ghc) {
+        extensions: DeriveDataTypeable, StandaloneDeriving,
+                    MagicHash, Rank2Types
+    }
+}
+
diff --git a/TestSuite/inputs/Puns/cabals/deepseq.cabal b/TestSuite/inputs/Puns/cabals/deepseq.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/deepseq.cabal
@@ -0,0 +1,35 @@
+name:		deepseq
+version:        1.3.0.0
+license:	BSD3
+license-file:	LICENSE
+maintainer:	libraries@haskell.org
+synopsis:	Deep evaluation of data structures
+category:       Control
+description:
+    This package provides methods for fully evaluating data structures
+    (\"deep evaluation\"). Deep evaluation is often used for adding
+    strictness to a program, e.g. in order to force pending exceptions,
+    remove space leaks, or force lazy I/O to happen. It is also useful
+    in parallel programs, to ensure pending work does not migrate to the
+    wrong thread.
+    .
+    The primary use of this package is via the 'deepseq' function, a
+    \"deep\" version of 'seq'. It is implemented on top of an 'NFData'
+    typeclass (\"Normal Form Data\", data structures with no unevaluated
+    components) which defines strategies for fully evaluating different
+    data types.
+    .
+build-type:     Simple
+cabal-version:  >=1.6
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/deepseq.git/
+
+library {
+  exposed-modules: Control.DeepSeq
+  build-depends: base       >= 3   && < 5, 
+                 array      >= 0.1 && < 0.5
+  ghc-options: -Wall
+  extensions: CPP
+}
diff --git a/TestSuite/inputs/Puns/cabals/mtl.cabal b/TestSuite/inputs/Puns/cabals/mtl.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/mtl.cabal
@@ -0,0 +1,51 @@
+Name:         mtl
+Version:      1.1.1.1
+Author:       Andy Gill
+Maintainer:   libraries@haskell.org
+Category:     Control
+Synopsis:     Monad transformer library
+Description:
+    A monad transformer library, inspired by the paper /Functional
+    Programming with Overloading and Higher-Order Polymorphism/,
+    by Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>),
+    Advanced School of Functional Programming, 1995.
+Build-Type:    Simple
+Cabal-Version: >= 1.6
+
+Library
+    Build-Depends: base < 5
+
+    Exposed-Modules:
+        Control.Monad.Cont
+        Control.Monad.Cont.Class
+        Control.Monad.Error
+        Control.Monad.Error.Class
+        Control.Monad.Identity
+        Control.Monad.List
+        Control.Monad.RWS
+        Control.Monad.RWS.Class
+        Control.Monad.RWS.Lazy
+        Control.Monad.RWS.Strict
+        Control.Monad.Reader
+        Control.Monad.Reader.Class
+        Control.Monad.State
+        Control.Monad.State.Class
+        Control.Monad.State.Lazy
+        Control.Monad.State.Strict
+        Control.Monad.Trans
+        Control.Monad.Writer
+        Control.Monad.Writer.Class
+        Control.Monad.Writer.Lazy
+        Control.Monad.Writer.Strict
+
+    Ghc-Options: -Wall
+
+    Extensions: MultiParamTypeClasses,
+                FunctionalDependencies,
+                FlexibleInstances,
+                TypeSynonymInstances
+
+    -- Compile all modules as __Safe__ modules
+    if impl(ghc >= 7.2)
+        Extensions: Safe
+
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/array.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/array.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/array.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/base.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/base.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/base.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/binary.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/binary.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/binary.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/bytestring.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/bytestring.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/bytestring.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/containers.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/containers.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/containers.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/deepseq.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/deepseq.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/deepseq.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/ghc-prim.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/ghc-prim.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/ghc-prim.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/integer-gmp.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/integer-gmp.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/integer-gmp.cabal
diff --git a/TestSuite/inputs/Puns/cabals/no_text_error/mtl.cabal b/TestSuite/inputs/Puns/cabals/no_text_error/mtl.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/no_text_error/mtl.cabal
@@ -0,0 +1,52 @@
+Name:         mtl
+Version:      1.1.1.1
+License:      PublicDomain
+Author:       Andy Gill
+Maintainer:   libraries@haskell.org
+Category:     Control
+Synopsis:     Monad transformer library
+Description:
+    A monad transformer library, inspired by the paper /Functional
+    Programming with Overloading and Higher-Order Polymorphism/,
+    by Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>),
+    Advanced School of Functional Programming, 1995.
+Build-Type:    Simple
+Cabal-Version: >= 1.6
+
+Library
+    Build-Depends: base < 5
+
+    Exposed-Modules:
+        Control.Monad.Cont
+        Control.Monad.Cont.Class
+        Control.Monad.Error
+        Control.Monad.Error.Class
+        Control.Monad.Identity
+        Control.Monad.List
+        Control.Monad.RWS
+        Control.Monad.RWS.Class
+        Control.Monad.RWS.Lazy
+        Control.Monad.RWS.Strict
+        Control.Monad.Reader
+        Control.Monad.Reader.Class
+        Control.Monad.State
+        Control.Monad.State.Class
+        Control.Monad.State.Lazy
+        Control.Monad.State.Strict
+        Control.Monad.Trans
+        Control.Monad.Writer
+        Control.Monad.Writer.Class
+        Control.Monad.Writer.Lazy
+        Control.Monad.Writer.Strict
+
+    Ghc-Options: -Wall
+
+    Extensions: MultiParamTypeClasses,
+                FunctionalDependencies,
+                FlexibleInstances,
+                TypeSynonymInstances
+
+    -- Compile all modules as __Safe__ modules
+    if impl(ghc >= 7.2)
+        Extensions: Safe
+
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/array.cabal b/TestSuite/inputs/Puns/cabals/parse_error/array.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/array.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/base.cabal b/TestSuite/inputs/Puns/cabals/parse_error/base.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/base.cabal
@@ -0,0 +1,253 @@
+name:           base
+version:        4.5.1.0
+license:        BSD3 -- can't comment here
+license-file:   LICENSE
+maintainer:     libraries@haskell.org
+bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries/base
+synopsis:       Basic libraries
+description:
+    This package contains the Prelude and its support libraries,
+    and a large collection of useful libraries ranging from data
+    structures to parsing combinators and debugging utilities.
+cabal-version:  >=1.6
+build-type: Configure
+extra-tmp-files:
+                config.log config.status autom4te.cache
+                include/HsBaseConfig.h include/EventConfig.h
+extra-source-files:
+                config.guess config.sub install-sh
+                aclocal.m4 configure.ac configure
+                include/CTypes.h include/md5.h
+
+source-repository head
+    type:     git
+    location: http://darcs.haskell.org/packages/base.git/
+
+Flag integer-simple
+    Description: Use integer-simple
+
+Library {
+    if impl(ghc) {
+        if flag(integer-simple)
+            build-depends: integer-simple
+        else
+            build-depends: integer-gmp
+            cpp-options: -DOPTIMISE_INTEGER_GCD_LCM
+        build-depends: rts, ghc-prim
+        exposed-modules:
+            Foreign.Concurrent,
+            GHC.Arr,
+            GHC.Base,
+            GHC.Conc,
+            GHC.Conc.IO,
+            GHC.Conc.Signal,
+            GHC.Conc.Sync,
+            GHC.ConsoleHandler,
+            GHC.Constants,
+            GHC.Desugar,
+            GHC.Enum,
+            GHC.Environment,
+            GHC.Err,
+            GHC.Exception,
+            GHC.Exts,
+            GHC.Fingerprint,
+            GHC.Fingerprint.Type,
+            GHC.Float,
+            GHC.Float.ConversionUtils,
+            GHC.Float.RealFracMethods,
+            GHC.Foreign,
+            GHC.ForeignPtr,
+            GHC.Handle,
+            GHC.IO,
+            GHC.IO.Buffer,
+            GHC.IO.BufferedIO,
+            GHC.IO.Device,
+            GHC.IO.Encoding,
+            GHC.IO.Encoding.CodePage,
+            GHC.IO.Encoding.Failure,
+            GHC.IO.Encoding.Iconv,
+            GHC.IO.Encoding.Latin1,
+            GHC.IO.Encoding.Types,
+            GHC.IO.Encoding.UTF16,
+            GHC.IO.Encoding.UTF32,
+            GHC.IO.Encoding.UTF8,
+            GHC.IO.Exception,
+            GHC.IO.FD,
+            GHC.IO.Handle,
+            GHC.IO.Handle.FD,
+            GHC.IO.Handle.Internals,
+            GHC.IO.Handle.Text,
+            GHC.IO.Handle.Types,
+            GHC.IO.IOMode,
+            GHC.IOArray,
+            GHC.IOBase,
+            GHC.IORef,
+            GHC.Int,
+            GHC.List,
+            GHC.MVar,
+            GHC.Num,
+            GHC.PArr,
+            GHC.Pack,
+            GHC.Ptr,
+            GHC.Read,
+            GHC.Real,
+            GHC.ST,
+            GHC.Stack,
+            GHC.Stats,
+            GHC.Show,
+            GHC.Stable,
+            GHC.Storable,
+            GHC.STRef,
+            GHC.TopHandler,
+            GHC.Unicode,
+            GHC.Weak,
+            GHC.Word,
+            System.Timeout
+        if os(windows)
+            exposed-modules: GHC.IO.Encoding.CodePage.Table
+                             GHC.Conc.Windows
+                             GHC.Windows
+    }
+    exposed-modules:
+        Control.Applicative,
+        Control.Arrow,
+        Control.Category,
+        Control.Concurrent,
+        Control.Concurrent.Chan,
+        Control.Concurrent.MVar,
+        Control.Concurrent.QSem,
+        Control.Concurrent.QSemN,
+        Control.Concurrent.SampleVar,
+        Control.Exception,
+        Control.Exception.Base
+        Control.OldException,
+        Control.Monad,
+        Control.Monad.Fix,
+        Control.Monad.Instances,
+        Control.Monad.ST,
+        Control.Monad.ST.Safe,
+        Control.Monad.ST.Unsafe,
+        Control.Monad.ST.Lazy,
+        Control.Monad.ST.Lazy.Safe,
+        Control.Monad.ST.Lazy.Unsafe,
+        Control.Monad.ST.Strict,
+        Control.Monad.Zip
+        Data.Bits,
+        Data.Bool,
+        Data.Char,
+        Data.Complex,
+        Data.Dynamic,
+        Data.Either,
+        Data.Eq,
+        Data.Data,
+        Data.Fixed,
+        Data.Foldable
+        Data.Function,
+        Data.Functor,
+        Data.HashTable,
+        Data.IORef,
+        Data.Int,
+        Data.Ix,
+        Data.List,
+        Data.Maybe,
+        Data.Monoid,
+        Data.Ord,
+        Data.Ratio,
+        Data.STRef
+        Data.STRef.Lazy
+        Data.STRef.Strict
+        Data.String,
+        Data.Traversable
+        Data.Tuple,
+        Data.Typeable,
+        Data.Typeable.Internal,
+        Data.Unique,
+        Data.Version,
+        Data.Word,
+        Debug.Trace,
+        Foreign,
+        Foreign.C,
+        Foreign.C.Error,
+        Foreign.C.String,
+        Foreign.C.Types,
+        Foreign.ForeignPtr,
+        Foreign.ForeignPtr.Safe,
+        Foreign.ForeignPtr.Unsafe,
+        Foreign.Marshal,
+        Foreign.Marshal.Alloc,
+        Foreign.Marshal.Array,
+        Foreign.Marshal.Error,
+        Foreign.Marshal.Pool,
+        Foreign.Marshal.Safe,
+        Foreign.Marshal.Utils,
+        Foreign.Marshal.Unsafe,
+        Foreign.Ptr,
+        Foreign.Safe,
+        Foreign.StablePtr,
+        Foreign.Storable,
+        Numeric,
+        Prelude,
+        System.Console.GetOpt
+        System.CPUTime,
+        System.Environment,
+        System.Exit,
+        System.IO,
+        System.IO.Error,
+        System.IO.Unsafe,
+        System.Info,
+        System.Mem,
+        System.Mem.StableName,
+        System.Mem.Weak,
+        System.Posix.Internals,
+        System.Posix.Types,
+        Text.ParserCombinators.ReadP,
+        Text.ParserCombinators.ReadPrec,
+        Text.Printf,
+        Text.Read,
+        Text.Read.Lex,
+        Text.Show,
+        Text.Show.Functions
+        Unsafe.Coerce
+    other-modules:
+        Control.Monad.ST.Imp
+        Control.Monad.ST.Lazy.Imp
+        Foreign.ForeignPtr.Imp
+    c-sources:
+        cbits/PrelIOUtils.c
+        cbits/WCsubst.c
+        cbits/Win32Utils.c
+        cbits/consUtils.c
+        cbits/iconv.c
+        cbits/inputReady.c
+        cbits/selectUtils.c
+        cbits/primFloat.c
+        cbits/md5.c
+    include-dirs: include
+    includes:    HsBase.h
+    install-includes:    HsBase.h HsBaseConfig.h EventConfig.h WCsubst.h consUtils.h Typeable.h
+    if os(windows) {
+        extra-libraries: wsock32, user32, shell32
+    }
+    if !os(windows) {
+        exposed-modules:
+            GHC.Event
+        other-modules:
+            GHC.Event.Array
+            GHC.Event.Clock
+            GHC.Event.Control
+            GHC.Event.EPoll
+            GHC.Event.IntMap
+            GHC.Event.Internal
+            GHC.Event.KQueue
+            GHC.Event.Manager
+            GHC.Event.PSQ
+            GHC.Event.Poll
+            GHC.Event.Thread
+            GHC.Event.Unique
+    }
+    -- We need to set the package name to base (without a version number)
+    -- as it's magic.
+    ghc-options: -package-name base
+    nhc98-options: -H4M -K3M
+    extensions: CPP
+}
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/binary.cabal b/TestSuite/inputs/Puns/cabals/parse_error/binary.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/binary.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/bytestring.cabal b/TestSuite/inputs/Puns/cabals/parse_error/bytestring.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/bytestring.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/containers.cabal b/TestSuite/inputs/Puns/cabals/parse_error/containers.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/containers.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/deepseq.cabal b/TestSuite/inputs/Puns/cabals/parse_error/deepseq.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/deepseq.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/ghc-prim.cabal b/TestSuite/inputs/Puns/cabals/parse_error/ghc-prim.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/ghc-prim.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/integer-gmp.cabal b/TestSuite/inputs/Puns/cabals/parse_error/integer-gmp.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/integer-gmp.cabal
diff --git a/TestSuite/inputs/Puns/cabals/parse_error/mtl.cabal b/TestSuite/inputs/Puns/cabals/parse_error/mtl.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/parse_error/mtl.cabal
diff --git a/TestSuite/inputs/Puns/cabals/transformers.cabal b/TestSuite/inputs/Puns/cabals/transformers.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/Puns/cabals/transformers.cabal
@@ -0,0 +1,68 @@
+name:         transformers
+version:      0.3.0.0
+license:      BSD3
+license-file: LICENSE
+author:       Andy Gill, Ross Paterson
+maintainer:   Ross Paterson <ross@soi.city.ac.uk>
+category:     Control
+synopsis:     Concrete functor and monad transformers
+description:
+    A portable library of functor and monad transformers, inspired by
+    the paper \"Functional Programming with Overloading and Higher-Order
+    Polymorphism\", by Mark P Jones,
+    in /Advanced School of Functional Programming/, 1995
+    (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>).
+    .
+    This package contains:
+    .
+    * the monad transformer class (in "Control.Monad.Trans.Class")
+    .
+    * concrete functor and monad transformers, each with associated
+      operations and functions to lift operations associated with other
+      transformers.
+    .
+    It can be used on its own in portable Haskell code, or with the monad
+    classes in the @mtl@ or @monads-tf@ packages, which automatically
+    lift operations introduced by monad transformers through other
+    transformers.
+build-type: Simple
+cabal-version: >= 1.6
+
+source-repository head
+  type: darcs
+  location: http://code.haskell.org/~ross/transformers
+
+flag ApplicativeInBase
+  description: Use the current base package, including Applicative and
+    other Functor classes.
+
+library
+  if flag(ApplicativeInBase)
+    build-depends: base >= 2 && < 6
+  else
+    build-depends: base >= 1.0 && < 2, special-functors >= 1.0 && < 1.1
+  exposed-modules:
+    Control.Applicative.Backwards
+    Control.Applicative.Lift
+    Control.Monad.IO.Class
+    Control.Monad.Trans.Class
+    Control.Monad.Trans.Cont
+    Control.Monad.Trans.Error
+    Control.Monad.Trans.Identity
+    Control.Monad.Trans.List
+    Control.Monad.Trans.Maybe
+    Control.Monad.Trans.Reader
+    Control.Monad.Trans.RWS
+    Control.Monad.Trans.RWS.Lazy
+    Control.Monad.Trans.RWS.Strict
+    Control.Monad.Trans.State
+    Control.Monad.Trans.State.Lazy
+    Control.Monad.Trans.State.Strict
+    Control.Monad.Trans.Writer
+    Control.Monad.Trans.Writer.Lazy
+    Control.Monad.Trans.Writer.Strict
+    Data.Functor.Compose
+    Data.Functor.Constant
+    Data.Functor.Identity
+    Data.Functor.Product
+    Data.Functor.Reverse
diff --git a/TestSuite/inputs/README.md b/TestSuite/inputs/README.md
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/README.md
@@ -0,0 +1,4 @@
+This directory contains test inputs for the test suite.
+
+NOTE: There are some hacks in the TestSuite that rely on the structure of this
+directory (for instance, see loadModule). 
diff --git a/TestSuite/inputs/TH/BlockingOps.hs b/TestSuite/inputs/TH/BlockingOps.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/TH/BlockingOps.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE TemplateHaskell, NamedFieldPuns #-}
+-- | Blocking operations such as
+--
+-- > readMVar v
+--
+-- may throw an exception such as
+--
+-- > thread blocked indefinitely in an MVar operation
+--
+-- Unfortunately, this exception does not give any information of _where_ in
+-- the code we are blocked indefinitely. Compiling with profiling info and
+-- running with +RTC -xc can address this to some extent, but (1) it requires
+-- that all profiling libraries are installed and (2) when we are running
+-- multithreaded code the resulting stack trace is often difficult to read
+-- (and still does not include line numbers). With this module you can replace
+-- the above code with
+--
+-- > $readMVar v
+--
+-- and the exception that will be thrown is
+--
+-- > YourModule:lineNumber: thread blocked indefinitely in an MVar operation
+--
+-- which is a lot more informative. When the CPP flag DEBUGGING is turned off
+-- then @$readMVar@ just turns into @readMVar@.
+--
+-- NOTE: The type of the exception changes when using DEBUGGING mode -- in order
+-- to be able to add the line number, all exceptions are turned into
+-- IOExceptions.
+module TH.BlockingOps (
+    -- * Generic debugging utilities
+    lineNumber
+  , traceOnException
+  , mapExceptionIO
+  , mapExceptionShow
+    -- * Blocking MVar ops
+  , putMVar
+  , takeMVar
+  , modifyMVar
+  , modifyMVar_
+  , withMVar
+  , readMVar
+    -- * Blocking Chan ops
+  , readChan
+  ) where
+
+import Language.Haskell.TH
+import qualified Control.Concurrent as C
+import System.IO (hPutStrLn, stderr)
+import qualified Control.Exception as Ex
+
+lineNumber :: ExpQ
+lineNumber = do
+  Loc{loc_module, loc_start=(line, _)} <- location
+  [| loc_module ++ ":" ++ show (line :: Int) |]
+
+mapExceptionIO :: (Ex.Exception e1, Ex.Exception e2)
+               => (e1 -> e2) -> IO a -> IO a
+mapExceptionIO f io = Ex.catch io (Ex.throwIO . f)
+
+mapExceptionShow :: (String -> String) -> IO a -> IO a
+mapExceptionShow f = mapExceptionIO (userError . f . showSomeException)
+  where
+    showSomeException :: Ex.SomeException -> String
+    showSomeException = show
+
+traceOnException :: String -> IO a -> IO a
+traceOnException str io = Ex.catch io $ \e -> do
+  hPutStrLn stderr (str ++ ": " ++ show e)
+  Ex.throwIO (e :: Ex.SomeException)
+
+rethrowWithLineNumber1 :: ExpQ -> ExpQ
+rethrowWithLineNumber1 expr =
+  [| \arg1 -> mapExceptionShow (\e -> $lineNumber ++ ": " ++ e)
+                               ($expr arg1)
+   |]
+
+rethrowWithLineNumber2 :: ExpQ -> ExpQ
+rethrowWithLineNumber2 expr =
+  [| \arg1 arg2 -> mapExceptionShow (\e -> $lineNumber ++ ": " ++ e)
+                                    ($expr arg1 arg2)
+   |]
+
+takeMVar :: ExpQ
+takeMVar = rethrowWithLineNumber1 [| C.takeMVar |]
+
+putMVar :: ExpQ
+putMVar = rethrowWithLineNumber2 [| C.putMVar |]
+
+readMVar :: ExpQ
+readMVar = rethrowWithLineNumber1 [| C.readMVar |]
+
+modifyMVar :: ExpQ
+modifyMVar = rethrowWithLineNumber2 [| C.modifyMVar |]
+
+modifyMVar_ :: ExpQ
+modifyMVar_ = rethrowWithLineNumber2 [| C.modifyMVar_ |]
+
+withMVar :: ExpQ
+withMVar = rethrowWithLineNumber2 [| C.withMVar |]
+
+readChan :: ExpQ
+readChan = rethrowWithLineNumber1 [| C.readChan |]
diff --git a/TestSuite/inputs/TH/TH.hs b/TestSuite/inputs/TH/TH.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/TH/TH.hs
@@ -0,0 +1,17 @@
+module TH.TH where
+
+import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar)
+import TH.BlockingOps (modifyMVar, modifyMVar_, putMVar, readMVar, takeMVar)
+
+main = do
+  mv <- newEmptyMVar
+  mv2 <- newEmptyMVar
+  $putMVar mv mv2
+  $putMVar mv2 42
+  mv3 <- $takeMVar mv
+  $putMVar mv mv3
+  $modifyMVar_ mv $ \mv3 -> $modifyMVar_ mv3 (\i -> return $ i + 1)
+                            >> return mv2
+  mv4 <- $takeMVar mv
+  i <- $readMVar mv3
+  print $ (mv2 == mv4, i)
diff --git a/TestSuite/inputs/bootMods/A.hs b/TestSuite/inputs/bootMods/A.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/bootMods/A.hs
@@ -0,0 +1,8 @@
+module A where
+
+import B( TB(..) )
+
+newtype TA = MkTA Int
+    
+f :: TB -> TA
+f (MkTB x) = MkTA x
diff --git a/TestSuite/inputs/bootMods/A.hs-boot b/TestSuite/inputs/bootMods/A.hs-boot
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/bootMods/A.hs-boot
@@ -0,0 +1,2 @@
+module A where
+newtype TA = MkTA Int
diff --git a/TestSuite/inputs/bootMods/B.hs b/TestSuite/inputs/bootMods/B.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/bootMods/B.hs
@@ -0,0 +1,8 @@
+module B where
+import {-# SOURCE #-} A( TA(..) )
+    
+data TB = MkTB !Int
+
+g :: TA -> TB
+g (MkTA x) = MkTB x
+
diff --git a/TestSuite/inputs/bootMods/C.hs b/TestSuite/inputs/bootMods/C.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/bootMods/C.hs
@@ -0,0 +1,6 @@
+module Main where
+import B
+import A -- FIX: GHC doesn't seem to figure out this dependency?!
+
+main :: IO ()
+main = let f = g in putStrLn "C"
diff --git a/TestSuite/inputs/compiler/utils/Exception.hs b/TestSuite/inputs/compiler/utils/Exception.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/compiler/utils/Exception.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fno-warn-deprecations #-}
+module Exception
+    (
+    module Control.Exception,
+    module Exception
+    )
+    where
+
+import Prelude hiding (catch)
+
+import Control.Exception
+
+catchIO :: IO a -> (IOException -> IO a) -> IO a
+catchIO = catch
+
+handleIO :: (IOException -> IO a) -> IO a -> IO a
+handleIO = flip catchIO
+
+tryIO :: IO a -> IO (Either IOException a)
+tryIO = try
+
+-- | A monad that can catch exceptions.  A minimal definition
+-- requires a definition of 'gcatch'.
+--
+-- Implementations on top of 'IO' should implement 'gblock' and 'gunblock' to
+-- eventually call the primitives 'Control.Exception.block' and
+-- 'Control.Exception.unblock' respectively.  These are used for
+-- implementations that support asynchronous exceptions.  The default
+-- implementations of 'gbracket' and 'gfinally' use 'gblock' and 'gunblock'
+-- thus rarely require overriding.
+--
+class Monad m => ExceptionMonad m where
+
+  -- | Generalised version of 'Control.Exception.catch', allowing an arbitrary
+  -- exception handling monad instead of just 'IO'.
+  gcatch :: Exception e => m a -> (e -> m a) -> m a
+
+  -- | Generalised version of 'Control.Exception.mask_', allowing an arbitrary
+  -- exception handling monad instead of just 'IO'.
+  gmask :: ((m a -> m a) -> m b) -> m b
+
+  -- | Generalised version of 'Control.Exception.bracket', allowing an arbitrary
+  -- exception handling monad instead of just 'IO'.
+  gbracket :: m a -> (a -> m b) -> (a -> m c) -> m c
+
+  -- | Generalised version of 'Control.Exception.finally', allowing an arbitrary
+  -- exception handling monad instead of just 'IO'.
+  gfinally :: m a -> m b -> m a
+
+  -- | DEPRECATED, here for backwards compatibilty.  Instances can
+  -- define either 'gmask', or both 'block' and 'unblock'.
+  gblock   :: m a -> m a
+  -- | DEPRECATED, here for backwards compatibilty  Instances can
+  -- define either 'gmask', or both 'block' and 'unblock'.
+  gunblock :: m a -> m a
+  -- XXX we're keeping these two methods for the time being because we
+  -- have to interact with Haskeline's MonadException class which
+  -- still has block/unblock; see GhciMonad.hs.
+
+  gmask    f = gblock (f gunblock)
+  gblock   f = gmask (\_ -> f)
+  gunblock f = f -- XXX wrong; better override this if you need it
+
+  gbracket before after thing =
+    gmask $ \restore -> do
+      a <- before
+      r <- restore (thing a) `gonException` after a
+      _ <- after a
+      return r
+
+  a `gfinally` sequel =
+    gmask $ \restore -> do
+      r <- restore a `gonException` sequel
+      _ <- sequel
+      return r
+
+#if __GLASGOW_HASKELL__ < 613
+instance ExceptionMonad IO where
+  gcatch    = catch
+  gmask f   = block $ f unblock
+  gblock    = block
+  gunblock  = unblock
+#else
+instance ExceptionMonad IO where
+  gcatch    = catch
+  gmask f   = mask (\x -> f x)
+  gblock    = undefined
+  gunblock  = undefined
+#endif
+
+gtry :: (ExceptionMonad m, Exception e) => m a -> m (Either e a)
+gtry act = gcatch (act >>= \a -> return (Right a))
+                  (\e -> return (Left e))
+
+-- | Generalised version of 'Control.Exception.handle', allowing an arbitrary
+-- exception handling monad instead of just 'IO'.
+ghandle :: (ExceptionMonad m, Exception e) => (e -> m a) -> m a -> m a
+ghandle = flip gcatch
+
+-- | Always executes the first argument.  If this throws an exception the
+-- second argument is executed and the exception is raised again.
+gonException :: (ExceptionMonad m) => m a -> m b -> m a
+gonException ioA cleanup = ioA `gcatch` \e ->
+                             do _ <- cleanup
+                                throw (e :: SomeException)
+
+main :: IO ()
+main = return ()
diff --git a/TestSuite/inputs/compiler/utils/Maybes.lhs b/TestSuite/inputs/compiler/utils/Maybes.lhs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/compiler/utils/Maybes.lhs
@@ -0,0 +1,158 @@
+%
+% (c) The University of Glasgow 2006
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
+%
+
+\begin{code}
+{-# OPTIONS -fno-warn-tabs #-}
+{-# LANGUAGE CPP #-}
+#if __GLASGOW_HASKELL__ == 708
+#endif
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and
+-- detab the module (please do the detabbing in a separate patch). See
+--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#TabsvsSpaces
+-- for details
+
+module Maybes (
+        module Data.Maybe,
+
+        MaybeErr(..), -- Instance of Monad
+        failME, isSuccess,
+
+        fmapM_maybe,
+        orElse,
+        mapCatMaybes,
+        allMaybes,
+        firstJust, firstJusts,
+        expectJust,
+        maybeToBool,
+
+        MaybeT(..),
+
+        main
+    ) where
+
+import Control.Applicative
+import Control.Monad
+import Data.Maybe
+
+infixr 4 `orElse`
+\end{code}
+
+%************************************************************************
+%*                                                                      *
+\subsection[Maybe type]{The @Maybe@ type}
+%*                                                                      *
+%************************************************************************
+
+\begin{code}
+maybeToBool :: Maybe a -> Bool
+maybeToBool Nothing  = False
+maybeToBool (Just _) = True
+
+-- | Collects a list of @Justs@ into a single @Just@, returning @Nothing@ if
+-- there are any @Nothings@.
+allMaybes :: [Maybe a] -> Maybe [a]
+allMaybes [] = Just []
+allMaybes (Nothing : _)  = Nothing
+allMaybes (Just x  : ms) = case allMaybes ms of
+                           Nothing -> Nothing
+                           Just xs -> Just (x:xs)
+
+firstJust :: Maybe a -> Maybe a -> Maybe a
+firstJust (Just a) _ = Just a
+firstJust Nothing  b = b
+
+-- | Takes a list of @Maybes@ and returns the first @Just@ if there is one, or
+-- @Nothing@ otherwise.
+firstJusts :: [Maybe a] -> Maybe a
+firstJusts = foldr firstJust Nothing
+\end{code}
+
+\begin{code}
+expectJust :: String -> Maybe a -> a
+{-# INLINE expectJust #-}
+expectJust _   (Just x) = x
+expectJust err Nothing  = error ("expectJust " ++ err)
+\end{code}
+
+\begin{code}
+mapCatMaybes :: (a -> Maybe b) -> [a] -> [b]
+mapCatMaybes _ [] = []
+mapCatMaybes f (x:xs) = case f x of
+                        Just y  -> y : mapCatMaybes f xs
+                        Nothing -> mapCatMaybes f xs
+\end{code}
+
+\begin{code}
+-- | flipped version of @fromMaybe@.
+orElse :: Maybe a -> a -> a
+(Just x) `orElse` _ = x
+Nothing  `orElse` y = y
+\end{code}
+
+\begin{code}
+fmapM_maybe :: Monad m => (a -> m b) -> Maybe a -> m (Maybe b)
+fmapM_maybe _ Nothing = return Nothing
+fmapM_maybe f (Just x) = do
+        x' <- f x
+        return $ Just x'
+\end{code}
+
+%************************************************************************
+%*									*
+\subsection[MaybeT type]{The @MaybeT@ monad transformer}
+%*									*
+%************************************************************************
+
+\begin{code}
+
+newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
+
+instance Functor m => Functor (MaybeT m) where
+  fmap f x = MaybeT $ fmap (fmap f) $ runMaybeT x
+
+instance (Monad m, Applicative m) => Applicative (MaybeT m) where
+  pure  = return
+  (<*>) = ap
+
+instance Monad m => Monad (MaybeT m) where
+  return = MaybeT . return . Just
+  x >>= f = MaybeT $ runMaybeT x >>= maybe (return Nothing) (runMaybeT . f)
+  fail _ = MaybeT $ return Nothing
+
+\end{code}
+
+
+%************************************************************************
+%*                                                                      *
+\subsection[MaybeErr type]{The @MaybeErr@ type}
+%*                                                                      *
+%************************************************************************
+
+\begin{code}
+data MaybeErr err val = Succeeded val | Failed err
+
+instance Functor (MaybeErr err) where
+  fmap = liftM
+
+instance Applicative (MaybeErr err) where
+  pure  = return
+  (<*>) = ap
+
+instance Monad (MaybeErr err) where
+  return v = Succeeded v
+  Succeeded v >>= k = k v
+  Failed e    >>= _ = Failed e
+
+isSuccess :: MaybeErr err val -> Bool
+isSuccess (Succeeded {}) = True
+isSuccess (Failed {})    = False
+
+failME :: err -> MaybeErr err val
+failME e = Failed e
+
+main :: IO ()
+main = print $ maybeToBool Nothing
+\end{code}
diff --git a/TestSuite/inputs/compiler/utils/OrdList.lhs b/TestSuite/inputs/compiler/utils/OrdList.lhs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/compiler/utils/OrdList.lhs
@@ -0,0 +1,93 @@
+%
+% (c) The University of Glasgow 2006
+% (c) The AQUA Project, Glasgow University, 1993-1998
+%
+
+This is useful, general stuff for the Native Code Generator.
+
+Provide trees (of instructions), so that lists of instructions
+can be appended in linear time.
+
+\begin{code}
+{-# OPTIONS -fno-warn-tabs #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and
+-- detab the module (please do the detabbing in a separate patch). See
+--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#TabsvsSpaces
+-- for details
+
+module OrdList (
+	OrdList, 
+        nilOL, isNilOL, unitOL, appOL, consOL, snocOL, concatOL,
+        mapOL, fromOL, toOL, foldrOL, foldlOL
+) where
+
+infixl 5  `appOL`
+infixl 5  `snocOL`
+infixr 5  `consOL`
+
+data OrdList a
+  = Many [a]        -- Invariant: non-empty
+  | Two (OrdList a) -- Invariant: non-empty
+        (OrdList a) -- Invariant: non-empty
+  | One  a
+  | None
+
+nilOL    :: OrdList a
+isNilOL  :: OrdList a -> Bool
+
+unitOL   :: a           -> OrdList a
+snocOL   :: OrdList a   -> a         -> OrdList a
+consOL   :: a           -> OrdList a -> OrdList a
+appOL    :: OrdList a   -> OrdList a -> OrdList a
+concatOL :: [OrdList a] -> OrdList a
+
+nilOL        = None
+unitOL as    = One as
+snocOL None b    = One b
+snocOL as   b    = Two as (One b)
+consOL a    None = One a
+consOL a    bs   = Two (One a) bs
+concatOL aas = foldr appOL None aas
+
+isNilOL None = True
+isNilOL _    = False
+
+appOL None bs   = bs
+appOL as   None = as
+appOL as   bs   = Two as bs
+
+mapOL :: (a -> b) -> OrdList a -> OrdList b
+mapOL _ None = None
+mapOL f (One x) = One (f x)
+mapOL f (Two x y) = Two (mapOL f x) (mapOL f y)
+mapOL f (Many xs) = Many (map f xs)
+
+instance Functor OrdList where
+  fmap = mapOL
+
+foldrOL :: (a->b->b) -> b -> OrdList a -> b
+foldrOL _ z None        = z
+foldrOL k z (One x)     = k x z
+foldrOL k z (Two b1 b2) = foldrOL k (foldrOL k z b2) b1
+foldrOL k z (Many xs)   = foldr k z xs
+
+foldlOL :: (b->a->b) -> b -> OrdList a -> b
+foldlOL _ z None        = z
+foldlOL k z (One x)     = k z x
+foldlOL k z (Two b1 b2) = foldlOL k (foldlOL k z b1) b2
+foldlOL k z (Many xs)   = foldl k z xs
+
+fromOL :: OrdList a -> [a]
+fromOL ol 
+   = flat ol []
+     where
+        flat None      rest = rest
+        flat (One x)   rest = x:rest
+        flat (Two a b) rest = flat a (flat b rest)
+        flat (Many xs) rest = xs ++ rest
+
+toOL :: [a] -> OrdList a
+toOL [] = None
+toOL xs = Many xs
+\end{code}
diff --git a/TestSuite/inputs/compiler/utils/Subdir/Main.lhs b/TestSuite/inputs/compiler/utils/Subdir/Main.lhs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/compiler/utils/Subdir/Main.lhs
@@ -0,0 +1,90 @@
+%
+% (c) The University of Glasgow 2006
+% (c) The AQUA Project, Glasgow University, 1993-1998
+%
+
+This is useful, general stuff for the Native Code Generator.
+
+Provide trees (of instructions), so that lists of instructions
+can be appended in linear time.
+
+\begin{code}
+{-# OPTIONS -fno-warn-tabs #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and
+-- detab the module (please do the detabbing in a separate patch). See
+--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#TabsvsSpaces
+-- for details
+
+infixl 5  `appOL`
+infixl 5  `snocOL`
+infixr 5  `consOL`
+
+data OrdList a
+  = Many [a]        -- Invariant: non-empty
+  | Two (OrdList a) -- Invariant: non-empty
+        (OrdList a) -- Invariant: non-empty
+  | One  a
+  | None
+
+nilOL    :: OrdList a
+isNilOL  :: OrdList a -> Bool
+
+unitOL   :: a           -> OrdList a
+snocOL   :: OrdList a   -> a         -> OrdList a
+consOL   :: a           -> OrdList a -> OrdList a
+appOL    :: OrdList a   -> OrdList a -> OrdList a
+concatOL :: [OrdList a] -> OrdList a
+
+nilOL        = None
+unitOL as    = One as
+snocOL None b    = One b
+snocOL as   b    = Two as (One b)
+consOL a    None = One a
+consOL a    bs   = Two (One a) bs
+concatOL aas = foldr appOL None aas
+
+isNilOL None = True
+isNilOL _    = False
+
+appOL None bs   = bs
+appOL as   None = as
+appOL as   bs   = Two as bs
+
+mapOL :: (a -> b) -> OrdList a -> OrdList b
+mapOL _ None = None
+mapOL f (One x) = One (f x)
+mapOL f (Two x y) = Two (mapOL f x) (mapOL f y)
+mapOL f (Many xs) = Many (map f xs)
+
+instance Functor OrdList where
+  fmap = mapOL
+
+foldrOL :: (a->b->b) -> b -> OrdList a -> b
+foldrOL _ z None        = z
+foldrOL k z (One x)     = k x z
+foldrOL k z (Two b1 b2) = foldrOL k (foldrOL k z b2) b1
+foldrOL k z (Many xs)   = foldr k z xs
+
+foldlOL :: (b->a->b) -> b -> OrdList a -> b
+foldlOL _ z None        = z
+foldlOL k z (One x)     = k z x
+foldlOL k z (Two b1 b2) = foldlOL k (foldlOL k z b1) b2
+foldlOL k z (Many xs)   = foldl k z xs
+
+fromOL :: OrdList a -> [a]
+fromOL ol 
+   = flat ol []
+     where
+        flat None      rest = rest
+        flat (One x)   rest = x:rest
+        flat (Two a b) rest = flat a (flat b rest)
+        flat (Many xs) rest = xs ++ rest
+
+toOL :: [a] -> OrdList a
+toOL [] = None
+toOL xs = Many xs
+
+main :: IO ()
+main = return ()
+\end{code}
diff --git a/TestSuite/inputs/simple-lib17/LICENSE b/TestSuite/inputs/simple-lib17/LICENSE
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/simple-lib17/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, 
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of  nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/TestSuite/inputs/simple-lib17/Setup.hs b/TestSuite/inputs/simple-lib17/Setup.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/simple-lib17/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/TestSuite/inputs/simple-lib17/SimpleLib.hs b/TestSuite/inputs/simple-lib17/SimpleLib.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/simple-lib17/SimpleLib.hs
@@ -0,0 +1,4 @@
+module SimpleLib where
+
+simpleLib :: Int
+simpleLib = 42
diff --git a/TestSuite/inputs/simple-lib17/build.sh b/TestSuite/inputs/simple-lib17/build.sh
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/simple-lib17/build.sh
@@ -0,0 +1,2 @@
+unset GHC_PACKAGE_PATH
+cabal install --package-db=clear --package-db=global --package-db=../packages
diff --git a/TestSuite/inputs/simple-lib17/simple-lib17.cabal b/TestSuite/inputs/simple-lib17/simple-lib17.cabal
new file mode 100644
--- /dev/null
+++ b/TestSuite/inputs/simple-lib17/simple-lib17.cabal
@@ -0,0 +1,19 @@
+-- Initial simple-lib.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                simple-lib17
+version:             0.1.0.0
+-- synopsis:            
+-- description:         
+license:             BSD3
+license-file:        LICENSE
+-- author:              
+-- maintainer:          
+-- copyright:           
+-- category:            
+build-type:          Simple
+cabal-version:       >=1.8
+
+library
+  exposed-modules:     SimpleLib
+  build-depends:       base
diff --git a/ide-backend.cabal b/ide-backend.cabal
--- a/ide-backend.cabal
+++ b/ide-backend.cabal
@@ -1,5 +1,5 @@
 name:                 ide-backend
-version:              0.9.0.11
+version:              0.10.0
 synopsis:             An IDE backend library
 description:          See README.md for more details
 license:              MIT
@@ -12,10 +12,39 @@
 build-type:           Simple
 cabal-version:        >=1.10
 extra-source-files:   README.md
+                      TestSuite/inputs/README.md
+                      TestSuite/inputs/ABerror/*.hs
+                      TestSuite/inputs/AnotherA/*.hs
+                      TestSuite/inputs/TH/*.hs
+                      TestSuite/inputs/simple-lib17/*.hs
+                      TestSuite/inputs/simple-lib17/*.sh
+                      TestSuite/inputs/simple-lib17/*.cabal
+                      TestSuite/inputs/simple-lib17/LICENSE
+                      TestSuite/inputs/MainModule/cabals/*.cabal
+                      TestSuite/inputs/MainModule/ParFib/*.hs
+                      TestSuite/inputs/MainModule/*.hs
+                      TestSuite/inputs/compiler/utils/Subdir/*.lhs
+                      TestSuite/inputs/compiler/utils/*.hs
+                      TestSuite/inputs/compiler/utils/*.lhs
+                      TestSuite/inputs/AnotherB/*.hs
+                      TestSuite/inputs/bootMods/*.hs
+                      TestSuite/inputs/bootMods/*.hs-boot
+                      TestSuite/inputs/ABnoError/*.hs
+                      TestSuite/inputs/AerrorB/*.hs
+                      TestSuite/inputs/FFI/ffiles/*.c
+                      TestSuite/inputs/FFI/ffiles/*.h
+                      TestSuite/inputs/FFI/*.hs
+                      TestSuite/inputs/FFI/*.h
+                      TestSuite/inputs/FFI/*.c
+                      TestSuite/inputs/Puns/cabals/parse_error/*.cabal
+                      TestSuite/inputs/Puns/cabals/no_text_error/*.cabal
+                      TestSuite/inputs/Puns/cabals/*.cabal
+                      TestSuite/inputs/Puns/GHC/RTS/*.h
+                      TestSuite/inputs/Puns/GHC/RTS/*.hs
 
 library
   exposed-modules:    IdeSession
-  other-modules:      IdeSession.State,
+                      IdeSession.State,
                       IdeSession.Config,
                       IdeSession.Cabal,
                       IdeSession.ExeCabalClient,
@@ -47,7 +76,7 @@
                       time                 >= 1.4     && < 1.6,
                       attoparsec           >= 0.10    && < 0.14,
                       utf8-string          >= 0.3     && < 1.1,
-                      ide-backend-common   >= 0.9.1   && < 0.10,
+                      ide-backend-common   >= 0.10    && < 0.11,
                       template-haskell,
                       -- our own private fork:
                       Cabal-ide-backend    >= 1.23,
@@ -174,7 +203,10 @@
                       random,
                       Cabal-ide-backend,
                       containers,
-                      deepseq
+                      deepseq,
+                      -- Dependencies of test suite at runtime
+                      parallel,
+                      monads-tf
   default-language:   Haskell2010
   ghc-options:        -Wall
                       -threaded
@@ -191,7 +223,7 @@
 test-suite rpc-server
   type:               exitcode-stdio-1.0
   main-is:            rpc-server.hs
-  hs-source-dirs:     . test
+  hs-source-dirs:     test
   build-depends:      base                 ==4.*,
                       directory            ==1.*,
                       filemanip            >= 0.3.6.2 && < 0.4,
@@ -209,6 +241,7 @@
                       executable-path      >= 0.0     && < 0.1,
                       unix                 >= 2.5     && < 2.8,
                       binary               >= 0.7.1.0 && < 0.8,
+                      ide-backend,
                       ide-backend-common,
                       template-haskell
 
