diff --git a/CabalHelper/Common.hs b/CabalHelper/Common.hs
deleted file mode 100644
--- a/CabalHelper/Common.hs
+++ /dev/null
@@ -1,106 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
-module CabalHelper.Common where
-
-import Control.Applicative
-import Control.Exception as E
-import Control.Monad
-import Data.List
-import Data.Maybe
-import Data.Version
-import Data.Typeable
-import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Char8 as BS8
-import System.Environment
-import System.IO
-import System.Exit
-import System.Directory
-import System.FilePath
-import Text.ParserCombinators.ReadP
-import Prelude
-
-data Panic = Panic String deriving (Typeable, Show)
-instance Exception Panic
-
-panic :: String -> a
-panic msg = throw $ Panic msg
-
-handlePanic :: IO a -> IO a
-handlePanic action =
-    action `E.catch` \(Panic msg) -> errMsg msg >> exitFailure
-
-errMsg :: String -> IO ()
-errMsg str = do
-  prog <- getProgName
-  hPutStrLn stderr $ prog ++ ": " ++ str
-
--- | @getCabalConfigHeader "dist/setup-config"@ returns the cabal version and
--- compiler version
-getCabalConfigHeader :: FilePath -> IO (Maybe (Version, (ByteString, Version)))
-getCabalConfigHeader file = bracket (openFile file ReadMode) hClose $ \h -> do
-  parseHeader <$> BS.hGetLine h
-
-parseHeader :: ByteString -> Maybe (Version, (ByteString, Version))
-parseHeader header = case BS8.words header of
-  ["Saved", "package", "config", "for", _pkgId ,
-   "written", "by", cabalId,
-   "using", compId]
-    -> liftM2 (,) (snd <$> parsePkgId cabalId) (parsePkgId compId)
-  _ -> Nothing
-
-parsePkgId :: ByteString -> Maybe (ByteString, Version)
-parsePkgId bs =
-    case BS8.split '-' bs of
-      [pkg, vers] -> Just (pkg, parseVer $ BS8.unpack vers)
-      _ -> Nothing
-
-parseVer :: String -> Version
-parseVer vers = runReadP parseVersion vers
-
-majorVer :: Version -> Version
-majorVer (Version b _) = Version (take 2 b) []
-
-sameMajorVersionAs :: Version -> Version -> Bool
-sameMajorVersionAs a b = majorVer a == majorVer b
-
-runReadP :: ReadP t -> String -> t
-runReadP p i = case filter ((=="") . snd) $ readP_to_S p i of
-                 (a,""):[] -> a
-                 _ -> error $ "Error parsing: " ++ show i
-
-appDataDir :: IO FilePath
-appDataDir = (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod"
-
-isCabalFile :: FilePath -> Bool
-isCabalFile f = takeExtension' f == ".cabal"
-
-takeExtension' :: FilePath -> String
-takeExtension' p =
-    if takeFileName p == takeExtension p
-      then "" -- just ".cabal" is not a valid cabal file
-      else takeExtension p
-
-replace :: String -> String -> String -> String
-replace n r hs' = go "" hs'
- where
-   go acc h
-       | take (length n) h == n =
-           reverse acc ++ r ++ drop (length n) h
-   go acc (h:hs) = go (h:acc) hs
-   go acc [] = reverse acc
diff --git a/CabalHelper/Compile.hs b/CabalHelper/Compile.hs
deleted file mode 100644
--- a/CabalHelper/Compile.hs
+++ /dev/null
@@ -1,479 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-{-# LANGUAGE RecordWildCards, FlexibleContexts #-}
-module CabalHelper.Compile where
-
-import Control.Applicative
-import Control.Arrow
-import Control.Exception as E
-import Control.Monad
-import Control.Monad.Trans.Maybe
-import Control.Monad.IO.Class
-import Data.Traversable
-import Data.Char
-import Data.List
-import Data.Maybe
-import Data.String
-import Data.Version
-import Text.Printf
-import System.Directory
-import System.FilePath
-import System.Process
-import System.Exit
-import System.IO
-import System.IO.Temp
-import Prelude
-
-import Distribution.System (buildPlatform)
-import Distribution.Text (display)
-
-import Paths_cabal_helper (version)
-import CabalHelper.Data
-import CabalHelper.Common
-import CabalHelper.Sandbox (getSandboxPkgDb)
-import CabalHelper.Types
-import CabalHelper.Log
-
-data Compile = Compile {
-      compCabalHelperSourceDir :: FilePath,
-      compCabalSourceDir :: Maybe FilePath,
-      compPackageDb      :: Maybe FilePath,
-      compCabalVersion   :: Either String Version,
-      compPackageDeps    :: [String]
-    }
-
-compileHelper :: Options -> Version -> FilePath -> FilePath -> IO (Either ExitCode FilePath)
-compileHelper opts cabalVer projdir distdir = withHelperSources $ \chdir -> do
-  case cabalPkgDb opts of
-    Nothing ->
-      run [
-            -- TODO: here ghc's caching fails and it always recompiles, probably
-            -- because we write the sources to a tempdir and they always look
-            -- newer than the Cabal sources, not sure if we can fix this
-            compileCabalSource chdir
-          , Right <$> MaybeT (cachedExe cabalVer)
-          , compileSandbox chdir
-          , compileGlobal chdir
-          , cachedCabalPkg chdir
-          , MaybeT (Just <$> compilePrivatePkgDb chdir)
-          ]
-    mdb ->
-      run [ Right <$> MaybeT (cachedExe cabalVer)
-          , liftIO $ compileWithPkg chdir mdb cabalVer
-          ]
-
- where
-   run actions = fromJust <$> runMaybeT (msum actions)
-
-   logMsg = "compiling helper with Cabal from "
-
-
--- for relaxed deps: find (sameMajorVersionAs cabalVer) . reverse . sort
-
-   -- | Check if this version is globally available
-   compileGlobal :: FilePath -> MaybeT IO (Either ExitCode FilePath)
-   compileGlobal chdir = do
-       ver <- MaybeT $ find (== cabalVer) <$> listCabalVersions opts
-       vLog opts $ logMsg ++ "user/global package-db"
-       liftIO $ compileWithPkg chdir Nothing ver
-
-   -- | Check if this version is available in the project sandbox
-   compileSandbox :: FilePath -> MaybeT IO (Either ExitCode FilePath)
-   compileSandbox chdir = do
-       sandbox <- MaybeT $ getSandboxPkgDb projdir (display buildPlatform) =<< ghcVersion opts
-       ver <- MaybeT $ logSomeError opts "compileSandbox" $
-         find (== cabalVer) <$> listCabalVersions' opts (Just sandbox)
-       vLog opts $ logMsg ++ "sandbox package-db"
-       liftIO $ compileWithPkg chdir (Just sandbox) ver
-
-
-   -- | Check if we already compiled this version of cabal into a private
-   -- package-db
-   cachedCabalPkg :: FilePath -> MaybeT IO (Either ExitCode FilePath)
-   cachedCabalPkg chdir = do
-       db_exists <- liftIO $ cabalPkgDbExists opts cabalVer
-       case db_exists of
-         False -> mzero
-         True -> do
-             db <- liftIO $ getPrivateCabalPkgDb opts (showVersion cabalVer)
-             vLog opts $ logMsg ++ "private package-db in " ++ db
-             liftIO $ compileWithPkg chdir (Just db) cabalVer
-
-   -- | See if we're in a cabal source tree
-   compileCabalSource :: FilePath -> MaybeT IO (Either ExitCode FilePath)
-   compileCabalSource chdir = do
-       let cabalFile = projdir </> "Cabal.cabal"
-           isCabalMagicVer = cabalVer == Version [1,9999] []
-       cabalSrc <- liftIO $ doesFileExist cabalFile
-
-       when isCabalMagicVer $
-         vLog opts $ "cabal magic version (1.9999) found"
-
-       when cabalSrc $
-         vLog opts $ "directory above distdir looks like cabal source tree (Cabal.cabal exists)"
-
-       case isCabalMagicVer || cabalSrc of
-         False -> mzero
-         True -> liftIO $ do
-           ver <- cabalFileVersion <$> readFile cabalFile
-           vLog opts $ "compiling helper with local Cabal source tree"
-           compileWithCabalTree chdir ver projdir
-
-   -- | Compile the requested cabal version into an isolated package-db
-   compilePrivatePkgDb :: FilePath -> IO (Either ExitCode FilePath)
-   compilePrivatePkgDb chdir = do
-       db <- installCabal opts cabalVer `E.catch`
-             \(SomeException _) -> errorInstallCabal cabalVer distdir
-       compileWithPkg chdir (Just db) cabalVer
-
-   compileWithCabalTree chdir ver srcDir =
-       compile distdir opts $ Compile chdir (Just srcDir) Nothing (Right ver) []
-
-   compileWithPkg chdir mdb ver =
-       compile distdir opts $ Compile chdir Nothing mdb (Right ver) [cabalPkgId ver]
-
-   cabalPkgId v = "Cabal-" ++ showVersion v
-
-compile :: FilePath -> Options -> Compile -> IO (Either ExitCode FilePath)
-compile distdir opts@Options {..} Compile {..} = do
-    cCabalSourceDir <- canonicalizePath `traverse` compCabalSourceDir
-    appdir <- appDataDir
-
-    let outdir' = maybe appdir (const $ distdir </> "cabal-helper") cCabalSourceDir
-    createDirectoryIfMissing True outdir'
-    outdir <- canonicalizePath outdir'
-
-    let exedir' = maybe outdir (const distdir) cCabalSourceDir
-    createDirectoryIfMissing True exedir'
-    exedir <- canonicalizePath exedir'
-    exe <- exePath' compCabalVersion <$> canonicalizePath exedir
-
-    vLog opts $ "outdir: " ++ outdir
-    vLog opts $ "exedir: " ++ exedir
-
-    let (mj:mi:_) = case compCabalVersion of
-                     Left _commitid -> [1, 10000]
-                     Right (Version vs _) -> vs
-    let ghc_opts =
-             concat [
-          [ "-outputdir", outdir
-          , "-o", exe
-          , "-optP-DCABAL_HELPER=1"
-          , "-optP-DCABAL_MAJOR=" ++ show mj
-          , "-optP-DCABAL_MINOR=" ++ show mi
-          ],
-          maybeToList $ ("-package-conf="++) <$> compPackageDb,
-          map ("-i"++) $ nub $ ".":maybeToList cCabalSourceDir,
-
-          if isNothing cCabalSourceDir
-             then [ "-hide-all-packages"
-                  , "-package", "base"
-                  , "-package", "containers"
-                  , "-package", "directory"
-                  , "-package", "filepath"
-                  , "-package", "process"
-                  , "-package", "bytestring"
-                  , "-package", "ghc-prim"
-                  ]
-             else [],
-
-          concatMap (\p -> ["-package", p]) compPackageDeps,
-          [ "--make",  "CabalHelper/Main.hs" ]
-         ]
-
-    vLog opts $ intercalate " " $ map (("\""++) . (++"\"")) $ ghcProgram:ghc_opts
-
-    -- TODO: touch exe after, ghc doesn't do that if the input files didn't
-    -- actually change
-    rv <- callProcessStderr' (Just compCabalHelperSourceDir) ghcProgram ghc_opts
-    return $ case rv of
-               ExitSuccess -> Right exe
-               e@(ExitFailure _) -> Left e
-
-exePath :: Either String Version -> IO FilePath
-exePath compCabalVersion = do
-    exePath' compCabalVersion <$> appDataDir
-
-exePath' :: Either String Version -> FilePath -> FilePath
-exePath' (Left commitid) outdir =
-    outdir </> "cabal-helper-" ++ showVersion version -- our ver
-            ++ "-Cabal-HEAD-" ++ commitid
-exePath' (Right compCabalVersion) outdir =
-    outdir </> "cabal-helper-" ++ showVersion version -- our ver
-            ++ "-Cabal-" ++ showVersion compCabalVersion
-
-callProcessStderr' :: Maybe FilePath -> FilePath -> [String] -> IO ExitCode
-callProcessStderr' mwd exe args = do
-  (_, _, _, h) <- createProcess (proc exe args) { std_out = UseHandle stderr
-                                                , cwd = mwd }
-  waitForProcess h
-
-callProcessStderr :: Maybe FilePath -> FilePath -> [String] -> IO ()
-callProcessStderr mwd exe args = do
-  rv <- callProcessStderr' mwd exe args
-  case rv of
-    ExitSuccess -> return ()
-    ExitFailure v -> processFailedException "callProcessStderr" exe args v
-
-processFailedException :: String -> String -> [String] -> Int -> IO a
-processFailedException fn exe args rv =
-      panic $ concat [fn, ": ", exe, " "
-                     , intercalate " " (map show args)
-                     , " (exit " ++ show rv ++ ")"]
-
-installCabal :: Options -> Version -> IO FilePath
-installCabal opts ver = do
-  appdir <- appDataDir
-  let sver = showVersion ver
-  hPutStr stderr $ printf "\
-\cabal-helper-wrapper: Installing a private copy of Cabal because we couldn't\n\
-\find the right version in your global/user package-db, this might take a\n\
-\while but will only happen once per Cabal version you're using.\n\
-\\n\
-\If anything goes horribly wrong just delete this directory and try again:\n\
-\    %s\n\
-\\n\
-\If you want to avoid this automatic installation altogether install\n\
-\version %s of Cabal manually (into your user or global package-db):\n\
-\    $ cabal install Cabal --constraint \"Cabal == %s\"\n\
-\\n\
-\Installing Cabal %s ...\n" appdir sver sver sver
-
-  withSystemTempDirectory "cabal-helper" $ \tmpdir -> do
-    let
-        mpatch :: Maybe (FilePath -> IO ())
-        mpatch = snd <$> find ((ver`elem`) . fst) patchyCabalVersions
-    msrcdir <- sequenceA $ unpackPatchedCabal opts ver tmpdir <$> mpatch
-    db <- createPkgDb opts (showVersion ver)
-    cabalInstall opts db (maybe (Right ver) Left msrcdir)
-    return db
-
-installCabalHEAD :: Options -> IO (FilePath, String)
-installCabalHEAD opts = do
-  withSystemTempDirectory "cabal-helper" $ \tmpdir -> do
-    (srcdir, commit) <- unpackCabalHEAD tmpdir
-    db <- createPkgDb opts commit
-    cabalInstall opts db (Left srcdir)
-    return (db, commit)
-
-cabalInstall :: Options -> FilePath -> Either FilePath Version -> IO ()
-cabalInstall opts db e_ver_msrcdir = do
-  cabalInstallVer <- cabalInstallVersion opts
-  cabal_opts <- return $ concat
-      [
-        [ "--package-db=clear"
-        , "--package-db=global"
-        , "--package-db=" ++ db
-        , "--prefix=" ++ db </> "prefix"
-        , "--with-ghc=" ++ ghcProgram opts
-        ]
-        , if cabalInstallVer >= Version [1,20,0,0] []
-             then ["--no-require-sandbox"]
-             else []
-        , if ghcPkgProgram opts /= ghcPkgProgram defaultOptions
-            then [ "--with-ghc-pkg=" ++ ghcPkgProgram opts ]
-            else []
-        ,
-          case e_ver_msrcdir of
-            Right ver ->
-                [ "install", "Cabal"
-                , "--constraint", "Cabal == " ++ showVersion ver
-                ]
-            Left srcdir ->
-                [ "install", srcdir ]
-      ]
-
-  vLog opts $ intercalate " "
-            $ map (("\""++) . (++"\""))
-            $ cabalProgram opts:cabal_opts
-
-  callProcessStderr (Just "/") (cabalProgram opts) cabal_opts
-  hPutStrLn stderr "done"
-
-patchyCabalVersions :: [([Version], FilePath -> IO ())]
-patchyCabalVersions = [
-    ( [ Version [1,18,1] [] ]
-    , fixArrayConstraint
-    ),
-
-
-    ( [ Version [1,18,0] [] ]
-    , \dir -> do
-        fixArrayConstraint dir
-        fixOrphanInstance dir
-    ),
-
-    -- just want the pristine version
-    ( [ Version [1,24,1,0] [] ]
-    , \_ -> return ()
-    )
-  ]
- where
-   fixArrayConstraint dir = do
-     let cabalFile    = dir </> "Cabal.cabal"
-         cabalFileTmp = cabalFile ++ ".tmp"
-
-     cf <- readFile cabalFile
-     writeFile cabalFileTmp $ replace "&& < 0.5" "&& < 0.6" cf
-     renameFile cabalFileTmp cabalFile
-
-   fixOrphanInstance dir = do
-     let versionFile    = dir </> "Distribution/Version.hs"
-         versionFileTmp = versionFile ++ ".tmp"
-
-     let languagePragma =
-           "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}"
-         languagePragmaCPP =
-           "{-# LANGUAGE CPP, DeriveDataTypeable, StandaloneDeriving #-}"
-
-         derivingDataVersion =
-           "deriving instance Data Version"
-         derivingDataVersionCPP = unlines [
-             "#if __GLASGOW_HASKELL__ < 707",
-             derivingDataVersion,
-             "#endif"
-           ]
-
-     vf <- readFile versionFile
-     writeFile versionFileTmp
-       $ replace derivingDataVersion derivingDataVersionCPP
-       $ replace languagePragma languagePragmaCPP vf
-
-     renameFile versionFileTmp versionFile
-
-unpackPatchedCabal ::
-    Options -> Version -> FilePath -> (FilePath -> IO ()) -> IO FilePath
-unpackPatchedCabal opts cabalVer tmpdir patch = do
-  dir <- unpackCabal opts cabalVer tmpdir
-  patch dir
-  return dir
-
-unpackCabal ::
-    Options -> Version -> FilePath -> IO FilePath
-unpackCabal opts cabalVer tmpdir = do
-  let cabal = "Cabal-" ++ showVersion cabalVer
-      dir = tmpdir </> cabal
-  callProcessStderr (Just tmpdir) (cabalProgram opts)
-                    [ "get", "--pristine", cabal ]
-  return dir
-
-unpackCabalHEAD :: FilePath -> IO (FilePath, String)
-unpackCabalHEAD tmpdir = do
-  let dir = tmpdir </> "cabal-head.git"
-      url = "https://github.com/haskell/cabal.git"
-  ExitSuccess <- rawSystem "git" [ "clone", "--depth=1", url, dir]
-  commit <- trim <$> readProcess "git" ["-C", dir, "rev-parse", "HEAD"] ""
-  return (dir </> "Cabal", commit)
-
-errorInstallCabal :: Version -> FilePath -> a
-errorInstallCabal cabalVer _distdir = panic $ printf "\
-\Installing Cabal version %s failed.\n\
-\\n\
-\You have the following choices to fix this:\n\
-\\n\
-\- The easiest way to try and fix this is just reconfigure the project and try\n\
-\  again:\n\
-\        $ cabal clean && cabal configure\n\
-\\n\
-\- If that fails you can try to install the version of Cabal mentioned above\n\
-\  into your global/user package-db somehow, you'll probably have to fix\n\
-\  something otherwise it wouldn't have failed above:\n\
-\        $ cabal install Cabal --constraint 'Cabal == %s'\n\
-\\n\
-\- If you're using `Build-Type: Simple`:\n\
-\  - You can see if you can reinstall your cabal-install executable while\n\
-\    having it linked to a version of Cabal that's available in you\n\
-\    package-dbs or can be built automatically:\n\
-\        $ ghc-pkg list | grep Cabal  # find an available Cabal version\n\
-\            Cabal-W.X.Y.Z\n\
-\        $ cabal install cabal-install --constraint 'Cabal == W.X.*'\n\
-\    Afterwards you'll have to reconfigure your project:\n\
-\        $ cabal clean && cabal configure\n\
-\\n\
-\- If you're using `Build-Type: Custom`:\n\
-\  - Have cabal-install rebuild your Setup.hs executable with a version of the\n\
-\    Cabal library that you have available in your global/user package-db:\n\
-\        $ cabal clean && cabal configure\n\
-\    You might also have to install some version of the Cabal to do this:\n\
-\        $ cabal install Cabal\n\
-\\n" sver sver
- where
-   sver = showVersion cabalVer
-
-cachedExe :: Version -> IO (Maybe FilePath)
-cachedExe compCabalVersion = do
-   exe <- exePath (Right compCabalVersion)
-   exists <- doesFileExist exe
-   return $ if exists then Just exe else Nothing
-
-listCabalVersions :: Options -> IO [Version]
-listCabalVersions opts = listCabalVersions' opts Nothing
-
--- TODO: Include sandbox? Probably only relevant for build-type:custom projects.
-listCabalVersions' :: Options -> Maybe FilePath -> IO [Version]
-listCabalVersions' Options {..} mdb = do
-  let mdbopt = ("--package-conf="++) <$> mdb
-      opts = ["list", "--simple-output", "Cabal"] ++ maybeToList mdbopt
-
-  catMaybes . map (fmap snd . parsePkgId . fromString) . words
-          <$> readProcess ghcPkgProgram opts ""
-
-cabalPkgDbExists :: Options -> Version -> IO Bool
-cabalPkgDbExists opts ver = do
-  db <- getPrivateCabalPkgDb opts (showVersion ver)
-  dexists <- doesDirectoryExist db
-  case dexists of
-    False -> return False
-    True -> do
-      vers <- listCabalVersions' opts (Just db)
-      return $ ver `elem` vers
-
-
-ghcVersion :: Options -> IO Version
-ghcVersion Options {..} = do
-    parseVer . trim <$> readProcess ghcProgram ["--numeric-version"] ""
-
-ghcPkgVersion :: Options -> IO Version
-ghcPkgVersion Options {..} = do
-    parseVer . trim . dropWhile (not . isDigit) <$> readProcess ghcPkgProgram ["--version"] ""
-
-cabalInstallVersion :: Options -> IO Version
-cabalInstallVersion Options {..} = do
-    parseVer . trim <$> readProcess cabalProgram ["--numeric-version"] ""
-
-trim :: String -> String
-trim = dropWhileEnd isSpace
-
-createPkgDb :: Options -> String -> IO FilePath
-createPkgDb opts@Options {..} ver = do
-  db <- getPrivateCabalPkgDb opts ver
-  exists <- doesDirectoryExist db
-  when (not exists) $ callProcessStderr Nothing ghcPkgProgram ["init", db]
-  return db
-
-getPrivateCabalPkgDb :: Options -> String -> IO FilePath
-getPrivateCabalPkgDb opts ver = do
-  appdir <- appDataDir
-  ghcVer <- ghcVersion opts
-  return $ appdir </> "Cabal-" ++ ver ++ "-db-" ++ showVersion ghcVer
-
--- | Find @version: XXX@ delcaration in a cabal file
-cabalFileVersion :: String -> Version
-cabalFileVersion cabalFile =
-  fromJust $ parseVer . extract <$> find ("version:" `isPrefixOf`) ls
- where
-  ls = map (map toLower) $ lines cabalFile
-  extract = dropWhile (/=':') >>> drop 1 >>> dropWhile isSpace >>> takeWhile (not . isSpace)
diff --git a/CabalHelper/Data.hs b/CabalHelper/Data.hs
deleted file mode 100644
--- a/CabalHelper/Data.hs
+++ /dev/null
@@ -1,46 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-{-# LANGUAGE TemplateHaskell #-}
-{-# OPTIONS_GHC -fforce-recomp #-}
-module CabalHelper.Data where
-
-import Control.Monad
-import Data.Functor
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.UTF8 as UTF8
-import Language.Haskell.TH
-import System.FilePath
-import System.Directory
-import System.IO.Temp
-import Prelude
-
-withHelperSources :: (FilePath -> IO a) -> IO a
-withHelperSources action = withSystemTempDirectory "cabal-helper" $ \dir -> do
-    let chdir = dir </> "CabalHelper"
-    createDirectory chdir
-    forM_ sourceFiles $ \(fn, src) ->
-        BS.writeFile (chdir </> fn) $ UTF8.fromString src
-    action dir
-
-sourceFiles :: [(FilePath, String)]
-sourceFiles =
-  [ ("Main.hs",   $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Main.hs")))
-  , ("Common.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Common.hs")))
-  , ("Sandbox.hs",  $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Sandbox.hs")))
-  , ("Licenses.hs",  $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Licenses.hs")))
-  , ("Types.hs",  $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "CabalHelper/Types.hs")))
-  ]
diff --git a/CabalHelper/GuessGhc.hs b/CabalHelper/GuessGhc.hs
deleted file mode 100644
--- a/CabalHelper/GuessGhc.hs
+++ /dev/null
@@ -1,86 +0,0 @@
-module CabalHelper.GuessGhc (guessToolFromGhcPath) where
-
-import Data.Maybe
-import Data.Char
-import Distribution.Simple.BuildPaths
-import System.Directory
-import System.FilePath
-
--- Copyright (c) 2003-2014, Isaac Jones, Simon Marlow, Martin Sjögren,
---                          Bjorn Bringert, Krasimir Angelov,
---                          Malcolm Wallace, Ross Patterson, Ian Lynagh,
---                          Duncan Coutts, Thomas Schilling,
---                          Johan Tibell, Mikhail Glushenkov
--- 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 Isaac Jones 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.
-
-guessToolFromGhcPath :: FilePath -- ^ Tool name
-                     -> FilePath -- ^ GHC exe path
-                     -> IO (Maybe FilePath)
-guessToolFromGhcPath toolname ghcPath
-  = do let
-           path              = ghcPath
-           dir               = takeDirectory path
-           versionSuffix     = takeVersionSuffix (dropExeExtension path)
-           guessNormal       = dir </> toolname <.> exeExtension'
-           guessGhcVersioned = dir </> (toolname ++ "-ghc" ++ versionSuffix)
-                               <.> exeExtension'
-           guessVersioned    = dir </> (toolname ++ versionSuffix)
-                               <.> exeExtension'
-           guesses | null versionSuffix = [guessNormal]
-                   | otherwise          = [guessGhcVersioned,
-                                           guessVersioned,
-                                           guessNormal]
-       exists <- mapM doesFileExist guesses
-       return $ listToMaybe [ file | (file, True) <- zip guesses exists ]
-
-  where takeVersionSuffix :: FilePath -> String
-        takeVersionSuffix = takeWhileEndLE isSuffixChar
-
-        isSuffixChar :: Char -> Bool
-        isSuffixChar c = isDigit c || c == '.' || c == '-'
-
-        dropExeExtension :: FilePath -> FilePath
-        dropExeExtension filepath =
-          case splitExtension filepath of
-            (filepath', extension) | extension == exeExtension' -> filepath'
-                                   | otherwise                 -> filepath
-
--- | @takeWhileEndLE p@ is equivalent to @reverse . takeWhile p . reverse@, but
--- is usually faster (as well as being easier to read).
-takeWhileEndLE :: (a -> Bool) -> [a] -> [a]
-takeWhileEndLE p = fst . foldr go ([], False)
-  where
-    go x (rest, done)
-      | not done && p x = (x:rest, False)
-      | otherwise = (rest, True)
-
-exeExtension' :: FilePath
-exeExtension' = Distribution.Simple.BuildPaths.exeExtension
diff --git a/CabalHelper/Licenses.hs b/CabalHelper/Licenses.hs
deleted file mode 100644
--- a/CabalHelper/Licenses.hs
+++ /dev/null
@@ -1,119 +0,0 @@
-{-# LANGUAGE CPP #-}
-module CabalHelper.Licenses (
-    displayDependencyLicenseList
-  , groupByLicense
-  , getDependencyInstalledPackageInfos
-  ) where
-
--- Copyright (c) 2014, Jasper Van der Jeugt <m@jaspervdj.be>
-
---------------------------------------------------------------------------------
-import Control.Arrow ((***), (&&&))
-import Control.Monad (forM_, unless)
-import Data.List (foldl', sort)
-import Data.Maybe (catMaybes)
-import Data.Version (Version)
-import Data.Set (Set)
-import qualified Data.Set as Set
-import System.Directory (getDirectoryContents)
-import System.Exit (exitFailure)
-import System.FilePath (takeExtension)
-import System.IO (hPutStrLn, stderr)
-
-import Distribution.InstalledPackageInfo
-import Distribution.License
-import Distribution.Package
-import Distribution.Simple.Configure
-import Distribution.Simple.LocalBuildInfo
-import Distribution.Simple.PackageIndex
-import Distribution.Text
-import Distribution.ModuleName
---------------------------------------------------------------------------------
-
-
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR > 22
-type CPackageIndex a = PackageIndex (InstalledPackageInfo)
-#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 22
-type CPackageIndex a = PackageIndex (InstalledPackageInfo_ a)
-#else
-type CPackageIndex a = PackageIndex
-#endif
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR >= 23
-type CInstalledPackageId = UnitId
-lookupInstalledPackageId' :: PackageIndex a -> UnitId -> Maybe a
-lookupInstalledPackageId' = lookupUnitId
-#elif CABAL_MAJOR == 1 && CABAL_MINOR > 22
-type CInstalledPackageId = ComponentId
-lookupInstalledPackageId' = lookupComponentId
-#else
-type CInstalledPackageId = InstalledPackageId
-lookupInstalledPackageId' = lookupInstalledPackageId
-#endif
-
-findTransitiveDependencies
-    :: CPackageIndex Distribution.ModuleName.ModuleName
-    -> Set CInstalledPackageId
-    -> Set CInstalledPackageId
-findTransitiveDependencies pkgIdx set0 = go Set.empty (Set.toList set0)
-  where
-    go set []  = set
-    go set (q : queue)
-        | q `Set.member` set = go set queue
-        | otherwise          =
-            case lookupInstalledPackageId' pkgIdx q of
-                Nothing  ->
-                    -- Not found can mean that the package still needs to be
-                    -- installed (e.g. a component of the target cabal package).
-                    -- We can ignore those.
-                    go set queue
-                Just ipi ->
-                    go (Set.insert q set) (Distribution.InstalledPackageInfo.depends ipi ++ queue)
-
-
---------------------------------------------------------------------------------
-getDependencyInstalledPackageIds
-    :: LocalBuildInfo -> Set CInstalledPackageId
-getDependencyInstalledPackageIds lbi =
-    findTransitiveDependencies (installedPkgs lbi) $
-      Set.fromList $ map fst $ externalPackageDeps lbi
-
---------------------------------------------------------------------------------
-getDependencyInstalledPackageInfos
-    :: LocalBuildInfo -> [InstalledPackageInfo]
-getDependencyInstalledPackageInfos lbi = catMaybes $
-    map (lookupInstalledPackageId' pkgIdx) $
-    Set.toList (getDependencyInstalledPackageIds lbi)
-  where
-    pkgIdx = installedPkgs lbi
-
-
---------------------------------------------------------------------------------
-groupByLicense
-    :: [InstalledPackageInfo]
-    -> [(License, [InstalledPackageInfo])]
-groupByLicense = foldl'
-    (\assoc ipi -> insertAList (license ipi) ipi assoc) []
-  where
-    -- 'Cabal.License' doesn't have an 'Ord' instance so we need to use an
-    -- association list instead of 'Map'. The number of licenses probably won't
-    -- exceed 100 so I think we're alright.
-    insertAList :: Eq k => k -> v -> [(k, [v])] -> [(k, [v])]
-    insertAList k v []   = [(k, [v])]
-    insertAList k v ((k', vs) : kvs)
-        | k == k'   = (k, v : vs) : kvs
-        | otherwise = (k', vs) : insertAList k v kvs
-
-
---------------------------------------------------------------------------------
-displayDependencyLicenseList
-    :: [(License, [InstalledPackageInfo])]
-    -> [(String, [(String, Version)])]
-displayDependencyLicenseList =
-    map (display *** map (getName &&& getVersion))
-  where
-    getName =
-        display . pkgName . sourcePackageId
-    getVersion =
-        pkgVersion . sourcePackageId
diff --git a/CabalHelper/Log.hs b/CabalHelper/Log.hs
deleted file mode 100644
--- a/CabalHelper/Log.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module CabalHelper.Log where
-
-import Control.Monad
-import Control.Monad.IO.Class
-import Control.Exception as E
-import Data.String
-import System.IO
-import Prelude
-
-import CabalHelper.Types
-
-vLog :: MonadIO m => Options -> String -> m ()
-vLog Options { verbose = True } msg =
-    liftIO $ hPutStrLn stderr msg
-vLog _ _ = return ()
-
-logSomeError :: Options -> String -> IO (Maybe a) -> IO (Maybe a)
-logSomeError opts label a = do
-  a `E.catch` \se@(SomeException _) -> do
-      vLog opts $ label ++ ": " ++ show se
-      return Nothing
diff --git a/CabalHelper/Main.hs b/CabalHelper/Main.hs
deleted file mode 100644
--- a/CabalHelper/Main.hs
+++ /dev/null
@@ -1,483 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-{-# LANGUAGE CPP, BangPatterns, RecordWildCards, RankNTypes #-}
-{-# OPTIONS_GHC -fno-warn-deprecations #-}
-import Distribution.Simple.Utils (cabalVersion)
-import Distribution.Simple.Configure
-
-import Distribution.Package (PackageIdentifier, InstalledPackageId, PackageId,
-                             packageName, packageVersion)
-import Distribution.PackageDescription (PackageDescription,
-                                        GenericPackageDescription(..),
-                                        Flag(..),
-                                        FlagName(..),
-                                        FlagAssignment,
-                                        Executable(..),
-                                        Library(..),
-                                        TestSuite(..),
-                                        Benchmark(..),
-                                        BuildInfo(..),
-                                        TestSuiteInterface(..),
-                                        BenchmarkInterface(..),
-                                        withLib)
-import Distribution.PackageDescription.Parse (readPackageDescription)
-import Distribution.PackageDescription.Configuration (flattenPackageDescription)
-
-import Distribution.Simple.Program (requireProgram, ghcProgram)
-import Distribution.Simple.Program.Types (ConfiguredProgram(..))
-import Distribution.Simple.Configure (getPersistBuildConfig)
-import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..),
-                                           Component(..),
-                                           ComponentName(..),
-                                           ComponentLocalBuildInfo(..),
-                                           componentBuildInfo,
-                                           externalPackageDeps,
-                                           withComponentsLBI,
-                                           withLibLBI)
-#if CABAL_MAJOR == 1 && CABAL_MINOR >= 23
-import Distribution.Simple.LocalBuildInfo (localUnitId)
-#elif CABAL_MAJOR == 1 && CABAL_MINOR <= 22
-import Distribution.Simple.LocalBuildInfo (inplacePackageId)
-#endif
-
-import Distribution.Simple.GHC (componentGhcOptions)
-import Distribution.Simple.Program.GHC (GhcOptions(..), renderGhcOptions)
-
-import Distribution.Simple.Setup (ConfigFlags(..),Flag(..))
-import Distribution.Simple.Build (initialBuildSteps)
-import Distribution.Simple.BuildPaths (autogenModuleName, cppHeaderName, exeExtension)
-import Distribution.Simple.Compiler (PackageDB(..), compilerId)
-
-import Distribution.Compiler (CompilerId(..))
-import Distribution.ModuleName (components)
-import qualified Distribution.ModuleName as C (ModuleName)
-import Distribution.Text (display)
-import Distribution.Verbosity (Verbosity, silent, deafening, normal)
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR >= 22
-import Distribution.Utils.NubList
-#endif
-
-import Control.Applicative ((<$>))
-import Control.Arrow (first, (&&&))
-import Control.Monad
-import Control.Exception (catch, PatternMatchFail(..))
-import Data.List
-import qualified Data.Map as Map
-import Data.Maybe
-import Data.Monoid
-import Data.IORef
-import System.Environment
-import System.Directory
-import System.FilePath
-import System.Exit
-import System.IO
-import System.IO.Unsafe (unsafeInterleaveIO, unsafePerformIO)
-import Text.Printf
-
-import CabalHelper.Licenses
-import CabalHelper.Sandbox
-import CabalHelper.Common
-import CabalHelper.Types hiding (Options(..))
-
-usage = do
-  prog <- getProgName
-  hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg
- where
-   usageMsg = ""
-     ++"PROJ_DIR DIST_DIR [--with-* ...] (\n"
-     ++"    version\n"
-     ++"  | print-lbi [--human]\n"
-     ++"  | package-id\n"
-     ++"  | flags\n"
-     ++"  | config-flags\n"
-     ++"  | non-default-config-flags\n"
-     ++"  | write-autogen-files\n"
-     ++"  | compiler-version\n"
-     ++"  | ghc-options     [--with-inplace]\n"
-     ++"  | ghc-src-options [--with-inplace]\n"
-     ++"  | ghc-pkg-options [--with-inplace]\n"
-     ++"  | ghc-merged-pkg-options [--with-inplace]\n"
-     ++"  | ghc-lang-options [--with-inplace]\n"
-     ++"  | package-db-stack\n"
-     ++"  | entrypoints\n"
-     ++"  | source-dirs\n"
-     ++"  | licenses\n"
-     ++"  ) ...\n"
-
-commands :: [String]
-commands = [ "print-lbi"
-           , "package-id"
-           , "flags"
-           , "config-flags"
-           , "non-default-config-flags"
-           , "write-autogen-files"
-           , "compiler-version"
-           , "ghc-options"
-           , "ghc-src-options"
-           , "ghc-pkg-options"
-           , "ghc-lang-options"
-           , "package-db-stack"
-           , "entrypoints"
-           , "source-dirs"
-           , "licenses"]
-
-main :: IO ()
-main = do
-  args <- getArgs
-
-  projdir:distdir:args' <- case args of
-                    [] -> usage >> exitFailure
-                    _ -> return args
-
-  ddexists <- doesDirectoryExist distdir
-  when (not ddexists) $ do
-         errMsg $ "distdir '"++distdir++"' does not exist"
-         exitFailure
-
-  [cfile] <- filter isCabalFile <$> getDirectoryContents projdir
-
-  v <- maybe silent (const deafening) . lookup  "GHC_MOD_DEBUG" <$> getEnvironment
-  lbi <- unsafeInterleaveIO $ getPersistBuildConfig distdir
-  gpd <- unsafeInterleaveIO $ readPackageDescription v (projdir </> cfile)
-  let pd = localPkgDescr lbi
-  let lvd = (lbi, v, distdir)
-
-  let
-      -- a =<< b $$ c   ==  (a =<< b) $$ c
-      infixr 2 $$
-      ($$) = ($)
-
-      collectCmdOptions :: [String] -> [[String]]
-      collectCmdOptions =
-          reverse . map reverse . foldl f [] . dropWhile isOpt
-       where
-         isOpt = ("--" `isPrefixOf`)
-         f [] x = [[x]]
-         f (a:as) x
-             | isOpt x = (x:a):as
-             | otherwise = [x]:(a:as)
-
-  let cmds = collectCmdOptions args'
-
-  if any (["version"] `isPrefixOf`) cmds
-    then do
-      putStrLn $
-       printf "using version %s of the Cabal library" (display cabalVersion)
-      exitSuccess
-    else return ()
-
-  print =<< flip mapM cmds $$ \cmd -> do
-  case cmd of
-    "flags":[] -> do
-      return $ Just $ ChResponseFlags $ sort $
-        map (flagName' &&& flagDefault) $ genPackageFlags gpd
-
-    "config-flags":[] -> do
-      return $ Just $ ChResponseFlags $ sort $
-        map (first unFlagName') $ configConfigurationsFlags $ configFlags lbi
-
-    "non-default-config-flags":[] -> do
-      let flagDefinitons = genPackageFlags gpd
-          flagAssgnments = configConfigurationsFlags $ configFlags lbi
-          nonDefaultFlags =
-              [ (fn, v)
-              | MkFlag {flagName=FlagName fn, flagDefault=dv} <- flagDefinitons
-              , (FlagName fn', v) <- flagAssgnments
-              , fn == fn'
-              , v /= dv
-              ]
-      return $ Just $ ChResponseFlags $ sort nonDefaultFlags
-
-    "write-autogen-files":[] -> do
-      initialBuildStepsForAllComponents distdir pd lbi v
-      return Nothing
-
-    "compiler-version":[] -> do
-      let CompilerId comp ver = compilerId $ compiler lbi
-      return $ Just $ ChResponseVersion (show comp) ver
-
-    "ghc-options":flags -> do
-      res <- componentOptions lvd True flags id
-      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
-
-    "ghc-src-options":flags -> do
-      res <- componentOptions lvd False flags $ \opts -> mempty {
-               -- Not really needed but "unexpected package db stack: []"
-               ghcOptPackageDBs      = [GlobalPackageDB, UserPackageDB],
-
-               ghcOptCppOptions      = ghcOptCppOptions opts,
-               ghcOptCppIncludePath  = ghcOptCppIncludePath opts,
-               ghcOptCppIncludes     = ghcOptCppIncludes opts,
-               ghcOptFfiIncludes     = ghcOptFfiIncludes opts,
-               ghcOptSourcePathClear = ghcOptSourcePathClear opts,
-               ghcOptSourcePath      = ghcOptSourcePath opts
-              }
-      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
-
-    "ghc-pkg-options":flags -> do
-      res <- componentOptions lvd True flags $ \opts -> mempty {
-                       ghcOptPackageDBs = ghcOptPackageDBs opts,
-                       ghcOptPackages   = ghcOptPackages opts,
-                       ghcOptHideAllPackages = ghcOptHideAllPackages opts
-                   }
-      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
-
-    "ghc-merged-pkg-options":flags -> do
-      let pd = localPkgDescr lbi
-      res <- mconcat . map snd <$> (componentOptions' lvd True flags (\_ _ o -> return o) $ \opts -> mempty {
-                       ghcOptPackageDBs = [],
-                       ghcOptHideAllPackages = NoFlag,
-                       ghcOptPackages   = ghcOptPackages opts
-                   })
-
-      let res' = nubPackageFlags $ res { ghcOptPackageDBs = withPackageDB lbi
-                                       , ghcOptHideAllPackages = Flag True
-                                       }
-
-      Just . ChResponseList <$> renderGhcOptions' lbi v res'
-
-    "ghc-lang-options":flags -> do
-      res <- componentOptions lvd False flags $ \opts -> mempty {
-                       ghcOptPackageDBs      = [GlobalPackageDB, UserPackageDB],
-
-                       ghcOptLanguage = ghcOptLanguage opts,
-                       ghcOptExtensions = ghcOptExtensions opts,
-                       ghcOptExtensionMap = ghcOptExtensionMap opts
-                   }
-      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
-
-    "package-db-stack":[] -> do
-      let
-          pkgDb GlobalPackageDB = ChPkgGlobal
-          pkgDb UserPackageDB   = ChPkgUser
-          pkgDb (SpecificPackageDB s) = ChPkgSpecific s
-
-      -- TODO: Setup.hs has access to the sandbox as well: ghc-mod#478
-      return $ Just $ ChResponsePkgDbs $ map pkgDb $ withPackageDB lbi
-
-    "entrypoints":[] -> do
-      eps <- componentsMap lbi v distdir $ \c clbi bi ->
-               return $ componentEntrypoints c
-      -- MUST append Setup component at the end otherwise CabalHelper gets
-      -- confused
-      let eps' = eps ++ [(ChSetupHsName, ChSetupEntrypoint)]
-      return $ Just $ ChResponseEntrypoints eps'
-
-    "source-dirs":[] -> do
-      res <- componentsMap lbi v distdir $$ \_ _ bi -> return $ hsSourceDirs bi
-      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
-
-    "licenses":[] -> do
-      return $ Just $ ChResponseLicenses $
-        displayDependencyLicenseList $ groupByLicense $ getDependencyInstalledPackageInfos lbi
-
-    "print-lbi":flags ->
-      case flags of
-        ["--human"] -> print lbi >> return Nothing
-        [] -> return $ Just $ ChResponseLbi $ show lbi
-
-    cmd:_ | not (cmd `elem` commands) ->
-            errMsg ("Unknown command: " ++ cmd) >> usage >> exitFailure
-    _ ->
-            errMsg "Invalid usage!" >> usage >> exitFailure
-
-flagName' = unFlagName' . flagName
-unFlagName' (FlagName n) = n
-
-getLibrary :: PackageDescription -> Library
-getLibrary pd = unsafePerformIO $ do
-  lr <- newIORef (error "libraryMap: empty IORef")
-  withLib pd (writeIORef lr)
-  readIORef lr
-
-getLibraryClbi pd lbi = unsafePerformIO $ do
-  lr <- newIORef Nothing
-
-  withLibLBI pd lbi $ \ lib clbi ->
-      writeIORef lr $ Just (lib,clbi)
-
-  readIORef lr
-
-
-componentsMap :: LocalBuildInfo
-              -> Verbosity
-              -> FilePath
-              -> (   Component
-                  -> ComponentLocalBuildInfo
-                  -> BuildInfo
-                  -> IO a)
-              -> IO [(ChComponentName, a)]
-componentsMap lbi v distdir f = do
-    let pd = localPkgDescr lbi
-
-    lr <- newIORef []
-
-    -- withComponentsLBI is deprecated but also exists in very old versions
-    -- it's equivalent to withAllComponentsInBuildOrder in newer versions
-    withComponentsLBI pd lbi $ \c clbi -> do
-        let bi = componentBuildInfo c
-            name = componentNameFromComponent c
-
-        l' <- readIORef lr
-        r <- f c clbi bi
-        writeIORef lr $ (componentNameToCh name, r):l'
-
-    reverse <$> readIORef lr
-
-componentOptions' (lbi, v, distdir) inplaceFlag flags rf f = do
-  let pd = localPkgDescr lbi
-  componentsMap lbi v distdir $ \c clbi bi -> let
-           outdir = componentOutDir lbi c
-           (clbi', adopts) = case flags of
-                               _ | not inplaceFlag -> (clbi, mempty)
-                               ["--with-inplace"] -> (clbi, mempty)
-                               [] -> removeInplaceDeps v lbi pd clbi
-           opts = componentGhcOptions normal lbi bi clbi' outdir
-           opts' = f opts
-
-         in rf lbi v $ nubPackageFlags $ opts' `mappend` adopts
-
-componentOptions (lbi, v, distdir) inplaceFlag flags f =
-    componentOptions' (lbi, v, distdir) inplaceFlag flags renderGhcOptions' f
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR < 25
-componentNameToCh CLibName = ChLibName ""
-#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 25
-componentNameToCh (CLibName n) = ChLibName n
-#endif
-componentNameToCh (CExeName n) = ChExeName n
-componentNameToCh (CTestName n) = ChTestName n
-componentNameToCh (CBenchName n) = ChBenchName n
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR < 25
-componentNameFromComponent (CLib Library {}) = CLibName
-#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 25
-componentNameFromComponent (CLib Library {..}) = CLibName libName
-#endif
-componentNameFromComponent (CExe Executable {..}) = CExeName exeName
-componentNameFromComponent (CTest TestSuite {..}) = CTestName testName
-componentNameFromComponent (CBench Benchmark {..}) = CBenchName benchmarkName
-
-componentOutDir lbi (CLib Library {..})= buildDir lbi
-componentOutDir lbi (CExe Executable {..})= exeOutDir lbi exeName
-componentOutDir lbi (CTest TestSuite { testInterface = TestSuiteExeV10 _ _, ..}) =
-    exeOutDir lbi testName
-componentOutDir lbi (CTest TestSuite { testInterface = TestSuiteLibV09 _ _, ..}) =
-    exeOutDir lbi (testName ++ "Stub")
-componentOutDir lbi (CBench Benchmark { benchmarkInterface = BenchmarkExeV10 _ _, ..})=
-    exeOutDir lbi benchmarkName
-
-gmModuleName :: C.ModuleName -> ChModuleName
-gmModuleName = ChModuleName . intercalate "." . components
-
-componentEntrypoints :: Component -> ChEntrypoint
-componentEntrypoints (CLib Library {..})
-    = ChLibEntrypoint
-        (map gmModuleName exposedModules)
-        (map gmModuleName $ otherModules libBuildInfo)
-componentEntrypoints (CExe Executable {..})
-    = ChExeEntrypoint modulePath (map gmModuleName $ otherModules buildInfo)
-componentEntrypoints (CTest TestSuite { testInterface = TestSuiteExeV10 _ fp, ..})
-    = ChExeEntrypoint fp (map gmModuleName $ otherModules testBuildInfo)
-componentEntrypoints (CTest TestSuite { testInterface = TestSuiteLibV09 _ mn, ..})
-    = ChLibEntrypoint [gmModuleName mn] (map gmModuleName $ otherModules testBuildInfo)
-componentEntrypoints (CTest TestSuite {})
-    = ChLibEntrypoint [] []
-componentEntrypoints (CBench Benchmark { benchmarkInterface = BenchmarkExeV10 _  fp, ..})
-    = ChExeEntrypoint fp (map gmModuleName $ otherModules benchmarkBuildInfo)
-componentEntrypoints (CBench Benchmark {})
-    = ChLibEntrypoint [] []
-
-exeOutDir :: LocalBuildInfo -> String -> FilePath
-exeOutDir lbi exeName' =
-  ----- Copied from Distribution/Simple/GHC.hs:buildOrReplExe
-  let targetDir = (buildDir lbi) </> exeName'
-      exeDir    = targetDir </> (exeName' ++ "-tmp")
-  in exeDir
-
-
-removeInplaceDeps :: Verbosity
-                  -> LocalBuildInfo
-                  -> PackageDescription
-                  -> ComponentLocalBuildInfo
-                  -> (ComponentLocalBuildInfo, GhcOptions)
-removeInplaceDeps v lbi pd clbi = let
-    (ideps, deps) = partition isInplaceDep (componentPackageDeps clbi)
-    hasIdeps = not $ null ideps
-    libopts =
-      case getLibraryClbi pd lbi of
-        Just (lib, libclbi) | hasIdeps ->
-          let
-            libbi = libBuildInfo lib
-            liboutdir = componentOutDir lbi (CLib lib)
-          in
-            (componentGhcOptions normal lbi libbi libclbi liboutdir) {
-                ghcOptPackageDBs = []
-#if CABAL_MAJOR == 1 && CABAL_MINOR > 22 && CABAL_MINOR < 23
-              , ghcOptComponentId = NoFlag
-#endif
-
-            }
-        _ -> mempty
-    clbi' = clbi { componentPackageDeps = deps }
-
-  in (clbi', libopts)
-
- where
-   isInplaceDep :: (InstalledPackageId, PackageId) -> Bool
-#if CABAL_MAJOR == 1 && CABAL_MINOR >= 23
-   isInplaceDep (ipid, pid) = localUnitId lbi == ipid
-#elif CABAL_MAJOR == 1 && CABAL_MINOR <= 22
-   isInplaceDep (ipid, pid) = inplacePackageId pid == ipid
-
-#endif
-
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR >= 22
--- >= 1.22 uses NubListR
-nubPackageFlags opts = opts
-#else
-nubPackageFlags opts = opts { ghcOptPackages = nub $ ghcOptPackages opts }
-#endif
-
-renderGhcOptions' :: LocalBuildInfo
-                  -> Verbosity
-                  -> GhcOptions
-                  -> IO [String]
-renderGhcOptions' lbi v opts = do
-#if CABAL_MAJOR == 1 && CABAL_MINOR < 20
-  (ghcProg, _) <- requireProgram v ghcProgram (withPrograms lbi)
-  let Just ghcVer = programVersion ghcProg
-  return $ renderGhcOptions ghcVer opts
-#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 20 && CABAL_MINOR < 24
--- && CABAL_MINOR < 24
-  return $ renderGhcOptions (compiler lbi) opts
-#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 24
---  CABAL_MAJOR == 1 && CABAL_MINOR >= 24
-  return $ renderGhcOptions (compiler lbi) (hostPlatform lbi) opts
-#endif
-
-
-#if CABAL_MAJOR == 1 && CABAL_MINOR < 25
-initialBuildStepsForAllComponents distdir pd lbi v =
-  initialBuildSteps distdir pd lbi v
-#elif CABAL_MAJOR == 1 && CABAL_MINOR >= 25
-initialBuildStepsForAllComponents distdir pd lbi v =
-  withComponentsLBI pd lbi $ \_c clbi ->
-    initialBuildSteps distdir pd lbi clbi v
-#endif
diff --git a/CabalHelper/Sandbox.hs b/CabalHelper/Sandbox.hs
deleted file mode 100644
--- a/CabalHelper/Sandbox.hs
+++ /dev/null
@@ -1,56 +0,0 @@
-module CabalHelper.Sandbox where
-
-import Control.Applicative
-import Data.Char
-import Data.Maybe
-import Data.List
-import Data.Version
-import System.FilePath
-import System.Directory
-import Prelude
-
-import qualified Data.Traversable as T
-
--- | Get the path to the sandbox package-db in a project
-getSandboxPkgDb :: FilePath
-             -- ^ Path to the cabal package root directory (containing the
-             -- @cabal.sandbox.config@ file)
-             -> String
-             -- ^ Cabal build platform, i.e. @buildPlatform@
-             -> Version
-             -- ^ GHC version (@cProjectVersion@ is your friend)
-             -> IO (Maybe FilePath)
-getSandboxPkgDb d platform ghcVer = do
-  mConf <- T.traverse readFile =<< mightExist (d </> "cabal.sandbox.config")
-  return $ fixPkgDbVer <$> (extractSandboxDbDir =<< mConf)
-
- where
-   fixPkgDbVer dir =
-       case takeFileName dir == ghcSandboxPkgDbDir platform ghcVer of
-         True -> dir
-         False -> takeDirectory dir </> ghcSandboxPkgDbDir platform ghcVer
-
-ghcSandboxPkgDbDir :: String -> Version -> String
-ghcSandboxPkgDbDir platform ghcVer =
-   platform ++ "-ghc-" ++ showVersion ghcVer ++ "-packages.conf.d"
-
--- | Extract the sandbox package db directory from the cabal.sandbox.config
--- file. Exception is thrown if the sandbox config file is broken.
-extractSandboxDbDir :: String -> Maybe FilePath
-extractSandboxDbDir conf = extractValue <$> parse conf
-  where
-    key = "package-db:"
-    keyLen = length key
-
-    parse = listToMaybe . filter (key `isPrefixOf`) . lines
-    extractValue = CabalHelper.Sandbox.dropWhileEnd isSpace . dropWhile isSpace . drop keyLen
-
-
-mightExist :: FilePath -> IO (Maybe FilePath)
-mightExist f = do
-  exists <- doesFileExist f
-  return $ if exists then (Just f) else (Nothing)
-
--- dropWhileEnd is not provided prior to base 4.5.0.0.
-dropWhileEnd :: (a -> Bool) -> [a] -> [a]
-dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
diff --git a/CabalHelper/Types.hs b/CabalHelper/Types.hs
deleted file mode 100644
--- a/CabalHelper/Types.hs
+++ /dev/null
@@ -1,71 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-{-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures #-}
-module CabalHelper.Types where
-
-import GHC.Generics
-import Data.Version
-
-newtype ChModuleName = ChModuleName String
-    deriving (Eq, Ord, Read, Show, Generic)
-
-data ChComponentName = ChSetupHsName
-                     | ChLibName String
-                     | ChExeName String
-                     | ChTestName String
-                     | ChBenchName String
-  deriving (Eq, Ord, Read, Show, Generic)
-
-data ChResponse
-    = ChResponseCompList    [(ChComponentName, [String])]
-    | ChResponseEntrypoints [(ChComponentName, ChEntrypoint)]
-    | ChResponseList        [String]
-    | ChResponsePkgDbs      [ChPkgDb]
-    | ChResponseLbi         String
-    | ChResponseVersion     String Version
-    | ChResponseLicenses    [(String, [(String, Version)])]
-    | ChResponseFlags       [(String, Bool)]
-  deriving (Eq, Ord, Read, Show, Generic)
-
-data ChEntrypoint = ChSetupEntrypoint -- ^ Almost like 'ChExeEntrypoint' but
-                                      -- @main-is@ could either be @"Setup.hs"@
-                                      -- or @"Setup.lhs"@. Since we don't know
-                                      -- where the source directory is you have
-                                      -- to find these files.
-                  | ChLibEntrypoint { chExposedModules :: [ChModuleName]
-                                    , chOtherModules   :: [ChModuleName]
-                                    }
-                  | ChExeEntrypoint { chMainIs         :: FilePath
-                                    , chOtherModules   :: [ChModuleName]
-                                    } deriving (Eq, Ord, Read, Show, Generic)
-
-data ChPkgDb = ChPkgGlobal
-             | ChPkgUser
-             | ChPkgSpecific FilePath
-               deriving (Eq, Ord, Read, Show, Generic)
-
-data Options = Options {
-          verbose       :: Bool
-        , ghcProgram    :: FilePath
-        , ghcPkgProgram :: FilePath
-        , cabalProgram  :: FilePath
-        , cabalVersion  :: Maybe Version
-        , cabalPkgDb    :: Maybe FilePath
-}
-
-defaultOptions :: Options
-defaultOptions = Options False "ghc" "ghc-pkg" "cabal" Nothing Nothing
diff --git a/CabalHelper/Wrapper.hs b/CabalHelper/Wrapper.hs
deleted file mode 100644
--- a/CabalHelper/Wrapper.hs
+++ /dev/null
@@ -1,154 +0,0 @@
--- cabal-helper: Simple interface to Cabal's configuration state
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-{-# LANGUAGE RecordWildCards, FlexibleContexts #-}
-module Main where
-
-import Control.Applicative
-import Control.Monad
-import Data.Char
-import Data.List
-import Data.Maybe
-import Data.String
-import Data.Version
-import Text.Printf
-import System.Console.GetOpt
-import System.Environment
-import System.Directory
-import System.FilePath
-import System.Process
-import System.Exit
-import System.IO
-import Prelude
-
-import Distribution.System (buildPlatform)
-import Distribution.Text (display)
-import Distribution.Verbosity (silent, deafening)
-import Distribution.PackageDescription.Parse (readPackageDescription)
-import Distribution.Package (packageName, packageVersion)
-
-import Paths_cabal_helper (version)
-import CabalHelper.Common
-import CabalHelper.GuessGhc
-import CabalHelper.Compile
-import CabalHelper.Types
-
-usage :: IO ()
-usage = do
-  prog <- getProgName
-  hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg
- where
-   usageMsg = "\
-\( print-appdatadir\n\
-\| print-build-platform\n\
-\| [--verbose]\n\
-\  [--with-ghc=GHC_PATH]\n\
-\  [--with-ghc-pkg=GHC_PKG_PATH]\n\
-\  [--with-cabal=CABAL_PATH]\n\
-\  [--with-cabal-version=VERSION]\n\
-\  [--with-cabal-pkg-db=PKG_DB]\n\
-\  PROJ_DIR DIST_DIR ( print-exe | package-id | [CABAL_HELPER_ARGS...] ) )\n"
-
-globalArgSpec :: [OptDescr (Options -> Options)]
-globalArgSpec =
-      [ option "" ["verbose"] "Be more verbose" $
-              NoArg $ \o -> o { verbose = True }
-
-      , option "" ["with-ghc"] "GHC executable to use" $
-              reqArg "PROG" $ \p o -> o { ghcProgram = p }
-
-      , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $
-              reqArg "PROG" $ \p o -> o { ghcPkgProgram = p }
-
-      , option "" ["with-cabal"] "cabal-install executable to use" $
-               reqArg "PROG" $ \p o -> o { cabalProgram = p }
-
-      , option "" ["with-cabal-version"] "Cabal library version to use" $
-               reqArg "VERSION" $ \p o -> o { cabalVersion = Just $ parseVer p }
-
-      , option "" ["with-cabal-pkg-db"] "package database to look for Cabal library in" $
-               reqArg "PKG_DB" $ \p o -> o { cabalPkgDb = Just p }
-
-      ]
- where
-   option :: [Char] -> [String] -> String -> ArgDescr a -> OptDescr a
-   option s l udsc dsc = Option s l dsc udsc
-
-   reqArg :: String -> (String -> a) -> ArgDescr a
-   reqArg udsc dsc = ReqArg dsc udsc
-
-parseCommandArgs :: Options -> [String] -> (Options, [String])
-parseCommandArgs opts argv
-    = case getOpt RequireOrder globalArgSpec argv of
-        (o,r,[])   -> (foldr id opts o, r)
-        (_,_,errs) ->
-            panic $ "Parsing command options failed:\n" ++ concat errs
-
-guessProgramPaths :: Options -> IO Options
-guessProgramPaths opts = do
-    if not (same ghcProgram opts dopts) && same ghcPkgProgram opts dopts
-       then do
-         mghcPkg <- guessToolFromGhcPath "ghc-pkg" (ghcProgram opts)
-         return opts {
-           ghcPkgProgram = fromMaybe (ghcPkgProgram opts) mghcPkg
-         }
-       else return opts
- where
-   same f o o'  = f o == f o'
-   dopts = defaultOptions
-
-main :: IO ()
-main = handlePanic $ do
-  (opts', args) <- parseCommandArgs defaultOptions <$> getArgs
-  opts <- guessProgramPaths opts'
-  case args of
-    [] -> usage
-    "help":[] -> usage
-    "version":[] -> putStrLn $ showVersion version
-    "print-appdatadir":[] -> putStrLn =<< appDataDir
-    "print-build-platform":[] -> putStrLn $ display buildPlatform
-
-    projdir:_distdir:"package-id":[] -> do
-      v <- maybe silent (const deafening) . lookup  "GHC_MOD_DEBUG" <$> getEnvironment
-      -- ghc-mod will catch multiple cabal files existing before we get here
-      [cfile] <- filter isCabalFile <$> getDirectoryContents projdir
-      gpd <- readPackageDescription v (projdir </> cfile)
-      putStrLn $ show $
-        [Just $ ChResponseVersion (display (packageName gpd)) (packageVersion gpd)]
-
-    projdir:distdir:args' -> do
-      cfgf <- canonicalizePath (distdir </> "setup-config")
-      mhdr <- getCabalConfigHeader cfgf
-      case mhdr of
-        Nothing -> panic $ printf "\
-\Could not read Cabal's persistent setup configuration header\n\
-\- Check first line of: %s\n\
-\- Maybe try: $ cabal configure" cfgf
-        Just (hdrCabalVersion, _) -> do
-          case cabalVersion opts of
-            Just ver | hdrCabalVersion /= ver -> panic $ printf "\
-\Cabal version %s was requested setup configuration was\n\
-\written by version %s" (showVersion ver) (showVersion hdrCabalVersion)
-            _ -> do
-              eexe <- compileHelper opts hdrCabalVersion projdir distdir
-              case eexe of
-                  Left e -> exitWith e
-                  Right exe ->
-                    case args' of
-                      "print-exe":_ -> putStrLn exe
-                      _ -> do
-                        (_,_,_,h) <- createProcess $ proc exe args
-                        exitWith =<< waitForProcess h
-    _ -> error "invalid command line"
diff --git a/Distribution/Helper.hs b/Distribution/Helper.hs
deleted file mode 100644
--- a/Distribution/Helper.hs
+++ /dev/null
@@ -1,480 +0,0 @@
--- ghc-mod: Making Haskell development *more* fun
--- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-{-# LANGUAGE CPP, RecordWildCards, FlexibleContexts, ConstraintKinds #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable, DeriveGeneric #-}
-
-module Distribution.Helper (
-    Programs(..)
-  , defaultPrograms
-  , QueryEnv
-  , qeReadProcess
-  , qePrograms
-  , qeProjectDir
-  , qeDistDir
-  , qeCabalPkgDb
-  , qeCabalVer
-  , defaultQueryEnv
-
-  -- * Running Queries
-  , Query
-  , runQuery
-
-  -- * Queries against Cabal\'s on disk state
-
-  , packageDbStack
-  , entrypoints
-  , sourceDirs
-  , ghcOptions
-  , ghcSrcOptions
-  , ghcPkgOptions
-  , ghcMergedPkgOptions
-  , ghcLangOptions
-  , pkgLicenses
-  , flags
-  , configFlags
-  , nonDefaultConfigFlags
-  , packageId
-  , compilerVersion
-
-  -- * Result types
-  , ChModuleName(..)
-  , ChComponentName(..)
-  , ChPkgDb(..)
-  , ChEntrypoint(..)
-
-  -- * General information
-  , buildPlatform
-
-  -- * Stuff that cabal-install really should export
-  , Distribution.Helper.getSandboxPkgDb
-
-  -- * Managing @dist/@
-  , prepare
-  , prepare'
-  , reconfigure
-  , writeAutogenFiles
-  , writeAutogenFiles'
-
-  -- * $libexec related error handling
-  , LibexecNotFoundError(..)
-  , libexecNotFoundError
-  ) where
-
-import Control.Applicative
-import Control.Monad
-import Control.Monad.IO.Class
-import Control.Monad.State.Strict
-import Control.Monad.Reader
-import Control.Exception as E
-import Data.Char
-import Data.List
-import Data.Maybe
-import Data.Version
-import Data.Typeable
-import Distribution.Simple.BuildPaths (exeExtension)
-import System.Environment
-import System.FilePath
-import System.Directory
-import System.Process
-import System.IO.Unsafe
-import Text.Printf
-import GHC.Generics
-import Prelude
-
-import Paths_cabal_helper (getLibexecDir, version)
-import CabalHelper.Types hiding (Options(..))
-import CabalHelper.Sandbox
-
--- | Paths or names of various programs we need.
-data Programs = Programs {
-      cabalProgram  :: FilePath,
-      ghcProgram    :: FilePath,
-      ghcPkgProgram :: FilePath
-    } deriving (Eq, Ord, Show, Read, Generic, Typeable)
-
-defaultPrograms :: Programs
-defaultPrograms = Programs "cabal" "ghc" "ghc-pkg"
-
-data QueryEnv = QueryEnv {
-      -- | How to start the cabal-helper process. Useful if you need to
-      -- capture stderr output from the helper.
-      qeReadProcess :: FilePath -> [String] -> String -> IO String,
-
-      qePrograms    :: Programs,
-
-      -- | Path to project directory, i.e. the one containing the
-      -- @project.cabal@ file
-      qeProjectDir  :: FilePath,
-
-      -- | Path to the @dist/@ directory
-      qeDistDir     :: FilePath,
-
-      -- | Where to look for the Cabal library when linking the helper
-      qeCabalPkgDb  :: Maybe FilePath,
-
-      -- | If @dist/setup-config@ wasn\'t written by this version of Cabal throw
-      -- an error
-      qeCabalVer    :: Maybe Version
-    }
-
-defaultQueryEnv :: FilePath
-                -- ^ Path to project directory, i.e. the one containing the
-                -- @project.cabal@ file
-                -> FilePath
-                -- ^ Path to the @dist/@ directory
-                -> QueryEnv
-defaultQueryEnv projdir distdir = QueryEnv {
-    qeReadProcess = readProcess
-  , qePrograms    = defaultPrograms
-  , qeProjectDir  = projdir
-  , qeDistDir     = distdir
-  , qeCabalPkgDb  = Nothing
-  , qeCabalVer    = Nothing
-  }
-
-data SomeLocalBuildInfo = SomeLocalBuildInfo {
-      slbiPackageDbStack      :: [ChPkgDb],
-      slbiEntrypoints         :: [(ChComponentName, ChEntrypoint)],
-      slbiSourceDirs          :: [(ChComponentName, [String])],
-      slbiGhcOptions          :: [(ChComponentName, [String])],
-      slbiGhcSrcOptions       :: [(ChComponentName, [String])],
-      slbiGhcPkgOptions       :: [(ChComponentName, [String])],
-      slbiGhcMergedPkgOptions :: [String],
-      slbiGhcLangOptions      :: [(ChComponentName, [String])],
-      slbiPkgLicenses         :: [(String, [(String, Version)])],
-      slbiFlags               :: [(String, Bool)],
-      slbiConfigFlags         :: [(String, Bool)],
-      slbiNonDefaultConfigFlags :: [(String, Bool)],
-      slbiCompilerVersion     :: (String, Version)
-    } deriving (Eq, Ord, Read, Show)
-
--- | Caches helper executable result so it doesn't have to be run more than once
--- as reading in Cabal's @LocalBuildInfo@ datatype from disk is very slow but
--- running all possible queries against it at once is cheap.
-newtype Query m a = Query { unQuery :: StateT (Maybe SomeLocalBuildInfo)
-                                         (ReaderT QueryEnv m) a }
-    deriving (Functor, Applicative, Monad, MonadIO)
-
-instance MonadTrans Query where
-    lift = Query . lift . lift
-
-type MonadQuery m = ( MonadIO m
-                    , MonadState (Maybe SomeLocalBuildInfo) m
-                    , MonadReader QueryEnv m)
-
-run :: Monad m => QueryEnv -> Maybe SomeLocalBuildInfo -> Query m a -> m a
-run e s action = flip runReaderT e (flip evalStateT s (unQuery action))
-
--- | @runQuery query distdir@. Run a 'Query'. @distdir@ is where Cabal's
--- @setup-config@ file is located.
-runQuery :: Monad m
-         => QueryEnv
-         -> Query m a
-         -> m a
-runQuery qe action = run qe Nothing action
-
-getSlbi :: MonadQuery m => m SomeLocalBuildInfo
-getSlbi = do
-  s <- get
-  case s of
-    Nothing -> do
-            slbi <- getSomeConfigState
-            put (Just slbi)
-            return slbi
-    Just slbi -> return slbi
-
--- | List of package databases to use.
-packageDbStack :: MonadIO m => Query m [ChPkgDb]
-
--- | Modules or files Cabal would have the compiler build directly. Can be used
--- to compute the home module closure for a component.
-entrypoints   :: MonadIO m => Query m [(ChComponentName, ChEntrypoint)]
-
--- | A component's @source-dirs@ field, beware as if this is empty implicit
--- behaviour in GHC kicks in.
-sourceDirs    :: MonadIO m => Query m [(ChComponentName, [FilePath])]
-
--- | All options cabal would pass to GHC.
-ghcOptions    :: MonadIO m => Query m [(ChComponentName, [String])]
-
--- | Only search path related GHC options.
-ghcSrcOptions :: MonadIO m => Query m [(ChComponentName, [String])]
-
--- | Only package related GHC options, sufficient for things don't need to
--- access any home modules.
-ghcPkgOptions :: MonadIO m => Query m [(ChComponentName, [String])]
-
--- | Like @ghcPkgOptions@ but for the whole package not just one component
-ghcMergedPkgOptions :: MonadIO m => Query m [String]
-
--- | Only language related options, i.e. @-XSomeExtension@
-ghcLangOptions :: MonadIO m => Query m [(ChComponentName, [String])]
-
--- | Get the licenses of the packages the current project is linking against.
-pkgLicenses :: MonadIO m => Query m [(String, [(String, Version)])]
-
--- | Flag definitions from cabal file
-flags :: MonadIO m => Query m [(String, Bool)]
-
--- | Flag assignments from setup-config
-configFlags :: MonadIO m => Query m [(String, Bool)]
-
--- | Flag assignments from setup-config which differ from the default
--- setting. This can also include flags which cabal decided to modify,
--- i.e. don't rely on these being the flags set by the user directly.
-nonDefaultConfigFlags :: MonadIO m => Query m [(String, Bool)]
-
--- | The version of GHC the project is configured to use
-compilerVersion :: MonadIO m => Query m (String, Version)
-
--- | Package identifier, i.e. package name and version
-packageId :: MonadIO m => Query m (String, Version)
-
-
-packageDbStack      = Query $ slbiPackageDbStack      `liftM` getSlbi
-entrypoints         = Query $ slbiEntrypoints         `liftM` getSlbi
-sourceDirs          = Query $ slbiSourceDirs          `liftM` getSlbi
-ghcOptions          = Query $ slbiGhcOptions          `liftM` getSlbi
-ghcSrcOptions       = Query $ slbiGhcSrcOptions       `liftM` getSlbi
-ghcPkgOptions       = Query $ slbiGhcPkgOptions       `liftM` getSlbi
-ghcMergedPkgOptions = Query $ slbiGhcMergedPkgOptions `liftM` getSlbi
-ghcLangOptions      = Query $ slbiGhcLangOptions      `liftM` getSlbi
-pkgLicenses         = Query $ slbiPkgLicenses         `liftM` getSlbi
-flags               = Query $ slbiFlags               `liftM` getSlbi
-configFlags         = Query $ slbiConfigFlags         `liftM` getSlbi
-nonDefaultConfigFlags = Query $ slbiNonDefaultConfigFlags `liftM` getSlbi
-compilerVersion     = Query $ slbiCompilerVersion     `liftM` getSlbi
-packageId           = Query $ getPackageId
-
--- | Run @cabal configure@
-reconfigure :: MonadIO m
-            => (FilePath -> [String] -> String -> IO String)
-            -> Programs -- ^ Program paths
-            -> [String] -- ^ Command line arguments to be passed to @cabal@
-            -> m ()
-reconfigure readProc progs cabalOpts = do
-    let progOpts =
-            [ "--with-ghc=" ++ ghcProgram progs ]
-            -- Only pass ghc-pkg if it was actually set otherwise we
-            -- might break cabal's guessing logic
-            ++ if ghcPkgProgram progs /= "ghc-pkg"
-                 then [ "--with-ghc-pkg=" ++ ghcPkgProgram progs ]
-                 else []
-            ++ cabalOpts
-    _ <- liftIO $ readProc (cabalProgram progs) ("configure":progOpts) ""
-    return ()
-
-readHelper :: (MonadIO m, MonadQuery m) => [String] -> m [Maybe ChResponse]
-readHelper args = ask >>= \qe -> liftIO $ do
-  out <- either error id <$> invokeHelper qe args
-  let res = read out
-  liftIO $ evaluate res `E.catch` \se@(SomeException _) -> do
-      md <- lookupEnv' "CABAL_HELPER_DEBUG"
-      let msg = "readHelper: exception: '" ++ show se ++ "'"
-      error $ msg ++ case md of
-        Nothing -> ", for more information set the environment variable CABAL_HELPER_DEBUG"
-        Just _ -> ", output: '"++ out ++"'"
-
-invokeHelper :: QueryEnv -> [String] -> IO (Either String String)
-invokeHelper QueryEnv {..} args = do
-  let progArgs = [ "--with-ghc="     ++ ghcProgram qePrograms
-                 , "--with-ghc-pkg=" ++ ghcPkgProgram qePrograms
-                 , "--with-cabal="   ++ cabalProgram qePrograms
-                 ]
-  exe  <- findLibexecExe
-  let args' = progArgs ++ qeProjectDir:qeDistDir:args
-  out <- qeReadProcess exe args' ""
-  (Right <$> evaluate out) `E.catch` \(SomeException _) ->
-      return $ Left $ concat
-                 ["invokeHelper", ": ", exe, " "
-                 , intercalate " " (map show args')
-                 , " failed"
-                 ]
-
-
-getPackageId :: MonadQuery m => m (String, Version)
-getPackageId = ask >>= \QueryEnv {..} -> do
-  [ Just (ChResponseVersion pkgName pkgVer) ] <- readHelper [ "package-id" ]
-  return (pkgName, pkgVer)
-
-
-getSomeConfigState :: MonadQuery m => m SomeLocalBuildInfo
-getSomeConfigState = ask >>= \QueryEnv {..} -> do
-  res <- readHelper
-         [ "package-db-stack"
-         , "entrypoints"
-         , "source-dirs"
-         , "ghc-options"
-         , "ghc-src-options"
-         , "ghc-pkg-options"
-         , "ghc-merged-pkg-options"
-         , "ghc-lang-options"
-         , "licenses"
-         , "flags"
-         , "config-flags"
-         , "non-default-config-flags"
-         , "compiler-version"
-         ]
-  let [ Just (ChResponsePkgDbs pkgDbs),
-        Just (ChResponseEntrypoints eps),
-        Just (ChResponseCompList srcDirs),
-        Just (ChResponseCompList ghcOpts),
-        Just (ChResponseCompList ghcSrcOpts),
-        Just (ChResponseCompList ghcPkgOpts),
-        Just (ChResponseList     ghcMergedPkgOpts),
-        Just (ChResponseCompList ghcLangOpts),
-        Just (ChResponseLicenses pkgLics),
-        Just (ChResponseFlags fls),
-        Just (ChResponseFlags cfls),
-        Just (ChResponseFlags ndcfls),
-        Just (ChResponseVersion comp compVer)
-        ] = res
-
-  return $ SomeLocalBuildInfo
-    pkgDbs eps srcDirs ghcOpts ghcSrcOpts ghcPkgOpts ghcMergedPkgOpts ghcLangOpts pkgLics fls cfls ndcfls (comp, compVer)
-
-prepare :: MonadIO m
-        => (FilePath -> [String] -> String -> IO String)
-        -> FilePath
-        -> FilePath
-        -> m ()
-prepare readProc projdir distdir = liftIO $ do
-  exe  <- findLibexecExe
-  void $ readProc exe [projdir, distdir] ""
-
-{-# DEPRECATED prepare
-  "Will be replaced by prepare' in the next major release" #-}
-
--- | Make sure the appropriate helper executable for the given project is
--- installed and ready to run queries.
-prepare' :: MonadIO m => QueryEnv -> m ()
-prepare' qe =
-  liftIO $ void $ invokeHelper qe []
-
-writeAutogenFiles :: MonadIO m
-                  => (FilePath -> [String] -> String -> IO String)
-                  -> FilePath
-                  -- ^ Path to project directory, i.e. the one containing the
-                  -- @project.cabal@ file
-                  -> FilePath
-                  -- ^ Path to the @dist/@ directory
-                  -> m ()
-writeAutogenFiles readProc projdir distdir = liftIO $ do
-  exe  <- findLibexecExe
-  void $ readProc exe [projdir, distdir, "write-autogen-files"] ""
-
-{-# DEPRECATED writeAutogenFiles
-  "Will be replaced by writeAutogenFiles' in the next major release" #-}
-
--- | Create @cabal_macros.h@ and @Paths_\<pkg\>@ possibly other generated files
--- in the usual place.
-writeAutogenFiles' :: MonadIO m => QueryEnv -> m ()
-writeAutogenFiles' qe  =
-  liftIO $ void $ invokeHelper qe ["write-autogen-files"]
-
--- | Get the path to the sandbox package-db in a project
-getSandboxPkgDb :: (FilePath -> [String] -> String -> IO String)
-             -> FilePath
-             -- ^ Cabal build platform, i.e. @buildPlatform@
-             -> Version
-             -- ^ GHC version (@cProjectVersion@ is your friend)
-             -> IO (Maybe FilePath)
-getSandboxPkgDb readProc =
-    CabalHelper.Sandbox.getSandboxPkgDb $ unsafePerformIO $ buildPlatform readProc
-
-buildPlatform :: (FilePath -> [String] -> String -> IO String) -> IO String
-buildPlatform readProc = do
-  exe  <- findLibexecExe
-  CabalHelper.Sandbox.dropWhileEnd isSpace <$> readProc exe ["print-build-platform"] ""
-
--- | This exception is thrown by all 'runQuery' functions if the internal
--- wrapper executable cannot be found. You may catch this and present the user
--- an appropriate error message however the default is to print
--- 'libexecNotFoundError'.
-data LibexecNotFoundError = LibexecNotFoundError String FilePath
-                          deriving (Typeable)
-instance Exception LibexecNotFoundError
-instance Show LibexecNotFoundError where
-  show (LibexecNotFoundError exe dir) =
-    libexecNotFoundError exe dir "https://github.com/DanielG/cabal-helper/issues"
-
-findLibexecExe :: IO FilePath
-findLibexecExe = do
-    libexecdir <- getLibexecDir
-    let exeName = "cabal-helper-wrapper"
-        exe = libexecdir </> exeName <.> exeExtension'
-
-    exists <- doesFileExist exe
-
-    if exists
-       then return exe
-       else do
-         mdir <- tryFindCabalHelperTreeLibexecDir
-         case mdir of
-           Nothing ->
-               error $ throw $ LibexecNotFoundError exeName libexecdir
-           Just dir ->
-               return $ dir </> "dist" </> "build" </> exeName </> exeName
-
-tryFindCabalHelperTreeLibexecDir :: IO (Maybe FilePath)
-tryFindCabalHelperTreeLibexecDir = do
-  exe <- getExecutablePath'
-  dir <- case takeFileName exe of
-    "ghc" -> do -- we're probably in ghci; try CWD
-        getCurrentDirectory
-    _ ->
-        return $ (!!4) $ iterate takeDirectory exe
-  exists <- doesFileExist $ dir </> "cabal-helper.cabal"
-  return $ if exists
-             then Just dir
-             else Nothing
-
-libexecNotFoundError :: String   -- ^ Name of the executable we were trying to
-                                 -- find
-                     -> FilePath -- ^ Path to @$libexecdir@
-                     -> String   -- ^ URL the user will be directed towards to
-                                 -- report a bug.
-                     -> String
-libexecNotFoundError exe dir reportBug = printf
- ( "Could not find $libexecdir/%s\n"
- ++"\n"
- ++"If you are a developer set the environment variable\n"
- ++"`cabal_helper_libexecdir' to override $libexecdir[1]. The following will\n"
- ++"work in the cabal-helper source tree:\n"
- ++"\n"
- ++"    $ export cabal_helper_libexecdir=$PWD/dist/build/%s\n"
- ++"\n"
- ++"[1]: %s\n"
- ++"\n"
- ++"If you don't know what I'm talking about something went wrong with your\n"
- ++"installation. Please report this problem here:\n"
- ++"\n"
- ++"    %s") exe exe dir reportBug
-
-getExecutablePath' :: IO FilePath
-getExecutablePath' =
-#if MIN_VERSION_base(4,6,0)
-    getExecutablePath
-#else
-    getProgName
-#endif
-
-lookupEnv' :: String -> IO (Maybe String)
-lookupEnv' k = lookup k <$> getEnvironment
-
-exeExtension' :: FilePath
-exeExtension' = Distribution.Simple.BuildPaths.exeExtension
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,29 +1,25 @@
 # cabal-helper
-
-`cabal-helper` provides a library which wraps the internal use of
-anexecutable to lift the restrictions imposed by linking against versions of
-GHC before `7.10`. This has the pleasant side effect of isolating the user
-from having to deal with Cabal version changes manually as `cabal-helper`
-can simply recompile it's helper program automatically as needed.
+[![build status](https://gitlab.com/dxld/cabal-helper/badges/master/build.svg)](https://gitlab.com/dxld/cabal-helper/commits/master)
 
-`cabal-helper` uses a wrapper executable to compile the actual cabal-helper
-executable at runtime while linking against an arbitrary version of
-Cabal. This runtime-compiled helper executable is then used to extract
-various bits and peices from Cabal's on disk state (dist/setup-config)
-written by it's configure command.
+Cabal's little helper provides access to build information gathered by `cabal`
+when configuring a project. Specifically we're interested in retrieving enough
+information to bring up a compiler session, using the GHC API, which is similar
+to running `cabal repl` in a project.
 
-In addition to this the wrapper executable also supports installing any
-version of Cabal from hackage in case it cannot be found in any available
-package database. The wrapper installs these instances of the Cabal library
-into a private package database so as to not interfere with the user's
-packages.
+While simple in principle this is complicated by the fact that the information
+Cabal writes to disk is in an unstable format and only really accessible through
+the Cabal API itself.
 
-Furthermore the wrapper supports one special case namely reading a state
-file for Cabal itself. This is needed as Cabal compiles it's Setup.hs using
-itself and not using any version of Cabal installed in any package database.
+Since we do not want to bind the user of a development tool which utilises this
+library to a specific version of Cabal we compile the code which interfaces with
+the Cabal library's API on the user's machine, at runtime, against whichever
+version of Cabal was used to write the on disk information for a given project.
 
-`cabal-helper` can compile with `Cabal >= 1.14` but requires `Cabal >= 1.16`
-at runtime.
+If this version of Cabal is not available on the users machine anymore, which is
+fairly likely since cabal-install is usually linked statically, we have support
+for compiling the Cabal library also. In this case the library is installed into
+a private, isolated, package database in `$XDG_CACHE_HOME/cabal-helper` so as to
+not interfere with the user's package database.
 
 ## IRC
 
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,6 +1,18 @@
-#!/usr/bin/env runhaskell
-{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
+{-# LANGUAGE CPP, RecordWildCards, NamedFieldPuns #-}
+
+#ifndef MIN_VERSION_Cabal
+#define MIN_VERSION_Cabal(x,y,z) 0
+#endif
+
+#if MIN_VERSION_Cabal(2,0,0)
+
+-- https://github.com/haskell/cabal/pull/4501 is upstream in 2.0, yey
 import Distribution.Simple
+main = defaultMain
+
+#else
+
+import Distribution.Simple
 import Distribution.Simple.Utils
 import Distribution.Simple.Setup
 import Distribution.Simple.Install
@@ -11,18 +23,10 @@
 import Distribution.Simple.Program
 import Distribution.PackageDescription
 
-import qualified Data.Map as M
-import Data.Map (Map)
-
-import Control.Arrow
-import Control.Applicative
+import Control.Applicative -- for GHC<7.10
 import Control.Monad
 import Data.List
 import Data.Maybe
-import Data.Version
-import Data.Monoid
-import System.Process
-import System.Exit
 import System.FilePath
 import System.Directory (renameFile)
 
@@ -70,7 +74,7 @@
                -> (PackageDescription -> LocalBuildInfo -> IO ())
                -> IO ()
 xInstallTarget pd lbi cf fn = do
-  let (extended, regular) = partition isInternal (executables pd)
+  let (extended, regular) = partition isExeScopePrivate (executables pd)
 
   let pd_regular = pd { executables = regular }
 
@@ -96,13 +100,16 @@
 
   fn pd_regular lbi
 
+isExeScopePrivate :: Executable -> Bool
+isExeScopePrivate exe =
+  fromMaybe False $ (=="private") <$> lookup "x-scope" fields
  where
-   isInternal :: Executable -> Bool
-   isInternal exe =
-    fromMaybe False $ (=="True") <$> lookup "x-internal" (customFieldsBI $ buildInfo exe)
+   fields = customFieldsBI $ buildInfo exe
 
 onlyExePackageDesc :: [Executable] -> PackageDescription -> PackageDescription
 onlyExePackageDesc exes pd = emptyPackageDescription {
                      package = package pd
                    , executables = exes
                    }
+
+#endif
diff --git a/cabal-helper.cabal b/cabal-helper.cabal
--- a/cabal-helper.cabal
+++ b/cabal-helper.cabal
@@ -1,156 +1,287 @@
 name:                cabal-helper
-version:             0.7.3.0
-synopsis:            Simple interface to some of Cabal's configuration state used by ghc-mod
+version:             0.8.0.0
+synopsis:
+ Simple interface to some of Cabal's configuration state, mainly used by ghc-mod
 description:
-    @cabal-helper@ provides a library which wraps the internal use of an
-    executable to lift the restrictions imposed by linking against versions of
-    GHC before @7.10@. This has the pleasant side effect of isolating the user
-    from having to deal with Cabal version changes manually as @cabal-helper@
-    can simply recompile it's helper program automatically as needed.
-    .
-    @cabal-helper@ uses a wrapper executable to compile the actual cabal-helper
-    executable at runtime while linking against an arbitrary version of
-    Cabal. This runtime-compiled helper executable is then used to extract
-    various bits and peices from Cabal\'s on disk state (dist/setup-config)
-    written by it's configure command.
+    Cabal's little helper provides access to build information gathered by
+    @cabal@ when configuring a project. Specifically we're interested in
+    retrieving enough information to bring up a compiler session, using the GHC
+    API, which is similar to running @cabal repl@ in a project.
     .
-    In addition to this the wrapper executable also supports installing any
-    version of Cabal from hackage in case it cannot be found in any available
-    package database. The wrapper installs these instances of the Cabal library
-    into a private package database so as to not interfere with the user's
-    packages.
+    While simple in principle this is complicated by the fact that the
+    information Cabal writes to disk is in an unstable format and only really
+    accessible through the Cabal API itself.
     .
-    Furthermore the wrapper supports one special case namely reading a state
-    file for Cabal itself. This is needed as Cabal compiles it's Setup.hs using
-    itself and not using any version of Cabal installed in any package database.
+    Since we do not want to bind the user of a development tool which utilises
+    this library to a specific version of Cabal we compile the code which
+    interfaces with the Cabal library's API on the user's machine, at runtime,
+    against whichever version of Cabal was used to write the on disk information
+    for a given project.
     .
-    @cabal-helper@ can compile with @Cabal >= 1.14@ but requires @Cabal >= 1.16@
-    at runtime.
-
+    If this version of Cabal is not available on the users machine anymore,
+    which is fairly likely since cabal-install is usually linked statically, we
+    have support for compiling the Cabal library also. In this case the library
+    is installed into a private, isolated, package database in
+    @$XDG_CACHE_HOME/cabal-helper@ so as to not interfere with the user's
+    package database.
 license:             AGPL-3
 license-file:        LICENSE
+license-files:       LICENSE
 author:              Daniel Gröber <dxld@darkboxed.org>
 maintainer:          dxld@darkboxed.org
 category:            Distribution
 build-type:          Custom
-cabal-version:       >=1.10
+cabal-version:       >=1.14
 extra-source-files:  README.md
-                     CabalHelper/Main.hs
-                     CabalHelper/Licenses.hs
+                     src/CabalHelper/Runtime/*.hs
 
+                     tests/*.hs
+
+                     tests/exelib/*.hs
+                     tests/exelib/*.cabal
+                     tests/exelib/lib/*.hs
+
+                     tests/exeintlib/*.hs
+                     tests/exeintlib/*.cabal
+                     tests/exeintlib/lib/*.hs
+                     tests/exeintlib/intlib/*.hs
+
+                     tests/fliblib/*.hs
+                     tests/fliblib/*.cabal
+                     tests/fliblib/lib/*.hs
+
+                     tests/bkpregex/*.cabal
+                     tests/bkpregex/*.hs
+                     tests/bkpregex/regex-example/*.hs
+                     tests/bkpregex/regex-indef/*.hs
+                     tests/bkpregex/regex-indef/*.hsig
+                     tests/bkpregex/regex-types/Regex/*.hs
+                     tests/bkpregex/str-impls/Str/*.hs
+
 source-repository head
   type:     git
   location: https://github.com/DanielG/cabal-helper.git
 
-Custom-Setup
-  Setup-Depends:         base
-                       , Cabal >= 1.14 && < 1.25
-                       , containers
-                       , filepath
-                       , directory
-                       , process
-                       , template-haskell
-                       , transformers
+custom-setup
+  setup-depends:         base
+                       , Cabal          < 2.1 && >= 2.0 || < 1.25 && >= 1.14
+                       , filepath       < 1.5
+                       , directory      < 1.4
 
+flag dev
+  description: Build development components
+  default:     False
+  manual:      True
+
+
+-- [Note test dependencies] Vaious tests need access to modules used in other
+-- components, unfortunately we can't use Cabal 2.0's new internal libraries for
+-- this because we'd like to support ancient Cabal versions. Instead we just
+-- copy 'hs-source-dirs', 'build-depends' and 'other-modules' fields as
+-- appropriate.
+--
+-- The following awk command will copy paragraphs starting with:
+--     -- Common some-name
+-- to following paragraphs starting with:
+--     -- Instantiate common some-name
+--
+-- $ gawk -i inplace -f scripts/update-cabal-common-section-instantiations.awk cabal-helper.cabal
+
 library
-  exposed-modules:     Distribution.Helper
-  other-modules:       Paths_cabal_helper
-                     , CabalHelper.Types
-                     , CabalHelper.Sandbox
   default-language:    Haskell2010
+  default-extensions:  NondecreasingIndentation
+  hs-source-dirs:      lib, src
+  exposed-modules:     Distribution.Helper
+  other-modules:
+                       CabalHelper.Shared.InterfaceTypes
+                       CabalHelper.Shared.Sandbox
+                       Paths_cabal_helper
   ghc-options:         -Wall
+
   build-depends:       base          < 5    && >= 4.5
-                     , Cabal         < 1.25 && >= 1.14
+  if os(windows)
+    build-depends:     base                    >= 4.7
+  build-depends:       Cabal         < 2.1  && >= 2.0     || < 1.26 && >= 1.14
                      , directory     < 1.4  && >= 1.1.0.2
                      , filepath      < 1.5  && >= 1.3.0.0
                      , transformers  < 0.6  && >= 0.3.0.0
                      , mtl           < 2.3  && >= 2.0
-                     , process       < 1.5  && >= 1.1.0.1
+                     , process       < 1.7  && >= 1.1.0.1
+  if !os(windows)
+    build-depends:     unix          < 2.8  && >= 2.5.1.1
+  build-depends:       unix-compat   < 0.6  && >= 0.4.3.1
+                     , semigroupoids < 5.3  && >= 5.2
                      , ghc-prim
 
+-- [Note ghc-prim]
+-- Exports GHC.Generics in GHC-7.4
+
+
 executable cabal-helper-wrapper
+  main-is:             CabalHelper/Compiletime/Wrapper.hs
+  if flag(dev)
+    ghc-options:         -Wall
+  scope:               private
+  x-scope:             private
+
+  -- Common c-h-wrapper-fields -- See [Note test dependencies]
   default-language:    Haskell2010
+  default-extensions:  NondecreasingIndentation
   other-extensions:    TemplateHaskell
-  main-is:             CabalHelper/Wrapper.hs
-  other-modules:       Paths_cabal_helper
-                       CabalHelper.Types
-                       CabalHelper.Common
-                       CabalHelper.GuessGhc
-                       CabalHelper.Data
-                       CabalHelper.Compile
-                       CabalHelper.Log
-                       CabalHelper.Sandbox
-  ghc-options:         -Wall
-  x-internal:          True
+  hs-source-dirs:      src
+  other-modules:
+                       CabalHelper.Compiletime.Compat.Environment
+                       CabalHelper.Compiletime.Compat.Version
+                       CabalHelper.Compiletime.Compile
+                       CabalHelper.Compiletime.Data
+                       CabalHelper.Compiletime.GuessGhc
+                       CabalHelper.Compiletime.Log
+                       CabalHelper.Compiletime.Types
+                       CabalHelper.Shared.Common
+                       CabalHelper.Shared.InterfaceTypes
+                       CabalHelper.Shared.Sandbox
+                       Paths_cabal_helper
+  build-tool-depends:  cabal-install:cabal
   build-depends:       base             < 5    && >= 4.5
-                     , Cabal            < 1.25 && >= 1.14
+  if os(windows)
+    build-depends:     base                       >= 4.7
+  build-depends:       Cabal            < 2.1  && >= 2.0 || < 1.26 && >= 1.14
                      , bytestring       < 0.11 && >= 0.9.2.1
                      , directory        < 1.4  && >= 1.1.0.2
+                     , exceptions       < 0.9  && >= 0.8.3
                      , filepath         < 1.5  && >= 1.3.0.0
-                     , transformers     < 0.6  && >= 0.3.0.0
                      , mtl              < 2.3  && >= 2.0
-                     , process          < 1.5  && >= 1.1.0.1
+                     , process          < 1.7  && >= 1.1.0.1
+                     , template-haskell < 2.13 && >= 2.7.0.0
                      , temporary        < 1.3  && >= 1.2.0.4
+                     , transformers     < 0.6  && >= 0.3.0.0
+  if !os(windows)
+    build-depends:     unix             < 2.8  && >= 2.5.1.1
+  build-depends:       unix-compat      < 0.6  && >= 0.4.3.1
                      , utf8-string      < 1.1  && >= 1.0.1.1
-
-                     , template-haskell
                      , ghc-prim
+  build-tools:         cabal
 
-test-suite spec
-  default-language:    Haskell2010
+
+test-suite compile-test
   type:                exitcode-stdio-1.0
-  main-is:             Spec.hs
-  other-modules:       CabalHelper.Common
-                       CabalHelper.Compile
-                       CabalHelper.Data
-                       CabalHelper.Log
-                       CabalHelper.Sandbox
-                       CabalHelper.Types
-                       Distribution.Helper
-                       Paths_cabal_helper
-  hs-source-dirs:      tests, .
+  main-is:             CompileTest.hs
+  hs-source-dirs:      tests
   ghc-options:         -Wall
   build-tools:         cabal
+
+  -- Instantiate common c-h-wrapper-fields -- See [Note test dependencies]
+  default-language:    Haskell2010
+  default-extensions:  NondecreasingIndentation
+  other-extensions:    TemplateHaskell
+  hs-source-dirs:      src
+  other-modules:
+                       CabalHelper.Compiletime.Compat.Environment
+                       CabalHelper.Compiletime.Compat.Version
+                       CabalHelper.Compiletime.Compile
+                       CabalHelper.Compiletime.Data
+                       CabalHelper.Compiletime.GuessGhc
+                       CabalHelper.Compiletime.Log
+                       CabalHelper.Compiletime.Types
+                       CabalHelper.Shared.Common
+                       CabalHelper.Shared.InterfaceTypes
+                       CabalHelper.Shared.Sandbox
+                       Paths_cabal_helper
+  build-tool-depends:  cabal-install:cabal
   build-depends:       base             < 5    && >= 4.5
-                     , Cabal            < 1.26 && >= 1.14
+  if os(windows)
+    build-depends:     base                       >= 4.7
+  build-depends:       Cabal            < 2.1  && >= 2.0 || < 1.26 && >= 1.14
                      , bytestring       < 0.11 && >= 0.9.2.1
                      , directory        < 1.4  && >= 1.1.0.2
+                     , exceptions       < 0.9  && >= 0.8.3
                      , filepath         < 1.5  && >= 1.3.0.0
-                     , transformers     < 0.6  && >= 0.3.0.0
                      , mtl              < 2.3  && >= 2.0
-                     , process          < 1.5  && >= 1.1.0.1
+                     , process          < 1.7  && >= 1.1.0.1
+                     , template-haskell < 2.13 && >= 2.7.0.0
                      , temporary        < 1.3  && >= 1.2.0.4
+                     , transformers     < 0.6  && >= 0.3.0.0
+  if !os(windows)
+    build-depends:     unix             < 2.8  && >= 2.5.1.1
+  build-depends:       unix-compat      < 0.6  && >= 0.4.3.1
                      , utf8-string      < 1.1  && >= 1.0.1.1
+                     , ghc-prim
+  build-tools:         cabal
 
-                     -- additional test deps
-                     , extra            < 1.5  && >= 1.4.10
-                     , unix             < 2.8  && >= 2.5.1.0
 
-                     , template-haskell
-                     , ghc-prim
+test-suite ghc-session
+  type:                exitcode-stdio-1.0
+  main-is:             GhcSession.hs
+  hs-source-dirs:      tests
+  ghc-options:         -Wall
+  build-depends:       base             < 5    && >= 4.5
+                     , ghc              < 8.3  && >= 7.4
+                     , ghc-paths        < 0.2  && >= 0.1.0.9
                      , cabal-helper
 
--- TODO: Use cabal_macros.h to replace -D flags by including it in
--- CabalHelper.Data
---
--- executable cabal-helper-main
---   if flag(dev)
---     buildable:         True
---   else
---     buildable:         False
---   default-language:    Haskell2010
---   default-extensions:  NondecreasingIndentation
---   main-is:             CabalHelper/Main.hs
---   other-modules:
---   ghc-options:         -Wall -fno-warn-unused-imports -optP-DCABAL_MAJOR=1 -optP-DCABAL_MINOR=25 -optP-DCABAL_HELPER=1 -optP-DCABAL_HELPER_DEV=1
---   build-depends:       base
---                      , Cabal
---                      , containers
---                      , bytestring
---                      , filepath
---                      , directory
---
--- flag dev
---   description: Build development components
---   default:     False
---   manual:      True
+  -- Instantiate common c-h-wrapper-fields -- See [Note test dependencies]
+  default-language:    Haskell2010
+  default-extensions:  NondecreasingIndentation
+  other-extensions:    TemplateHaskell
+  hs-source-dirs:      src
+  other-modules:
+                       CabalHelper.Compiletime.Compat.Environment
+                       CabalHelper.Compiletime.Compat.Version
+                       CabalHelper.Compiletime.Compile
+                       CabalHelper.Compiletime.Data
+                       CabalHelper.Compiletime.GuessGhc
+                       CabalHelper.Compiletime.Log
+                       CabalHelper.Compiletime.Types
+                       CabalHelper.Shared.Common
+                       CabalHelper.Shared.InterfaceTypes
+                       CabalHelper.Shared.Sandbox
+                       Paths_cabal_helper
+  build-tool-depends:  cabal-install:cabal
+  build-depends:       base             < 5    && >= 4.5
+  if os(windows)
+    build-depends:     base                       >= 4.7
+  build-depends:       Cabal            < 2.1  && >= 2.0 || < 1.26 && >= 1.14
+                     , bytestring       < 0.11 && >= 0.9.2.1
+                     , directory        < 1.4  && >= 1.1.0.2
+                     , exceptions       < 0.9  && >= 0.8.3
+                     , filepath         < 1.5  && >= 1.3.0.0
+                     , mtl              < 2.3  && >= 2.0
+                     , process          < 1.7  && >= 1.1.0.1
+                     , template-haskell < 2.13 && >= 2.7.0.0
+                     , temporary        < 1.3  && >= 1.2.0.4
+                     , transformers     < 0.6  && >= 0.3.0.0
+  if !os(windows)
+    build-depends:     unix             < 2.8  && >= 2.5.1.1
+  build-depends:       unix-compat      < 0.6  && >= 0.4.3.1
+                     , utf8-string      < 1.1  && >= 1.0.1.1
+                     , ghc-prim
+  build-tools:         cabal
+
+
+executable cabal-helper-main
+  default-language:    Haskell2010
+  default-extensions:  NondecreasingIndentation
+  main-is:             CabalHelper/Runtime/Main.hs
+  hs-source-dirs:      src
+  other-modules:
+                       CabalHelper.Shared.Common
+                       CabalHelper.Shared.InterfaceTypes
+                       CabalHelper.Shared.Sandbox
+
+  -- GHC 7.4 ships Cabal-1.14 which we can't compile with and since cabal can't
+  -- solve the component dependencies seperately in our case (because of too-old
+  -- cabal-install and build-type:custom) we can't build this on GHC 7.4
+  if flag(dev) && impl(ghc >= 7.6)
+    buildable:         True
+  else
+    buildable:         False
+
+  -- Common c-h-main-fields -- See [Note test dependencies]
+  ghc-options:         -Wall -fno-warn-unused-imports
+  build-depends:       base             < 5    && >= 4.5
+                     , Cabal
+                     , containers
+                     , bytestring
+                     , filepath
+                     , directory
+                     , ghc-prim
diff --git a/lib/Distribution/Helper.hs b/lib/Distribution/Helper.hs
new file mode 100644
--- /dev/null
+++ b/lib/Distribution/Helper.hs
@@ -0,0 +1,528 @@
+-- ghc-mod: Making Haskell development *more* fun
+-- Copyright (C) 2015,2017  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-# LANGUAGE CPP, RecordWildCards, FlexibleContexts, ConstraintKinds,
+  GeneralizedNewtypeDeriving, DeriveDataTypeable, DeriveGeneric, DeriveFunctor
+ #-}
+
+{-|
+Module      : Distribution.Helper
+License     : AGPL-3
+Maintainer  : dxld@darkboxed.org
+Portability : POSIX
+-}
+
+module Distribution.Helper (
+  -- * Running Queries
+    Query
+  , runQuery
+
+  -- * Queries against Cabal\'s on disk state
+
+  -- ** Package queries
+  , packageId
+  , packageDbStack
+  , packageFlags
+  , compilerVersion
+
+  , ghcMergedPkgOptions
+
+  -- ** cabal-install queries
+  , configFlags
+  , nonDefaultConfigFlags
+
+
+  -- ** Component queries
+  , ComponentQuery
+  , components
+
+  , ghcSrcOptions
+  , ghcPkgOptions
+  , ghcLangOptions
+  , ghcOptions
+  , sourceDirs
+  , entrypoints
+  , needsBuildOutput
+
+  -- * Query environment
+  , QueryEnv
+  , mkQueryEnv
+  , qeReadProcess
+  , qePrograms
+  , qeProjectDir
+  , qeDistDir
+  , qeCabalPkgDb
+  , qeCabalVer
+
+  , Programs(..)
+  , defaultPrograms
+
+
+  -- * Result types
+  , ChModuleName(..)
+  , ChComponentName(..)
+  , ChPkgDb(..)
+  , ChEntrypoint(..)
+  , NeedsBuildOutput(..)
+
+  -- * General information
+  , buildPlatform
+
+  -- * Stuff that cabal-install really should export
+  , Distribution.Helper.getSandboxPkgDb
+
+  -- * Managing @dist/@
+  , prepare
+  , reconfigure
+  , writeAutogenFiles
+
+  -- * $libexec related error handling
+  , LibexecNotFoundError(..)
+  , libexecNotFoundError
+
+  -- * Reexports
+  , module Data.Functor.Apply
+  ) where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.State.Strict
+import Control.Monad.Reader
+import Control.Exception as E
+import Data.Char
+import Data.List
+import Data.Maybe
+import Data.Version
+import Data.Typeable
+import Data.Functor.Apply
+import Distribution.Simple.BuildPaths (exeExtension)
+import System.Environment
+import System.FilePath hiding ((<.>))
+import qualified System.FilePath as FP
+import System.Directory
+import System.Process
+import System.IO.Unsafe
+import Text.Printf
+import GHC.Generics
+import Prelude
+
+import Paths_cabal_helper (getLibexecDir)
+import CabalHelper.Shared.InterfaceTypes
+import CabalHelper.Shared.Sandbox
+
+-- | Paths or names of various programs we need.
+data Programs = Programs {
+      -- | The path to the @cabal@ program.
+      cabalProgram  :: FilePath,
+
+      -- | The path to the @ghc@ program.
+      ghcProgram    :: FilePath,
+
+      -- | The path to the @ghc-pkg@ program. If
+      -- not changed it will be derived from the path to 'ghcProgram'.
+      ghcPkgProgram :: FilePath
+    } deriving (Eq, Ord, Show, Read, Generic, Typeable)
+
+-- | Default all programs to their unqualified names, i.e. they will be searched
+-- for on @PATH@.
+defaultPrograms :: Programs
+defaultPrograms = Programs "cabal" "ghc" "ghc-pkg"
+
+-- | Environment for running a 'Query'. The real constructor is not exposed,
+-- the field accessors are however. See below. Use the 'mkQueryEnv' smart
+-- constructor to construct one.
+data QueryEnv = QueryEnv {
+      -- | Field accessor for 'QueryEnv'. Defines how to start the cabal-helper
+      --  process. Useful if you need to capture stderr output from the helper.
+      qeReadProcess :: FilePath -> [String] -> String -> IO String,
+
+      -- | Field accessor for 'QueryEnv'.
+      qePrograms    :: Programs,
+
+      -- | Field accessor for 'QueryEnv'. Defines path to the project directory,
+      -- i.e. a directory containing a @project.cabal@ file
+      qeProjectDir  :: FilePath,
+
+
+      -- | Field accessor for 'QueryEnv'. Defines path to the @dist/@ directory,
+      -- /builddir/ in Cabal terminology.
+      qeDistDir     :: FilePath,
+
+      -- | Field accessor for 'QueryEnv'. Defines where to look for the Cabal
+      -- library when linking the helper.
+      qeCabalPkgDb  :: Maybe FilePath,
+
+      -- | Field accessor for 'QueryEnv'. If @dist/setup-config@ wasn\'t written
+      -- by this version of Cabal an error is thrown when running the query.
+      qeCabalVer    :: Maybe Version
+    }
+
+-- | @mkQueryEnv projdir distdir@. Smart constructor for 'QueryEnv'.
+-- Sets fields 'qeProjectDir' and 'qeDistDir' to @projdir@ and @distdir@
+-- respectively and provides sensible defaults for the other fields.
+mkQueryEnv :: FilePath
+           -- ^ Path to the project directory, i.e. the directory containing a
+           -- @project.cabal@ file
+           -> FilePath
+           -- ^ Path to the @dist/@ directory, called /builddir/ in Cabal
+           -- terminology.
+           -> QueryEnv
+mkQueryEnv projdir distdir = QueryEnv {
+    qeReadProcess = readProcess
+  , qePrograms    = defaultPrograms
+  , qeProjectDir  = projdir
+  , qeDistDir     = distdir
+  , qeCabalPkgDb  = Nothing
+  , qeCabalVer    = Nothing
+  }
+
+data SomeLocalBuildInfo = SomeLocalBuildInfo {
+      slbiPackageDbStack      :: [ChPkgDb],
+      slbiPackageFlags        :: [(String, Bool)],
+      slbiCompilerVersion     :: (String, Version),
+
+      slbiGhcMergedPkgOptions :: [String],
+
+      slbiConfigFlags         :: [(String, Bool)],
+      slbiNonDefaultConfigFlags :: [(String, Bool)],
+
+      slbiGhcSrcOptions       :: [(ChComponentName, [String])],
+      slbiGhcPkgOptions       :: [(ChComponentName, [String])],
+      slbiGhcLangOptions      :: [(ChComponentName, [String])],
+      slbiGhcOptions          :: [(ChComponentName, [String])],
+
+      slbiSourceDirs          :: [(ChComponentName, [String])],
+      slbiEntrypoints         :: [(ChComponentName, ChEntrypoint)],
+      slbiNeedsBuildOutput    :: [(ChComponentName, NeedsBuildOutput)]
+    } deriving (Eq, Ord, Read, Show)
+
+-- | A lazy, cached, query against a package's Cabal configuration. Use
+-- 'runQuery' to execute it.
+newtype Query m a = Query { unQuery :: StateT (Maybe SomeLocalBuildInfo)
+                                         (ReaderT QueryEnv m) a }
+    deriving (Functor, Applicative, Monad, MonadIO)
+
+instance MonadTrans Query where
+    lift = Query . lift . lift
+
+type MonadQuery m = ( MonadIO m
+                    , MonadState (Maybe SomeLocalBuildInfo) m
+                    , MonadReader QueryEnv m)
+
+-- | A 'Query' to run on all components of a package. Use 'components' to get a
+-- regular 'Query'.
+newtype ComponentQuery m a = ComponentQuery (Query m [(ChComponentName, a)])
+    deriving (Functor)
+
+instance (Functor m, Monad m) => Apply (ComponentQuery m) where
+    ComponentQuery flab <.> ComponentQuery fla =
+        ComponentQuery $ liftM2 go flab fla
+      where
+        go :: [(ChComponentName, a -> b)]
+           -> [(ChComponentName, a)]
+           -> [(ChComponentName, b)]
+        go lab la =
+            [ (cn, ab a)
+            | (cn,  ab) <- lab
+            , (cn', a)  <- la
+            , cn == cn'
+            ]
+
+run :: Monad m => QueryEnv -> Maybe SomeLocalBuildInfo -> Query m a -> m a
+run e s action = flip runReaderT e (flip evalStateT s (unQuery action))
+
+-- | @runQuery env query@. Run a 'Query' under a given 'QueryEnv'.
+runQuery :: Monad m
+         => QueryEnv
+         -> Query m a
+         -> m a
+runQuery qe action = run qe Nothing action
+
+getSlbi :: MonadQuery m => m SomeLocalBuildInfo
+getSlbi = do
+  s <- get
+  case s of
+    Nothing -> do
+            slbi <- getSomeConfigState
+            put (Just slbi)
+            return slbi
+    Just slbi -> return slbi
+
+-- | List of package databases to use.
+packageDbStack :: MonadIO m => Query m [ChPkgDb]
+
+-- | Like @ghcPkgOptions@ but for the whole package not just one component
+ghcMergedPkgOptions :: MonadIO m => Query m [String]
+
+-- | Flag definitions from cabal file
+packageFlags :: MonadIO m => Query m [(String, Bool)]
+
+-- | Flag assignments from setup-config
+configFlags :: MonadIO m => Query m [(String, Bool)]
+
+-- | Flag assignments from setup-config which differ from the default
+-- setting. This can also include flags which cabal decided to modify,
+-- i.e. don't rely on these being the flags set by the user directly.
+nonDefaultConfigFlags :: MonadIO m => Query m [(String, Bool)]
+
+-- | The version of GHC the project is configured to use
+compilerVersion :: MonadIO m => Query m (String, Version)
+
+-- | Package identifier, i.e. package name and version
+packageId :: MonadIO m => Query m (String, Version)
+
+-- | Run a ComponentQuery on all components of the package.
+components :: Monad m => ComponentQuery m (ChComponentName -> b) -> Query m [b]
+components (ComponentQuery sc) = map (\(cn, f) -> f cn) `liftM` sc
+
+-- | Modules or files Cabal would have the compiler build directly. Can be used
+-- to compute the home module closure for a component.
+entrypoints   :: MonadIO m => ComponentQuery m ChEntrypoint
+
+-- | The component has a non-default module renaming, so needs build output ().
+needsBuildOutput :: MonadIO m => ComponentQuery m NeedsBuildOutput
+
+-- | A component's @source-dirs@ field, beware since if this is empty implicit
+-- behaviour in GHC kicks in.
+sourceDirs    :: MonadIO m => ComponentQuery m [FilePath]
+
+-- | All options Cabal would pass to GHC.
+ghcOptions    :: MonadIO m => ComponentQuery m [String]
+
+-- | Only search path related GHC options.
+ghcSrcOptions :: MonadIO m => ComponentQuery m [String]
+
+-- | Only package related GHC options, sufficient for things don't need to
+-- access any home modules.
+ghcPkgOptions :: MonadIO m => ComponentQuery m [String]
+
+-- | Only language related options, i.e. @-XSomeExtension@
+ghcLangOptions :: MonadIO m => ComponentQuery m [String]
+
+packageId             = Query $ getPackageId
+packageDbStack        = Query $ slbiPackageDbStack        `liftM` getSlbi
+packageFlags          = Query $ slbiPackageFlags          `liftM` getSlbi
+compilerVersion       = Query $ slbiCompilerVersion       `liftM` getSlbi
+ghcMergedPkgOptions   = Query $ slbiGhcMergedPkgOptions   `liftM` getSlbi
+configFlags           = Query $ slbiConfigFlags           `liftM` getSlbi
+nonDefaultConfigFlags = Query $ slbiNonDefaultConfigFlags `liftM` getSlbi
+
+ghcSrcOptions    = ComponentQuery $ Query $ slbiGhcSrcOptions    `liftM` getSlbi
+ghcPkgOptions    = ComponentQuery $ Query $ slbiGhcPkgOptions    `liftM` getSlbi
+ghcOptions       = ComponentQuery $ Query $ slbiGhcOptions       `liftM` getSlbi
+ghcLangOptions   = ComponentQuery $ Query $ slbiGhcLangOptions   `liftM` getSlbi
+sourceDirs       = ComponentQuery $ Query $ slbiSourceDirs       `liftM` getSlbi
+entrypoints      = ComponentQuery $ Query $ slbiEntrypoints      `liftM` getSlbi
+needsBuildOutput = ComponentQuery $ Query $ slbiNeedsBuildOutput `liftM` getSlbi
+
+-- | Run @cabal configure@
+reconfigure :: MonadIO m
+            => (FilePath -> [String] -> String -> IO String)
+            -> Programs -- ^ Program paths
+            -> [String] -- ^ Command line arguments to be passed to @cabal@
+            -> m ()
+reconfigure readProc progs cabalOpts = do
+    let progOpts =
+            [ "--with-ghc=" ++ ghcProgram progs ]
+            -- Only pass ghc-pkg if it was actually set otherwise we
+            -- might break cabal's guessing logic
+            ++ if ghcPkgProgram progs /= "ghc-pkg"
+                 then [ "--with-ghc-pkg=" ++ ghcPkgProgram progs ]
+                 else []
+            ++ cabalOpts
+    _ <- liftIO $ readProc (cabalProgram progs) ("configure":progOpts) ""
+    return ()
+
+readHelper :: (MonadIO m, MonadQuery m) => [String] -> m [Maybe ChResponse]
+readHelper args = ask >>= \qe -> liftIO $ do
+  out <- either error id <$> invokeHelper qe args
+  let res = read out
+  liftIO $ evaluate res `E.catch` \se@(SomeException _) -> do
+      md <- lookupEnv' "CABAL_HELPER_DEBUG"
+      let msg = "readHelper: exception: '" ++ show se ++ "'"
+      error $ msg ++ case md of
+        Nothing -> ", for more information set the environment variable CABAL_HELPER_DEBUG"
+        Just _ -> ", output: '"++ out ++"'"
+
+invokeHelper :: QueryEnv -> [String] -> IO (Either String String)
+invokeHelper QueryEnv {..} args = do
+  let progArgs = [ "--with-ghc="     ++ ghcProgram qePrograms
+                 , "--with-ghc-pkg=" ++ ghcPkgProgram qePrograms
+                 , "--with-cabal="   ++ cabalProgram qePrograms
+                 ]
+  exe  <- findLibexecExe
+  let args' = progArgs ++ qeProjectDir:qeDistDir:args
+  out <- qeReadProcess exe args' ""
+  (Right <$> evaluate out) `E.catch` \(SomeException _) ->
+      return $ Left $ concat
+                 ["invokeHelper", ": ", exe, " "
+                 , intercalate " " (map show args')
+                 , " failed"
+                 ]
+
+getPackageId :: MonadQuery m => m (String, Version)
+getPackageId = ask >>= \QueryEnv {..} -> do
+  [ Just (ChResponseVersion pkgName pkgVer) ] <- readHelper [ "package-id" ]
+  return (pkgName, pkgVer)
+
+getSomeConfigState :: MonadQuery m => m SomeLocalBuildInfo
+getSomeConfigState = ask >>= \QueryEnv {..} -> do
+  res <- readHelper
+         [ "package-db-stack"
+         , "flags"
+         , "compiler-version"
+
+         , "ghc-merged-pkg-options"
+
+         , "config-flags"
+         , "non-default-config-flags"
+
+         , "ghc-src-options"
+         , "ghc-pkg-options"
+         , "ghc-lang-options"
+         , "ghc-options"
+
+         , "source-dirs"
+         , "entrypoints"
+         , "needs-build-output"
+         ]
+  let [ Just (ChResponsePkgDbs      slbiPackageDbStack),
+        Just (ChResponseFlags       slbiPackageFlags),
+        Just (ChResponseVersion     comp compVer),
+
+        Just (ChResponseList        slbiGhcMergedPkgOptions),
+
+        Just (ChResponseFlags       slbiConfigFlags),
+        Just (ChResponseFlags       slbiNonDefaultConfigFlags),
+
+        Just (ChResponseCompList    slbiGhcSrcOptions),
+        Just (ChResponseCompList    slbiGhcPkgOptions),
+        Just (ChResponseCompList    slbiGhcLangOptions),
+        Just (ChResponseCompList    slbiGhcOptions),
+
+        Just (ChResponseCompList    slbiSourceDirs),
+        Just (ChResponseEntrypoints slbiEntrypoints),
+        Just (ChResponseNeedsBuild  slbiNeedsBuildOutput)
+        ] = res
+      slbiCompilerVersion = (comp, compVer)
+  return $ SomeLocalBuildInfo {..}
+
+
+-- | Make sure the appropriate helper executable for the given project is
+-- installed and ready to run queries.
+prepare :: MonadIO m => QueryEnv -> m ()
+prepare qe =
+  liftIO $ void $ invokeHelper qe []
+
+-- | Create @cabal_macros.h@ and @Paths_\<pkg\>@ possibly other generated files
+-- in the usual place.
+writeAutogenFiles :: MonadIO m => QueryEnv -> m ()
+writeAutogenFiles qe  =
+  liftIO $ void $ invokeHelper qe ["write-autogen-files"]
+
+-- | Get the path to the sandbox package-db in a project
+getSandboxPkgDb :: (FilePath -> [String] -> String -> IO String)
+             -> FilePath
+             -- ^ Cabal build platform, i.e. @buildPlatform@
+             -> Version
+             -- ^ GHC version (@cProjectVersion@ is your friend)
+             -> IO (Maybe FilePath)
+getSandboxPkgDb readProc =
+    CabalHelper.Shared.Sandbox.getSandboxPkgDb $ unsafePerformIO $ buildPlatform readProc
+
+buildPlatform :: (FilePath -> [String] -> String -> IO String) -> IO String
+buildPlatform readProc = do
+  exe  <- findLibexecExe
+  CabalHelper.Shared.Sandbox.dropWhileEnd isSpace <$> readProc exe ["print-build-platform"] ""
+
+-- | This exception is thrown by all 'runQuery' functions if the internal
+-- wrapper executable cannot be found. You may catch this and present the user
+-- an appropriate error message however the default is to print
+-- 'libexecNotFoundError'.
+data LibexecNotFoundError = LibexecNotFoundError String FilePath
+                          deriving (Typeable)
+instance Exception LibexecNotFoundError
+instance Show LibexecNotFoundError where
+  show (LibexecNotFoundError exe dir) =
+    libexecNotFoundError exe dir "https://github.com/DanielG/cabal-helper/issues"
+
+findLibexecExe :: IO FilePath
+findLibexecExe = do
+    libexecdir <- getLibexecDir
+    let exeName = "cabal-helper-wrapper"
+        exe = libexecdir </> exeName FP.<.> exeExtension'
+
+    exists <- doesFileExist exe
+
+    if exists
+       then return exe
+       else do
+         mdir <- tryFindCabalHelperTreeLibexecDir
+         case mdir of
+           Nothing ->
+               error $ throw $ LibexecNotFoundError exeName libexecdir
+           Just dir ->
+               return $ dir </> "dist" </> "build" </> exeName </> exeName
+
+tryFindCabalHelperTreeLibexecDir :: IO (Maybe FilePath)
+tryFindCabalHelperTreeLibexecDir = do
+  exe <- getExecutablePath'
+  dir <- case takeFileName exe of
+    "ghc" -> do -- we're probably in ghci; try CWD
+        getCurrentDirectory
+    _ ->
+        return $ (!!4) $ iterate takeDirectory exe
+  exists <- doesFileExist $ dir </> "cabal-helper.cabal"
+  return $ if exists
+             then Just dir
+             else Nothing
+
+libexecNotFoundError :: String   -- ^ Name of the executable we were trying to
+                                 -- find
+                     -> FilePath -- ^ Path to @$libexecdir@
+                     -> String   -- ^ URL the user will be directed towards to
+                                 -- report a bug.
+                     -> String
+libexecNotFoundError exe dir reportBug = printf
+ ( "Could not find $libexecdir/%s\n"
+ ++"\n"
+ ++"If you are a cabal-helper developer you can set the environment variable\n"
+ ++"`cabal_helper_libexecdir' to override $libexecdir[1]. The following will\n"
+ ++"work in the cabal-helper source tree:\n"
+ ++"\n"
+ ++"    $ export cabal_helper_libexecdir=$PWD/dist/build/%s\n"
+ ++"\n"
+ ++"[1]: %s\n"
+ ++"\n"
+ ++"If you don't know what I'm talking about something went wrong with your\n"
+ ++"installation. Please report this problem here:\n"
+ ++"\n"
+ ++"    %s") exe exe dir reportBug
+
+getExecutablePath' :: IO FilePath
+getExecutablePath' =
+#if MIN_VERSION_base(4,6,0)
+    getExecutablePath
+#else
+    getProgName
+#endif
+
+lookupEnv' :: String -> IO (Maybe String)
+lookupEnv' k = lookup k <$> getEnvironment
+
+exeExtension' :: FilePath
+exeExtension' = Distribution.Simple.BuildPaths.exeExtension
diff --git a/src/CabalHelper/Compiletime/Compat/Environment.hs b/src/CabalHelper/Compiletime/Compat/Environment.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Compat/Environment.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE CPP #-}
+module CabalHelper.Compiletime.Compat.Environment where
+
+import qualified System.Environment
+#ifndef mingw32_HOST_OS
+import qualified System.Posix.Env (setEnv)
+#endif
+
+lookupEnv :: String -> IO (Maybe String)
+lookupEnv var =
+  do env <- System.Environment.getEnvironment
+     return (lookup var env)
+
+setEnv :: String -> String -> IO ()
+#ifdef mingw32_HOST_OS
+setEnv = System.Environment.setEnv
+#else
+setEnv k v = System.Posix.Env.setEnv k v True
+#endif
diff --git a/src/CabalHelper/Compiletime/Compat/Version.hs b/src/CabalHelper/Compiletime/Compat/Version.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Compat/Version.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE CPP #-}
+module CabalHelper.Compiletime.Compat.Version
+    ( DataVersion
+    , toDataVersion
+    , fromDataVersion
+    , Data.Version.showVersion
+    ) where
+
+import qualified Data.Version
+import qualified Distribution.Version (Version)
+#if MIN_VERSION_Cabal(2,0,0)
+import qualified Distribution.Version  (versionNumbers, mkVersion)
+#endif
+
+type DataVersion = Data.Version.Version
+
+toDataVersion :: Distribution.Version.Version -> Data.Version.Version
+fromDataVersion :: Data.Version.Version -> Distribution.Version.Version
+#if MIN_VERSION_Cabal(2,0,0)
+toDataVersion v = Data.Version.Version (Distribution.Version.versionNumbers v) []
+fromDataVersion (Data.Version.Version vs _) = Distribution.Version.mkVersion vs
+#else
+toDataVersion = id
+fromDataVersion = id
+#endif
diff --git a/src/CabalHelper/Compiletime/Compile.hs b/src/CabalHelper/Compiletime/Compile.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Compile.hs
@@ -0,0 +1,691 @@
+-- Copyright (C) 2015,2017  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+{-# LANGUAGE RecordWildCards, FlexibleContexts, NamedFieldPuns, DeriveFunctor,
+GADTs #-}
+
+{-|
+Module      : CabalHelper.Compiletime.Compile
+Description : Runtime compilation machinery
+License     : AGPL-3
+-}
+
+module CabalHelper.Compiletime.Compile where
+
+import Control.Applicative
+import Control.Arrow
+import Control.Exception as E
+import Control.Monad
+import Control.Monad.Trans.Maybe
+import Control.Monad.IO.Class
+import Data.Char
+import Data.List
+import Data.Maybe
+import Data.String
+import Data.Version
+import GHC.IO.Exception (IOErrorType(OtherError))
+import Text.Printf
+import System.Directory
+import System.FilePath
+import System.Process
+import System.Exit
+import System.IO
+import System.IO.Error
+import System.IO.Temp
+import Prelude
+
+import Distribution.System (buildPlatform)
+import Distribution.Text (display)
+
+import Paths_cabal_helper (version)
+import CabalHelper.Compiletime.Data
+import CabalHelper.Compiletime.Log
+import CabalHelper.Compiletime.Types
+import CabalHelper.Shared.Common
+import CabalHelper.Shared.Sandbox (getSandboxPkgDb)
+
+data Compile
+    = CompileWithCabalSource
+      { compCabalSourceDir     :: CabalSourceDir
+      , compCabalSourceVersion :: Version
+      }
+    | CompileWithCabalPackage
+      { compPackageDb      :: Maybe PackageDbDir
+      , compCabalVersion   :: CabalVersion
+      , compPackageDeps    :: [String]
+      , compProductTarget  :: CompilationProductScope
+      }
+
+data CompPaths = CompPaths
+    { compSrcDir  :: FilePath
+    , compOutDir  :: FilePath
+    , compExePath :: FilePath
+    }
+
+-- | The Helper executable we produce as a compilation product can either be
+-- placed in a per-project location, or a per-user/global location in the user's
+-- home directory. This type controls where the compilation process places the
+-- executable.
+data CompilationProductScope = CPSGlobal | CPSProject
+
+compileHelper :: Options -> Version -> FilePath -> FilePath -> IO (Either ExitCode FilePath)
+compileHelper opts hdrCabalVersion projdir distdir = do
+  Just (prepare, comp) <- runMaybeT $ msum $
+    case oCabalPkgDb opts of
+      Nothing ->
+        [ compileCabalSource
+        , compileSandbox
+        , compileGlobal
+        , MaybeT $ Just <$> compileWithCabalInPrivatePkgDb
+        ]
+      Just db ->
+          [ return $ (return (), compileWithPkg (Just db) hdrCabalVersion CPSProject)
+        ]
+
+  appdir <- appCacheDir
+
+  let cp@CompPaths {compExePath} = compPaths appdir distdir comp
+  exists <- doesFileExist compExePath
+  if exists
+    then do
+      vLog opts $ "helper already compiled, using exe: "++compExePath
+      return (Right compExePath)
+    else do
+      vLog opts $ "helper exe does not exist, compiling"++compExePath
+      prepare >> compile comp cp opts
+
+ where
+   logMsg = "using helper compiled with Cabal from "
+
+-- for relaxed deps: find (sameMajorVersionAs hdrCabalVersion) . reverse . sort
+
+   -- | Check if this version is globally available
+   compileGlobal :: MaybeT IO (IO (), Compile)
+   compileGlobal = do
+       ver <- MaybeT $ find (== hdrCabalVersion) <$> listCabalVersions opts
+       vLog opts $ logMsg ++ "user/global package-db"
+       return $ (return (), compileWithPkg Nothing ver CPSGlobal)
+
+   -- | Check if this version is available in the project sandbox
+   compileSandbox :: MaybeT IO (IO (), Compile)
+   compileSandbox = do
+       let ghcVer = ghcVersion opts
+           mdb_path = getSandboxPkgDb projdir (display buildPlatform) =<< ghcVer
+       sandbox <- PackageDbDir <$> MaybeT mdb_path
+       ver <- MaybeT $ logIOError opts "compileSandbox" $
+         find (== hdrCabalVersion) <$> listCabalVersions' opts (Just sandbox)
+       vLog opts $ logMsg ++ "sandbox package-db"
+       return $ (return (), compileWithPkg (Just sandbox) ver CPSProject)
+
+   -- | Compile the requested Cabal version into an isolated package-db if it's
+   -- not there already
+   compileWithCabalInPrivatePkgDb :: IO (IO (), Compile)
+   compileWithCabalInPrivatePkgDb = do
+       db@(PackageDbDir db_path)
+           <- getPrivateCabalPkgDb opts (CabalVersion hdrCabalVersion)
+       vLog opts $ logMsg ++ "private package-db in " ++ db_path
+       return (prepare db, compileWithPkg (Just db) hdrCabalVersion CPSGlobal)
+     where
+       prepare db = do
+         db_exists <- liftIO $ cabalVersionExistsInPkgDb opts hdrCabalVersion db
+         when (not db_exists) $
+           void $ installCabal opts (Right hdrCabalVersion) `E.catch`
+             \(SomeException _) -> errorInstallCabal hdrCabalVersion distdir
+
+   -- | See if we're in a cabal source tree
+   compileCabalSource :: MaybeT IO (IO (), Compile)
+   compileCabalSource = do
+       let cabalFile = projdir </> "Cabal.cabal"
+       cabalSrc <- liftIO $ doesFileExist cabalFile
+       let projdir' = CabalSourceDir projdir
+       case cabalSrc of
+         False -> mzero
+         True -> do
+           vLog opts $ "projdir looks like Cabal source tree (Cabal.cabal exists)"
+           cf <- liftIO $ readFile cabalFile
+           let buildType = cabalFileBuildType cf
+               ver       = cabalFileVersion cf
+
+           case buildType of
+             "simple" -> do
+                 vLog opts $ "Cabal source tree is build-type:simple, moving on"
+                 mzero
+             "custom" -> do
+                 vLog opts $ "compiling helper with local Cabal source tree"
+                 return $ (return (), compileWithCabalSource projdir' ver)
+             _ -> error $ "compileCabalSource: unknown build-type: '"++buildType++"'"
+
+   compileWithCabalSource srcDir ver =
+       CompileWithCabalSource
+          { compCabalSourceDir       = srcDir
+          , compCabalSourceVersion   = ver
+          }
+
+   compileWithPkg mdb ver target =
+       CompileWithCabalPackage
+          { compPackageDb            = mdb
+          , compCabalVersion         = CabalVersion ver
+          , compPackageDeps          = [cabalPkgId ver]
+          , compProductTarget        = target
+          }
+
+   cabalPkgId v = "Cabal-" ++ showVersion v
+
+compile :: Compile -> CompPaths -> Options -> IO (Either ExitCode FilePath)
+compile comp paths@CompPaths {..} opts@Options {..} = do
+    createDirectoryIfMissing True compOutDir
+    createHelperSources compSrcDir
+
+    vLog opts $ "compSrcDir: " ++ compSrcDir
+    vLog opts $ "compOutDir: " ++ compOutDir
+    vLog opts $ "compExePath: " ++ compExePath
+
+    invokeGhc opts $ compGhcInvocation comp paths
+
+compPaths :: FilePath -> FilePath -> Compile -> CompPaths
+compPaths appdir distdir c =
+    case c of
+      CompileWithCabalPackage {compProductTarget=CPSGlobal,..} -> CompPaths {..}
+        where
+          compSrcDir  = appdir </> exeName compCabalVersion <.> "build"
+          compOutDir  = compSrcDir
+          compExePath = appdir </> exeName compCabalVersion
+
+      CompileWithCabalPackage {compProductTarget=CPSProject,..} -> distdirPaths
+      CompileWithCabalSource {..} -> distdirPaths
+  where
+    distdirPaths = CompPaths {..}
+        where
+          compSrcDir  = distdir </> "cabal-helper"
+          compOutDir  = compSrcDir
+          compExePath = compOutDir </> "cabal-helper"
+
+data GhcInvocation = GhcInvocation
+    { giOutDir          :: FilePath
+    , giOutput          :: FilePath
+    , giCPPOptions      :: [String]
+    , giPackageDBs      :: [PackageDbDir]
+    , giIncludeDirs     :: [FilePath]
+    , giHideAllPackages :: Bool
+    , giPackages        :: [String]
+    , giWarningFlags    :: [String]
+    , giInputs          :: [String]
+    }
+
+compGhcInvocation :: Compile -> CompPaths -> GhcInvocation
+compGhcInvocation comp CompPaths {..} =
+    case comp of
+      CompileWithCabalSource {..} ->
+        GhcInvocation
+          { giIncludeDirs = [compSrcDir, unCabalSourceDir compCabalSourceDir]
+          , giPackageDBs  = []
+          , giHideAllPackages = False
+          , giPackages    = []
+          , giCPPOptions = cppOptions compCabalSourceVersion
+                           ++ [cabalVersionMacro compCabalSourceVersion]
+          , ..
+          }
+      CompileWithCabalPackage {..} ->
+        GhcInvocation
+          { giIncludeDirs = [compSrcDir]
+          , giPackageDBs = maybeToList compPackageDb
+          , giHideAllPackages = True
+          , giPackages =
+              [ "base"
+              , "containers"
+              , "directory"
+              , "filepath"
+              , "process"
+              , "bytestring"
+              , "ghc-prim"
+              ] ++ compPackageDeps
+          , giCPPOptions = cppOptions (unCabalVersion compCabalVersion)
+          , ..
+          }
+  where
+
+    unCabalVersion (CabalVersion ver) = ver
+    unCabalVersion (CabalHEAD _)      = Version [10000000, 0, 0] []
+
+    cppOptions cabalVer =
+        [ "-DCABAL_HELPER=1"
+        , cabalMinVersionMacro cabalVer
+        ]
+
+    giOutDir = compOutDir
+    giOutput = compExePath
+    giWarningFlags = [ "-w" ] -- no point in bothering end users with warnings
+    giInputs = [compSrcDir</>"CabalHelper"</>"Runtime"</>"Main.hs"]
+
+cabalVersionMacro :: Version -> String
+cabalVersionMacro (Version vs _) =
+  "-DCABAL_VERSION="++intercalate "," (map show vs)
+
+cabalMinVersionMacro :: Version -> String
+cabalMinVersionMacro (Version (mj1:mj2:mi:_) _) =
+  "-DCH_MIN_VERSION_Cabal(major1,major2,minor)=\
+  \(  (major1)  < "++show mj1++" \
+  \|| (major1) == "++show mj1++" && (major2)  < "++show mj2++" \
+  \|| (major1) == "++show mj1++" && (major2) == "++show mj2++" && (minor) <= "++show mi++
+  ")"
+cabalMinVersionMacro _ =
+    error "cabalMinVersionMacro: Version must have at least 3 components"
+
+invokeGhc :: Options -> GhcInvocation -> IO (Either ExitCode FilePath)
+invokeGhc opts@Options {..} GhcInvocation {..} = do
+  rv <- callProcessStderr' opts Nothing oGhcProgram $ concat
+    [ [ "-outputdir", giOutDir
+      , "-o", giOutput
+      ]
+    , map ("-optP"++) giCPPOptions
+    , map ("-package-conf="++) $ unPackageDbDir <$> giPackageDBs
+    , map ("-i"++) $ nub $ "" : giIncludeDirs
+    , if giHideAllPackages then ["-hide-all-packages"] else []
+    , concatMap (\p -> ["-package", p]) giPackages
+    , giWarningFlags
+    , ["--make"]
+    , giInputs
+    ]
+  return $
+    case rv of
+      ExitSuccess -> Right giOutput
+      e@(ExitFailure _) -> Left e
+
+
+-- | Cabal library version we're compiling the helper exe against.
+data CabalVersion
+    = CabalHEAD  { cvCommitId   :: CommitId }
+    | CabalVersion { cabalVersion :: Version }
+
+newtype CommitId = CommitId { unCommitId :: String }
+
+exeName :: CabalVersion -> String
+exeName (CabalHEAD commitid) = intercalate "-"
+    [ "cabal-helper" ++ showVersion version
+    , "CabalHEAD" ++ unCommitId commitid
+    ]
+exeName CabalVersion {cabalVersion} = intercalate "-"
+    [ "cabal-helper" ++ showVersion version
+    , "Cabal" ++ showVersion cabalVersion
+    ]
+
+callProcessStderr'
+    :: Options -> Maybe FilePath -> FilePath -> [String] -> IO ExitCode
+callProcessStderr' opts mwd exe args = do
+  let cd = case mwd of
+             Nothing -> []; Just wd -> [ "cd", formatProcessArg wd++";" ]
+  vLog opts $ intercalate " " $ cd ++ map formatProcessArg (exe:args)
+  (_, _, _, h) <- createProcess (proc exe args) { std_out = UseHandle stderr
+                                                , cwd = mwd }
+  waitForProcess h
+
+callProcessStderr :: Options -> Maybe FilePath -> FilePath -> [String] -> IO ()
+callProcessStderr opts mwd exe args = do
+  rv <- callProcessStderr' opts mwd exe args
+  case rv of
+    ExitSuccess -> return ()
+    ExitFailure v -> processFailedException "callProcessStderr" exe args v
+
+processFailedException :: String -> String -> [String] -> Int -> IO a
+processFailedException fn exe args rv =
+    ioError $ mkIOError OtherError msg Nothing Nothing
+  where
+    msg = concat [ fn, ": ", exe, " "
+                 , intercalate " " (map formatProcessArg args)
+                 , " (exit " ++ show rv ++ ")"
+                 ]
+
+formatProcessArg :: String -> String
+formatProcessArg xs
+    | any isSpace xs = "'"++ xs ++"'"
+    | otherwise      = xs
+
+data HEAD = HEAD deriving (Eq, Show)
+
+installCabal :: Options -> Either HEAD Version -> IO (PackageDbDir, CabalVersion)
+installCabal opts ever = do
+  appdir <- appCacheDir
+  let message ver = do
+      let sver = showVersion ver
+      hPutStr stderr $ printf "\
+\cabal-helper-wrapper: Installing a private copy of Cabal because we couldn't\n\
+\find the right version in your global/user package-db, this might take a\n\
+\while but will only happen once per Cabal version you're using.\n\
+\\n\
+\If anything goes horribly wrong just delete this directory and try again:\n\
+\    %s\n\
+\\n\
+\If you want to avoid this automatic installation altogether install\n\
+\version %s of Cabal manually (into your user or global package-db):\n\
+\    $ cabal install Cabal --constraint \"Cabal == %s\"\n\
+\\n\
+\Installing Cabal %s ...\n" appdir sver sver sver
+
+  withSystemTempDirectory "cabal-helper-Cabal-source" $ \tmpdir -> do
+    (srcdir, cabalVer) <- case ever of
+      Left HEAD -> do
+        second CabalHEAD <$> unpackCabalHEAD tmpdir
+      Right ver -> do
+        message ver
+        let patch = fromMaybe nopCabalPatchDescription $
+              find ((ver`elem`) . cpdVersions) patchyCabalVersions
+        (,) <$> unpackPatchedCabal opts ver tmpdir patch <*> pure (CabalVersion ver)
+
+    db <- createPkgDb opts cabalVer
+
+    runCabalInstall opts db srcdir ever
+
+    return (db, cabalVer)
+
+{-
+TODO: If the Cabal version we want to install is less than or equal to one we
+have available, either through act-as-setup or in a package-db we should be able
+to use act-as-setup or build a default Setup.hs exe and patch the Cabal source
+to say build-type:simple. This will sidestep bugs in c-i>=1.24
+
+See conversation in
+https://github.com/haskell/cabal/commit/e2bf243300957321497353a2f85517e464f764ab
+
+Otherwise we might be able to use the shipped Setup.hs
+
+-}
+
+runCabalInstall
+    :: Options -> PackageDbDir -> CabalSourceDir -> Either HEAD Version-> IO ()
+runCabalInstall opts (PackageDbDir db) (CabalSourceDir srcdir) ever = do
+  civ@CabalInstallVersion {..} <- cabalInstallVersion opts
+  cabal_opts <- return $ concat
+      [
+        [ "--package-db=clear"
+        , "--package-db=global"
+        , "--package-db=" ++ db
+        , "--prefix=" ++ db </> "prefix"
+        ]
+        , withGHCProgramOptions opts
+        , if cabalInstallVer >= Version [1,20,0,0] []
+             then ["--no-require-sandbox"]
+             else []
+        , [ "install", srcdir ]
+        , if oVerbose opts
+            then ["-v"]
+            else []
+        , [ "--only-dependencies" ]
+      ]
+
+  callProcessStderr opts (Just "/") (oCabalProgram opts) cabal_opts
+
+  runSetupHs opts db srcdir ever civ
+
+  hPutStrLn stderr "done"
+
+withGHCProgramOptions :: Options -> [String]
+withGHCProgramOptions opts =
+    concat [ [ "--with-ghc=" ++ oGhcProgram opts ]
+           , if oGhcPkgProgram opts /= oGhcPkgProgram defaultOptions
+               then [ "--with-ghc-pkg=" ++ oGhcPkgProgram opts ]
+               else []
+           ]
+
+runSetupHs
+    :: Options
+    -> FilePath
+    -> FilePath
+    -> Either HEAD Version
+    -> CabalInstallVersion
+    -> IO ()
+runSetupHs opts@Options {..} db srcdir ever CabalInstallVersion {..}
+    | cabalInstallVer >= parseVer "1.24" = do
+      go $ \args -> callProcessStderr opts (Just srcdir) oCabalProgram $
+        [ "act-as-setup", "--" ] ++ args
+    | otherwise = do
+      SetupProgram {..} <- compileSetupHs opts db srcdir
+      go $ callProcessStderr opts (Just srcdir) setupProgram
+  where
+    parmake_opt
+        | Right ver <- ever,  ver >= Version [1,20] [] = ["-j"]
+        | otherwise = []
+
+    go :: ([String] -> IO ()) -> IO ()
+    go run = do
+      run $ [ "configure", "--package-db", db, "--prefix", db </> "prefix" ]
+              ++ withGHCProgramOptions opts
+      run $ [ "build" ] ++ parmake_opt
+      run [ "copy" ]
+      run [ "register" ]
+
+
+
+
+newtype SetupProgram = SetupProgram { setupProgram :: FilePath }
+compileSetupHs :: Options -> FilePath -> FilePath -> IO SetupProgram
+compileSetupHs opts db srcdir = do
+  ver <- ghcVersion opts
+  let no_version_macros
+        | ver >= Version [8] [] = [ "-fno-version-macros" ]
+        | otherwise             = []
+
+      file = srcdir </> "Setup"
+
+  callProcessStderr opts (Just srcdir) (oGhcProgram opts) $ concat
+    [ [ "--make"
+      , "-package-conf", db
+      ]
+    , no_version_macros
+    , [ file <.> "hs"
+      , "-o", file
+      ]
+    ]
+  return $ SetupProgram file
+
+data CabalPatchDescription = CabalPatchDescription {
+      cpdVersions      :: [Version],
+      cpdUnpackVariant :: UnpackCabalVariant,
+      cpdPatchFn       :: FilePath -> IO ()
+    }
+nopCabalPatchDescription :: CabalPatchDescription
+nopCabalPatchDescription = CabalPatchDescription [] LatestRevision (const (return ()))
+
+patchyCabalVersions :: [CabalPatchDescription]
+patchyCabalVersions = [
+  let versions  = [ Version [1,18,1] [] ]
+      variant   = Pristine
+      patch     = fixArrayConstraint
+  in CabalPatchDescription versions variant patch,
+
+  let versions  = [ Version [1,18,0] [] ]
+      variant   = Pristine
+      patch dir = do
+        fixArrayConstraint dir
+        fixOrphanInstance dir
+  in CabalPatchDescription versions variant patch,
+
+  let versions  = [ Version [1,24,1,0] [] ]
+      variant   = Pristine
+      patch _   = return ()
+  in CabalPatchDescription versions variant patch
+  ]
+ where
+   fixArrayConstraint dir = do
+     let cabalFile    = dir </> "Cabal.cabal"
+         cabalFileTmp = cabalFile ++ ".tmp"
+
+     cf <- readFile cabalFile
+     writeFile cabalFileTmp $ replace "&& < 0.5" "&& < 0.6" cf
+     renameFile cabalFileTmp cabalFile
+
+   fixOrphanInstance dir = do
+     let versionFile    = dir </> "Distribution/Version.hs"
+         versionFileTmp = versionFile ++ ".tmp"
+
+     let languagePragma =
+           "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}"
+         languagePragmaCPP =
+           "{-# LANGUAGE CPP, DeriveDataTypeable, StandaloneDeriving #-}"
+
+         derivingDataVersion =
+           "deriving instance Data Version"
+         derivingDataVersionCPP = unlines [
+             "#if __GLASGOW_HASKELL__ < 707",
+             derivingDataVersion,
+             "#endif"
+           ]
+
+     vf <- readFile versionFile
+     writeFile versionFileTmp
+       $ replace derivingDataVersion derivingDataVersionCPP
+       $ replace languagePragma languagePragmaCPP vf
+
+     renameFile versionFileTmp versionFile
+
+unpackPatchedCabal
+    :: Options
+    -> Version
+    -> FilePath
+    -> CabalPatchDescription
+    -> IO CabalSourceDir
+unpackPatchedCabal opts cabalVer tmpdir (CabalPatchDescription _ variant patch) = do
+  res@(CabalSourceDir dir) <- unpackCabal opts cabalVer tmpdir variant
+  patch dir
+  return res
+
+data UnpackCabalVariant = Pristine | LatestRevision
+newtype CabalSourceDir = CabalSourceDir { unCabalSourceDir :: FilePath }
+unpackCabal
+    :: Options -> Version -> FilePath -> UnpackCabalVariant -> IO CabalSourceDir
+unpackCabal opts cabalVer tmpdir variant = do
+  let cabal = "Cabal-" ++ showVersion cabalVer
+      dir = tmpdir </> cabal
+      variant_opts = case variant of Pristine -> [ "--pristine" ]; _ -> []
+      args = [ "get", cabal ] ++ variant_opts
+  callProcessStderr opts (Just tmpdir) (oCabalProgram opts) args
+  return $ CabalSourceDir dir
+
+unpackCabalHEAD :: FilePath -> IO (CabalSourceDir, CommitId)
+unpackCabalHEAD tmpdir = do
+  let dir = tmpdir </> "cabal-head.git"
+      url = "https://github.com/haskell/cabal.git"
+  ExitSuccess <- rawSystem "git" [ "clone", "--depth=1", url, dir]
+  commit <-
+      withDirectory_ dir $ trim <$> readProcess "git" ["rev-parse", "HEAD"] ""
+  return (CabalSourceDir $ dir </> "Cabal", CommitId commit)
+ where
+   withDirectory_ :: FilePath -> IO a -> IO a
+   withDirectory_ dir action =
+       bracket
+         (liftIO getCurrentDirectory)
+         (liftIO . setCurrentDirectory)
+         (\_ -> liftIO (setCurrentDirectory dir) >> action)
+
+errorInstallCabal :: Version -> FilePath -> IO a
+errorInstallCabal cabalVer _distdir = panicIO $ printf "\
+\Installing Cabal version %s failed.\n\
+\\n\
+\You have the following choices to fix this:\n\
+\\n\
+\- The easiest way to try and fix this is just reconfigure the project and try\n\
+\  again:\n\
+\        $ cabal clean && cabal configure\n\
+\\n\
+\- If that fails you can try to install the version of Cabal mentioned above\n\
+\  into your global/user package-db somehow, you'll probably have to fix\n\
+\  something otherwise it wouldn't have failed above:\n\
+\        $ cabal install Cabal --constraint 'Cabal == %s'\n\
+\\n\
+\- If you're using `Build-Type: Simple`:\n\
+\  - You can see if you can reinstall your cabal-install executable while\n\
+\    having it linked to a version of Cabal that's available in you\n\
+\    package-dbs or can be built automatically:\n\
+\        $ ghc-pkg list | grep Cabal  # find an available Cabal version\n\
+\            Cabal-W.X.Y.Z\n\
+\        $ cabal install cabal-install --constraint 'Cabal == W.X.*'\n\
+\    Afterwards you'll have to reconfigure your project:\n\
+\        $ cabal clean && cabal configure\n\
+\\n\
+\- If you're using `Build-Type: Custom`:\n\
+\  - Have cabal-install rebuild your Setup.hs executable with a version of the\n\
+\    Cabal library that you have available in your global/user package-db:\n\
+\        $ cabal clean && cabal configure\n\
+\    You might also have to install some version of the Cabal to do this:\n\
+\        $ cabal install Cabal\n\
+\\n" sver sver
+ where
+   sver = showVersion cabalVer
+
+listCabalVersions :: Options -> IO [Version]
+listCabalVersions opts = listCabalVersions' opts Nothing
+
+-- TODO: Include sandbox? Probably only relevant for build-type:custom projects.
+listCabalVersions' :: Options -> Maybe PackageDbDir -> IO [Version]
+listCabalVersions' Options {..} mdb = do
+  let mdbopt = ("--package-conf="++) <$> unPackageDbDir <$> mdb
+      opts = ["list", "--simple-output", "Cabal"] ++ maybeToList mdbopt
+
+  catMaybes . map (fmap snd . parsePkgId . fromString) . words
+          <$> readProcess oGhcPkgProgram opts ""
+
+cabalVersionExistsInPkgDb :: Options -> Version -> PackageDbDir -> IO Bool
+cabalVersionExistsInPkgDb opts cabalVer db@(PackageDbDir db_path) = do
+  exists <- doesDirectoryExist db_path
+  case exists of
+    False -> return False
+    True -> do
+      vers <- listCabalVersions' opts (Just db)
+      return $ cabalVer `elem` vers
+
+ghcVersion :: Options -> IO Version
+ghcVersion Options {..} = do
+    parseVer . trim <$> readProcess oGhcProgram ["--numeric-version"] ""
+
+ghcPkgVersion :: Options -> IO Version
+ghcPkgVersion Options {..} = do
+    parseVer . trim . dropWhile (not . isDigit) <$> readProcess oGhcPkgProgram ["--version"] ""
+
+newtype CabalInstallVersion = CabalInstallVersion { cabalInstallVer :: Version }
+cabalInstallVersion :: Options -> IO CabalInstallVersion
+cabalInstallVersion Options {..} = do
+    CabalInstallVersion . parseVer . trim
+      <$> readProcess oCabalProgram ["--numeric-version"] ""
+
+createPkgDb :: Options -> CabalVersion -> IO PackageDbDir
+createPkgDb opts@Options {..} cabalVer = do
+  db@(PackageDbDir db_path) <- getPrivateCabalPkgDb opts cabalVer
+  exists <- doesDirectoryExist db_path
+  when (not exists) $ callProcessStderr opts Nothing oGhcPkgProgram ["init", db_path]
+  return db
+
+getPrivateCabalPkgDb :: Options -> CabalVersion -> IO PackageDbDir
+getPrivateCabalPkgDb opts cabalVer = do
+  appdir <- appCacheDir
+  ghcVer <- ghcVersion opts
+  let db_path = appdir </> exeName cabalVer
+                ++ "-ghc" ++ showVersion ghcVer
+                ++ ".package-db"
+  return $ PackageDbDir db_path
+
+-- "Cabal" ++ ver ++ "-ghc" ++ showVersion ghcVer
+
+-- | Find @version: XXX@ delcaration in a cabal file
+cabalFileVersion :: String -> Version
+cabalFileVersion = parseVer . cabalFileTopField "version"
+
+-- | Find @build-type: XXX@ delcaration in a cabal file
+cabalFileBuildType :: String -> String
+cabalFileBuildType = cabalFileTopField "build-type"
+
+cabalFileTopField :: String -> String -> String
+cabalFileTopField field cabalFile = value
+ where
+  Just value = extract <$> find ((field++":") `isPrefixOf`) ls
+  ls = map (map toLower) $ lines cabalFile
+  extract = dropWhile (/=':') >>> drop 1 >>> dropWhile isSpace >>> takeWhile (not . isSpace)
diff --git a/src/CabalHelper/Compiletime/Data.hs b/src/CabalHelper/Compiletime/Data.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Data.hs
@@ -0,0 +1,80 @@
+-- Copyright (C) 2015,2017  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -fforce-recomp #-}
+
+{-|
+Module      : CabalHelper.Compiletime.Data
+Description : Embeds source code for runtime component using TH
+License     : AGPL-3
+-}
+
+module CabalHelper.Compiletime.Data where
+
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.Functor
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.UTF8 as UTF8
+import Language.Haskell.TH
+import System.Directory
+import System.FilePath
+import System.IO.Temp
+import System.PosixCompat.Files
+import System.PosixCompat.Time
+import System.PosixCompat.Types
+import Prelude
+
+import CabalHelper.Compiletime.Compat.Environment
+
+withSystemTempDirectoryEnv :: String -> (FilePath -> IO b) -> IO b
+withSystemTempDirectoryEnv tpl f = do
+  m <- liftIO $ lookupEnv "CABAL_HELPER_KEEP_SOURCEDIR"
+  case m of
+    Nothing -> withSystemTempDirectory tpl f
+    Just _  -> do
+           tmpdir <- getCanonicalTemporaryDirectory
+           f =<< createTempDirectory tmpdir tpl
+
+createHelperSources :: FilePath -> IO ()
+createHelperSources dir = do
+    let chdir = dir </> "CabalHelper"
+    liftIO $ do
+      createDirectoryIfMissing True $ chdir </> "Runtime"
+      createDirectoryIfMissing True $ chdir </> "Shared"
+
+    let modtime :: EpochTime
+        modtime = fromIntegral $ (read :: String -> Integer)
+          -- See https://reproducible-builds.org/specs/source-date-epoch/
+          $(runIO $ do
+             msde :: Maybe Integer
+                  <- fmap read <$> lookupEnv "SOURCE_DATE_EPOCH"
+             (current_time :: Integer) <- round . toRational <$> epochTime
+             return $ LitE . StringL $ show $ maybe current_time id msde)
+
+    liftIO $ forM_ sourceFiles $ \(fn, src) -> do
+        let path = chdir </> fn
+        BS.writeFile path $ UTF8.fromString src
+        setFileTimes path modtime modtime
+
+
+sourceFiles :: [(FilePath, String)]
+sourceFiles =
+  [ ("Runtime/Main.hs",     $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Runtime/Main.hs")))
+  , ("Shared/Common.hs",    $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Shared/Common.hs")))
+  , ("Shared/Sandbox.hs",   $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Shared/Sandbox.hs")))
+  , ("Shared/InterfaceTypes.hs",     $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Shared/InterfaceTypes.hs")))
+  ]
diff --git a/src/CabalHelper/Compiletime/GuessGhc.hs b/src/CabalHelper/Compiletime/GuessGhc.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/GuessGhc.hs
@@ -0,0 +1,92 @@
+-- Copyright (c) 2003-2014, Isaac Jones, Simon Marlow, Martin Sjögren,
+--                          Bjorn Bringert, Krasimir Angelov,
+--                          Malcolm Wallace, Ross Patterson, Ian Lynagh,
+--                          Duncan Coutts, Thomas Schilling,
+--                          Johan Tibell, Mikhail Glushenkov
+-- 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 Isaac Jones 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.
+
+{-|
+Module      : CabalHelper.Compiletime.GuessGhc
+Description : Logic for finding @ghc-pkg@ based on path to @ghc@
+License     : BSD3
+-}
+
+module CabalHelper.Compiletime.GuessGhc (guessToolFromGhcPath) where
+
+import Data.Maybe
+import Data.Char
+import Distribution.Simple.BuildPaths
+import System.Directory
+import System.FilePath
+
+guessToolFromGhcPath :: FilePath -- ^ Tool name
+                     -> FilePath -- ^ GHC exe path
+                     -> IO (Maybe FilePath)
+guessToolFromGhcPath toolname ghcPath
+  = do let
+           path              = ghcPath
+           dir               = takeDirectory path
+           versionSuffix     = takeVersionSuffix (dropExeExtension path)
+           guessNormal       = dir </> toolname <.> exeExtension'
+           guessGhcVersioned = dir </> (toolname ++ "-ghc" ++ versionSuffix)
+                               <.> exeExtension'
+           guessVersioned    = dir </> (toolname ++ versionSuffix)
+                               <.> exeExtension'
+           guesses | null versionSuffix = [guessNormal]
+                   | otherwise          = [guessGhcVersioned,
+                                           guessVersioned,
+                                           guessNormal]
+       exists <- mapM doesFileExist guesses
+       return $ listToMaybe [ file | (file, True) <- zip guesses exists ]
+
+  where takeVersionSuffix :: FilePath -> String
+        takeVersionSuffix = takeWhileEndLE isSuffixChar
+
+        isSuffixChar :: Char -> Bool
+        isSuffixChar c = isDigit c || c == '.' || c == '-'
+
+        dropExeExtension :: FilePath -> FilePath
+        dropExeExtension filepath =
+          case splitExtension filepath of
+            (filepath', extension) | extension == exeExtension' -> filepath'
+                                   | otherwise                 -> filepath
+
+-- | @takeWhileEndLE p@ is equivalent to @reverse . takeWhile p . reverse@, but
+-- is usually faster (as well as being easier to read).
+takeWhileEndLE :: (a -> Bool) -> [a] -> [a]
+takeWhileEndLE p = fst . foldr go ([], False)
+  where
+    go x (rest, done)
+      | not done && p x = (x:rest, False)
+      | otherwise = (rest, True)
+
+exeExtension' :: FilePath
+exeExtension' = Distribution.Simple.BuildPaths.exeExtension
diff --git a/src/CabalHelper/Compiletime/Log.hs b/src/CabalHelper/Compiletime/Log.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Log.hs
@@ -0,0 +1,44 @@
+-- Copyright (C) 2017  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-|
+Module      : CabalHelper.Compiletime.Log
+Description : Basic logging facilities
+License     : AGPL-3
+-}
+
+module CabalHelper.Compiletime.Log where
+
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Exception as E
+import Data.String
+import System.IO
+import Prelude
+
+import CabalHelper.Compiletime.Types
+
+vLog :: MonadIO m => Options -> String -> m ()
+vLog Options { oVerbose = True } msg =
+    liftIO $ hPutStrLn stderr msg
+vLog _ _ = return ()
+
+logIOError :: Options -> String -> IO (Maybe a) -> IO (Maybe a)
+logIOError opts label a = do
+  a `E.catch` \(ex :: IOError) -> do
+      vLog opts $ label ++ ": " ++ show ex
+      return Nothing
diff --git a/src/CabalHelper/Compiletime/Types.hs b/src/CabalHelper/Compiletime/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Types.hs
@@ -0,0 +1,40 @@
+-- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures #-}
+
+{-|
+Module      : CabalHelper.Compiletime.Types
+Description : Types used throughout
+License     : AGPL-3
+-}
+
+module CabalHelper.Compiletime.Types where
+
+import Data.Version
+
+data Options = Options {
+          oVerbose       :: Bool
+        , oGhcProgram    :: FilePath
+        , oGhcPkgProgram :: FilePath
+        , oCabalProgram  :: FilePath
+        , oCabalVersion  :: Maybe Version
+        , oCabalPkgDb    :: Maybe PackageDbDir
+}
+
+newtype PackageDbDir = PackageDbDir { unPackageDbDir :: FilePath }
+
+defaultOptions :: Options
+defaultOptions = Options False "ghc" "ghc-pkg" "cabal" Nothing Nothing
diff --git a/src/CabalHelper/Compiletime/Wrapper.hs b/src/CabalHelper/Compiletime/Wrapper.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Compiletime/Wrapper.hs
@@ -0,0 +1,164 @@
+-- cabal-helper: Simple interface to Cabal's configuration state
+-- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+{-# LANGUAGE RecordWildCards, FlexibleContexts #-}
+module Main where
+
+import Control.Applicative
+import Control.Monad
+import Data.Char
+import Data.List
+import Data.Maybe
+import Data.String
+import Text.Printf
+import System.Console.GetOpt
+import System.Environment
+import System.Directory
+import System.FilePath
+import System.Process
+import System.Exit
+import System.IO
+import Prelude
+
+import Distribution.System (buildPlatform)
+import Distribution.Text (display)
+import Distribution.Verbosity (silent, deafening)
+import Distribution.PackageDescription.Parse (readPackageDescription)
+import Distribution.Package (packageName, packageVersion)
+
+import Paths_cabal_helper (version)
+import CabalHelper.Compiletime.Compat.Version
+import CabalHelper.Compiletime.Compile
+import CabalHelper.Compiletime.GuessGhc
+import CabalHelper.Compiletime.Types
+import CabalHelper.Shared.Common
+import CabalHelper.Shared.InterfaceTypes
+
+usage :: IO ()
+usage = do
+  prog <- getProgName
+  hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg
+ where
+   usageMsg = "\
+\( print-appcachedir\n\
+\| print-build-platform\n\
+\| [--verbose]\n\
+\  [--with-ghc=GHC_PATH]\n\
+\  [--with-ghc-pkg=GHC_PKG_PATH]\n\
+\  [--with-cabal=CABAL_PATH]\n\
+\  [--with-cabal-version=VERSION]\n\
+\  [--with-cabal-pkg-db=PKG_DB]\n\
+\  PROJ_DIR DIST_DIR ( print-exe | package-id | [CABAL_HELPER_ARGS...] ) )\n"
+
+globalArgSpec :: [OptDescr (Options -> Options)]
+globalArgSpec =
+      [ option "" ["verbose"] "Be more verbose" $
+              NoArg $ \o -> o { oVerbose = True }
+
+      , option "" ["with-ghc"] "GHC executable to use" $
+              reqArg "PROG" $ \p o -> o { oGhcProgram = p }
+
+      , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $
+              reqArg "PROG" $ \p o -> o { oGhcPkgProgram = p }
+
+      , option "" ["with-cabal"] "cabal-install executable to use" $
+               reqArg "PROG" $ \p o -> o { oCabalProgram = p }
+
+      , option "" ["with-cabal-version"] "Cabal library version to use" $
+               reqArg "VERSION" $ \p o -> o { oCabalVersion = Just $ parseVer p }
+
+      , option "" ["with-cabal-pkg-db"] "package database to look for Cabal library in" $
+               reqArg "PKG_DB" $ \p o -> o { oCabalPkgDb = Just (PackageDbDir p) }
+
+      ]
+ where
+   option :: [Char] -> [String] -> String -> ArgDescr a -> OptDescr a
+   option s l udsc dsc = Option s l dsc udsc
+
+   reqArg :: String -> (String -> a) -> ArgDescr a
+   reqArg udsc dsc = ReqArg dsc udsc
+
+parseCommandArgs :: Options -> [String] -> (Options, [String])
+parseCommandArgs opts argv
+    = case getOpt RequireOrder globalArgSpec argv of
+        (o,r,[])   -> (foldr id opts o, r)
+        (_,_,errs) ->
+            panic $ "Parsing command options failed:\n" ++ concat errs
+
+guessProgramPaths :: Options -> IO Options
+guessProgramPaths opts = do
+    if not (same oGhcProgram opts dopts) && same oGhcPkgProgram opts dopts
+       then do
+         mghcPkg <- guessToolFromGhcPath "ghc-pkg" (oGhcProgram opts)
+         return opts {
+           oGhcPkgProgram = fromMaybe (oGhcPkgProgram opts) mghcPkg
+         }
+       else return opts
+ where
+   same f o o'  = f o == f o'
+   dopts = defaultOptions
+
+overrideVerbosityEnvVar :: Options -> IO Options
+overrideVerbosityEnvVar opts = do
+  x <- lookup  "CABAL_HELPER_DEBUG" <$> getEnvironment
+  return $ case x of
+    Just _  -> opts { oVerbose = True }
+    Nothing -> opts
+
+main :: IO ()
+main = handlePanic $ do
+  (opts', args) <- parseCommandArgs defaultOptions <$> getArgs
+  opts <- overrideVerbosityEnvVar =<< guessProgramPaths opts'
+  case args of
+    [] -> usage
+    "help":[] -> usage
+    "version":[] -> putStrLn $ showVersion version
+    "print-appdatadir":[] -> putStrLn =<< appCacheDir
+    "print-appcachedir":[] -> putStrLn =<< appCacheDir
+    "print-build-platform":[] -> putStrLn $ display buildPlatform
+
+    projdir:_distdir:"package-id":[] -> do
+      let v | oVerbose opts = deafening
+            | otherwise    = silent
+      -- ghc-mod will catch multiple cabal files existing before we get here
+      [cfile] <- filter isCabalFile <$> getDirectoryContents projdir
+      gpd <- readPackageDescription v (projdir </> cfile)
+      putStrLn $ show $
+        [Just $ ChResponseVersion (display (packageName gpd)) (toDataVersion $ packageVersion gpd)]
+
+    projdir:distdir:args' -> do
+      cfgf <- canonicalizePath (distdir </> "setup-config")
+      mhdr <- getCabalConfigHeader cfgf
+      case mhdr of
+        Nothing -> panic $ printf "\
+\Could not read Cabal's persistent setup configuration header\n\
+\- Check first line of: %s\n\
+\- Maybe try: $ cabal configure" cfgf
+        Just (hdrCabalVersion, _) -> do
+          case oCabalVersion opts of
+            Just ver | hdrCabalVersion /= ver -> panic $ printf "\
+\Cabal version %s was requested but setup configuration was\n\
+\written by version %s" (showVersion ver) (showVersion hdrCabalVersion)
+            _ -> do
+              eexe <- compileHelper opts hdrCabalVersion projdir distdir
+              case eexe of
+                  Left e -> exitWith e
+                  Right exe ->
+                    case args' of
+                      "print-exe":_ -> putStrLn exe
+                      _ -> do
+                        (_,_,_,h) <- createProcess $ proc exe args
+                        exitWith =<< waitForProcess h
+    _ -> error "invalid command line"
diff --git a/src/CabalHelper/Runtime/Main.hs b/src/CabalHelper/Runtime/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Runtime/Main.hs
@@ -0,0 +1,828 @@
+-- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-# LANGUAGE CPP, BangPatterns, RecordWildCards, RankNTypes, ViewPatterns #-}
+
+#ifdef MIN_VERSION_Cabal
+#undef CH_MIN_VERSION_Cabal
+#define CH_MIN_VERSION_Cabal MIN_VERSION_Cabal
+#endif
+
+import Distribution.Simple.Utils (cabalVersion)
+import Distribution.Simple.Configure
+import Distribution.Package
+  ( PackageIdentifier
+  , InstalledPackageId
+  , PackageId
+  , packageName
+  , packageVersion
+  )
+import Distribution.PackageDescription
+  ( PackageDescription
+  , GenericPackageDescription(..)
+  , Flag(..)
+  , FlagName(..)
+  , FlagAssignment
+  , Executable(..)
+  , Library(..)
+  , TestSuite(..)
+  , Benchmark(..)
+  , BuildInfo(..)
+  , TestSuiteInterface(..)
+  , BenchmarkInterface(..)
+  , withLib
+  )
+import Distribution.PackageDescription.Parse
+  ( readPackageDescription
+  )
+import Distribution.PackageDescription.Configuration
+  ( flattenPackageDescription
+  )
+import Distribution.Simple.Program
+  ( requireProgram
+  , ghcProgram
+  )
+import Distribution.Simple.Program.Types
+  ( ConfiguredProgram(..)
+  )
+import Distribution.Simple.Configure
+  ( getPersistBuildConfig
+  )
+import Distribution.Simple.LocalBuildInfo
+  ( LocalBuildInfo(..)
+  , Component(..)
+  , ComponentName(..)
+  , ComponentLocalBuildInfo(..)
+  , componentBuildInfo
+  , externalPackageDeps
+  , withComponentsLBI
+  , withLibLBI
+  , withExeLBI
+  )
+import Distribution.Simple.GHC
+  ( componentGhcOptions
+  )
+import Distribution.Simple.Program.GHC
+  ( GhcOptions(..)
+  , renderGhcOptions
+  )
+import Distribution.Simple.Setup
+  ( ConfigFlags(..)
+  , Flag(..)
+  , fromFlagOrDefault
+  )
+import Distribution.Simple.Build
+  ( initialBuildSteps
+  )
+import Distribution.Simple.BuildPaths
+  ( autogenModuleName
+  , cppHeaderName
+  , exeExtension
+  )
+import Distribution.Simple.Compiler
+  ( PackageDB(..)
+  , compilerId
+  )
+import Distribution.Compiler
+  ( CompilerId(..)
+  )
+import Distribution.ModuleName
+  ( components
+  )
+import qualified Distribution.ModuleName as C
+  ( ModuleName
+  )
+import Distribution.Text
+  ( display
+  )
+import Distribution.Verbosity
+  ( Verbosity
+  , silent
+  , deafening
+  , normal
+  )
+import Distribution.Version
+  ( Version
+  )
+
+#if CH_MIN_VERSION_Cabal(1,22,0)
+-- CPP >= 1.22
+import Distribution.Utils.NubList
+#endif
+
+#if CH_MIN_VERSION_Cabal(1,23,0)
+-- >= 1.23
+import Distribution.Simple.LocalBuildInfo
+  ( localUnitId
+  )
+#else
+-- <= 1.22
+import Distribution.Simple.LocalBuildInfo
+  ( inplacePackageId
+  )
+#endif
+
+#if CH_MIN_VERSION_Cabal(1,25,0)
+-- >=1.25
+import Distribution.PackageDescription
+  ( unFlagName
+  -- , mkFlagName
+  )
+import Distribution.Types.ForeignLib
+  ( ForeignLib(..)
+  )
+import Distribution.Types.UnqualComponentName
+  ( unUnqualComponentName
+  )
+#endif
+
+#if CH_MIN_VERSION_Cabal(2,0,0)
+-- CPP >= 2.0
+import Distribution.Simple.LocalBuildInfo
+  ( allLibModules
+  , componentBuildDir
+  )
+import Distribution.Simple.Register
+  ( internalPackageDBPath
+  )
+import Distribution.Backpack
+  ( OpenUnitId(..),
+    OpenModule(..)
+  )
+import Distribution.ModuleName
+  ( ModuleName
+  )
+import Distribution.Types.ComponentId
+  ( unComponentId
+  )
+import Distribution.Types.ComponentLocalBuildInfo
+  ( maybeComponentInstantiatedWith
+  )
+import Distribution.Types.ModuleRenaming
+  ( ModuleRenaming(..),
+    isDefaultRenaming
+  )
+import Distribution.Types.MungedPackageId
+  ( MungedPackageId
+  )
+import Distribution.Types.UnitId
+  ( UnitId
+  , unDefUnitId
+  , unUnitId
+  )
+import Distribution.Types.UnitId
+  ( DefUnitId
+  )
+import Distribution.Utils.NubList
+  ( toNubListR
+  )
+import Distribution.Version
+  ( versionNumbers
+  , mkVersion
+  )
+import qualified Distribution.InstalledPackageInfo as Installed
+#endif
+
+import Control.Applicative ((<$>))
+import Control.Arrow (first, second, (&&&))
+import Control.Monad
+import Control.Exception (catch, PatternMatchFail(..))
+import Data.List
+import qualified Data.Map as Map
+import Data.Maybe
+import Data.Monoid
+import Data.IORef
+import qualified Data.Version as DataVersion
+import System.Environment
+import System.Directory
+import System.FilePath
+import System.Exit
+import System.IO
+import System.IO.Unsafe (unsafeInterleaveIO, unsafePerformIO)
+import Text.Printf
+
+import CabalHelper.Shared.Sandbox
+import CabalHelper.Shared.Common
+import CabalHelper.Shared.InterfaceTypes
+
+usage :: IO ()
+usage = do
+  prog <- getProgName
+  hPutStr stderr $ "Usage: " ++ prog ++ " " ++ usageMsg
+ where
+   usageMsg = ""
+     ++"PROJ_DIR DIST_DIR [--with-* ...] (\n"
+     ++"    version\n"
+     ++"  | print-lbi [--human]\n"
+     ++"  | package-id\n"
+     ++"  | flags\n"
+     ++"  | config-flags\n"
+     ++"  | non-default-config-flags\n"
+     ++"  | write-autogen-files\n"
+     ++"  | compiler-version\n"
+     ++"  | ghc-options     [--with-inplace]\n"
+     ++"  | ghc-src-options [--with-inplace]\n"
+     ++"  | ghc-pkg-options [--with-inplace]\n"
+     ++"  | ghc-merged-pkg-options [--with-inplace]\n"
+     ++"  | ghc-lang-options [--with-inplace]\n"
+     ++"  | package-db-stack\n"
+     ++"  | entrypoints\n"
+     ++"  | needs-build-output\n"
+     ++"  | source-dirs\n"
+     ++"  ) ...\n"
+
+commands :: [String]
+commands = [ "print-lbi"
+           , "package-id"
+           , "flags"
+           , "config-flags"
+           , "non-default-config-flags"
+           , "write-autogen-files"
+           , "compiler-version"
+           , "ghc-options"
+           , "ghc-src-options"
+           , "ghc-pkg-options"
+           , "ghc-lang-options"
+           , "package-db-stack"
+           , "entrypoints"
+           , "needs-build-output"
+           , "source-dirs"
+           ]
+
+main :: IO ()
+main = do
+  args <- getArgs
+
+  projdir:distdir:args' <- case args of
+                    [] -> usage >> exitFailure
+                    _ -> return args
+
+  ddexists <- doesDirectoryExist distdir
+  when (not ddexists) $ do
+         errMsg $ "distdir '"++distdir++"' does not exist"
+         exitFailure
+
+  [cfile] <- filter isCabalFile <$> getDirectoryContents projdir
+
+  v <- maybe silent (const deafening) . lookup  "CABAL_HELPER_DEBUG" <$> getEnvironment
+  lbi <- unsafeInterleaveIO $ getPersistBuildConfig distdir
+  gpd <- unsafeInterleaveIO $ readPackageDescription v (projdir </> cfile)
+  let pd = localPkgDescr lbi
+  let lvd = (lbi, v, distdir)
+
+  let
+      -- a =<< b $$ c   ==  (a =<< b) $$ c
+      infixr 2 $$
+      ($$) = ($)
+
+      collectCmdOptions :: [String] -> [[String]]
+      collectCmdOptions =
+          reverse . map reverse . foldl f [] . dropWhile isOpt
+       where
+         isOpt = ("--" `isPrefixOf`)
+         f [] x = [[x]]
+         f (a:as) x
+             | isOpt x = (x:a):as
+             | otherwise = [x]:(a:as)
+
+  let cmds = collectCmdOptions args'
+
+  if any (["version"] `isPrefixOf`) cmds
+    then do
+      putStrLn $
+       printf "using version %s of the Cabal library" (display cabalVersion)
+      exitSuccess
+    else return ()
+
+  print =<< flip mapM cmds $$ \x -> do
+  case x of
+    "flags":[] -> do
+      return $ Just $ ChResponseFlags $ sort $
+        map (flagName' &&& flagDefault) $ genPackageFlags gpd
+
+    "config-flags":[] -> do
+      return $ Just $ ChResponseFlags $ sort $
+        map (first unFlagName) $ configConfigurationsFlags $ configFlags lbi
+
+    "non-default-config-flags":[] -> do
+      let flagDefinitons = genPackageFlags gpd
+          flagAssgnments = configConfigurationsFlags $ configFlags lbi
+          nonDefaultFlags =
+              [ (flag_name, val)
+              | MkFlag {flagName=(unFlagName -> flag_name'), flagDefault=def_val} <- flagDefinitons
+              , (unFlagName -> flag_name, val) <- flagAssgnments
+              , flag_name == flag_name'
+              , val /= def_val
+              ]
+      return $ Just $ ChResponseFlags $ sort nonDefaultFlags
+
+    "write-autogen-files":[] -> do
+      initialBuildStepsForAllComponents distdir pd lbi v
+      return Nothing
+
+    "compiler-version":[] -> do
+      let CompilerId comp ver = compilerId $ compiler lbi
+      return $ Just $ ChResponseVersion (show comp) (toDataVersion ver)
+
+    "ghc-options":flags -> do
+      res <- componentOptions lvd True flags id
+      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
+
+    "ghc-src-options":flags -> do
+      res <- componentOptions lvd False flags $ \opts -> mempty {
+               -- Not really needed but "unexpected package db stack: []"
+               ghcOptPackageDBs      = [GlobalPackageDB, UserPackageDB],
+
+               ghcOptCppOptions      = ghcOptCppOptions opts,
+               ghcOptCppIncludePath  = ghcOptCppIncludePath opts,
+               ghcOptCppIncludes     = ghcOptCppIncludes opts,
+               ghcOptFfiIncludes     = ghcOptFfiIncludes opts,
+               ghcOptSourcePathClear = ghcOptSourcePathClear opts,
+               ghcOptSourcePath      = ghcOptSourcePath opts
+              }
+      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
+
+    "ghc-pkg-options":flags -> do
+      res <- componentOptions lvd True flags $ \opts -> mempty {
+                       ghcOptPackageDBs = ghcOptPackageDBs opts,
+                       ghcOptPackages   = ghcOptPackages opts,
+                       ghcOptHideAllPackages = ghcOptHideAllPackages opts
+                   }
+      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
+
+    "ghc-merged-pkg-options":flags -> do
+      res <- mconcat . map snd <$> (componentOptions' lvd True flags (\_ _ o -> return o) $ \opts -> mempty {
+                       ghcOptPackageDBs = [],
+                       ghcOptHideAllPackages = NoFlag,
+                       ghcOptPackages   = ghcOptPackages opts
+                   })
+
+      let res' = nubPackageFlags $ res { ghcOptPackageDBs = withPackageDB lbi
+                                       , ghcOptHideAllPackages = Flag True
+                                       }
+
+      Just . ChResponseList <$> renderGhcOptions' lbi v res'
+
+    "ghc-lang-options":flags -> do
+      res <- componentOptions lvd False flags $ \opts -> mempty {
+                       ghcOptPackageDBs      = [GlobalPackageDB, UserPackageDB],
+
+                       ghcOptLanguage = ghcOptLanguage opts,
+                       ghcOptExtensions = ghcOptExtensions opts,
+                       ghcOptExtensionMap = ghcOptExtensionMap opts
+                   }
+      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
+
+    "package-db-stack":[] -> do
+      let
+          pkgDb GlobalPackageDB = ChPkgGlobal
+          pkgDb UserPackageDB   = ChPkgUser
+          pkgDb (SpecificPackageDB s) = ChPkgSpecific s
+
+      -- TODO: Setup.hs has access to the sandbox as well: ghc-mod#478
+      return $ Just $ ChResponsePkgDbs $ map pkgDb $ withPackageDB lbi
+
+    "entrypoints":[] -> do
+#if CH_MIN_VERSION_Cabal(2,0,0)
+      includeDirMap <- recursiveDepInfo lbi v distdir
+      eps <- componentsMap lbi v distdir $ \c clbi _bi -> do
+               case needsBuildOutput includeDirMap (componentUnitId clbi) of
+                 ProduceBuildOutput -> return $ componentEntrypoints c
+                 NoBuildOutput -> return seps
+                   where (_,_,seps) = recursiveIncludeDirs includeDirMap (componentUnitId clbi)
+#else
+      eps <- componentsMap lbi v distdir $ \c _clbi _bi ->
+               return $ componentEntrypoints c
+#endif
+      -- MUST append Setup component at the end otherwise CabalHelper gets
+      -- confused
+      let eps' = eps ++ [(ChSetupHsName, ChSetupEntrypoint)]
+      return $ Just $ ChResponseEntrypoints eps'
+
+    "needs-build-output":[] -> do
+#if CH_MIN_VERSION_Cabal(2,0,0)
+      includeDirMap <- recursiveDepInfo lbi v distdir
+      nbs <- componentsMap lbi v distdir $ \c clbi _bi ->
+               return $ needsBuildOutput includeDirMap (componentUnitId clbi)
+#else
+      nbs <- componentsMap lbi v distdir $ \c _clbi _bi ->
+               return $ NoBuildOutput
+#endif
+      return $ Just $ ChResponseNeedsBuild nbs
+
+    "source-dirs":[] -> do
+      res <- componentsMap lbi v distdir $$ \_ _ bi -> return $ hsSourceDirs bi
+      return $ Just $ ChResponseCompList (res ++ [(ChSetupHsName, [])])
+
+    "print-lbi":flags ->
+      case flags of
+        ["--human"] -> print lbi >> return Nothing
+        []          -> return $ Just $ ChResponseLbi $ show lbi
+
+    cmd:_ | not (cmd `elem` commands) ->
+            errMsg ("Unknown command: " ++ cmd) >> usage >> exitFailure
+    _ ->
+            errMsg "Invalid usage!" >> usage >> exitFailure
+
+flagName' = unFlagName . flagName
+
+-- getLibrary :: PackageDescription -> Library
+-- getLibrary pd = unsafePerformIO $ do
+--   lr <- newIORef (error "libraryMap: empty IORef")
+--   withLib pd (writeIORef lr)
+--   readIORef lr
+
+getLibraryClbi pd lbi = unsafePerformIO $ do
+  lr <- newIORef Nothing
+
+  withLibLBI pd lbi $ \ lib clbi ->
+      writeIORef lr $ Just (lib,clbi)
+
+  readIORef lr
+
+
+getExeClbi pd lbi = unsafePerformIO $ do
+  lr <- newIORef Nothing
+
+  withExeLBI pd lbi $ \ exe clbi ->
+      writeIORef lr $ Just (exe,clbi)
+
+  readIORef lr
+
+
+componentsMap :: LocalBuildInfo
+              -> Verbosity
+              -> FilePath
+              -> (   Component
+                  -> ComponentLocalBuildInfo
+                  -> BuildInfo
+                  -> IO a)
+              -> IO [(ChComponentName, a)]
+componentsMap lbi _v _distdir f = do
+    let pd = localPkgDescr lbi
+
+    lr <- newIORef []
+
+    -- withComponentsLBI is deprecated but also exists in very old versions
+    -- it's equivalent to withAllComponentsInBuildOrder in newer versions
+    withComponentsLBI pd lbi $ \c clbi -> do
+        let bi = componentBuildInfo c
+            name = componentNameFromComponent c
+
+        l' <- readIORef lr
+        r <- f c clbi bi
+#if CH_MIN_VERSION_Cabal(2,0,0)
+        writeIORef lr $ (componentNameToCh (unUnitId $ componentUnitId clbi) name, r):l'
+#else
+        writeIORef lr $ (componentNameToCh "" name, r):l'
+#endif
+
+    reverse <$> readIORef lr
+
+componentOptions' (lbi, v, distdir) inplaceFlag flags rf f = do
+  let pd = localPkgDescr lbi
+#if CH_MIN_VERSION_Cabal(2,0,0)
+  includeDirMap <- recursiveDepInfo lbi v distdir
+#endif
+
+  componentsMap lbi v distdir $ \c clbi bi ->
+         let
+#if CH_MIN_VERSION_Cabal(2,0,0)
+           outdir = componentBuildDir lbi clbi
+#else
+           outdir = componentOutDir lbi c
+#endif
+           (clbi', adopts) = case flags of
+                               _ | not inplaceFlag -> (clbi, mempty)
+                               ["--with-inplace"] -> (clbi, mempty)
+#if CH_MIN_VERSION_Cabal(2,0,0)
+                               [] -> removeInplaceDeps v lbi pd clbi includeDirMap
+#else
+                               [] -> removeInplaceDeps v lbi pd clbi
+#endif
+           opts = componentGhcOptions normal lbi bi clbi' outdir
+           opts' = f opts
+
+         in rf lbi v $ nubPackageFlags $ opts' `mappend` adopts
+
+componentOptions (lbi, v, distdir) inplaceFlag flags f =
+    componentOptions' (lbi, v, distdir) inplaceFlag flags renderGhcOptions' f
+
+gmModuleName :: C.ModuleName -> ChModuleName
+gmModuleName = ChModuleName . intercalate "." . components
+
+#if CH_MIN_VERSION_Cabal(2,0,0)
+removeInplaceDeps :: Verbosity
+                  -> LocalBuildInfo
+                  -> PackageDescription
+                  -> ComponentLocalBuildInfo
+                  -> Map.Map UnitId SubDeps
+                  -> (ComponentLocalBuildInfo, GhcOptions)
+removeInplaceDeps _v lbi pd clbi includeDirs = let
+    removeInplace c =
+      let
+        (ideps, incs) = partition (isInplaceCompInc c) (componentIncludes c)
+        hasIdeps' = not $ null ideps
+        c' = c { componentPackageDeps  = error "using deprecated field:componentPackageDeps"
+               , componentInternalDeps = []
+               , componentIncludes     = incs }
+      in (hasIdeps',c')
+
+    needsBuild = needsBuildOutput includeDirs (componentUnitId clbi)
+
+    cleanRecursiveOpts :: Component
+                       -> BuildInfo -> ComponentLocalBuildInfo -> GhcOptions
+    cleanRecursiveOpts comp libbi libclbi =
+      let
+        liboutdir = componentOutDir lbi comp
+        (_,libclbi') = removeInplace libclbi
+        (extraIncludes,extraDeps',_ems) = recursiveIncludeDirs includeDirs (componentUnitId libclbi)
+        (_,extraDeps) = partition (isInplaceCompInc libclbi) extraDeps'
+        opts = (componentGhcOptions normal lbi libbi libclbi' liboutdir) {
+                  ghcOptPackageDBs = []
+               }
+
+      in
+        opts { ghcOptSourcePath = ghcOptSourcePath opts <> toNubListR extraIncludes
+             , ghcOptPackages   = ghcOptPackages   opts <> toNubListR extraDeps }
+
+    libopts =
+      case (getLibraryClbi pd lbi,getExeClbi pd lbi) of
+        (Just (lib, libclbi),_) | hasIdeps ->
+          let
+            libbi = libBuildInfo lib
+            opts = cleanRecursiveOpts (CLib lib) libbi libclbi
+          in
+            opts { ghcOptInputModules = ghcOptInputModules opts <> (toNubListR $ allLibModules lib libclbi) }
+        (_,Just (exe,execlbi)) | hasIdeps ->
+          let
+            exebi = buildInfo exe
+          in
+            cleanRecursiveOpts (CExe exe) exebi execlbi
+        _ -> mempty
+
+    distDir = fromFlagOrDefault ("." </> "dist") (configDistPref $ configFlags lbi)
+    packageDbDir = internalPackageDBPath lbi distDir
+    (hasIdeps,clbi') = case needsBuild of
+                         NoBuildOutput -> removeInplace clbi
+                         ProduceBuildOutput -> (False, clbi)
+    libopts' = case needsBuild of
+                 NoBuildOutput -> libopts
+                 ProduceBuildOutput -> mempty { ghcOptPackageDBs = [SpecificPackageDB packageDbDir] }
+  in (clbi', libopts')
+#else
+removeInplaceDeps :: Verbosity
+                  -> LocalBuildInfo
+                  -> PackageDescription
+                  -> ComponentLocalBuildInfo
+                  -> (ComponentLocalBuildInfo, GhcOptions)
+removeInplaceDeps _v lbi pd clbi = let
+    (ideps, deps) = partition (isInplaceDep lbi) (componentPackageDeps clbi)
+    hasIdeps = not $ null ideps
+    libopts =
+      case getLibraryClbi pd lbi of
+        Just (lib, libclbi) | hasIdeps ->
+          let
+            libbi = libBuildInfo lib
+            liboutdir = componentOutDir lbi (CLib lib)
+          in
+            (componentGhcOptions normal lbi libbi libclbi liboutdir) {
+                ghcOptPackageDBs = []
+            }
+        _ -> mempty
+    clbi' = clbi { componentPackageDeps = deps }
+  in (clbi', libopts)
+#endif
+
+
+#if CH_MIN_VERSION_Cabal(2,0,0)
+recursiveDepInfo lbi v distdir = do
+  includeDirs <- componentsMap lbi v distdir $ \c clbi bi -> do
+    return (componentUnitId clbi
+           , ( SubDeps
+                { sdComponentInternalDeps = componentInternalDeps clbi
+                , sdHsSourceDirs          = hsSourceDirs bi
+                , sdComponentIncludes     = componentIncludes clbi
+                , sdComponentEntryPoints  = componentEntrypoints c})  )
+  return $ Map.fromList $ map snd includeDirs
+
+data SubDeps = SubDeps
+  { sdComponentInternalDeps :: [UnitId]
+  , sdHsSourceDirs          :: [FilePath]
+  , sdComponentIncludes     :: [(OpenUnitId, ModuleRenaming)]
+  , sdComponentEntryPoints  :: ChEntrypoint
+  }
+
+recursiveIncludeDirs :: Map.Map UnitId SubDeps
+                     -> UnitId -> ([FilePath], [(OpenUnitId, ModuleRenaming)]
+                                  , ChEntrypoint)
+recursiveIncludeDirs includeDirs unit = go ([],[],Nothing) [unit]
+  where
+    go (afp,aci,Nothing  ) [] = (afp,aci,error "recursiveIncludeDirs:no ChEntrypoint")
+    go (afp,aci,Just amep) [] = (afp,aci,amep)
+    go acc@(afp,aci,amep) (u:us) = case Map.lookup u includeDirs of
+      Nothing -> go acc us
+      Just (SubDeps us' sfp sci sep) -> go (afp++sfp,aci++sci,combineEp amep sep) (us++us')
+
+needsBuildOutput :: Map.Map UnitId SubDeps -> UnitId -> NeedsBuildOutput
+needsBuildOutput includeDirs unit = go [unit]
+  where
+    isIndef (IndefFullUnitId _ _) = True
+    isIndef _                     = False
+    go [] = NoBuildOutput
+    go (u:us) = case Map.lookup u includeDirs of
+      Nothing -> go us
+      Just (SubDeps us' sfp sci sep) ->
+        if any (isIndef . fst) sci
+          then ProduceBuildOutput
+          else go (us++us')
+
+combineEp Nothing e = Just e
+combineEp (Just ChSetupEntrypoint) e = Just e
+combineEp (Just (ChLibEntrypoint es1 os1 ss1)) (ChLibEntrypoint es2 os2 ss2) = Just (ChLibEntrypoint (nub $ es2++es1) (nub $ os2++os1) (nub $ ss2++ss1))
+combineEp (Just (ChLibEntrypoint es1 os1 ss1)) (ChExeEntrypoint  mi os2)     = Just (ChExeEntrypoint mi (nub $ os2++es1++os1++ss1))
+combineEp (Just (ChExeEntrypoint   _ os1))     (ChLibEntrypoint es2 os2 ss2) = Just (ChLibEntrypoint es2 (nub $ os2++os1) ss2)
+combineEp (Just (ChExeEntrypoint   _ os1))     (ChExeEntrypoint  mi os2)     = Just (ChExeEntrypoint mi  (nub $ os2++os1))
+
+instantiatedGhcPackage :: (ModuleName,OpenModule) -> [(OpenUnitId, ModuleRenaming)]
+instantiatedGhcPackage (_,OpenModule oui@(DefiniteUnitId _) _) = [(oui,DefaultRenaming)]
+instantiatedGhcPackage (_, _) = []
+#endif
+
+initialBuildStepsForAllComponents distdir pd lbi v =
+  initialBuildSteps distdir pd lbi v
+
+
+
+
+
+
+#if !CH_MIN_VERSION_Cabal(1,25,0)
+-- CPP < 1.25
+unFlagName (FlagName n) = n
+-- mkFlagName n = FlagName n
+#endif
+
+toDataVersion :: Version -> DataVersion.Version
+--fromDataVersion :: DataVersion.Version -> Version
+#if CH_MIN_VERSION_Cabal(2,0,0)
+toDataVersion v = DataVersion.Version (versionNumbers v) []
+--fromDataVersion (DataVersion.Version vs _) = mkVersion vs
+#else
+toDataVersion = id
+--fromDataVersion = id
+#endif
+
+componentNameToCh _uid CLibName = ChLibName
+#if CH_MIN_VERSION_Cabal(1,25,0)
+-- CPP >= 1.25
+#if CH_MIN_VERSION_Cabal(2,0,0)
+componentNameToCh uid (CSubLibName n) = ChSubLibName uid
+#else
+componentNameToCh _uid (CSubLibName n) = ChSubLibName (unUnqualComponentName' n)
+#endif
+componentNameToCh uid (CFLibName   n) = ChFLibName (unUnqualComponentName' n)
+#endif
+componentNameToCh _uid (CExeName n) = ChExeName (unUnqualComponentName' n)
+componentNameToCh _uid (CTestName n) = ChTestName (unUnqualComponentName' n)
+componentNameToCh _uid (CBenchName n) = ChBenchName (unUnqualComponentName' n)
+
+#if CH_MIN_VERSION_Cabal(1,25,0)
+-- CPP >= 1.25
+unUnqualComponentName' = unUnqualComponentName
+#else
+unUnqualComponentName' = id
+#endif
+
+#if !CH_MIN_VERSION_Cabal(1,25,0)
+-- CPP < 1.25
+componentNameFromComponent (CLib Library {}) = CLibName
+#elif CH_MIN_VERSION_Cabal(1,25,0)
+-- CPP >= 1.25 (redundant)
+componentNameFromComponent (CLib Library { libName = Nothing }) = CLibName
+componentNameFromComponent (CLib Library { libName = Just n })  = CSubLibName n
+componentNameFromComponent (CFLib ForeignLib {..}) = CFLibName foreignLibName
+#endif
+componentNameFromComponent (CExe Executable {..}) = CExeName exeName
+componentNameFromComponent (CTest TestSuite {..}) = CTestName testName
+componentNameFromComponent (CBench Benchmark {..}) = CBenchName benchmarkName
+
+componentOutDir lbi (CLib Library {..})=
+    buildDir lbi
+#if CH_MIN_VERSION_Cabal(2,0,0)
+componentOutDir lbi (CFLib ForeignLib {..}) =
+    componentOutDir' lbi (unUnqualComponentName foreignLibName)
+#endif
+componentOutDir lbi (CExe Executable {..}) =
+    componentOutDir' lbi (unUnqualComponentName' exeName)
+componentOutDir lbi (CTest TestSuite { testInterface = TestSuiteExeV10 _ _, ..}) =
+    componentOutDir' lbi (unUnqualComponentName' testName)
+componentOutDir lbi (CTest TestSuite { testInterface = TestSuiteLibV09 _ _, ..}) =
+    componentOutDir' lbi (unUnqualComponentName' testName ++ "Stub")
+componentOutDir lbi (CBench Benchmark { benchmarkInterface = BenchmarkExeV10 _ _, ..})=
+    componentOutDir' lbi (unUnqualComponentName' benchmarkName)
+
+componentOutDir' :: LocalBuildInfo -> String -> FilePath
+componentOutDir' lbi compName' =
+  ----- Copied from Distribution/Simple/GHC.hs:buildOrReplExe
+  let targetDir = (buildDir lbi) </> compName'
+      compDir    = targetDir </> (compName' ++ "-tmp")
+  in compDir
+
+componentEntrypoints :: Component -> ChEntrypoint
+componentEntrypoints (CLib Library {..})
+    = ChLibEntrypoint
+        (map gmModuleName exposedModules)
+        (map gmModuleName $ otherModules libBuildInfo)
+#if CH_MIN_VERSION_Cabal(2,0,0)
+        (map gmModuleName signatures)
+#else
+        [] -- no signatures prior to Cabal 2.0
+#endif
+#if CH_MIN_VERSION_Cabal(2,0,0)
+componentEntrypoints (CFLib (ForeignLib{..}))
+    = ChLibEntrypoint
+        []
+        (map gmModuleName $ otherModules foreignLibBuildInfo)
+        []
+#endif
+componentEntrypoints (CExe Executable {..})
+    = ChExeEntrypoint
+#if CH_MIN_VERSION_Cabal(2,0,0)
+        --
+        ( head ((hsSourceDirs buildInfo) ++ ["."]) </> modulePath)
+        -- modulePath
+#else
+        modulePath
+#endif
+        (map gmModuleName $ otherModules buildInfo)
+componentEntrypoints (CTest TestSuite { testInterface = TestSuiteExeV10 _ fp, ..})
+    = ChExeEntrypoint fp (map gmModuleName $ otherModules testBuildInfo)
+componentEntrypoints (CTest TestSuite { testInterface = TestSuiteLibV09 _ mn, ..})
+    = ChLibEntrypoint [gmModuleName mn] (map gmModuleName $ otherModules testBuildInfo) []
+componentEntrypoints (CTest TestSuite {})
+    = ChLibEntrypoint [] [] []
+componentEntrypoints (CBench Benchmark { benchmarkInterface = BenchmarkExeV10 _  fp, ..})
+    = ChExeEntrypoint fp (map gmModuleName $ otherModules benchmarkBuildInfo)
+componentEntrypoints (CBench Benchmark {})
+    = ChLibEntrypoint [] [] []
+
+#if CH_MIN_VERSION_Cabal(2,0,0)
+isInplaceCompInc :: ComponentLocalBuildInfo -> (OpenUnitId, ModuleRenaming) -> Bool
+isInplaceCompInc clbi (DefiniteUnitId uid, _mr)     = unDefUnitId uid `elem` componentInternalDeps clbi
+isInplaceCompInc clbi (IndefFullUnitId uid _, _mmr) = False
+#endif
+
+#if CH_MIN_VERSION_Cabal(2,0,0)
+isInplaceDep :: LocalBuildInfo -> ComponentLocalBuildInfo -> (UnitId, MungedPackageId) -> Bool
+isInplaceDep lbi clbi (uid, _mpid) = uid `elem` componentInternalDeps clbi
+#else
+isInplaceDep :: LocalBuildInfo -> (InstalledPackageId, PackageId) -> Bool
+#  if CH_MIN_VERSION_Cabal(1,23,0)
+-- CPP >= 1.23
+isInplaceDep lbi (ipid, _pid) = localUnitId lbi == ipid
+#  else
+-- CPP <= 1.22
+isInplaceDep _lbi (ipid, pid) = inplacePackageId pid == ipid
+#  endif
+#endif
+
+#if CH_MIN_VERSION_Cabal(1,22,0)
+-- CPP >= 1.22
+-- >= 1.22 uses NubListR
+nubPackageFlags opts = opts
+#else
+nubPackageFlags opts = opts { ghcOptPackages = nub $ ghcOptPackages opts }
+#endif
+
+renderGhcOptions' :: LocalBuildInfo
+                  -> Verbosity
+                  -> GhcOptions
+                  -> IO [String]
+#if !CH_MIN_VERSION_Cabal(1,20,0)
+renderGhcOptions' lbi v opts = do
+-- CPP < 1.20
+  (ghcProg, _) <- requireProgram v ghcProgram (withPrograms lbi)
+  let Just ghcVer = programVersion ghcProg
+  return $ renderGhcOptions ghcVer opts
+#elif CH_MIN_VERSION_Cabal(1,20,0) && !CH_MIN_VERSION_Cabal(1,24,0)
+renderGhcOptions' lbi _v opts = do
+-- CPP >= 1.20 && < 1.24
+  return $ renderGhcOptions (compiler lbi) opts
+#else
+renderGhcOptions' lbi _v opts = do
+-- CPP >= 1.24
+  return $ renderGhcOptions (compiler lbi) (hostPlatform lbi) opts
+#endif
diff --git a/src/CabalHelper/Shared/Common.hs b/src/CabalHelper/Shared/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Shared/Common.hs
@@ -0,0 +1,132 @@
+-- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-|
+Module      : CabalHelper.Shared.Common
+Description : Shared utility functions
+License     : AGPL-3
+-}
+
+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
+module CabalHelper.Shared.Common where
+
+import Control.Applicative
+import Control.Exception as E
+import Control.Monad
+import Data.Char
+import Data.List
+import Data.Maybe
+import Data.Version
+import Data.Typeable
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as BS8
+import System.Environment
+import System.IO
+import qualified System.Info
+import System.Exit
+import System.Directory
+import System.FilePath
+import Text.ParserCombinators.ReadP
+import Prelude
+
+data Panic = Panic String deriving (Typeable, Show)
+instance Exception Panic
+
+panic :: String -> a
+panic msg = throw $ Panic msg
+
+panicIO :: String -> IO a
+panicIO msg = throwIO $ Panic msg
+
+handlePanic :: IO a -> IO a
+handlePanic action =
+    action `E.catch` \(Panic msg) -> errMsg msg >> exitFailure
+
+errMsg :: String -> IO ()
+errMsg str = do
+  prog <- getProgName
+  hPutStrLn stderr $ prog ++ ": " ++ str
+
+-- | @getCabalConfigHeader "dist/setup-config"@ returns the cabal version and
+-- compiler version
+getCabalConfigHeader :: FilePath -> IO (Maybe (Version, (ByteString, Version)))
+getCabalConfigHeader file = bracket (openFile file ReadMode) hClose $ \h -> do
+  parseHeader <$> BS.hGetLine h
+
+parseHeader :: ByteString -> Maybe (Version, (ByteString, Version))
+parseHeader header = case BS8.words header of
+  ["Saved", "package", "config", "for", _pkgId ,
+   "written", "by", cabalId,
+   "using", compId]
+    -> liftM2 (,) (snd <$> parsePkgId cabalId) (parsePkgId compId)
+  _ -> Nothing
+
+parsePkgId :: ByteString -> Maybe (ByteString, Version)
+parsePkgId bs =
+    case BS8.split '-' bs of
+      [pkg, vers] -> Just (pkg, parseVer $ BS8.unpack vers)
+      _ -> Nothing
+
+parseVer :: String -> Version
+parseVer vers = runReadP parseVersion vers
+
+trim :: String -> String
+trim = dropWhileEnd isSpace
+
+majorVer :: Version -> Version
+majorVer (Version b _) = Version (take 2 b) []
+
+sameMajorVersionAs :: Version -> Version -> Bool
+sameMajorVersionAs a b = majorVer a == majorVer b
+
+runReadP :: ReadP t -> String -> t
+runReadP p i = case filter ((=="") . snd) $ readP_to_S p i of
+                 (a,""):[] -> a
+                 _ -> error $ "Error parsing: " ++ show i
+
+appCacheDir :: IO FilePath
+appCacheDir =
+    (</> "cabal-helper") <$> getEnvDefault "XDG_CACHE_HOME" (homeRel cache)
+ where
+    -- for GHC 7.4
+    lookupEnv' var = do env <- getEnvironment; return (lookup var env)
+    getEnvDefault var def = lookupEnv' var >>= \m -> case m of Nothing -> def; Just x -> return x
+    homeRel path = (</> path) <$> getHomeDirectory
+    cache =
+        case System.Info.os of
+          "mingw32" -> windowsCache
+          _         -> unixCache
+
+    windowsCache = "Local Settings" </> "Cache"
+    unixCache = ".cache"
+
+isCabalFile :: FilePath -> Bool
+isCabalFile f = takeExtension' f == ".cabal"
+
+takeExtension' :: FilePath -> String
+takeExtension' p =
+    if takeFileName p == takeExtension p
+      then "" -- just ".cabal" is not a valid cabal file
+      else takeExtension p
+
+replace :: String -> String -> String -> String
+replace n r hs' = go "" hs'
+ where
+   go acc h
+       | take (length n) h == n =
+           reverse acc ++ r ++ drop (length n) h
+   go acc (h:hs) = go (h:acc) hs
+   go acc [] = reverse acc
diff --git a/src/CabalHelper/Shared/InterfaceTypes.hs b/src/CabalHelper/Shared/InterfaceTypes.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Shared/InterfaceTypes.hs
@@ -0,0 +1,80 @@
+-- Copyright (C) 2015,2017  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures #-}
+
+{-|
+Module      : CabalHelper.Shared.InterfaceTypes
+Description : Types which are used by c-h library and executable to communicate
+License     : AGPL-3
+
+These types are used to communicate between the cabal-helper library and main
+executable, using Show/Read. If any types in this module change the major
+version must be bumped since this will be exposed in the @Distribution.Helper@
+module.
+
+The cached executables in @$XDG_CACHE_HOME/cabal-helper@ use the cabal-helper
+version (among other things) as a cache key so we don't need to worry about
+talking to an old executable.
+-}
+module CabalHelper.Shared.InterfaceTypes where
+
+import GHC.Generics
+import Data.Version
+
+data ChResponse
+    = ChResponseCompList    [(ChComponentName, [String])]
+    | ChResponseEntrypoints [(ChComponentName, ChEntrypoint)]
+    | ChResponseNeedsBuild  [(ChComponentName, NeedsBuildOutput)]
+    | ChResponseList        [String]
+    | ChResponsePkgDbs      [ChPkgDb]
+    | ChResponseLbi         String
+    | ChResponseVersion     String Version
+    | ChResponseLicenses    [(String, [(String, Version)])]
+    | ChResponseFlags       [(String, Bool)]
+  deriving (Eq, Ord, Read, Show, Generic)
+
+data ChComponentName = ChSetupHsName
+                     | ChLibName
+                     | ChSubLibName String
+                     | ChFLibName String
+                     | ChExeName String
+                     | ChTestName String
+                     | ChBenchName String
+  deriving (Eq, Ord, Read, Show, Generic)
+
+newtype ChModuleName = ChModuleName String
+    deriving (Eq, Ord, Read, Show, Generic)
+
+data ChEntrypoint = ChSetupEntrypoint -- ^ Almost like 'ChExeEntrypoint' but
+                                      -- @main-is@ could either be @"Setup.hs"@
+                                      -- or @"Setup.lhs"@. Since we don't know
+                                      -- where the source directory is you have
+                                      -- to find these files.
+                  | ChLibEntrypoint { chExposedModules :: [ChModuleName]
+                                    , chOtherModules   :: [ChModuleName]
+                                    , chSignatures     :: [ChModuleName] -- backpack only
+                                    }
+                  | ChExeEntrypoint { chMainIs         :: FilePath
+                                    , chOtherModules   :: [ChModuleName]
+                                    } deriving (Eq, Ord, Read, Show, Generic)
+
+data ChPkgDb = ChPkgGlobal
+             | ChPkgUser
+             | ChPkgSpecific FilePath
+               deriving (Eq, Ord, Read, Show, Generic)
+
+data NeedsBuildOutput = ProduceBuildOutput | NoBuildOutput
+               deriving (Eq, Ord, Read, Show, Generic)
diff --git a/src/CabalHelper/Shared/Sandbox.hs b/src/CabalHelper/Shared/Sandbox.hs
new file mode 100644
--- /dev/null
+++ b/src/CabalHelper/Shared/Sandbox.hs
@@ -0,0 +1,77 @@
+-- Copyright (C) 2015  Daniel Gröber <dxld ÄT darkboxed DOT org>
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+{-|
+Module      : CabalHelper.Shared.Sandbox
+Description : Extracting information from @cabal.sandbox.config@ files
+License     : AGPL-3
+-}
+
+module CabalHelper.Shared.Sandbox where
+
+import Control.Applicative
+import Data.Char
+import Data.Maybe
+import Data.List
+import Data.Version
+import System.FilePath
+import System.Directory
+import Prelude
+
+import qualified Data.Traversable as T
+
+-- | Get the path to the sandbox package-db in a project
+getSandboxPkgDb :: FilePath
+             -- ^ Path to the cabal package root directory (containing the
+             -- @cabal.sandbox.config@ file)
+             -> String
+             -- ^ Cabal build platform, i.e. @buildPlatform@
+             -> Version
+             -- ^ GHC version (@cProjectVersion@ is your friend)
+             -> IO (Maybe FilePath)
+getSandboxPkgDb d platform ghcVer = do
+  mConf <- T.traverse readFile =<< mightExist (d </> "cabal.sandbox.config")
+  return $ fixPkgDbVer <$> (extractSandboxDbDir =<< mConf)
+
+ where
+   fixPkgDbVer dir =
+       case takeFileName dir == ghcSandboxPkgDbDir platform ghcVer of
+         True -> dir
+         False -> takeDirectory dir </> ghcSandboxPkgDbDir platform ghcVer
+
+ghcSandboxPkgDbDir :: String -> Version -> String
+ghcSandboxPkgDbDir platform ghcVer =
+   platform ++ "-ghc-" ++ showVersion ghcVer ++ "-packages.conf.d"
+
+-- | Extract the sandbox package db directory from the cabal.sandbox.config
+-- file. Exception is thrown if the sandbox config file is broken.
+extractSandboxDbDir :: String -> Maybe FilePath
+extractSandboxDbDir conf = extractValue <$> parse conf
+  where
+    key = "package-db:"
+    keyLen = length key
+
+    parse = listToMaybe . filter (key `isPrefixOf`) . lines
+    extractValue = CabalHelper.Shared.Sandbox.dropWhileEnd isSpace . dropWhile isSpace . drop keyLen
+
+
+mightExist :: FilePath -> IO (Maybe FilePath)
+mightExist f = do
+  exists <- doesFileExist f
+  return $ if exists then (Just f) else (Nothing)
+
+-- dropWhileEnd is not provided prior to base 4.5.0.0.
+dropWhileEnd :: (a -> Bool) -> [a] -> [a]
+dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
diff --git a/tests/CompileTest.hs b/tests/CompileTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/CompileTest.hs
@@ -0,0 +1,184 @@
+{-# LANGUAGE ScopedTypeVariables, GADTs #-}
+
+import System.Environment (getArgs)
+import System.Directory
+import System.FilePath
+import System.Process
+import System.Exit
+import System.IO
+import Control.Exception as E
+import Data.List
+import Data.Maybe
+import Data.Version
+import Data.Functor
+import Data.Function
+import qualified Distribution.Compat.ReadP as Dist
+import Distribution.Version (VersionRange, withinRange)
+import Distribution.Text
+import Control.Arrow
+import Control.Monad
+import Prelude
+
+import CabalHelper.Compiletime.Compat.Environment
+import CabalHelper.Compiletime.Compat.Version
+import CabalHelper.Compiletime.Compile
+import CabalHelper.Compiletime.Types
+import CabalHelper.Shared.Common
+
+runReadP'Dist :: Dist.ReadP t t -> String -> t
+runReadP'Dist p i = case filter ((=="") . snd) $ Dist.readP_to_S p i of
+                 (a,""):[] -> a
+                 _ -> error $ "Error parsing: " ++ show i
+
+withinRange'CH :: Either HEAD Version -> VersionRange -> Bool
+withinRange'CH v r =
+    withinRange (fromDataVersion v') r
+  where
+    v' = either (const $ parseVer "1000000000") id v
+
+setupHOME :: IO ()
+setupHOME = do
+  tmp <- fromMaybe "/tmp" <$> lookupEnv "TMPDIR"
+  let home = tmp </> "compile-test-home"
+  _ <- rawSystem "rm" ["-r", home]
+  createDirectory    home
+  setEnv "HOME" home
+
+main :: IO ()
+main = do
+  args <- getArgs
+
+  let action
+       | null args = testAllCabalVersions
+       | otherwise = testCabalVersions $ map parseVer' args
+
+  setupHOME
+
+  _ <- rawSystem "cabal" ["update"]
+
+  action
+
+parseVer' :: String -> Either HEAD Version
+parseVer' "HEAD" = Left HEAD
+parseVer' v      = Right $ parseVer v
+
+testAllCabalVersions :: IO ()
+testAllCabalVersions = do
+  let cabal_versions :: [Either HEAD Version]
+      cabal_versions = map parseVer'
+           -- "1.14.0" -- not supported at runtime
+           [ "1.16.0"
+           , "1.16.0.1"
+           , "1.16.0.2"
+           , "1.16.0.3"
+           , "1.18.0"
+           , "1.18.1"
+           , "1.18.1.1"
+           , "1.18.1.2"
+           , "1.18.1.3"
+           , "1.18.1.4"
+           , "1.18.1.5"
+           , "1.18.1.6"
+           , "1.18.1.7"
+           , "1.20.0.0"
+           , "1.20.0.1"
+           , "1.20.0.2"
+           , "1.20.0.3"
+           , "1.20.0.4"
+           , "1.22.0.0"
+           , "1.22.1.0"
+           , "1.22.1.1"
+           , "1.22.2.0"
+           , "1.22.3.0"
+           , "1.22.4.0"
+           , "1.22.5.0"
+           , "1.22.6.0"
+           , "1.22.7.0"
+           , "1.22.8.0"
+           , "1.24.0.0"
+           , "1.24.1.0"
+           , "1.24.2.0"
+           , "2.0.0.2"
+           , "2.0.1.0"
+           , "2.0.1.1"
+           , "HEAD"
+           ]
+
+  ghc_ver <- ghcVersion defaultOptions
+
+  let constraint :: VersionRange
+      constraint =
+          fromMaybe (snd $ last constraint_table) $
+          fmap snd $
+          find (and . (zipWith (==) `on` versionBranch) ghc_ver . fst) $
+          constraint_table
+
+      constraint_table =
+          map (parseVer *** runReadP'Dist parse) $
+              [ ("7.4"  , ">= 1.14    && < 2")
+              , ("7.6"  , ">= 1.16    && < 2")
+              , ("7.8"  , ">= 1.18    && < 2")
+              , ("7.10" , ">= 1.22.2  && < 2")
+              , ("8.0.1", ">= 1.24          ")
+              , ("8.0.2", ">= 1.24.2        ")
+              , ("8.2.1", ">= 2.0.0.2       ")
+              , ("8.2.2", ">= 2.0.0.2       ")
+              ]
+
+      relevant_cabal_versions =
+          reverse $ filter (flip withinRange'CH constraint) cabal_versions
+
+  testCabalVersions relevant_cabal_versions
+
+testCabalVersions :: [Either HEAD Version] -> IO ()
+testCabalVersions versions = do
+  rvs <- forM versions $ \ver -> do
+           let sver = either show showVersion ver
+           hPutStrLn stderr $ "\n\n\n\n\n\n====== Compiling with Cabal-" ++ sver
+           compilePrivatePkgDb ver
+
+  let printStatus (cv, rv) = putStrLn $ "- Cabal "++ver++" "++status
+        where  ver = case cv of Left _ -> "HEAD"; Right v -> showVersion v
+               status = case rv of
+                         Right _ ->
+                             "succeeded"
+                         Left rvc ->
+                             "failed (exit code "++show rvc++")"
+
+  let drvs = versions `zip` rvs
+
+  mapM_ printStatus drvs
+  if any isLeft' $ map snd $ filter ((/=Left HEAD) . fst) drvs
+     then exitFailure
+     else exitSuccess
+
+ where
+   isLeft' (Left _) = True
+   isLeft' (Right _) = False
+
+compilePrivatePkgDb :: Either HEAD Version -> IO (Either ExitCode FilePath)
+compilePrivatePkgDb eCabalVer = do
+    res <- E.try $ installCabal defaultOptions { oVerbose = True } eCabalVer
+    case res of
+      Right (db, cabalVer) ->
+          compileWithPkg db cabalVer
+      Left (ioe :: IOException) -> do
+          print ioe
+          return $ Left (ExitFailure 1)
+
+compileWithPkg :: PackageDbDir
+               -> CabalVersion
+               -> IO (Either ExitCode FilePath)
+compileWithPkg db cabalVer = do
+    appdir <- appCacheDir
+    let comp =
+          CompileWithCabalPackage (Just db) cabalVer [cabalPkgId cabalVer] CPSGlobal
+    compile
+      comp
+      (compPaths appdir (error "compile-test: distdir not available") comp)
+      defaultOptions { oVerbose = True }
+
+
+cabalPkgId :: CabalVersion -> String
+cabalPkgId (CabalHEAD _commitid) = "Cabal"
+cabalPkgId (CabalVersion v) = "Cabal-" ++ showVersion v
diff --git a/tests/GhcSession.hs b/tests/GhcSession.hs
new file mode 100644
--- /dev/null
+++ b/tests/GhcSession.hs
@@ -0,0 +1,177 @@
+{-# LANGUAGE TupleSections, ScopedTypeVariables, CPP #-}
+module Main where
+
+import GHC
+#if __GLASGOW_HASKELL__ <= 706
+import GhcMonad
+#endif
+import GHC.Paths (libdir)
+import DynFlags
+
+import qualified Control.Exception as E
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.List
+import Data.Version
+import System.Environment (getArgs)
+import System.Exit
+import System.FilePath ((</>))
+import System.Directory
+import System.IO
+import System.IO.Temp
+import System.Process (readProcess)
+
+import Distribution.Helper
+
+import CabalHelper.Shared.Common
+
+
+main :: IO ()
+main = do
+  args <- getArgs
+  topdir <- getCurrentDirectory
+  res <- mapM (setup topdir test) $ case args of
+    [] -> [ ("tests/exelib"   , parseVer "1.10")
+          , ("tests/exeintlib", parseVer "2.0")
+          , ("tests/fliblib"  , parseVer "2.0")
+          , ("tests/bkpregex" , parseVer "2.0")
+          ]
+    xs -> map (,parseVer "0") xs
+
+  if any (==False) $ concat res
+    then exitFailure
+    else exitSuccess
+
+cabalInstallVersion :: IO Version
+cabalInstallVersion =
+    parseVer . trim <$> readProcess "cabal" ["--numeric-version"] ""
+
+cabalInstallBuiltinCabalVersion :: IO Version
+cabalInstallBuiltinCabalVersion =
+    parseVer . trim <$> readProcess "cabal"
+        ["act-as-setup", "--", "--numeric-version"] ""
+
+setup :: FilePath -> (FilePath -> IO [Bool]) -> (FilePath, Version) -> IO [Bool]
+setup topdir act (srcdir, min_cabal_ver) = do
+    ci_ver <- cabalInstallVersion
+    c_ver <- cabalInstallBuiltinCabalVersion
+    let mreason
+          | (ci_ver < parseVer "1.24") =
+            Just $ "cabal-install-" ++ showVersion ci_ver ++ " is too old"
+          | c_ver < min_cabal_ver =
+            Just $ "Cabal-" ++ showVersion c_ver
+                   ++ " < " ++ showVersion min_cabal_ver
+          | otherwise =
+            Nothing
+
+    case mreason of
+      Just reason -> do
+        putStrLn $ "Skipping test '" ++ srcdir ++ "' because " ++ reason ++ "."
+        return []
+      Nothing -> do
+        putStrLn $ "Running test '" ++ srcdir ++ "' ------------------------------"
+        withSystemTempDirectory "cabal-helper.ghc-session.test" $ \dir -> do
+          setCurrentDirectory $ topdir </> srcdir
+          run "cabal" [ "sdist", "--output-dir", dir ]
+
+          setCurrentDirectory dir
+          run "cabal" [ "configure" ]
+
+          act dir
+
+run :: String -> [String] -> IO ()
+run x xs = do
+  print $ x:xs
+  o <- readProcess x xs ""
+  putStrLn o
+  return ()
+
+test :: FilePath -> IO [Bool]
+test dir = do
+    let qe = mkQueryEnv dir (dir </> "dist")
+    cs <- runQuery qe $ components $ (,,,) <$> entrypoints <.> ghcOptions <.> needsBuildOutput
+    forM cs $ \(ep, opts, nb, cn) -> do
+
+        putStrLn $ "\n" ++ show cn ++ ":::: " ++ show nb
+
+        when (nb == ProduceBuildOutput) $ do
+          run "cabal" [ "build" ]
+
+        let opts' = "-Werror" : opts
+
+        let sopts = intercalate " " $ map formatArg $ "\nghc" : opts'
+        putStrLn $ "\n" ++ show cn ++ ": " ++ sopts
+        hFlush stdout
+        compileModule nb ep opts'
+  where
+    formatArg x
+        | "-" `isPrefixOf` x = "\n  "++x
+        | otherwise          = x
+
+
+compileModule :: NeedsBuildOutput -> ChEntrypoint -> [String] -> IO Bool
+compileModule nb ep opts = do
+
+    putStrLn $ "compiling:" ++ show ep ++ " (" ++ show nb ++ ")"
+
+    E.handle (\(ec :: ExitCode) -> print ec >> return False) $ do
+
+#if __GLASGOW_HASKELL__ <= 704
+    defaultErrorHandler defaultLogAction $ do
+#else
+    defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
+#endif
+
+    runGhc (Just libdir) $ do
+
+    handleSourceError (\e -> GHC.printException e >> return False) $ do
+
+    let target = case nb of
+          ProduceBuildOutput -> HscNothing -- AZ: what should this be?
+          NoBuildOutput      -> HscInterpreted
+
+    dflags0 <- getSessionDynFlags
+    let dflags1 = dflags0 {
+        ghcMode   = CompManager
+      , ghcLink   = LinkInMemory
+      , hscTarget = target
+      , optLevel  = 0
+      }
+
+    (dflags2, _, _) <- parseDynamicFlags dflags1 (map noLoc opts)
+    _ <- setSessionDynFlags dflags2
+
+    ts <- mapM (\t -> guessTarget t Nothing) $
+         case ep of
+           ChLibEntrypoint ms ms' ss -> map unChModuleName $ ms ++ ms' ++ ss
+           ChExeEntrypoint m  ms     -> [m] ++ map unChModuleName ms
+           ChSetupEntrypoint         -> ["Setup.hs"]
+    let ts' = case nb of
+                NoBuildOutput -> map (\t -> t { targetAllowObjCode = False }) ts
+                ProduceBuildOutput -> ts
+
+    setTargets ts'
+    _ <- load LoadAllTargets
+
+#if __GLASGOW_HASKELL__ >= 706
+    when (nb == NoBuildOutput) $ do
+      setContext $ case ep of
+        ChLibEntrypoint ms ms' ss ->
+            map (IIModule . mkModuleName . unChModuleName) $ ms ++ ms' ++ ss
+        ChExeEntrypoint _  ms  ->
+            map (IIModule . mkModuleName . unChModuleName) $ ChModuleName "Main" : ms
+        ChSetupEntrypoint      ->
+            map (IIModule . mkModuleName) ["Main"]
+#endif
+
+    liftIO'CH $ print ExitSuccess
+    return True
+
+unChModuleName :: ChModuleName -> String
+unChModuleName (ChModuleName  mn) = mn
+
+#if __GLASGOW_HASKELL__ <= 706
+liftIO'CH = GhcMonad.liftIO
+#else
+liftIO'CH = liftIO
+#endif
diff --git a/tests/Spec.hs b/tests/Spec.hs
deleted file mode 100644
--- a/tests/Spec.hs
+++ /dev/null
@@ -1,127 +0,0 @@
-import Distribution.Helper
-import System.Environment.Extra (lookupEnv)
-import System.Posix.Env (setEnv)
-import System.Process
-import System.Exit
-import Data.Maybe
-import Data.Version
-import Data.Functor
-import Control.Exception as E
-import Control.Arrow
-import Prelude
-
-import CabalHelper.Common
-import CabalHelper.Compile
-import CabalHelper.Types
-
-
-main :: IO ()
-main = do
-  flip (setEnv "HOME") True =<< fromMaybe "/tmp" <$> lookupEnv "TMPDIR"
-  _ <- rawSystem "cabal" ["update"]
-
-  writeAutogenFiles' $ defaultQueryEnv "." "./dist"
-
-  let parseVer' "HEAD" = Left HEAD
-      parseVer' v = Right $ parseVer v
-
-  let vers :: [(Version, [Either HEAD Version])]
-      vers = map (parseVer *** map parseVer') [
-               ("7.4", [ -- "1.14.0" -- not supported at runtime
-                       ]),
-
-               ("7.6", [ "1.16.0"
-                       , "1.16.0.1"
-                       , "1.16.0.2"
-                       , "1.16.0.3"
-                       ]),
-
-               ("7.8", [
-                         "1.18.0"
-                       , "1.18.1"
-                       , "1.18.1.1"
-                       , "1.18.1.2"
-                       , "1.18.1.3"
-                       , "1.18.1.4"
-                       , "1.18.1.5"
-                       , "1.18.1.6"
-                       , "1.18.1.7"
-
-                       , "1.20.0.0"
-                       , "1.20.0.1"
-                       , "1.20.0.2"
-                       , "1.20.0.3"
-                       , "1.20.0.4"
-                       , "1.22.0.0"
-                       , "1.22.1.0"
-                       , "1.22.1.1"
-                       ]),
-
-               ("7.10", [
-                         "1.22.2.0"
-                       , "1.22.3.0"
-                       , "1.22.4.0"
-                       , "1.22.5.0"
-                       , "1.22.6.0"
-                       , "1.22.7.0"
-                       , "1.22.8.0"
-                       ]),
-               ("8.0", [
-                         "1.24.0.0"
-                       , "1.24.1.0"
-                       , "1.24.2.0"
---                       , "HEAD"
-                       ])
-             ]
-
-  ghcVer <- majorVer <$> ghcVersion defaultOptions
-
-  let cabalVers = reverse $ concat $ map snd $ dropWhile ((<ghcVer) . fst) vers
-
-  rvs <- mapM compilePrivatePkgDb cabalVers
-
-  let printStatus (cv, rv) = putStrLn $ "- Cabal "++show cv++" "++status
-        where status = case rv of
-                         Right _ ->
-                             "suceeded"
-                         Left rvc ->
-                             "failed (exit code "++show rvc++")"
-
-  let drvs = cabalVers `zip` rvs
-
-  mapM_ printStatus (cabalVers `zip` rvs)
-  if any isLeft' $ map snd $ filter ((/=Left HEAD) . fst) drvs
-     then exitFailure
-     else exitSuccess
-
- where
-   isLeft' (Left _) = True
-   isLeft' (Right _) = False
-
-data HEAD = HEAD deriving (Eq, Show)
-
-compilePrivatePkgDb :: Either HEAD Version -> IO (Either ExitCode FilePath)
-compilePrivatePkgDb (Left HEAD) = do
-    _ <- rawSystem "rm" [ "-r", "/tmp/.ghc-mod" ]
-    (db, commit) <- installCabalHEAD defaultOptions { verbose = True } `E.catch`
-        \(SomeException ex) ->
-            error $ "Installing cabal HEAD failed: " ++ show ex
-    compileWithPkg "." (Just db) (Left commit)
-compilePrivatePkgDb (Right cabalVer) = do
-    _ <- rawSystem "rm" [ "-r", "/tmp/.ghc-mod" ]
-    db <- installCabal defaultOptions { verbose = True } cabalVer `E.catch`
-        \(SomeException _) ->
-            errorInstallCabal cabalVer "dist"
-    compileWithPkg "." (Just db) (Right cabalVer)
-
-compileWithPkg :: FilePath
-               -> Maybe FilePath
-               -> Either String Version
-               -> IO (Either ExitCode FilePath)
-compileWithPkg chdir mdb ver =
-    compile "dist" defaultOptions { verbose = True } $
-      Compile chdir Nothing mdb ver [cabalPkgId ver]
-
-cabalPkgId :: Either String Version -> String
-cabalPkgId (Left _commitid) = "Cabal"
-cabalPkgId (Right v) = "Cabal-" ++ showVersion v
diff --git a/tests/bkpregex/Setup.hs b/tests/bkpregex/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/bkpregex/bkpregex.cabal b/tests/bkpregex/bkpregex.cabal
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/bkpregex.cabal
@@ -0,0 +1,29 @@
+name:                bkpregex
+version:             0.1.0.0
+build-type:          Simple
+cabal-version:       >=2.0
+
+library str-impls
+  build-depends:       base, bytestring
+  exposed-modules:     Str.String, Str.ByteString
+  hs-source-dirs:      str-impls
+
+library regex-types
+  build-depends:       base
+  exposed-modules:     Regex.Types
+  hs-source-dirs:      regex-types
+
+library regex-indef
+  build-depends:       base, regex-types
+  signatures:          Str
+  exposed-modules:     Regex
+  hs-source-dirs:      regex-indef
+
+executable regex-example
+  main-is:             Main.hs
+  build-depends:       base, regex-indef, regex-types, str-impls
+  mixins:              regex-indef (Regex as Regex.String)
+                          requires (Str as Str.String),
+                       regex-indef (Regex as Regex.ByteString)
+                          requires (Str as Str.ByteString)
+  hs-source-dirs:      regex-example
diff --git a/tests/bkpregex/regex-example/Main.hs b/tests/bkpregex/regex-example/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/regex-example/Main.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Main where
+
+import Regex.Types
+import qualified Regex.String
+import qualified Regex.ByteString
+
+nocs = Rep (Alt (Sym 'a') (Sym 'b'))
+onec = Seq nocs (Sym 'c')
+evencs = Seq (Rep (Seq onec onec)) nocs
+main = print (Regex.String.accept evencs "acc") >>
+       print (Regex.ByteString.accept evencs "acc")
diff --git a/tests/bkpregex/regex-indef/Regex.hs b/tests/bkpregex/regex-indef/Regex.hs
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/regex-indef/Regex.hs
@@ -0,0 +1,14 @@
+module Regex where
+
+import Prelude hiding (null)
+import Str
+import Regex.Types
+
+accept :: Reg -> Str -> Bool
+accept Eps       u = null u
+accept (Sym c)   u = u == singleton c
+accept (Alt p q) u = accept p u || accept q u
+accept (Seq p q) u =
+    or [accept p u1 && accept q u2 | (u1, u2) <- splits u]
+accept (Rep r) u =
+    or [and [accept r ui | ui <- ps] | ps <- parts u]
diff --git a/tests/bkpregex/regex-indef/Str.hsig b/tests/bkpregex/regex-indef/Str.hsig
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/regex-indef/Str.hsig
@@ -0,0 +1,9 @@
+signature Str where
+
+data Str
+instance Eq Str
+
+null :: Str -> Bool
+singleton :: Char -> Str
+splits :: Str -> [(Str, Str)]
+parts :: Str -> [[Str]]
diff --git a/tests/bkpregex/regex-types/Regex/Types.hs b/tests/bkpregex/regex-types/Regex/Types.hs
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/regex-types/Regex/Types.hs
@@ -0,0 +1,7 @@
+module Regex.Types where
+
+data Reg = Eps
+         | Sym Char
+         | Alt Reg Reg
+         | Seq Reg Reg
+         | Rep Reg
diff --git a/tests/bkpregex/str-impls/Str/ByteString.hs b/tests/bkpregex/str-impls/Str/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/str-impls/Str/ByteString.hs
@@ -0,0 +1,17 @@
+module Str.ByteString(module Data.ByteString.Char8, module Str.ByteString) where
+
+import Prelude hiding (length, null, splitAt)
+import Data.ByteString.Char8
+import Data.ByteString
+
+type Str = ByteString
+
+splits :: Str -> [(Str, Str)]
+splits s = fmap (\n -> splitAt n s) [0..length s]
+
+parts :: Str -> [[Str]]
+parts s | null s    = [[]]
+        | otherwise = do
+            n <- [1..length s]
+            let (l, r) = splitAt n s
+            fmap (l:) (parts r)
diff --git a/tests/bkpregex/str-impls/Str/String.hs b/tests/bkpregex/str-impls/Str/String.hs
new file mode 100644
--- /dev/null
+++ b/tests/bkpregex/str-impls/Str/String.hs
@@ -0,0 +1,21 @@
+module Str.String where
+
+import Prelude hiding (null)
+import qualified Prelude as P
+
+type Str = String
+
+null :: Str -> Bool
+null = P.null
+
+singleton :: Char -> Str
+singleton c = [c]
+
+splits :: Str -> [(Str, Str)]
+splits [] = [([], [])]
+splits (c:cs) = ([], c:cs):[(c:s1,s2) | (s1,s2) <- splits cs]
+
+parts :: Str -> [[Str]]
+parts [] = [[]]
+parts [c] = [[[c]]]
+parts (c:cs) = concat [[(c:p):ps, [c]:p:ps] | p:ps <- parts cs]
diff --git a/tests/exeintlib/Exe.hs b/tests/exeintlib/Exe.hs
new file mode 100644
--- /dev/null
+++ b/tests/exeintlib/Exe.hs
@@ -0,0 +1,5 @@
+module Main where
+
+import Lib
+
+main = print lib
diff --git a/tests/exeintlib/Setup.hs b/tests/exeintlib/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/exeintlib/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/exeintlib/exeintlib.cabal b/tests/exeintlib/exeintlib.cabal
new file mode 100644
--- /dev/null
+++ b/tests/exeintlib/exeintlib.cabal
@@ -0,0 +1,31 @@
+name:                exeintlib
+version:             0
+build-type:          Simple
+cabal-version:       >=2.0
+
+library
+  exposed-modules:     Lib
+  hs-source-dirs:      lib
+  build-depends:       base, filepath, intlib
+  default-language:    Haskell2010
+
+library intlib
+  exposed-modules:     IntLib
+  hs-source-dirs:      intlib
+  build-depends:       base, directory
+  default-language:    Haskell2010
+
+executable exe
+  main-is:             Exe.hs
+  build-depends:       base, exeintlib
+  default-language:    Haskell2010
+
+test-suite exe-test
+    type:              exitcode-stdio-1.0
+    main-is:           Exe.hs
+    build-depends:     base, exeintlib
+
+benchmark exe-bench
+    type:              exitcode-stdio-1.0
+    main-is:           Exe.hs
+    build-depends:     base, exeintlib
diff --git a/tests/exeintlib/intlib/IntLib.hs b/tests/exeintlib/intlib/IntLib.hs
new file mode 100644
--- /dev/null
+++ b/tests/exeintlib/intlib/IntLib.hs
@@ -0,0 +1,7 @@
+module IntLib where
+
+import System.Directory
+
+directory = doesFileExist "Exe.hs"
+
+intlib = 1
diff --git a/tests/exeintlib/lib/Lib.hs b/tests/exeintlib/lib/Lib.hs
new file mode 100644
--- /dev/null
+++ b/tests/exeintlib/lib/Lib.hs
@@ -0,0 +1,8 @@
+module Lib where
+
+import System.FilePath
+import IntLib
+
+filepath  = "a" </> "b"
+
+lib = 1 + intlib
diff --git a/tests/exelib/Exe.hs b/tests/exelib/Exe.hs
new file mode 100644
--- /dev/null
+++ b/tests/exelib/Exe.hs
@@ -0,0 +1,5 @@
+module Main where
+
+import Lib
+
+main = print foo
diff --git a/tests/exelib/Setup.hs b/tests/exelib/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/exelib/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/exelib/exelib.cabal b/tests/exelib/exelib.cabal
new file mode 100644
--- /dev/null
+++ b/tests/exelib/exelib.cabal
@@ -0,0 +1,25 @@
+name:                exelib
+version:             0
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Lib
+  hs-source-dirs:      lib
+  build-depends:       base, filepath, directory
+  default-language:    Haskell2010
+
+executable exelib
+  main-is:             Exe.hs
+  build-depends:       base, exelib
+  default-language:    Haskell2010
+
+test-suite exe-test
+    type:              exitcode-stdio-1.0
+    main-is:           Exe.hs
+    build-depends:     base, exelib
+
+benchmark exe-bench
+    type:              exitcode-stdio-1.0
+    main-is:           Exe.hs
+    build-depends:     base, exelib
diff --git a/tests/exelib/lib/Lib.hs b/tests/exelib/lib/Lib.hs
new file mode 100644
--- /dev/null
+++ b/tests/exelib/lib/Lib.hs
@@ -0,0 +1,8 @@
+module Lib where
+
+import System.Directory
+import System.FilePath
+
+filepath  = "a" </> "b"
+directory = doesFileExist "Exe.hs"
+foo = 1
diff --git a/tests/fliblib/FLib.hs b/tests/fliblib/FLib.hs
new file mode 100644
--- /dev/null
+++ b/tests/fliblib/FLib.hs
@@ -0,0 +1,5 @@
+module FLib where
+
+import Lib
+
+flib = print foo
diff --git a/tests/fliblib/Setup.hs b/tests/fliblib/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/fliblib/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/fliblib/fliblib.cabal b/tests/fliblib/fliblib.cabal
new file mode 100644
--- /dev/null
+++ b/tests/fliblib/fliblib.cabal
@@ -0,0 +1,19 @@
+name:                fliblib
+version:             0
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Lib
+  hs-source-dirs:      lib
+  build-depends:       base, filepath, directory
+  default-language:    Haskell2010
+
+foreign-library flib
+  other-modules:       FLib
+  build-depends:       base, fliblib
+  hs-source-dirs:      .
+  type:                native-shared
+  if os(Windows)
+    options:           standalone
+  default-language:    Haskell2010
diff --git a/tests/fliblib/lib/Lib.hs b/tests/fliblib/lib/Lib.hs
new file mode 100644
--- /dev/null
+++ b/tests/fliblib/lib/Lib.hs
@@ -0,0 +1,8 @@
+module Lib where
+
+import System.Directory
+import System.FilePath
+
+filepath  = "a" </> "b"
+directory = doesFileExist "Exe.hs"
+foo = 1
