diff --git a/Cabal-syntax/Cabal-syntax.cabal b/Cabal-syntax/Cabal-syntax.cabal
index 997b91363ce..64aee5d23a4 100644
--- a/Cabal-syntax/Cabal-syntax.cabal
+++ b/Cabal-syntax/Cabal-syntax.cabal
@@ -140,6 +140,7 @@ library
     Distribution.Types.Executable
     Distribution.Types.Executable.Lens
     Distribution.Types.ExecutableScope
+    Distribution.Types.ExtraSource
     Distribution.Types.ExposedModule
     Distribution.Types.Flag
     Distribution.Types.ForeignLib
diff --git a/Cabal-syntax/src/Distribution/Backpack.hs b/Cabal-syntax/src/Distribution/Backpack.hs
index e70b9ffc692..048abdc0239 100644
--- a/Cabal-syntax/src/Distribution/Backpack.hs
+++ b/Cabal-syntax/src/Distribution/Backpack.hs
@@ -53,6 +53,10 @@ import Distribution.Utils.Base62
 import qualified Data.Map as Map
 import qualified Data.Set as Set
 
+import GHC.Stack (HasCallStack)
+
+import Unsafe.Coerce (unsafeCoerce)
+
 -----------------------------------------------------------------------
 -- OpenUnitId
 
@@ -147,9 +151,7 @@ mkOpenUnitId uid cid insts =
 mkDefUnitId :: ComponentId -> Map ModuleName Module -> DefUnitId
 mkDefUnitId cid insts =
   unsafeMkDefUnitId
-    ( mkUnitId
-        (unComponentId cid ++ maybe "" ("+" ++) (hashModuleSubst insts))
-    )
+    (addSuffixToUnitId (maybe "" ("+" ++) (hashModuleSubst insts)) (unsafeCoerce cid))
 
 -- impose invariant!
 
@@ -254,7 +256,7 @@ openModuleSubstFreeHoles insts = Set.unions (map openModuleFreeHoles (Map.elems
 -- | When typechecking, we don't demand that a freshly instantiated
 -- 'IndefFullUnitId' be compiled; instead, we just depend on the
 -- installed indefinite unit installed at the 'ComponentId'.
-abstractUnitId :: OpenUnitId -> UnitId
+abstractUnitId :: HasCallStack => OpenUnitId -> UnitId
 abstractUnitId (DefiniteUnitId def_uid) = unDefUnitId def_uid
 abstractUnitId (IndefFullUnitId cid _) = newSimpleUnitId cid
 
diff --git a/Cabal-syntax/src/Distribution/Compat/Graph.hs b/Cabal-syntax/src/Distribution/Compat/Graph.hs
index c716563f52a..6fe6bb3bfec 100644
--- a/Cabal-syntax/src/Distribution/Compat/Graph.hs
+++ b/Cabal-syntax/src/Distribution/Compat/Graph.hs
@@ -105,6 +105,8 @@ import qualified Data.Set as Set
 import qualified Data.Tree as Tree
 import qualified Distribution.Compat.Prelude as Prelude
 
+import GHC.Stack (HasCallStack)
+
 -- | A graph of nodes @a@.  The nodes are expected to have instance
 -- of class 'IsNode'.
 data Graph a = Graph
@@ -377,16 +379,18 @@ fromMap m =
     bounds = (0, Map.size m - 1)
 
 -- | /O(V log V)/. Convert a list of nodes (with distinct keys) into a graph.
-fromDistinctList :: (IsNode a, Show (Key a)) => [a] -> Graph a
-fromDistinctList =
+fromDistinctList :: (HasCallStack, IsNode a, Show (Key a)) => [a] -> Graph a
+fromDistinctList xs =
   fromMap
     . Map.fromListWith (\_ -> duplicateError)
-    . map (\n -> n `seq` (nodeKey n, n))
+    . map (\n -> n `seq` (nodeKey n, n)) $ xs
   where
     duplicateError n =
       error $
         "Graph.fromDistinctList: duplicate key: "
           ++ show (nodeKey n)
+          ++ " in "
+          ++ unlines (map (show . nodeKey) xs)
 
 -- Map-like operations
 
diff --git a/Cabal-syntax/src/Distribution/Compiler.hs b/Cabal-syntax/src/Distribution/Compiler.hs
index 40148776ee1..e79b18e846f 100644
--- a/Cabal-syntax/src/Distribution/Compiler.hs
+++ b/Cabal-syntax/src/Distribution/Compiler.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE FlexibleInstances #-}
 
 -----------------------------------------------------------------------------
 
@@ -173,7 +174,7 @@ instance (Semigroup a, Monoid a) => Monoid (PerCompilerFlavor a) where
 -- ------------------------------------------------------------
 
 data CompilerId = CompilerId CompilerFlavor Version
-  deriving (Eq, Generic, Ord, Read, Show)
+  deriving (Eq, Generic, Ord, Read, Show, Data)
 
 instance Binary CompilerId
 instance Structured CompilerId
@@ -184,12 +185,18 @@ instance Pretty CompilerId where
     | v == nullVersion = pretty f
     | otherwise = pretty f <<>> Disp.char '-' <<>> pretty v
 
+instance Pretty (Maybe CompilerId) where
+  pretty = maybe Disp.empty pretty
+
 instance Parsec CompilerId where
   parsec = do
     flavour <- parsec
     version <- (P.char '-' >> parsec) <|> return nullVersion
     return (CompilerId flavour version)
 
+instance Parsec (Maybe CompilerId) where
+  parsec = Just <$> parsec <|> return Nothing
+
 lowercase :: String -> String
 lowercase = map toLower
 
@@ -216,10 +223,13 @@ data CompilerInfo = CompilerInfo
   , compilerInfoExtensions :: Maybe [Extension]
   -- ^ Supported extensions, if known.
   }
-  deriving (Generic, Show, Read)
+  deriving (Generic, Show, Read, Eq)
 
 instance Binary CompilerInfo
+instance Structured CompilerInfo
+instance NFData CompilerInfo where rnf = genericRnf
 
+-- | AbiTag logic
 data AbiTag
   = NoAbiTag
   | AbiTag String
@@ -227,6 +237,7 @@ data AbiTag
 
 instance Binary AbiTag
 instance Structured AbiTag
+instance NFData AbiTag where rnf = genericRnf
 
 instance Pretty AbiTag where
   pretty NoAbiTag = Disp.empty
diff --git a/Cabal-syntax/src/Distribution/FieldGrammar/Class.hs b/Cabal-syntax/src/Distribution/FieldGrammar/Class.hs
index fa815a49a5e..f439f9d2b01 100644
--- a/Cabal-syntax/src/Distribution/FieldGrammar/Class.hs
+++ b/Cabal-syntax/src/Distribution/FieldGrammar/Class.hs
@@ -23,6 +23,8 @@ import Distribution.FieldGrammar.Newtypes
 import Distribution.Fields.Field
 import Distribution.Utils.ShortText
 
+import GHC.Stack (HasCallStack)
+
 -- | 'FieldGrammar' is parametrised by
 --
 -- * @s@ which is a structure we are parsing. We need this to provide prettyprinter
@@ -43,11 +45,11 @@ class
     | g -> c
   where
   -- | Unfocus, zoom out, /blur/ 'FieldGrammar'.
-  blurFieldGrammar :: ALens' a b -> g b d -> g a d
+  blurFieldGrammar :: HasCallStack => ALens' a b -> g b d -> g a d
 
   -- | Field which should be defined, exactly once.
   uniqueFieldAla
-    :: (c b, Newtype a b)
+    :: (c b, Newtype a b, HasCallStack)
     => FieldName
     -- ^ field name
     -> (a -> b)
@@ -58,7 +60,7 @@ class
 
   -- | Boolean field with a default value.
   booleanFieldDef
-    :: FieldName
+    :: HasCallStack => FieldName
     -- ^ field name
     -> ALens' s Bool
     -- ^ lens into the field
@@ -68,7 +70,7 @@ class
 
   -- | Optional field.
   optionalFieldAla
-    :: (c b, Newtype a b)
+    :: (c b, Newtype a b, HasCallStack)
     => FieldName
     -- ^ field name
     -> (a -> b)
@@ -79,7 +81,7 @@ class
 
   -- | Optional field with default value.
   optionalFieldDefAla
-    :: (c b, Newtype a b, Eq a)
+    :: (c b, Newtype a b, Eq a, HasCallStack)
     => FieldName
     -- ^ field name
     -> (a -> b)
@@ -95,7 +97,7 @@ class
   --
   -- @since 3.0.0.0
   freeTextField
-    :: FieldName
+    :: HasCallStack => FieldName
     -> ALens' s (Maybe String)
     -- ^ lens into the field
     -> g s (Maybe String)
@@ -105,14 +107,14 @@ class
   --
   -- @since 3.0.0.0
   freeTextFieldDef
-    :: FieldName
+    :: HasCallStack => FieldName
     -> ALens' s String
     -- ^ lens into the field
     -> g s String
 
   -- | @since 3.2.0.0
   freeTextFieldDefST
-    :: FieldName
+    :: HasCallStack => FieldName
     -> ALens' s ShortText
     -- ^ lens into the field
     -> g s ShortText
@@ -123,7 +125,7 @@ class
   --
   -- /Note:/ 'optionalFieldAla' is a @monoidalField@ with 'Last' monoid.
   monoidalFieldAla
-    :: (c b, Monoid a, Newtype a b)
+    :: (c b, Monoid a, Newtype a b, HasCallStack)
     => FieldName
     -- ^ field name
     -> (a -> b)
@@ -134,21 +136,21 @@ class
 
   -- | Parser matching all fields with a name starting with a prefix.
   prefixedFields
-    :: FieldName
+    :: HasCallStack => FieldName
     -- ^ field name prefix
     -> ALens' s [(String, String)]
     -- ^ lens into the field
     -> g s [(String, String)]
 
   -- | Known field, which we don't parse, nor pretty print.
-  knownField :: FieldName -> g s ()
+  knownField :: HasCallStack => FieldName -> g s ()
 
   -- | Field which is parsed but not pretty printed.
-  hiddenField :: g s a -> g s a
+  hiddenField :: HasCallStack => g s a -> g s a
 
   -- | Deprecated since
   deprecatedSince
-    :: CabalSpecVersion
+    :: HasCallStack => CabalSpecVersion
     -- ^ version
     -> String
     -- ^ deprecation message
@@ -157,7 +159,7 @@ class
 
   -- | Removed in. If we encounter removed field, parsing fails.
   removedIn
-    :: CabalSpecVersion
+    :: HasCallStack => CabalSpecVersion
     -- ^ version
     -> String
     -- ^ removal message
@@ -166,7 +168,7 @@ class
 
   -- | Annotate field with since spec-version.
   availableSince
-    :: CabalSpecVersion
+    :: HasCallStack => CabalSpecVersion
     -- ^ spec version
     -> a
     -- ^ default value
@@ -181,7 +183,7 @@ class
   --
   -- @since 3.4.0.0
   availableSinceWarn
-    :: CabalSpecVersion
+    :: HasCallStack => CabalSpecVersion
     -- ^ spec version
     -> g s a
     -> g s a
@@ -189,7 +191,7 @@ class
 
 -- | Field which can be defined at most once.
 uniqueField
-  :: (FieldGrammar c g, c (Identity a))
+  :: (FieldGrammar c g, c (Identity a), HasCallStack)
   => FieldName
   -- ^ field name
   -> ALens' s a
@@ -199,7 +201,7 @@ uniqueField fn l = uniqueFieldAla fn Identity l
 
 -- | Field which can be defined at most once.
 optionalField
-  :: (FieldGrammar c g, c (Identity a))
+  :: (FieldGrammar c g, c (Identity a), HasCallStack)
   => FieldName
   -- ^ field name
   -> ALens' s (Maybe a)
@@ -209,7 +211,7 @@ optionalField fn l = optionalFieldAla fn Identity l
 
 -- | Optional field with default value.
 optionalFieldDef
-  :: (FieldGrammar c g, Functor (g s), c (Identity a), Eq a)
+  :: (FieldGrammar c g, Functor (g s), c (Identity a), Eq a, HasCallStack)
   => FieldName
   -- ^ field name
   -> ALens' s a
@@ -221,7 +223,7 @@ optionalFieldDef fn l x = optionalFieldDefAla fn Identity l x
 
 -- | Field which can be define multiple times, and the results are @mappend@ed.
 monoidalField
-  :: (FieldGrammar c g, c (Identity a), Monoid a)
+  :: (FieldGrammar c g, c (Identity a), Monoid a, HasCallStack)
   => FieldName
   -- ^ field name
   -> ALens' s a
@@ -231,7 +233,7 @@ monoidalField fn l = monoidalFieldAla fn Identity l
 
 -- | Default implementation for 'freeTextFieldDefST'.
 defaultFreeTextFieldDefST
-  :: (Functor (g s), FieldGrammar c g)
+  :: (Functor (g s), FieldGrammar c g, HasCallStack)
   => FieldName
   -> ALens' s ShortText
   -- ^ lens into the field
diff --git a/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs b/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs
index 2c15d678335..dc2499f7a38 100644
--- a/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs
+++ b/Cabal-syntax/src/Distribution/InstalledPackageInfo.hs
@@ -62,11 +62,15 @@ import qualified Text.PrettyPrint as Disp
 import Distribution.Types.InstalledPackageInfo
 import Distribution.Types.InstalledPackageInfo.FieldGrammar
 
-installedComponentId :: InstalledPackageInfo -> ComponentId
+import GHC.Stack (HasCallStack)
+
+import Unsafe.Coerce (unsafeCoerce)
+
+installedComponentId :: HasCallStack => InstalledPackageInfo -> ComponentId
 installedComponentId ipi =
-  case unComponentId (installedComponentId_ ipi) of
-    "" -> mkComponentId (unUnitId (installedUnitId ipi))
-    _ -> installedComponentId_ ipi
+  fromMaybe
+    (unsafeCoerce (installedUnitId ipi))
+    (installedComponentId_ ipi)
 
 -- | Get the indefinite unit identity representing this package.
 -- This IS NOT guaranteed to give you a substitution; for
@@ -93,7 +97,7 @@ sourceComponentName = CLibName . sourceLibName
 
 -- | Return either errors, or IPI with list of warnings
 parseInstalledPackageInfo
-  :: ByteString
+  :: HasCallStack => ByteString
   -> Either (NonEmpty String) ([String], InstalledPackageInfo)
 parseInstalledPackageInfo s = case P.readFields s of
   Left err -> Left (show err :| [])
@@ -117,12 +121,12 @@ parseInstalledPackageInfo s = case P.readFields s of
 -- | Pretty print 'InstalledPackageInfo'.
 --
 -- @pkgRoot@ isn't printed, as ghc-pkg prints it manually (as GHC-8.4).
-showInstalledPackageInfo :: InstalledPackageInfo -> String
+showInstalledPackageInfo :: HasCallStack => InstalledPackageInfo -> String
 showInstalledPackageInfo ipi =
   showFullInstalledPackageInfo ipi{pkgRoot = Nothing}
 
 -- | The variant of 'showInstalledPackageInfo' which outputs @pkgroot@ field too.
-showFullInstalledPackageInfo :: InstalledPackageInfo -> String
+showFullInstalledPackageInfo :: HasCallStack => InstalledPackageInfo -> String
 showFullInstalledPackageInfo = P.showFields (const NoComment) . prettyFieldGrammar cabalSpecLatest ipiFieldGrammar
 
 -- |
@@ -130,7 +134,7 @@ showFullInstalledPackageInfo = P.showFields (const NoComment) . prettyFieldGramm
 -- >>> let ipi = emptyInstalledPackageInfo { maintainer = fromString "Tester" }
 -- >>> fmap ($ ipi) $ showInstalledPackageInfoField "maintainer"
 -- Just "maintainer: Tester"
-showInstalledPackageInfoField :: String -> Maybe (InstalledPackageInfo -> String)
+showInstalledPackageInfoField :: HasCallStack => String -> Maybe (InstalledPackageInfo -> String)
 showInstalledPackageInfoField fn =
   fmap (\g -> Disp.render . ppField fn . g) $ fieldDescrPretty ipiFieldGrammar (toUTF8BS fn)
 
diff --git a/Cabal-syntax/src/Distribution/PackageDescription.hs b/Cabal-syntax/src/Distribution/PackageDescription.hs
index 47d46673e5f..71078654e68 100644
--- a/Cabal-syntax/src/Distribution/PackageDescription.hs
+++ b/Cabal-syntax/src/Distribution/PackageDescription.hs
@@ -48,6 +48,9 @@ module Distribution.PackageDescription
   , module Distribution.Types.HookedBuildInfo
   , module Distribution.Types.SetupBuildInfo
 
+   -- * Extra sources
+  , module Distribution.Types.ExtraSource
+
     -- * Flags
   , module Distribution.Types.Flag
 
@@ -95,6 +98,7 @@ import Distribution.Types.ComponentName
 import Distribution.Types.CondTree
 import Distribution.Types.Condition
 import Distribution.Types.ConfVar
+import Distribution.Types.ExtraSource
 import Distribution.Types.Dependency
 import Distribution.Types.ExeDependency
 import Distribution.Types.Executable
diff --git a/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs b/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs
index 49db03ee3c1..27e98ee0a82 100644
--- a/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs
+++ b/Cabal-syntax/src/Distribution/PackageDescription/FieldGrammar.hs
@@ -74,7 +74,7 @@ import Prelude ()
 
 import Distribution.CabalSpecVersion
 import Distribution.Compat.Newtype (Newtype, pack', unpack')
-import Distribution.Compiler (CompilerFlavor (..), PerCompilerFlavor (..))
+import Distribution.Compiler (CompilerFlavor (..), PerCompilerFlavor (..), CompilerId)
 import Distribution.FieldGrammar
 import Distribution.Fields
 import Distribution.ModuleName (ModuleName)
@@ -101,6 +101,7 @@ packageDescriptionFieldGrammar
      , c (Identity BuildType)
      , c (Identity PackageName)
      , c (Identity Version)
+     , c (Identity CompilerId)
      , forall from to. c (List FSep (RelativePathNT from to) (RelativePath from to))
      , forall from to. c (List VCat (RelativePathNT from to) (RelativePath from to))
      , c (List FSep TestedWith (CompilerFlavor, VersionRange))
@@ -150,6 +151,7 @@ packageDescriptionFieldGrammar =
       PackageIdentifier
         <$> uniqueField "name" L.pkgName
         <*> uniqueField "version" L.pkgVersion
+        <*> optionalField "compiler" L.pkgCompiler
 
     licenseFilesGrammar =
       (++)
@@ -174,6 +176,8 @@ libraryFieldGrammar
      , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)
      , c (List CommaVCat (Identity Dependency) Dependency)
      , c (List CommaVCat (Identity Mixin) Mixin)
+     , c (List VCat (Identity (ExtraSource Pkg)) (ExtraSource Pkg))
+     , c (List VCat (Identity (ExtraSource Build)) (ExtraSource Build))
      , c (List CommaVCat (Identity ModuleReexport) ModuleReexport)
      , c (List FSep (MQuoted Extension) Extension)
      , c (List FSep (MQuoted Language) Language)
@@ -224,6 +228,8 @@ foreignLibFieldGrammar
      , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)
      , c (List CommaVCat (Identity Dependency) Dependency)
      , c (List CommaVCat (Identity Mixin) Mixin)
+     , c (List VCat (Identity (ExtraSource Pkg)) (ExtraSource Pkg))
+     , c (List VCat (Identity (ExtraSource Build)) (ExtraSource Build))
      , c (List FSep (Identity ForeignLibOption) ForeignLibOption)
      , c (List FSep (MQuoted Extension) Extension)
      , c (List FSep (MQuoted Language) Language)
@@ -263,6 +269,8 @@ executableFieldGrammar
      , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)
      , c (List CommaVCat (Identity Dependency) Dependency)
      , c (List CommaVCat (Identity Mixin) Mixin)
+     , c (List VCat (Identity (ExtraSource Pkg)) (ExtraSource Pkg))
+     , c (List VCat (Identity (ExtraSource Build)) (ExtraSource Build))
      , c (List FSep (MQuoted Extension) Extension)
      , c (List FSep (MQuoted Language) Language)
      , c (List FSep Token String)
@@ -339,6 +347,8 @@ testSuiteFieldGrammar
      , c (List CommaFSep Token String)
      , c (List CommaVCat (Identity Dependency) Dependency)
      , c (List CommaVCat (Identity Mixin) Mixin)
+     , c (List VCat (Identity (ExtraSource Pkg)) (ExtraSource Pkg))
+     , c (List VCat (Identity (ExtraSource Build)) (ExtraSource Build))
      , c (List FSep (MQuoted Extension) Extension)
      , c (List FSep (MQuoted Language) Language)
      , c (List FSep Token String)
@@ -483,6 +493,8 @@ benchmarkFieldGrammar
      , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)
      , c (List CommaVCat (Identity Dependency) Dependency)
      , c (List CommaVCat (Identity Mixin) Mixin)
+     , c (List VCat (Identity (ExtraSource Pkg)) (ExtraSource Pkg))
+     , c (List VCat (Identity (ExtraSource Build)) (ExtraSource Build))
      , c (List FSep (MQuoted Extension) Extension)
      , c (List FSep (MQuoted Language) Language)
      , c (List FSep Token String)
@@ -585,6 +597,8 @@ buildInfoFieldGrammar
      , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)
      , c (List CommaVCat (Identity Dependency) Dependency)
      , c (List CommaVCat (Identity Mixin) Mixin)
+     , c (List VCat (Identity (ExtraSource Pkg)) (ExtraSource Pkg))
+     , c (List VCat (Identity (ExtraSource Build)) (ExtraSource Build))
      , c (List FSep (MQuoted Extension) Extension)
      , c (List FSep (MQuoted Language) Language)
      , c (List FSep Token String)
@@ -627,14 +641,16 @@ buildInfoFieldGrammar =
     <*> monoidalFieldAla "pkgconfig-depends" (alaList CommaFSep) L.pkgconfigDepends
     <*> monoidalFieldAla "frameworks" (alaList' FSep RelativePathNT) L.frameworks
     <*> monoidalFieldAla "extra-framework-dirs" (alaList' FSep SymbolicPathNT) L.extraFrameworkDirs
-    <*> monoidalFieldAla "asm-sources" (alaList' VCat SymbolicPathNT) L.asmSources
+    <*> monoidalFieldAla "asm-sources" formatExtraSources L.asmSources
       ^^^ availableSince CabalSpecV3_0 []
-    <*> monoidalFieldAla "cmm-sources" (alaList' VCat SymbolicPathNT) L.cmmSources
+    <*> monoidalFieldAla "cmm-sources" formatExtraSources L.cmmSources
       ^^^ availableSince CabalSpecV3_0 []
-    <*> monoidalFieldAla "c-sources" (alaList' VCat SymbolicPathNT) L.cSources
-    <*> monoidalFieldAla "cxx-sources" (alaList' VCat SymbolicPathNT) L.cxxSources
+    <*> monoidalFieldAla "autogen-cmm-sources" formatExtraSources L.autogenCmmSources
+    -- FIXME ^^^ availableSince CabalSpecV3_0 []
+    <*> monoidalFieldAla "c-sources" formatExtraSources L.cSources
+    <*> monoidalFieldAla "cxx-sources" formatExtraSources L.cxxSources
       ^^^ availableSince CabalSpecV2_2 []
-    <*> monoidalFieldAla "js-sources" (alaList' VCat SymbolicPathNT) L.jsSources
+    <*> monoidalFieldAla "js-sources" formatExtraSources L.jsSources
     <*> hsSourceDirsGrammar
     <*> monoidalFieldAla "other-modules" formatOtherModules L.otherModules
     <*> monoidalFieldAla "virtual-modules" (alaList' VCat MQuoted) L.virtualModules
@@ -834,6 +850,9 @@ formatOtherExtensions = alaList' FSep MQuoted
 formatOtherModules :: [ModuleName] -> List VCat (MQuoted ModuleName) ModuleName
 formatOtherModules = alaList' VCat MQuoted
 
+formatExtraSources :: [ExtraSource pkg] -> List VCat (Identity (ExtraSource pkg)) (ExtraSource pkg)
+formatExtraSources = alaList' VCat Identity
+
 -------------------------------------------------------------------------------
 -- newtypes
 -------------------------------------------------------------------------------
diff --git a/Cabal-syntax/src/Distribution/Parsec.hs b/Cabal-syntax/src/Distribution/Parsec.hs
index d0197616fd6..dcc236b840f 100644
--- a/Cabal-syntax/src/Distribution/Parsec.hs
+++ b/Cabal-syntax/src/Distribution/Parsec.hs
@@ -70,6 +70,8 @@ import qualified Distribution.Compat.DList as DList
 import qualified Distribution.Compat.MonadFail as Fail
 import qualified Text.Parsec as Parsec
 
+import GHC.Stack (HasCallStack)
+
 -------------------------------------------------------------------------------
 -- Class
 -------------------------------------------------------------------------------
@@ -78,7 +80,7 @@ import qualified Text.Parsec as Parsec
 --
 -- For parsing @.cabal@ like file structure, see "Distribution.Fields".
 class Parsec a where
-  parsec :: CabalParsing m => m a
+  parsec :: (HasCallStack, CabalParsing m) => m a
 
 -- | Parsing class which
 --
@@ -94,7 +96,7 @@ class (P.CharParsing m, MonadPlus m, Fail.MonadFail m) => CabalParsing m where
   askCabalSpecVersion :: m CabalSpecVersion
 
 -- | 'parsec' /could/ consume trailing spaces, this function /will/ consume.
-lexemeParsec :: (CabalParsing m, Parsec a) => m a
+lexemeParsec :: (HasCallStack, CabalParsing m, Parsec a) => m a
 lexemeParsec = parsec <* P.spaces
 
 newtype ParsecParser a = PP
@@ -175,14 +177,14 @@ instance CabalParsing ParsecParser where
   askCabalSpecVersion = PP pure
 
 -- | Parse a 'String' with 'lexemeParsec'.
-simpleParsec :: Parsec a => String -> Maybe a
+simpleParsec :: (HasCallStack, Parsec a) => String -> Maybe a
 simpleParsec =
   either (const Nothing) Just
     . runParsecParser lexemeParsec "<simpleParsec>"
     . fieldLineStreamFromString
 
 -- | Like 'simpleParsec' but for 'ByteString'
-simpleParsecBS :: Parsec a => ByteString -> Maybe a
+simpleParsecBS :: (HasCallStack, Parsec a) => ByteString -> Maybe a
 simpleParsecBS =
   either (const Nothing) Just
     . runParsecParser lexemeParsec "<simpleParsec>"
@@ -191,7 +193,7 @@ simpleParsecBS =
 -- | Parse a 'String' with 'lexemeParsec' using specific 'CabalSpecVersion'.
 --
 -- @since 3.4.0.0
-simpleParsec' :: Parsec a => CabalSpecVersion -> String -> Maybe a
+simpleParsec' :: (HasCallStack, Parsec a) => CabalSpecVersion -> String -> Maybe a
 simpleParsec' spec =
   either (const Nothing) Just
     . runParsecParser' spec lexemeParsec "<simpleParsec>"
@@ -201,7 +203,7 @@ simpleParsec' spec =
 -- Fail if there are any warnings.
 --
 -- @since 3.4.0.0
-simpleParsecW' :: Parsec a => CabalSpecVersion -> String -> Maybe a
+simpleParsecW' :: (HasCallStack, Parsec a) => CabalSpecVersion -> String -> Maybe a
 simpleParsecW' spec =
   either (const Nothing) (\(x, ws) -> if null ws then Just x else Nothing)
     . runParsecParser' spec ((,) <$> lexemeParsec <*> liftParsec Parsec.getState) "<simpleParsec>"
diff --git a/Cabal-syntax/src/Distribution/Types/BuildInfo.hs b/Cabal-syntax/src/Distribution/Types/BuildInfo.hs
index 2d7a5edeae1..1d7340e4de5 100644
--- a/Cabal-syntax/src/Distribution/Types/BuildInfo.hs
+++ b/Cabal-syntax/src/Distribution/Types/BuildInfo.hs
@@ -21,6 +21,7 @@ import Prelude ()
 
 import Distribution.Types.Dependency
 import Distribution.Types.ExeDependency
+import Distribution.Types.ExtraSource
 import Distribution.Types.LegacyExeDependency
 import Distribution.Types.Mixin
 import Distribution.Types.PkgconfigDependency
@@ -70,14 +71,19 @@ data BuildInfo = BuildInfo
   , frameworks :: [RelativePath Framework File]
   -- ^ support frameworks for Mac OS X
   , extraFrameworkDirs :: [SymbolicPath Pkg (Dir Framework)]
-  -- ^ extra locations to find frameworks.
-  , asmSources :: [SymbolicPath Pkg File]
-  -- ^ Assembly files.
-  , cmmSources :: [SymbolicPath Pkg File]
-  -- ^ C-- files.
-  , cSources :: [SymbolicPath Pkg File]
-  , cxxSources :: [SymbolicPath Pkg File]
-  , jsSources :: [SymbolicPath Pkg File]
+  -- ^ extra locations to find frameworks
+  , asmSources :: [ExtraSource Pkg]
+  -- ^ Assembly source files
+  , cmmSources :: [ExtraSource Pkg]
+  -- ^ C-- source files
+  , autogenCmmSources :: [ExtraSource Build]
+  -- ^ C-- generated source files
+  , cSources :: [ExtraSource Pkg]
+  -- ^ C source files
+  , cxxSources :: [ExtraSource Pkg]
+  -- ^ C++ source files
+  , jsSources :: [ExtraSource Pkg]
+  -- ^ JavaScript source file
   , hsSourceDirs :: [SymbolicPath Pkg (Dir Source)]
   -- ^ where to look for the Haskell module hierarchy
   , -- NB: these are symbolic paths are not relative paths,
@@ -168,6 +174,7 @@ instance Monoid BuildInfo where
       , extraFrameworkDirs = []
       , asmSources = []
       , cmmSources = []
+      , autogenCmmSources = []
       , cSources = []
       , cxxSources = []
       , jsSources = []
@@ -221,6 +228,7 @@ instance Semigroup BuildInfo where
       , extraFrameworkDirs = combineNub extraFrameworkDirs
       , asmSources = combineNub asmSources
       , cmmSources = combineNub cmmSources
+      , autogenCmmSources = combineNub autogenCmmSources
       , cSources = combineNub cSources
       , cxxSources = combineNub cxxSources
       , jsSources = combineNub jsSources
diff --git a/Cabal-syntax/src/Distribution/Types/BuildInfo/Lens.hs b/Cabal-syntax/src/Distribution/Types/BuildInfo/Lens.hs
index ac99f3c65a5..2d82a81a0bd 100644
--- a/Cabal-syntax/src/Distribution/Types/BuildInfo/Lens.hs
+++ b/Cabal-syntax/src/Distribution/Types/BuildInfo/Lens.hs
@@ -13,6 +13,7 @@ import Prelude ()
 import Distribution.Compiler (PerCompilerFlavor)
 import Distribution.ModuleName (ModuleName)
 import Distribution.Types.BuildInfo (BuildInfo)
+import Distribution.Types.ExtraSource (ExtraSource)
 import Distribution.Types.Dependency (Dependency)
 import Distribution.Types.ExeDependency (ExeDependency)
 import Distribution.Types.LegacyExeDependency (LegacyExeDependency)
@@ -79,23 +80,27 @@ class HasBuildInfo a where
   extraFrameworkDirs = buildInfo . extraFrameworkDirs
   {-# INLINE extraFrameworkDirs #-}
 
-  asmSources :: Lens' a [SymbolicPath Pkg File]
+  asmSources :: Lens' a [ExtraSource Pkg]
   asmSources = buildInfo . asmSources
   {-# INLINE asmSources #-}
 
-  cmmSources :: Lens' a [SymbolicPath Pkg File]
+  autogenCmmSources :: Lens' a [ExtraSource Build]
+  autogenCmmSources = buildInfo . autogenCmmSources
+  {-# INLINE autogenCmmSources #-}
+
+  cmmSources :: Lens' a [ExtraSource Pkg]
   cmmSources = buildInfo . cmmSources
   {-# INLINE cmmSources #-}
 
-  cSources :: Lens' a [SymbolicPath Pkg File]
+  cSources :: Lens' a [ExtraSource Pkg]
   cSources = buildInfo . cSources
   {-# INLINE cSources #-}
 
-  cxxSources :: Lens' a [SymbolicPath Pkg File]
+  cxxSources :: Lens' a [ExtraSource Pkg]
   cxxSources = buildInfo . cxxSources
   {-# INLINE cxxSources #-}
 
-  jsSources :: Lens' a [SymbolicPath Pkg File]
+  jsSources :: Lens' a [ExtraSource Pkg]
   jsSources = buildInfo . jsSources
   {-# INLINE jsSources #-}
 
@@ -267,7 +272,7 @@ instance HasBuildInfo BuildInfo where
   cSources f s = fmap (\x -> s{T.cSources = x}) (f (T.cSources s))
   {-# INLINE cSources #-}
 
-  cxxSources f s = fmap (\x -> s{T.cSources = x}) (f (T.cxxSources s))
+  cxxSources f s = fmap (\x -> s{T.cxxSources = x}) (f (T.cxxSources s))
   {-# INLINE cxxSources #-}
 
   jsSources f s = fmap (\x -> s{T.jsSources = x}) (f (T.jsSources s))
diff --git a/Cabal-syntax/src/Distribution/Types/ComponentId.hs b/Cabal-syntax/src/Distribution/Types/ComponentId.hs
index fa770448363..fd2467972e9 100644
--- a/Cabal-syntax/src/Distribution/Types/ComponentId.hs
+++ b/Cabal-syntax/src/Distribution/Types/ComponentId.hs
@@ -17,6 +17,7 @@ import Distribution.Pretty
 import qualified Distribution.Compat.CharParsing as P
 import Text.PrettyPrint (text)
 
+import GHC.Stack (HasCallStack, prettyCallStack, callStack)
 -- | A 'ComponentId' uniquely identifies the transitive source
 -- code closure of a component (i.e. libraries, executables).
 --
@@ -30,8 +31,21 @@ import Text.PrettyPrint (text)
 -- This type is opaque since @Cabal-2.0@
 --
 -- @since 2.0.0.2
-newtype ComponentId = ComponentId ShortText
-  deriving (Generic, Read, Show, Eq, Ord, Data)
+data ComponentId = ComponentId {unitComp :: ShortText, unitId :: ShortText, wasPartial :: Bool }
+                 | PartialComponentId ShortText
+  deriving (Generic, Read, Show, Data)
+
+instance Eq ComponentId where
+  (ComponentId c1 s1 _) == (ComponentId c2 s2 _) = c1 == c2 && s1 == s2
+  (PartialComponentId s1) == (PartialComponentId s2) = s1 == s2
+  _ == _ = False
+
+instance Ord ComponentId where
+  compare (ComponentId c1 s1 _) (ComponentId c2 s2 _) = compare (c1, s1) (c2, s2)
+  compare (PartialComponentId s1) (PartialComponentId s2) = compare s1 s2
+  compare (PartialComponentId _) _ = LT
+  compare _ (PartialComponentId _) = GT
+
 
 -- | Construct a 'ComponentId' from a 'String'
 --
@@ -41,14 +55,27 @@ newtype ComponentId = ComponentId ShortText
 -- 'ComponentId' is valid
 --
 -- @since 2.0.0.2
-mkComponentId :: String -> ComponentId
-mkComponentId = ComponentId . toShortText
+mkComponentId :: HasCallStack => String -> ComponentId
+mkComponentId s = case (simpleParsec s) of
+    -- Just cid@ComponentId{ unitComp = c, unitId = i } | (fromShortText c) == "ghc-9.8.4", (fromShortText i) == "rts-1.0.3-cec100dd" -> trace ("### ComponentId: `" ++ (fromShortText c) ++ "' `" ++ (fromShortText i) ++ "' is a full one.\n" ++ prettyCallStack callStack) cid
+    Just cid@ComponentId{} -> cid
+    Just cid@PartialComponentId{} -> error $ "mkPartialComponentId: `" ++ s ++ "' is a partial component id, not a full one."
+    _ -> error $ "Unable to parse PartialComponentId: `" ++ s ++ "'."
+
+mkComponentId' :: HasCallStack => String -> String -> Bool -> ComponentId
+-- mkComponentId' c i b | c == "ghc-9.8.4", i == "rts-1.0.3-cec100dd" = trace ("### mkComponentId': `" ++ c ++ "' `" ++ i ++ "' is a full one.\n" ++ prettyCallStack callStack) (ComponentId (toShortText c) (toShortText i) b)
+mkComponentId' c i b = ComponentId (toShortText c) (toShortText i) b
+
+mkPartialComponentId :: HasCallStack => String -> ComponentId
+mkPartialComponentId s = PartialComponentId (toShortText s)
 
 -- | Convert 'ComponentId' to 'String'
 --
 -- @since 2.0.0.2
-unComponentId :: ComponentId -> String
-unComponentId (ComponentId s) = fromShortText s
+unComponentId :: HasCallStack => ComponentId -> String
+unComponentId (ComponentId c s False) = fromShortText c ++ '_':fromShortText s
+unComponentId (ComponentId c s True) = fromShortText s
+unComponentId (PartialComponentId s) = fromShortText s
 
 -- | 'mkComponentId'
 --
@@ -63,8 +90,12 @@ instance Pretty ComponentId where
   pretty = text . unComponentId
 
 instance Parsec ComponentId where
-  parsec = mkComponentId `fmap` P.munch1 abi_char
+  parsec = P.try (mkComponentId' <$> compid <* P.char '_' <*> P.munch1 abi_char <*> return False)
+        <|> mkPartialComponentId <$> P.munch1 abi_char
     where
+      compid = (\f v -> f ++ "-" ++ v) <$> P.munch1 isAlpha <* P.char '-' <*> P.munch1 isVerChar
+      isVerChar :: Char -> Bool
+      isVerChar c = c `elem` '.':['0'..'9']
       abi_char c = isAlphaNum c || c `elem` "-_."
 
 instance NFData ComponentId where
diff --git a/Cabal-syntax/src/Distribution/Types/ExtraSource.hs b/Cabal-syntax/src/Distribution/Types/ExtraSource.hs
new file mode 100644
index 00000000000..4b888a0d28e
--- /dev/null
+++ b/Cabal-syntax/src/Distribution/Types/ExtraSource.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
+
+module Distribution.Types.ExtraSource
+  ( ExtraSource (..)
+  , ExtraSourceClass (..)
+  ) where
+
+import Distribution.Compat.Prelude
+import Prelude ()
+
+import Distribution.Parsec
+import Distribution.Pretty
+import Distribution.Utils.Path (Build, FileOrDir (..), Pkg, RelativePath, SymbolicPath, relativeSymbolicPath, unsafeCoerceSymbolicPath)
+
+import qualified Distribution.Compat.CharParsing as P
+import qualified Text.PrettyPrint as PP
+
+data family ExtraSource pkg
+
+data instance ExtraSource Pkg = ExtraSourcePkg (SymbolicPath Pkg File) [String]
+  deriving (Generic, Show, Read, Eq, Ord, Typeable, Data)
+
+data instance ExtraSource Build = ExtraSourceBuild (RelativePath Build File) [String]
+  deriving (Generic, Show, Read, Eq, Ord, Typeable, Data)
+
+class ExtraSourceClass e where
+  extraSourceOpts :: e -> [String]
+  extraSourceFile :: e -> SymbolicPath Pkg 'File
+
+instance ExtraSourceClass (ExtraSource Pkg) where
+  extraSourceOpts (ExtraSourcePkg _ opts) = opts
+  extraSourceFile (ExtraSourcePkg f _) = f
+
+instance ExtraSourceClass (ExtraSource Build) where
+  extraSourceOpts (ExtraSourceBuild _ opts) = opts
+
+  -- FIXME
+  extraSourceFile (ExtraSourceBuild f _) = unsafeCoerceSymbolicPath (relativeSymbolicPath f)
+
+instance Binary (ExtraSource Pkg)
+instance Structured (ExtraSource Pkg)
+instance NFData (ExtraSource Pkg) where rnf = genericRnf
+
+instance Binary (ExtraSource Build)
+instance Structured (ExtraSource Build)
+instance NFData (ExtraSource Build) where rnf = genericRnf
+
+instance Parsec (ExtraSource Pkg) where
+  parsec = do
+    path <- parsec <* P.spaces
+    opts <- P.optional (parensLax (P.sepBy p P.spaces))
+    return (ExtraSourcePkg path (fromMaybe mempty opts))
+    where
+      p :: P.CharParsing p => p String
+      p = some $ P.satisfy (\c -> not (isSpace c) && (c /= ')'))
+
+instance Parsec (ExtraSource Build) where
+  parsec = do
+    path <- parsec <* P.spaces
+    opts <- P.optional (parensLax (P.sepBy p P.spaces))
+    return (ExtraSourceBuild path (fromMaybe mempty opts))
+    where
+      p :: P.CharParsing p => p String
+      p = some $ P.satisfy (\c -> not (isSpace c) && (c /= ')'))
+
+instance Pretty (ExtraSource Pkg) where
+  pretty (ExtraSourcePkg path opts) =
+    pretty path <<>> PP.parens (PP.hsep (map PP.text opts))
+
+instance Pretty (ExtraSource Build) where
+  pretty (ExtraSourceBuild path opts) =
+    pretty path <<>> PP.parens (PP.hsep (map PP.text opts))
+
+parensLax :: P.CharParsing m => m a -> m a
+parensLax p = P.between (P.char '(' *> P.spaces) (P.char ')' *> P.spaces) p
diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs
index f57457d2e5b..49893ab1462 100644
--- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs
+++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs
@@ -16,11 +16,13 @@ import Prelude ()
 
 import Distribution.Backpack
 import Distribution.Compat.Graph (IsNode (..))
+import Distribution.Compiler (CompilerId, buildCompilerId)
 import Distribution.License
 import Distribution.ModuleName
-import Distribution.Package hiding (installedUnitId)
+import Distribution.Package hiding (installedUnitId, pkgCompiler)
 import Distribution.Types.AbiDependency
 import Distribution.Types.ExposedModule
+import Distribution.Types.Flag (FlagAssignment)
 import Distribution.Types.LibraryName
 import Distribution.Types.LibraryVisibility
 import Distribution.Types.MungedPackageId
@@ -41,7 +43,7 @@ data InstalledPackageInfo = InstalledPackageInfo
     -- exactly the same as PackageDescription
     sourcePackageId :: PackageId
   , sourceLibName :: LibraryName
-  , installedComponentId_ :: ComponentId
+  , installedComponentId_ :: Maybe ComponentId
   , libVisibility :: LibraryVisibility
   , installedUnitId :: UnitId
   , -- INVARIANT: if this package is definite, OpenModule's
@@ -61,7 +63,8 @@ data InstalledPackageInfo = InstalledPackageInfo
   , description :: !ShortText
   , category :: !ShortText
   , -- these parts are required by an installed package only:
-    abiHash :: AbiHash
+    unitFlags :: FlagAssignment
+  , abiHash :: AbiHash
   , indefinite :: Bool
   , exposed :: Bool
   , -- INVARIANT: if the package is definite, OpenModule's
@@ -93,6 +96,7 @@ data InstalledPackageInfo = InstalledPackageInfo
   , haddockInterfaces :: [FilePath]
   , haddockHTMLs :: [FilePath]
   , pkgRoot :: Maybe FilePath
+  , pkgCompiler :: Maybe CompilerId
   }
   deriving (Eq, Generic, Read, Show)
 
@@ -119,8 +123,8 @@ instance IsNode InstalledPackageInfo where
   nodeNeighbors = depends
 
 mungedPackageId :: InstalledPackageInfo -> MungedPackageId
-mungedPackageId ipi =
-  MungedPackageId (mungedPackageName ipi) (packageVersion ipi)
+mungedPackageId ipi@InstalledPackageInfo{pkgCompiler = comp} =
+  MungedPackageId (mungedPackageName ipi) (packageVersion ipi) comp
 
 -- | Returns the munged package name, which we write into @name@ for
 -- compatibility with old versions of GHC.
@@ -130,9 +134,9 @@ mungedPackageName ipi = MungedPackageName (packageName ipi) (sourceLibName ipi)
 emptyInstalledPackageInfo :: InstalledPackageInfo
 emptyInstalledPackageInfo =
   InstalledPackageInfo
-    { sourcePackageId = PackageIdentifier (mkPackageName "") nullVersion
+    { sourcePackageId = PackageIdentifier (mkPackageName "") nullVersion Nothing
     , sourceLibName = LMainLibName
-    , installedComponentId_ = mkComponentId ""
+    , installedComponentId_ = Nothing
     , installedUnitId = mkUnitId ""
     , instantiatedWith = []
     , compatPackageKey = ""
@@ -146,6 +150,7 @@ emptyInstalledPackageInfo =
     , synopsis = ""
     , description = ""
     , category = ""
+    , unitFlags = mempty
     , abiHash = mkAbiHash ""
     , indefinite = False
     , exposed = False
@@ -174,4 +179,5 @@ emptyInstalledPackageInfo =
     , haddockHTMLs = []
     , pkgRoot = Nothing
     , libVisibility = LibraryVisibilityPrivate
+    , pkgCompiler = Nothing
     }
diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs
index 7578907b590..9853efc287b 100644
--- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs
+++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs
@@ -15,13 +15,15 @@ import Distribution.Backpack
 import Distribution.CabalSpecVersion
 import Distribution.Compat.Lens (Lens', (&), (.~))
 import Distribution.Compat.Newtype
+import Distribution.Compiler (CompilerId(..))
 import Distribution.FieldGrammar
 import Distribution.FieldGrammar.FieldDescrs
 import Distribution.License
 import Distribution.ModuleName
-import Distribution.Package
+import Distribution.Package hiding (pkgCompiler)
 import Distribution.Parsec
 import Distribution.Pretty
+import Distribution.Types.Flag (FlagAssignment)
 import Distribution.Types.LibraryName
 import Distribution.Types.LibraryVisibility
 import Distribution.Types.MungedPackageName
@@ -37,7 +39,9 @@ import qualified Text.PrettyPrint as Disp
 import Distribution.Types.InstalledPackageInfo
 
 import qualified Distribution.Types.InstalledPackageInfo.Lens as L
-import qualified Distribution.Types.PackageId.Lens as L
+import qualified Distribution.Types.PackageId.Lens as L hiding (pkgCompiler)
+
+import GHC.Stack (HasCallStack)
 
 -- Note: GHC goes nuts and inlines everything,
 -- One can see e.g. in -ddump-simpl-stats:
@@ -58,6 +62,7 @@ ipiFieldGrammar
      , Applicative (g InstalledPackageInfo)
      , Applicative (g Basic)
      , c (Identity AbiHash)
+     , c (Identity FlagAssignment)
      , c (Identity LibraryVisibility)
      , c (Identity PackageName)
      , c (Identity UnitId)
@@ -73,7 +78,8 @@ ipiFieldGrammar
      , c ExposedModules
      , c InstWith
      , c SpecLicenseLenient
-     )
+     , c (Identity (Maybe CompilerId))
+     , HasCallStack )
   => g InstalledPackageInfo InstalledPackageInfo
 ipiFieldGrammar =
   mkInstalledPackageInfo
@@ -84,7 +90,10 @@ ipiFieldGrammar =
     -- Very basic fields: name, version, package-name, lib-name and visibility
     <@> blurFieldGrammar basic basicFieldGrammar
     -- Basic fields
-    <@> optionalFieldDef "id" L.installedUnitId (mkUnitId "")
+    -- [note: forced default values]
+    -- Observe optionalFieldDef fn l x = optionalFieldDefAla fn Identity l x
+    -- and optionalFieldDefAla will force x.
+    <@> optionalFieldDef "id" L.installedUnitId (mkUnitId "invalid-invalid")
     <@> optionalFieldDefAla "instantiated-with" InstWith L.instantiatedWith []
     <@> optionalFieldDefAla "key" CompatPackageKey L.compatPackageKey ""
     <@> optionalFieldDefAla "license" SpecLicenseLenient L.license (Left SPDX.NONE)
@@ -98,6 +107,7 @@ ipiFieldGrammar =
     <@> freeTextFieldDefST "description" L.description
     <@> freeTextFieldDefST "category" L.category
     -- Installed fields
+    <@> optionalFieldDef "flags" L.unitFlags mempty
     <@> optionalFieldDef "abi" L.abiHash (mkAbiHash "")
     <@> booleanFieldDef "indefinite" L.indefinite False
     <@> booleanFieldDef "exposed" L.exposed False
@@ -125,14 +135,15 @@ ipiFieldGrammar =
     <@> monoidalFieldAla "haddock-interfaces" (alaList' FSep FilePathNT) L.haddockInterfaces
     <@> monoidalFieldAla "haddock-html" (alaList' FSep FilePathNT) L.haddockHTMLs
     <@> optionalFieldAla "pkgroot" FilePathNT L.pkgRoot
+    <@> optionalFieldDef "compiler" L.pkgCompiler Nothing
   where
     mkInstalledPackageInfo _ Basic{..} =
       InstalledPackageInfo
         -- _basicPkgName is not used
         -- setMaybePackageId says it can be no-op.
-        (PackageIdentifier pn _basicVersion)
+        (PackageIdentifier pn _basicVersion _basicCompilerId)
         (combineLibraryName ln _basicLibName)
-        (mkComponentId "") -- installedComponentId_, not in use
+        Nothing
         _basicLibVisibility
       where
         MungedPackageName pn ln = _basicName
@@ -253,6 +264,7 @@ data Basic = Basic
   , _basicPkgName :: Maybe PackageName
   , _basicLibName :: LibraryName
   , _basicLibVisibility :: LibraryVisibility
+  , _basicCompilerId :: Maybe CompilerId
   }
 
 basic :: Lens' InstalledPackageInfo Basic
@@ -265,14 +277,16 @@ basic f ipi = g <$> f b
         (maybePackageName ipi)
         (sourceLibName ipi)
         (libVisibility ipi)
+        (pkgCompiler ipi)
 
-    g (Basic n v pn ln lv) =
+    g (Basic n v pn ln lv compid) =
       ipi
         & setMungedPackageName n
         & L.sourcePackageId . L.pkgVersion .~ v
         & setMaybePackageName pn
         & L.sourceLibName .~ ln
         & L.libVisibility .~ lv
+        & L.pkgCompiler .~ compid
 
 basicName :: Lens' Basic MungedPackageName
 basicName f b = (\x -> b{_basicName = x}) <$> f (_basicName b)
@@ -316,7 +330,7 @@ basicFieldGrammar =
     <*> optionalField "lib-name" basicLibName
     <*> optionalFieldDef "visibility" basicLibVisibility LibraryVisibilityPrivate
   where
-    mkBasic n v pn ln lv = Basic n v pn ln' lv'
+    mkBasic n v pn ln lv = Basic n v pn ln' lv' Nothing
       where
         ln' = maybe LMainLibName LSubLibName ln
         -- Older GHCs (<8.8) always report installed libraries as private
diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs
index 47fa1c96f40..9a35a03816e 100644
--- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs
+++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs
@@ -7,10 +7,12 @@ import Distribution.Compat.Lens
 import Distribution.Compat.Prelude
 import Prelude ()
 
+import Distribution.Compiler (CompilerId)
 import Distribution.Backpack (OpenModule)
 import Distribution.License (License)
 import Distribution.ModuleName (ModuleName)
 import Distribution.Package (AbiHash, ComponentId, PackageIdentifier, UnitId)
+import Distribution.Types.Flag (FlagAssignment)
 import Distribution.Types.InstalledPackageInfo (AbiDependency, ExposedModule, InstalledPackageInfo)
 import Distribution.Types.LibraryName (LibraryName)
 import Distribution.Types.LibraryVisibility (LibraryVisibility)
@@ -27,7 +29,11 @@ installedUnitId :: Lens' InstalledPackageInfo UnitId
 installedUnitId f s = fmap (\x -> s{T.installedUnitId = x}) (f (T.installedUnitId s))
 {-# INLINE installedUnitId #-}
 
-installedComponentId_ :: Lens' InstalledPackageInfo ComponentId
+unitFlags :: Lens' InstalledPackageInfo FlagAssignment
+unitFlags f s = fmap (\x -> s{T.unitFlags = x}) (f (T.unitFlags s))
+{-# INLINE unitFlags #-}
+
+installedComponentId_ :: Lens' InstalledPackageInfo (Maybe ComponentId)
 installedComponentId_ f s = fmap (\x -> s{T.installedComponentId_ = x}) (f (T.installedComponentId_ s))
 {-# INLINE installedComponentId_ #-}
 
@@ -194,3 +200,7 @@ pkgRoot f s = fmap (\x -> s{T.pkgRoot = x}) (f (T.pkgRoot s))
 libVisibility :: Lens' InstalledPackageInfo LibraryVisibility
 libVisibility f s = fmap (\x -> s{T.libVisibility = x}) (f (T.libVisibility s))
 {-# INLINE libVisibility #-}
+
+pkgCompiler :: Lens' InstalledPackageInfo (Maybe CompilerId)
+pkgCompiler f s = fmap (\x -> s{T.pkgCompiler = x}) (f (T.pkgCompiler s))
+{-# INLINE pkgCompiler #-}
diff --git a/Cabal-syntax/src/Distribution/Types/MungedPackageId.hs b/Cabal-syntax/src/Distribution/Types/MungedPackageId.hs
index 8e879620478..b0edcd99bf5 100644
--- a/Cabal-syntax/src/Distribution/Types/MungedPackageId.hs
+++ b/Cabal-syntax/src/Distribution/Types/MungedPackageId.hs
@@ -15,6 +15,7 @@ import Distribution.Types.LibraryName
 import Distribution.Types.MungedPackageName
 import Distribution.Types.PackageId
 import Distribution.Version (Version, nullVersion)
+import Distribution.Compiler (CompilerId)
 
 import qualified Text.PrettyPrint as Disp
 
@@ -27,6 +28,7 @@ data MungedPackageId = MungedPackageId
   -- 'MungedPackageName'.
   , mungedVersion :: Version
   -- ^ The version of this package / component, eg 1.2
+  , mingledCompilerId :: Maybe CompilerId
   }
   deriving (Generic, Read, Show, Eq, Ord, Data)
 
@@ -41,8 +43,9 @@ instance Structured MungedPackageId
 -- >>> prettyShow $ MungedPackageId (MungedPackageName "servant" (LSubLibName "lackey")) (mkVersion [0,1,2])
 -- "z-servant-z-lackey-0.1.2"
 instance Pretty MungedPackageId where
-  pretty (MungedPackageId n v)
-    | v == nullVersion = pretty n -- if no version, don't show version.
+  pretty (MungedPackageId n v c)
+    | v == nullVersion = pretty c <<>> Disp.char '-' <<>> pretty n -- if no version, don't show version.
+    | Just c' <- c = pretty c' <<>> Disp.char '-' <<>> pretty n <<>> Disp.char '-' <<>> pretty v
     | otherwise = pretty n <<>> Disp.char '-' <<>> pretty v
 
 -- |
@@ -66,15 +69,15 @@ instance Pretty MungedPackageId where
 -- Nothing
 instance Parsec MungedPackageId where
   parsec = do
-    PackageIdentifier pn v <- parsec
-    return $ MungedPackageId (decodeCompatPackageName pn) v
+    PackageIdentifier pn v comp <- parsec
+    return $ MungedPackageId (decodeCompatPackageName pn) v comp
 
 instance NFData MungedPackageId where
-  rnf (MungedPackageId name version) = rnf name `seq` rnf version
+  rnf (MungedPackageId name version compiler) = rnf name `seq` rnf version `seq` rnf compiler
 
 computeCompatPackageId :: PackageId -> LibraryName -> MungedPackageId
-computeCompatPackageId (PackageIdentifier pn vr) ln =
-  MungedPackageId (MungedPackageName pn ln) vr
+computeCompatPackageId (PackageIdentifier pn vr comp) ln =
+  MungedPackageId (MungedPackageName pn ln) vr comp
 
 -- $setup
 -- >>> :seti -XOverloadedStrings
diff --git a/Cabal-syntax/src/Distribution/Types/PackageDescription.hs b/Cabal-syntax/src/Distribution/Types/PackageDescription.hs
index 4b00a8ef526..17f72e54c95 100644
--- a/Cabal-syntax/src/Distribution/Types/PackageDescription.hs
+++ b/Cabal-syntax/src/Distribution/Types/PackageDescription.hs
@@ -207,6 +207,7 @@ emptyPackageDescription =
         PackageIdentifier
           (mkPackageName "")
           nullVersion
+          Nothing
     , licenseRaw = Right UnspecifiedLicense -- TODO:
     , licenseFiles = []
     , specVersion = CabalSpecV1_0
diff --git a/Cabal-syntax/src/Distribution/Types/PackageId.hs b/Cabal-syntax/src/Distribution/Types/PackageId.hs
index 9cd88a2f810..bc366716216 100644
--- a/Cabal-syntax/src/Distribution/Types/PackageId.hs
+++ b/Cabal-syntax/src/Distribution/Types/PackageId.hs
@@ -13,6 +13,7 @@ import Distribution.Parsec (Parsec (..), simpleParsec)
 import Distribution.Pretty
 import Distribution.Types.PackageName
 import Distribution.Version (Version, nullVersion)
+import Distribution.Compiler (CompilerId)
 
 import qualified Data.List.NonEmpty as NE
 import qualified Distribution.Compat.CharParsing as P
@@ -27,6 +28,8 @@ data PackageIdentifier = PackageIdentifier
   -- ^ The name of this package, eg. foo
   , pkgVersion :: Version
   -- ^ the version of this package, eg 1.2
+  , pkgCompiler :: Maybe CompilerId
+  -- ^ the associated compiler id of the package, eg ghc-9.8.4
   }
   deriving (Generic, Read, Show, Eq, Ord, Data)
 
@@ -34,8 +37,11 @@ instance Binary PackageIdentifier
 instance Structured PackageIdentifier
 
 instance Pretty PackageIdentifier where
-  pretty (PackageIdentifier n v)
-    | v == nullVersion = pretty n -- if no version, don't show version.
+  pretty (PackageIdentifier n v c)
+    -- we must never print the compiler, as other tools like hackage-security rely on the Pretty instance
+    -- | Just c' <- c, v == nullVersion = pretty c <<>> Disp.char ':' <<>> pretty n -- if no version, don't show version.
+    -- | Just c' <- c = pretty c' <<>> Disp.char ':' <<>> pretty n <<>> Disp.char '-' <<>> pretty v
+    | v == nullVersion = pretty n
     | otherwise = pretty n <<>> Disp.char '-' <<>> pretty v
 
 -- |
@@ -61,15 +67,16 @@ instance Pretty PackageIdentifier where
 -- Nothing
 instance Parsec PackageIdentifier where
   parsec = do
+    -- comp <- Just <$> (parsec <* P.char ':') <|> return Nothing
     xs' <- P.sepByNonEmpty component (P.char '-')
     (v, xs) <- case simpleParsec (NE.last xs') of
       Nothing -> return (nullVersion, toList xs') -- all components are version
       Just v -> return (v, NE.init xs')
     if not (null xs) && all (\c -> all (/= '.') c && not (all isDigit c)) xs
-      then return $ PackageIdentifier (mkPackageName (intercalate "-" xs)) v
+      then return $ PackageIdentifier (mkPackageName (intercalate "-" xs)) v Nothing --comp
       else fail "all digits or a dot in a portion of package name"
     where
       component = P.munch1 (\c -> isAlphaNum c || c == '.')
 
 instance NFData PackageIdentifier where
-  rnf (PackageIdentifier name version) = rnf name `seq` rnf version
+  rnf (PackageIdentifier name version compiler) = rnf name `seq` rnf version `seq` rnf compiler
diff --git a/Cabal-syntax/src/Distribution/Types/PackageId/Lens.hs b/Cabal-syntax/src/Distribution/Types/PackageId/Lens.hs
index 08305234fbd..5a4682c7ed3 100644
--- a/Cabal-syntax/src/Distribution/Types/PackageId/Lens.hs
+++ b/Cabal-syntax/src/Distribution/Types/PackageId/Lens.hs
@@ -10,6 +10,7 @@ import Prelude ()
 import Distribution.Types.PackageId (PackageIdentifier)
 import Distribution.Types.PackageName (PackageName)
 import Distribution.Version (Version)
+import Distribution.Compiler (CompilerId)
 
 import qualified Distribution.Types.PackageId as T
 
@@ -20,3 +21,8 @@ pkgName f s = fmap (\x -> s{T.pkgName = x}) (f (T.pkgName s))
 pkgVersion :: Lens' PackageIdentifier Version
 pkgVersion f s = fmap (\x -> s{T.pkgVersion = x}) (f (T.pkgVersion s))
 {-# INLINE pkgVersion #-}
+
+pkgCompiler :: Lens' PackageIdentifier (Maybe CompilerId)
+pkgCompiler f s = fmap (\x -> s{T.pkgCompiler = x}) (f (T.pkgCompiler s))
+{-# INLINE pkgCompiler #-}
+
diff --git a/Cabal-syntax/src/Distribution/Types/PackageVersionConstraint.hs b/Cabal-syntax/src/Distribution/Types/PackageVersionConstraint.hs
index 013226ca2d5..5c2b8d9b0b1 100644
--- a/Cabal-syntax/src/Distribution/Types/PackageVersionConstraint.hs
+++ b/Cabal-syntax/src/Distribution/Types/PackageVersionConstraint.hs
@@ -53,7 +53,7 @@ instance Pretty PackageVersionConstraint where
 -- Just (PackageVersionConstraint (PackageName "foo") (ThisVersion (mkVersion [2,0])))
 instance Parsec PackageVersionConstraint where
   parsec = do
-    PackageIdentifier name ver <- parsec
+    PackageIdentifier name ver comp <- parsec
     if ver == nullVersion
       then do
         P.spaces
@@ -64,7 +64,7 @@ instance Parsec PackageVersionConstraint where
 
 -- | @since 3.4.0.0
 thisPackageVersionConstraint :: PackageIdentifier -> PackageVersionConstraint
-thisPackageVersionConstraint (PackageIdentifier pn vr) =
+thisPackageVersionConstraint (PackageIdentifier pn vr comp) =
   PackageVersionConstraint pn (thisVersion vr)
 
 -- | @since 3.4.0.0
diff --git a/Cabal-syntax/src/Distribution/Types/UnitId.hs b/Cabal-syntax/src/Distribution/Types/UnitId.hs
index 0b5ca4bdf7b..a5aae2a38bd 100644
--- a/Cabal-syntax/src/Distribution/Types/UnitId.hs
+++ b/Cabal-syntax/src/Distribution/Types/UnitId.hs
@@ -7,14 +7,19 @@ module Distribution.Types.UnitId
   ( UnitId
   , unUnitId
   , mkUnitId
+  , isPartialUnitId
+  , addPrefixToUnitId
+  , addSuffixToUnitId
   , DefUnitId
   , unsafeMkDefUnitId
   , unDefUnitId
   , newSimpleUnitId
   , mkLegacyUnitId
   , getHSLibraryName
+  , getAbiTag
   ) where
 
+import Distribution.Compiler (AbiTag (..))
 import Distribution.Compat.Prelude
 import Distribution.Utils.ShortText
 import Prelude ()
@@ -27,6 +32,11 @@ import Distribution.Types.PackageId
 
 import Text.PrettyPrint (text)
 
+import GHC.Stack (HasCallStack, prettyCallStack, callStack)
+import Data.List (isInfixOf)
+
+import Unsafe.Coerce (unsafeCoerce)
+
 -- | A unit identifier identifies a (possibly instantiated)
 -- package/component that can be installed the installed package
 -- database.  There are several types of components that can be
@@ -63,11 +73,24 @@ import Text.PrettyPrint (text)
 -- representation of a UnitId to pass, e.g., as a @-package-id@
 -- flag, use the 'display' function, which will work on all
 -- versions of Cabal.
-newtype UnitId = UnitId ShortText
-  deriving (Generic, Read, Show, Eq, Ord, Data, NFData)
+data UnitId = UnitId {unitComp :: ShortText, unitId :: ShortText, wasPartial :: Bool }
+            | PartialUnitId ShortText
+  deriving (Generic, Read, Show, Data)
+
+instance Eq UnitId where
+  (UnitId c1 s1 _) == (UnitId c2 s2 _) = c1 == c2 && s1 == s2
+  (PartialUnitId s1) == (PartialUnitId s2) = s1 == s2
+  _ == _ = False
+
+instance Ord UnitId where
+  compare (UnitId c1 s1 _) (UnitId c2 s2 _) = compare (c1, s1) (c2, s2)
+  compare (PartialUnitId s1) (PartialUnitId s2) = compare s1 s2
+  compare (PartialUnitId _) _ = LT
+  compare _ (PartialUnitId _) = GT
 
 instance Binary UnitId
 instance Structured UnitId
+instance NFData UnitId
 
 -- | The textual format for 'UnitId' coincides with the format
 -- GHC accepts for @-package-id@.
@@ -77,8 +100,12 @@ instance Pretty UnitId where
 -- | The textual format for 'UnitId' coincides with the format
 -- GHC accepts for @-package-id@.
 instance Parsec UnitId where
-  parsec = mkUnitId <$> P.munch1 isUnitChar
+  parsec = P.try (mkUnitId' <$> compid <* P.char '_' <*> P.munch1 isUnitChar <*> return False)
+        <|> (mkPartialUnitId <$> P.munch1 isUnitChar)
     where
+      compid = (\f v -> f ++ "-" ++ v) <$> P.munch1 isAlpha <* P.char '-' <*> P.munch1 isVerChar
+      isVerChar :: Char -> Bool
+      isVerChar c = c `elem` '.':['0'..'9']
       -- https://gitlab.haskell.org/ghc/ghc/issues/17752
       isUnitChar '-' = True
       isUnitChar '_' = True
@@ -86,13 +113,45 @@ instance Parsec UnitId where
       isUnitChar '+' = True
       isUnitChar c = isAlphaNum c
 
+isPartialUnitId :: HasCallStack => UnitId -> Bool
+isPartialUnitId (PartialUnitId _) = True
+isPartialUnitId _ = False
+
+addPrefixToUnitId :: HasCallStack => String -> UnitId -> UnitId
+-- addPrefixToUnitId prefix (PartialUnitId s) | s == toShortText "process-1.6.25.0-inplace" = trace ("### addPrefixToUnitId': `" ++ prefix ++ "' `" ++ (fromShortText s) ++ "'.\n" ++ prettyCallStack callStack) $ UnitId (toShortText prefix) s True
+addPrefixToUnitId prefix (PartialUnitId s) = UnitId (toShortText prefix) s True
+addPrefixToUnitId prefix uid@(UnitId _ _ _) = error $ "addPrefixToUnitId: UnitId " ++ show uid ++ " already has a prefix; can't add: " ++ prefix
+
+addSuffixToUnitId :: HasCallStack => String -> UnitId -> UnitId
+addSuffixToUnitId suffix (UnitId c s fromPartial) = UnitId c (s <> toShortText suffix) fromPartial
+addSuffixToUnitId suffix (PartialUnitId s) = PartialUnitId (s <> toShortText suffix)
+
+
+dropPrefixFromUnitId :: HasCallStack => UnitId -> UnitId
+dropPrefixFromUnitId (PartialUnitId s) = PartialUnitId s
+dropPrefixFromUnitId (UnitId _c s _fromPartial) = PartialUnitId s
+
 -- | If you need backwards compatibility, consider using 'display'
 -- instead, which is supported by all versions of Cabal.
-unUnitId :: UnitId -> String
-unUnitId (UnitId s) = fromShortText s
-
-mkUnitId :: String -> UnitId
-mkUnitId = UnitId . toShortText
+unUnitId :: HasCallStack => UnitId -> String
+unUnitId (UnitId c s False) = fromShortText c ++ '_':fromShortText s
+unUnitId (UnitId c s True) = fromShortText s
+unUnitId (PartialUnitId s) = fromShortText s
+
+mkUnitId :: HasCallStack => String -> UnitId
+mkUnitId s = case (simpleParsec s) of
+    -- Just uid@UnitId{ unitComp = c, unitId = i } | (fromShortText c) == "ghc-9.8.4", (fromShortText i) == "rts-1.0.3-cec100dd" -> trace ("### mkUnitId: `" ++ (fromShortText c) ++ "' `" ++ (fromShortText i) ++ "' is a full one.\n" ++ prettyCallStack callStack) uid
+    Just uid@UnitId{} -> uid
+    Just uid@PartialUnitId{} -> uid -- error $ "mkUnitId: `" ++ s ++ "' is a partial unit id, not a full one."
+    _ -> error $ "Unable to parse UnitId: `" ++ s ++ "'."
+
+mkUnitId' :: HasCallStack => String -> String -> Bool -> UnitId
+-- mkUnitId' c i b | c == "ghc-9.8.4", i == "rts-1.0.3-cec100dd" = trace ("### mkUnitId': `" ++ c ++ "' `" ++ i ++ "' is a full one.\n" ++ prettyCallStack callStack) (UnitId (toShortText c) (toShortText i) b)
+mkUnitId' c i b = UnitId (toShortText c) (toShortText i) b
+
+mkPartialUnitId :: HasCallStack => String -> UnitId
+-- mkPartialUnitId s | s == "process-1.6.25.0-inplace" = trace ("### mkPartialUnitId: `" ++ s ++ "' is a partial unit id, not a full one.\n" ++ prettyCallStack callStack) (PartialUnitId (toShortText s))
+mkPartialUnitId s = PartialUnitId (toShortText s)
 
 -- | 'mkUnitId'
 --
@@ -102,17 +161,17 @@ instance IsString UnitId where
 
 -- | Create a unit identity with no associated hash directly
 -- from a 'ComponentId'.
-newSimpleUnitId :: ComponentId -> UnitId
-newSimpleUnitId = mkUnitId . unComponentId
+newSimpleUnitId :: HasCallStack => ComponentId -> UnitId
+newSimpleUnitId = unsafeCoerce
 
 -- | Make an old-style UnitId from a package identifier.
 -- Assumed to be for the public library
-mkLegacyUnitId :: PackageId -> UnitId
+mkLegacyUnitId :: HasCallStack => PackageId -> UnitId
 mkLegacyUnitId = newSimpleUnitId . mkComponentId . prettyShow
 
 -- | Returns library name prefixed with HS, suitable for filenames
 getHSLibraryName :: UnitId -> String
-getHSLibraryName uid = "HS" ++ prettyShow uid
+getHSLibraryName uid = "HS" ++ prettyShow (dropPrefixFromUnitId uid)
 
 -- | A 'UnitId' for a definite package.  The 'DefUnitId' invariant says
 -- that a 'UnitId' identified this way is definite; i.e., it has no
@@ -131,3 +190,17 @@ instance Parsec DefUnitId where
 -- is to ensure that the 'DefUnitId' invariant holds.
 unsafeMkDefUnitId :: UnitId -> DefUnitId
 unsafeMkDefUnitId = DefUnitId
+
+-- | The ABI tag is the part of the unit id that comes after the
+-- last hyphen.  It is used to distinguish between different
+-- versions of the same package that are ABI compatible.
+--
+-- FIXME: ideally this would be part of the proper structure of the
+-- datatype, instead of some heuristic of the string.
+getAbiTag :: HasCallStack => UnitId -> AbiTag
+getAbiTag (UnitId _c s _) = case takeWhile (/= '-') . reverse . fromShortText $ s of
+  [] -> NoAbiTag
+  xs -> AbiTag . reverse $ xs
+getAbiTag (PartialUnitId s) = case takeWhile (/= '-') . reverse . fromShortText $ s of
+  [] -> NoAbiTag
+  xs -> AbiTag . reverse $ xs
\ No newline at end of file
diff --git a/Cabal-syntax/src/Distribution/Utils/Path.hs b/Cabal-syntax/src/Distribution/Utils/Path.hs
index a0f18a1dfdd..3d63703f501 100644
--- a/Cabal-syntax/src/Distribution/Utils/Path.hs
+++ b/Cabal-syntax/src/Distribution/Utils/Path.hs
@@ -460,7 +460,7 @@ data CWD
 -- | Abstract directory: package directory (e.g. a directory containing the @.cabal@ file).
 --
 -- See Note [Symbolic paths] in Distribution.Utils.Path.
-data Pkg
+data Pkg deriving (Data)
 
 -- | Abstract directory: dist directory (e.g. @dist-newstyle@).
 --
@@ -490,7 +490,7 @@ data Framework
 -- | Abstract directory: build directory.
 --
 -- See Note [Symbolic paths] in Distribution.Utils.Path.
-data Build
+data Build deriving (Data)
 
 -- | Abstract directory: directory for build artifacts, such as documentation or @.hie@ files.
 --
diff --git a/Cabal-tests/tests/NoThunks.hs b/Cabal-tests/tests/NoThunks.hs
index 6a81475dc03..63f6bcae070 100644
--- a/Cabal-tests/tests/NoThunks.hs
+++ b/Cabal-tests/tests/NoThunks.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP                 #-}
+{-# LANGUAGE FlexibleInstances #-}
 #if !(__GLASGOW_HASKELL__ >= 806 && defined(MIN_VERSION_nothunks))
 module Main (main) where
 main :: IO ()
@@ -24,7 +25,7 @@ import Distribution.ModuleName                (ModuleName)
 import Distribution.PackageDescription.Parsec (parseGenericPackageDescription)
 import Distribution.SPDX                      (License, LicenseExceptionId, LicenseExpression, LicenseId, LicenseRef, SimpleLicenseExpression)
 import Distribution.System                    (Arch, OS)
-import Distribution.Utils.Path                (SymbolicPathX)
+import Distribution.Utils.Path                (SymbolicPathX, Pkg, Build)
 import Distribution.Utils.ShortText           (ShortText)
 import Distribution.Version                   (Version, VersionRange)
 import Language.Haskell.Extension             (Extension, KnownExtension, Language)
@@ -72,6 +73,8 @@ instance NoThunks ConfVar
 instance NoThunks Dependency
 instance NoThunks Executable
 instance NoThunks ExecutableScope
+instance NoThunks (ExtraSource Build)
+instance NoThunks (ExtraSource Pkg)
 instance NoThunks FlagName
 instance NoThunks ForeignLib
 instance NoThunks ForeignLibOption
diff --git a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs
index 47a0cc1bd2c..cd0f73aeedd 100644
--- a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs
+++ b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs
@@ -29,8 +29,8 @@ md5Check proxy md5Int = structureHash proxy @?= md5FromInteger md5Int
 
 md5CheckGenericPackageDescription :: Proxy GenericPackageDescription -> Assertion
 md5CheckGenericPackageDescription proxy = md5Check proxy
-    0x09251b46ffc5178a7526d31e794d9c62
+    0x77f4c09dc9b1c6967b07516ad35e73af
 
 md5CheckLocalBuildInfo :: Proxy LocalBuildInfo -> Assertion
 md5CheckLocalBuildInfo proxy = md5Check proxy
-    0x8fa7b2c8cc611407bfdcb734ecb460a2
+    0x69dab39bf8b56871b28a2c14d423b18a
diff --git a/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs b/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs
index cb36fe680db..fd7e4eea3c0 100644
--- a/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs
+++ b/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs
@@ -1,5 +1,6 @@
 {-# OPTIONS_GHC -freduction-depth=0 #-}
 {-# OPTIONS_GHC -Wno-orphans #-}
+{-# LANGUAGE FlexibleInstances #-}
 module Data.TreeDiff.Instances.Cabal () where
 
 import Data.TreeDiff
@@ -28,7 +29,7 @@ import Distribution.Types.DumpBuildInfo            (DumpBuildInfo)
 import Distribution.Types.PackageVersionConstraint
 import Distribution.Types.UnitId                   (DefUnitId, UnitId)
 import Distribution.Utils.NubList                  (NubList)
-import Distribution.Utils.Path                     (SymbolicPathX)
+import Distribution.Utils.Path                     (SymbolicPathX, Build, Pkg)
 import Distribution.Utils.ShortText                (ShortText, fromShortText)
 import Distribution.Verbosity
 import Distribution.Verbosity.Internal
@@ -77,6 +78,8 @@ instance ToExpr ExeDependency
 instance ToExpr Executable
 instance ToExpr ExecutableScope
 instance ToExpr ExposedModule
+instance ToExpr (ExtraSource Build)
+instance ToExpr (ExtraSource Pkg)
 instance ToExpr FlagAssignment
 instance ToExpr FlagName
 instance ToExpr ForeignLib
diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal
index 27f7c958016..dab60621228 100644
--- a/Cabal/Cabal.cabal
+++ b/Cabal/Cabal.cabal
@@ -415,3 +415,17 @@ library
     TypeOperators
     TypeSynonymInstances
     UndecidableInstances
+
+executable cabal-main-simple
+  default-language: Haskell2010
+  main-is: src/main_simple.hs
+  build-depends:
+    base,
+    Cabal
+
+executable cabal-main-configure
+  default-language: Haskell2010
+  main-is: src/main_configure.hs
+  build-depends:
+    base,
+    Cabal
diff --git a/Cabal/src/Distribution/Backpack/Configure.hs b/Cabal/src/Distribution/Backpack/Configure.hs
index d4c00b75c7b..41bf7929bc3 100644
--- a/Cabal/src/Distribution/Backpack/Configure.hs
+++ b/Cabal/src/Distribution/Backpack/Configure.hs
@@ -56,6 +56,8 @@ import qualified Data.Set as Set
 import Distribution.Pretty
 import Text.PrettyPrint
 
+import Unsafe.Coerce (unsafeCoerce)
+
 ------------------------------------------------------------------------------
 -- Pipeline
 ------------------------------------------------------------------------------
@@ -144,7 +146,7 @@ configureComponentLocalBuildInfos
                 ,
                   ( DefiniteUnitId
                       ( unsafeMkDefUnitId
-                          (mkUnitId (unComponentId (ann_id aid)))
+                          (unsafeCoerce (ann_id aid))
                       )
                   , emptyModuleShape
                   )
@@ -303,35 +305,35 @@ toComponentLocalBuildInfos
     -- TODO: Move this into a helper function
     --
     -- TODO: This is probably wrong for Backpack
-    let pseudoTopPkg :: InstalledPackageInfo
-        pseudoTopPkg =
-          emptyInstalledPackageInfo
-            { Installed.installedUnitId = mkLegacyUnitId (packageId pkg_descr)
-            , Installed.sourcePackageId = packageId pkg_descr
-            , Installed.depends = map pc_uid externalPkgDeps
-            }
-    case PackageIndex.dependencyInconsistencies
-      . PackageIndex.insert pseudoTopPkg
-      $ packageDependsIndex of
-      [] -> return ()
-      inconsistencies ->
-        warnProgress $
-          hang
-            ( text "This package indirectly depends on multiple versions of the same"
-                <+> text "package. This is very likely to cause a compile failure."
-            )
-            2
-            ( vcat
-                [ text "package"
-                  <+> pretty (packageName user)
-                  <+> parens (pretty (installedUnitId user))
-                  <+> text "requires"
-                  <+> pretty inst
-                | (_dep_key, insts) <- inconsistencies
-                , (inst, users) <- insts
-                , user <- users
-                ]
-            )
+    -- let pseudoTopPkg :: InstalledPackageInfo
+    --     pseudoTopPkg =
+    --       emptyInstalledPackageInfo
+    --         { Installed.installedUnitId = mkLegacyUnitId (packageId pkg_descr)
+    --         , Installed.sourcePackageId = packageId pkg_descr
+    --         , Installed.depends = map pc_uid externalPkgDeps
+    --         }
+    -- case PackageIndex.dependencyInconsistencies
+    --   . PackageIndex.insert pseudoTopPkg
+    --   $ packageDependsIndex of
+    --   [] -> return ()
+    --   inconsistencies ->
+    --     warnProgress $
+    --       hang
+    --         ( text "This package indirectly depends on multiple versions of the same"
+    --             <+> text "package. This is very likely to cause a compile failure."
+    --         )
+    --         2
+    --         ( vcat
+    --             [ text "package"
+    --               <+> pretty (packageName user)
+    --               <+> parens (pretty (installedUnitId user))
+    --               <+> text "requires"
+    --               <+> pretty inst
+    --             | (_dep_key, insts) <- inconsistencies
+    --             , (inst, users) <- insts
+    --             , user <- users
+    --             ]
+    --         )
     let clbis = mkLinkedComponentsLocalBuildInfo comp graph
     -- forM clbis $ \(clbi,deps) -> info verbosity $ "UNIT" ++ hashUnitId (componentUnitId clbi) ++ "\n" ++ intercalate "\n" (map hashUnitId deps)
     return (clbis, packageDependsIndex)
diff --git a/Cabal/src/Distribution/Backpack/PreExistingComponent.hs b/Cabal/src/Distribution/Backpack/PreExistingComponent.hs
index 0fba79bcb87..a72fe20f07f 100644
--- a/Cabal/src/Distribution/Backpack/PreExistingComponent.hs
+++ b/Cabal/src/Distribution/Backpack/PreExistingComponent.hs
@@ -71,9 +71,9 @@ instance HasMungedPackageId PreExistingComponent where
   mungedId = pc_munged_id
 
 instance Package PreExistingComponent where
-  packageId pec = PackageIdentifier (pc_pkgname pec) v
+  packageId pec = PackageIdentifier (pc_pkgname pec) v compid
     where
-      MungedPackageId _ v = pc_munged_id pec
+      MungedPackageId _ v compid = pc_munged_id pec
 
 instance HasUnitId PreExistingComponent where
   installedUnitId = pc_uid
diff --git a/Cabal/src/Distribution/Backpack/ReadyComponent.hs b/Cabal/src/Distribution/Backpack/ReadyComponent.hs
index 7a3523d5eab..b871ebff59f 100644
--- a/Cabal/src/Distribution/Backpack/ReadyComponent.hs
+++ b/Cabal/src/Distribution/Backpack/ReadyComponent.hs
@@ -300,6 +300,7 @@ toReadyComponents pid_map subst0 comps =
                     MungedPackageId
                       (MungedPackageName nonExistentPackageThisIsCabalBug LMainLibName)
                       (mkVersion [0])
+                      Nothing
               instc =
                 InstantiatedComponent
                   { instc_insts = Map.toList insts
diff --git a/Cabal/src/Distribution/PackageDescription/Check.hs b/Cabal/src/Distribution/PackageDescription/Check.hs
index 925bc69d6e1..799e677af17 100644
--- a/Cabal/src/Distribution/PackageDescription/Check.hs
+++ b/Cabal/src/Distribution/PackageDescription/Check.hs
@@ -582,7 +582,7 @@ checkSetupBuildInfo (Just (SetupBuildInfo ds _)) = do
   checkPVPs (checkDependencyVersionRange hasGTLowerBound) gtlck ds
 
 checkPackageId :: Monad m => PackageIdentifier -> CheckM m ()
-checkPackageId (PackageIdentifier pkgName_ _pkgVersion_) = do
+checkPackageId (PackageIdentifier pkgName_ _pkgVersion_ _compid) = do
   checkP
     (not . FilePath.Windows.isValid . prettyShow $ pkgName_)
     (PackageDistInexcusable $ InvalidNameWin pkgName_)
diff --git a/Cabal/src/Distribution/PackageDescription/Check/Target.hs b/Cabal/src/Distribution/PackageDescription/Check/Target.hs
index 0bf173cb980..c62f7e40756 100644
--- a/Cabal/src/Distribution/PackageDescription/Check/Target.hs
+++ b/Cabal/src/Distribution/PackageDescription/Check/Target.hs
@@ -445,11 +445,11 @@ checkBuildInfoPathsContent bi = do
 -- Paths well-formedness check for BuildInfo.
 checkBuildInfoPathsWellFormedness :: Monad m => BuildInfo -> CheckM m ()
 checkBuildInfoPathsWellFormedness bi = do
-  mapM_ (checkPath False "asm-sources" PathKindFile . getSymbolicPath) (asmSources bi)
-  mapM_ (checkPath False "cmm-sources" PathKindFile . getSymbolicPath) (cmmSources bi)
-  mapM_ (checkPath False "c-sources" PathKindFile . getSymbolicPath) (cSources bi)
-  mapM_ (checkPath False "cxx-sources" PathKindFile . getSymbolicPath) (cxxSources bi)
-  mapM_ (checkPath False "js-sources" PathKindFile . getSymbolicPath) (jsSources bi)
+  mapM_ (checkPath False "asm-sources" PathKindFile . getSymbolicPath . extraSourceFile) (asmSources bi)
+  mapM_ (checkPath False "cmm-sources" PathKindFile . getSymbolicPath . extraSourceFile) (cmmSources bi)
+  mapM_ (checkPath False "c-sources" PathKindFile . getSymbolicPath . extraSourceFile) (cSources bi)
+  mapM_ (checkPath False "cxx-sources" PathKindFile . getSymbolicPath . extraSourceFile) (cxxSources bi)
+  mapM_ (checkPath False "js-sources" PathKindFile . getSymbolicPath . extraSourceFile) (jsSources bi)
   mapM_
     (checkPath False "install-includes" PathKindFile . getSymbolicPath)
     (installIncludes bi)
@@ -515,8 +515,8 @@ checkBuildInfoFeatures bi sv = do
     (PackageBuildWarning CVExtensionsDeprecated)
 
   -- asm-sources, cmm-sources and friends only w/ spec ≥ 1.10
-  checkCVSources (map getSymbolicPath $ asmSources bi)
-  checkCVSources (map getSymbolicPath $ cmmSources bi)
+  checkCVSources (map (getSymbolicPath . extraSourceFile) $ asmSources bi)
+  checkCVSources (map (getSymbolicPath . extraSourceFile) $ cmmSources bi)
   checkCVSources (extraBundledLibs bi)
   checkCVSources (extraLibFlavours bi)
 
diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs
index 0ebd51e48ef..008a270848f 100644
--- a/Cabal/src/Distribution/Simple/Build.hs
+++ b/Cabal/src/Distribution/Simple/Build.hs
@@ -642,35 +642,35 @@ generateCode codeGens nm pdesc bi lbi clbi verbosity = do
 addExtraCSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo
 addExtraCSources bi extras = bi{cSources = new}
   where
-    new = ordNub (extras ++ cSources bi)
+    new = ordNub (map (flip ExtraSourcePkg []) extras ++ cSources bi)
 
 -- | Add extra C++ sources generated by preprocessing to build
 -- information.
 addExtraCxxSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo
 addExtraCxxSources bi extras = bi{cxxSources = new}
   where
-    new = ordNub (extras ++ cxxSources bi)
+    new = ordNub (map (flip ExtraSourcePkg []) extras ++ cxxSources bi)
 
 -- | Add extra C-- sources generated by preprocessing to build
 -- information.
 addExtraCmmSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo
 addExtraCmmSources bi extras = bi{cmmSources = new}
   where
-    new = ordNub (extras ++ cmmSources bi)
+    new = ordNub (map (flip ExtraSourcePkg []) extras ++ cmmSources bi)
 
 -- | Add extra ASM sources generated by preprocessing to build
 -- information.
 addExtraAsmSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo
 addExtraAsmSources bi extras = bi{asmSources = new}
   where
-    new = ordNub (extras ++ asmSources bi)
+    new = ordNub (map (flip ExtraSourcePkg []) extras ++ asmSources bi)
 
 -- | Add extra JS sources generated by preprocessing to build
 -- information.
 addExtraJsSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo
 addExtraJsSources bi extras = bi{jsSources = new}
   where
-    new = ordNub (extras ++ jsSources bi)
+    new = ordNub (map (flip ExtraSourcePkg []) extras ++ jsSources bi)
 
 -- | Add extra HS modules generated by preprocessing to build
 -- information.
@@ -716,7 +716,7 @@ replComponent
     preprocessComponent pkg_descr comp lbi clbi False verbosity suffixHandlers
     extras <- preprocessExtras verbosity comp lbi
     let libbi = libBuildInfo lib
-        lib' = lib{libBuildInfo = libbi{cSources = cSources libbi ++ extras}}
+        lib' = lib{libBuildInfo = libbi{cSources = cSources libbi ++ map (flip ExtraSourcePkg []) extras}}
     replLib replFlags pkg lbi lib' libClbi
 replComponent
   replFlags
@@ -733,23 +733,23 @@ replComponent
       case comp of
         CLib lib -> do
           let libbi = libBuildInfo lib
-              lib' = lib{libBuildInfo = libbi{cSources = cSources libbi ++ extras}}
+              lib' = lib{libBuildInfo = libbi{cSources = cSources libbi ++ map (flip ExtraSourcePkg []) extras}}
           replLib replFlags pkg_descr lbi lib' clbi
         CFLib flib ->
           replFLib replFlags pkg_descr lbi flib clbi
         CExe exe -> do
           let ebi = buildInfo exe
-              exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ extras}}
+              exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ map (flip ExtraSourcePkg []) extras}}
           replExe replFlags pkg_descr lbi exe' clbi
         CTest test@TestSuite{testInterface = TestSuiteExeV10{}} -> do
           let exe = testSuiteExeV10AsExe test
           let ebi = buildInfo exe
-              exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ extras}}
+              exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ map (flip ExtraSourcePkg []) extras}}
           replExe replFlags pkg_descr lbi exe' clbi
         CBench bm@Benchmark{benchmarkInterface = BenchmarkExeV10{}} -> do
           let exe = benchmarkExeV10asExe bm
           let ebi = buildInfo exe
-              exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ extras}}
+              exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ map (flip ExtraSourcePkg []) extras}}
           replExe replFlags pkg_descr lbi exe' clbi
 #if __GLASGOW_HASKELL__ < 811
 -- silence pattern-match warnings prior to GHC 9.0
@@ -821,7 +821,7 @@ testSuiteLibV09AsLibAndExe
           }
       -- This is, like, the one place where we use a CTestName for a library.
       -- Should NOT use library name, since that could conflict!
-      PackageIdentifier pkg_name pkg_ver = package pkg_descr
+      PackageIdentifier pkg_name pkg_ver _pkg_compid = package pkg_descr
       -- Note: we do make internal library from the test!
       compat_name = MungedPackageName pkg_name (LSubLibName (testName test))
       compat_key = computeCompatPackageKey (compiler lbi) compat_name pkg_ver (componentUnitId clbi)
diff --git a/Cabal/src/Distribution/Simple/Build/Macros.hs b/Cabal/src/Distribution/Simple/Build/Macros.hs
index f3c51d71c96..e71d5a4a644 100644
--- a/Cabal/src/Distribution/Simple/Build/Macros.hs
+++ b/Cabal/src/Distribution/Simple/Build/Macros.hs
@@ -67,11 +67,11 @@ generateCabalMacrosHeader pkg_descr lbi clbi =
       , Z.zMangleStr = map fixchar
       }
   where
-    getPid (_, MungedPackageId (MungedPackageName pn _) v) =
+    getPid (_, MungedPackageId (MungedPackageName pn _) v compid) =
       -- NB: Drop the library name! We're just reporting package versions.
       -- This would have to be revisited if you are allowed to depend
       -- on different versions of the same package
-      PackageIdentifier pn v
+      PackageIdentifier pn v compid
 
 -- | Helper function that generates just the @VERSION_pkg@ and @MIN_VERSION_pkg@
 -- macros for a list of package ids (usually used with the specific deps of
@@ -91,7 +91,7 @@ generatePackageVersionMacros ver pkgids =
       }
 
 mkZPackage :: PackageId -> Z.ZPackage
-mkZPackage (PackageIdentifier name ver) =
+mkZPackage (PackageIdentifier name ver compid) =
   Z.ZPackage
     { Z.zpkgName = name
     , Z.zpkgVersion = ver
diff --git a/Cabal/src/Distribution/Simple/BuildTarget.hs b/Cabal/src/Distribution/Simple/BuildTarget.hs
index ffa7a609e6b..1d987c7715f 100644
--- a/Cabal/src/Distribution/Simple/BuildTarget.hs
+++ b/Cabal/src/Distribution/Simple/BuildTarget.hs
@@ -497,11 +497,11 @@ pkgComponentInfo pkg =
     , cinfoSrcDirs = map getSymbolicPath $ hsSourceDirs bi
     , cinfoModules = componentModules c
     , cinfoHsFiles = map getSymbolicPath $ componentHsFiles c
-    , cinfoAsmFiles = map getSymbolicPath $ asmSources bi
-    , cinfoCmmFiles = map getSymbolicPath $ cmmSources bi
-    , cinfoCFiles = map getSymbolicPath $ cSources bi
-    , cinfoCxxFiles = map getSymbolicPath $ cxxSources bi
-    , cinfoJsFiles = map getSymbolicPath $ jsSources bi
+    , cinfoAsmFiles = map (getSymbolicPath . extraSourceFile) $ asmSources bi
+    , cinfoCmmFiles = map (getSymbolicPath . extraSourceFile) $ cmmSources bi
+    , cinfoCFiles = map (getSymbolicPath . extraSourceFile) $ cSources bi
+    , cinfoCxxFiles = map (getSymbolicPath . extraSourceFile) $ cxxSources bi
+    , cinfoJsFiles = map (getSymbolicPath . extraSourceFile) $ jsSources bi
     }
   | c <- pkgComponents pkg
   , let bi = componentBuildInfo c
diff --git a/Cabal/src/Distribution/Simple/Configure.hs b/Cabal/src/Distribution/Simple/Configure.hs
index 7c96efb33fc..1ed13020f82 100644
--- a/Cabal/src/Distribution/Simple/Configure.hs
+++ b/Cabal/src/Distribution/Simple/Configure.hs
@@ -183,6 +183,10 @@ import qualified Data.Maybe as M
 import qualified Data.Set as Set
 import qualified Distribution.Compat.NonEmptySet as NES
 
+import GHC.Stack (HasCallStack)
+
+import Unsafe.Coerce (unsafeCoerce)
+
 type UseExternalInternalDeps = Bool
 
 -- | The errors that can be thrown when reading the @setup-config@ file.
@@ -335,7 +339,7 @@ writePersistBuildConfig mbWorkDir distPref lbi = do
 
 -- | Identifier of the current Cabal package.
 currentCabalId :: PackageIdentifier
-currentCabalId = PackageIdentifier (mkPackageName "Cabal") cabalVersion
+currentCabalId = PackageIdentifier (mkPackageName "Cabal") cabalVersion Nothing
 
 -- | Identifier of the current compiler package.
 currentCompilerId :: PackageIdentifier
@@ -343,6 +347,7 @@ currentCompilerId =
   PackageIdentifier
     (mkPackageName System.Info.compilerName)
     (mkVersion' System.Info.compilerVersion)
+    Nothing
 
 -- | Parse the @setup-config@ file header, returning the package identifiers
 -- for Cabal and the compiler.
@@ -936,7 +941,8 @@ configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform programDb0 pac
   return (lbc, pbd)
 
 finalizeAndConfigurePackage
-  :: ConfigFlags
+  :: HasCallStack
+  => ConfigFlags
   -> LBC.LocalBuildConfig
   -> GenericPackageDescription
   -> Compiler
@@ -991,9 +997,17 @@ finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do
     , requiredDepsMap :: Map (PackageName, ComponentName) InstalledPackageInfo
     ) <-
     either (dieWithException verbosity) return $
-      combinedConstraints
+      -- why do we need this? We internally now treat everything as properly
+      -- qualified Component/UnitIds. Thus we need to turn Partial ones into
+      -- fully qualified ones. Otherwise any lookup internally will fail.
+      -- installedPackageSet will already contain properly qualified ones.
+      let updCompId :: GivenComponent -> GivenComponent
+          updCompId (GivenComponent pn ln cid) | isPartialUnitId (unsafeCoerce cid) =
+            GivenComponent pn ln (unsafeCoerce (addPrefixToUnitId (prettyShow (compilerId comp)) (unsafeCoerce cid)))
+          updCompId (GivenComponent pn ln cid) = GivenComponent pn ln cid
+      in combinedConstraints
         (configConstraints cfg)
-        (configDependencies cfg)
+        (map updCompId (configDependencies cfg))
         installedPackageSet
 
   let
@@ -1987,7 +2001,7 @@ selectDependency
       -- It's an internal library, and we're not per-component build
       do_internal lib
         | Set.member lib internalIndex =
-            Right $ InternalDependency $ PackageIdentifier dep_pkgname $ packageVersion pkgid
+            Right $ InternalDependency $ PackageIdentifier dep_pkgname (packageVersion pkgid) (pkgCompiler pkgid)
         | otherwise =
             Left $ DependencyMissingInternal dep_pkgname lib
 
@@ -2051,7 +2065,7 @@ reportFailedDependencies verbosity failed =
 -- with a warning and treated as empty ones, since technically they do not
 -- contain any package.
 getInstalledPackages
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> Compiler
   -> Maybe (SymbolicPath CWD (Dir from))
   -> PackageDBStackX (SymbolicPath from (Dir PkgDB))
@@ -2094,7 +2108,7 @@ getInstalledPackages verbosity comp mbWorkDir packageDBs progdb = do
 -- on the package database stack in question.  However, when sandboxes
 -- are involved these sanity checks are not desirable.
 getPackageDBContents
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> Compiler
   -> Maybe (SymbolicPath CWD (Dir Pkg))
   -> PackageDB
@@ -2184,7 +2198,8 @@ interpretPackageDbFlags userInstall specificDBs =
 -- deps in the end. So we still need to remember which installed packages to
 -- pick.
 combinedConstraints
-  :: [PackageVersionConstraint]
+  :: HasCallStack
+  => [PackageVersionConstraint]
   -> [GivenComponent]
   -- ^ installed dependencies
   -> InstalledPackageIndex
@@ -2219,7 +2234,7 @@ combinedConstraints constraints dependencies installedPackages = do
         ]
 
     -- The dependencies along with the installed package info, if it exists
-    dependenciesPkgInfo :: [(PackageName, ComponentName, ComponentId, Maybe InstalledPackageInfo)]
+    dependenciesPkgInfo :: HasCallStack => [(PackageName, ComponentName, ComponentId, Maybe InstalledPackageInfo)]
     dependenciesPkgInfo =
       [ (pkgname, CLibName lname, cid, mpkg)
       | GivenComponent pkgname lname cid <- dependencies
diff --git a/Cabal/src/Distribution/Simple/Errors.hs b/Cabal/src/Distribution/Simple/Errors.hs
index 1ce4f7ca06a..0d9062cbe08 100644
--- a/Cabal/src/Distribution/Simple/Errors.hs
+++ b/Cabal/src/Distribution/Simple/Errors.hs
@@ -60,7 +60,7 @@ data CabalException
   | OnlySupportSpecificPackageDb
   | FailedToParseOutputDescribe String PackageId
   | DumpFailed String String
-  | FailedToParseOutputDump String
+  | FailedToParseOutputDump String String
   | ListFailed String
   | FailedToParseOutputList String
   | ProgramNotFound String
@@ -336,7 +336,7 @@ exceptionMessage e = case e of
   OnlySupportSpecificPackageDb -> "HcPkg.writeRegistrationFileDirectly: only supports SpecificPackageDB for now"
   FailedToParseOutputDescribe programId pkgId -> "failed to parse output of '" ++ programId ++ " describe " ++ prettyShow pkgId ++ "'"
   DumpFailed programId exception -> programId ++ " dump failed: " ++ exception
-  FailedToParseOutputDump programId -> "failed to parse output of '" ++ programId ++ " dump'"
+  FailedToParseOutputDump programId err -> "failed to parse output of '" ++ programId ++ " dump': " ++ err
   ListFailed programId -> programId ++ " list failed"
   FailedToParseOutputList programId -> "failed to parse output of '" ++ programId ++ " list'"
   ProgramNotFound progName -> "The program '" ++ progName ++ "' is required but it could not be found"
diff --git a/Cabal/src/Distribution/Simple/GHC.hs b/Cabal/src/Distribution/Simple/GHC.hs
index 45fbee0fed1..e865fe436d2 100644
--- a/Cabal/src/Distribution/Simple/GHC.hs
+++ b/Cabal/src/Distribution/Simple/GHC.hs
@@ -150,6 +150,8 @@ import System.Directory
 import Distribution.Simple.Setup (BuildingWhat (..))
 import Distribution.Simple.Setup.Build
 
+import GHC.Stack (HasCallStack)
+
 -- -----------------------------------------------------------------------------
 -- Configuring
 
@@ -249,7 +251,7 @@ configure verbosity hcPath hcPkgPath conf0 = do
       compilerId = CompilerId GHC ghcVersion
 
       compilerAbiTag :: AbiTag
-      compilerAbiTag = maybe NoAbiTag AbiTag (Map.lookup "Project Unit Id" ghcInfoMap >>= stripPrefix (prettyShow compilerId <> "-"))
+      compilerAbiTag = fromMaybe NoAbiTag (getAbiTag . fromString <$> Map.lookup "Project Unit Id" ghcInfoMap)
 
   let comp =
         Compiler
@@ -400,7 +402,7 @@ getGhcInfo verbosity ghcProg = Internal.getGhcInfo verbosity implInfo ghcProg
 
 -- | Given a single package DB, return all installed packages.
 getPackageDBContents
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> Maybe (SymbolicPath CWD (Dir from))
   -> PackageDBX (SymbolicPath from (Dir PkgDB))
   -> ProgramDb
@@ -411,7 +413,7 @@ getPackageDBContents verbosity mbWorkDir packagedb progdb = do
 
 -- | Given a package DB stack, return all installed packages.
 getInstalledPackages
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> Compiler
   -> Maybe (SymbolicPath CWD (Dir from))
   -> PackageDBStackX (SymbolicPath from (Dir PkgDB))
@@ -421,9 +423,22 @@ getInstalledPackages verbosity comp mbWorkDir packagedbs progdb = do
   checkPackageDbEnvVar verbosity
   checkPackageDbStack verbosity comp packagedbs
   pkgss <- getInstalledPackages' verbosity mbWorkDir packagedbs progdb
-  index <- toPackageIndex verbosity pkgss progdb
+  let pkgss' = [ (packagedb, updDependsUnitIds . updUnitId . setPkgCompiler <$> pkgs)
+               | (packagedb, pkgs) <- pkgss
+               ]
+  index <- toPackageIndex verbosity pkgss' progdb
   return $! hackRtsPackage index
   where
+    setPkgCompiler pkg = pkg{ InstalledPackageInfo.pkgCompiler = Just (compilerId comp) }
+    updUnitId pkg | isPartialUnitId (installedUnitId pkg) =
+      pkg { InstalledPackageInfo.installedUnitId =
+        addPrefixToUnitId (prettyShow (compilerId comp)) (InstalledPackageInfo.installedUnitId pkg)
+        }
+    updUnitId pkg = pkg
+    updDependsUnitIds pkg =
+      pkg { InstalledPackageInfo.depends =
+        map (\x -> if isPartialUnitId x then addPrefixToUnitId (prettyShow (compilerId comp)) x else x) (InstalledPackageInfo.depends pkg)
+        }
     hackRtsPackage index =
       case PackageIndex.lookupPackageName index (mkPackageName "rts") of
         [(_, [rts])] ->
@@ -535,7 +550,7 @@ removeMingwIncludeDir pkg =
 
 -- | Get the packages from specific PackageDBs, not cumulative.
 getInstalledPackages'
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> Maybe (SymbolicPath CWD (Dir from))
   -> [PackageDBX (SymbolicPath from (Dir PkgDB))]
   -> ProgramDb
diff --git a/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs b/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs
index f2ca9aba02f..800059a9402 100644
--- a/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs
+++ b/Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs
@@ -1,5 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DisambiguateRecordFields #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RankNTypes #-}
@@ -26,6 +28,7 @@ import Distribution.Simple.Program.Types
 import Distribution.Simple.Setup.Common (commonSetupTempFileOptions)
 import Distribution.System (Arch (JavaScript), Platform (..))
 import Distribution.Types.ComponentLocalBuildInfo
+import Distribution.Types.ExtraSource (ExtraSource (..))
 import Distribution.Utils.Path
 import Distribution.Verbosity (Verbosity)
 
@@ -51,25 +54,24 @@ buildAllExtraSources =
     , buildJsSources
     , buildAsmSources
     , buildCmmSources
+    , buildAutogenCmmSources
     ]
 
-buildCSources
-  , buildCxxSources
-  , buildJsSources
-  , buildAsmSources
-  , buildCmmSources
-    :: Maybe (SymbolicPath Pkg File)
-    -- ^ An optional non-Haskell Main file
-    -> ConfiguredProgram
-    -- ^ The GHC configured program
-    -> SymbolicPath Pkg (Dir Artifacts)
-    -- ^ The build directory for this target
-    -> (Bool -> [BuildWay], Bool -> BuildWay, BuildWay)
-    -- ^ Needed build ways
-    -> PreBuildComponentInputs
-    -- ^ The context and component being built in it.
-    -> IO (NubListR (SymbolicPath Pkg File))
-    -- ^ Returns the list of extra sources that were built
+type ExtraSourceBuilder =
+  Maybe (SymbolicPath Pkg File)
+      -- ^ An optional non-Haskell Main file
+      -> ConfiguredProgram
+      -- ^ The GHC configured program
+      -> SymbolicPath Pkg (Dir Artifacts)
+      -- ^ The build directory for this target
+      -> (Bool -> [BuildWay], Bool -> BuildWay, BuildWay)
+      -- ^ Needed build ways
+      -> PreBuildComponentInputs
+      -- ^ The context and component being built in it.
+      -> IO (NubListR (SymbolicPath Pkg File))
+      -- ^ Returns the list of extra sources that were built
+
+buildCSources :: ExtraSourceBuilder
 buildCSources mbMainFile =
   buildExtraSources
     "C Sources"
@@ -80,9 +82,11 @@ buildCSources mbMainFile =
           CExe{}
             | Just main <- mbMainFile
             , isC $ getSymbolicPath main ->
-                cFiles ++ [main]
+                cFiles ++ [ExtraSourcePkg main mempty]
           _otherwise -> cFiles
     )
+
+buildCxxSources :: ExtraSourceBuilder
 buildCxxSources mbMainFile =
   buildExtraSources
     "C++ Sources"
@@ -93,9 +97,11 @@ buildCxxSources mbMainFile =
           CExe{}
             | Just main <- mbMainFile
             , isCxx $ getSymbolicPath main ->
-                cxxFiles ++ [main]
+                cxxFiles ++ [ExtraSourcePkg main mempty]
           _otherwise -> cxxFiles
     )
+
+buildJsSources :: ExtraSourceBuilder
 buildJsSources _mbMainFile ghcProg buildTargetDir neededWays = do
   Platform hostArch _ <- hostPlatform <$> localBuildInfo
   let hasJsSupport = hostArch == JavaScript
@@ -114,36 +120,49 @@ buildJsSources _mbMainFile ghcProg buildTargetDir neededWays = do
     ghcProg
     buildTargetDir
     neededWays
+
+buildAsmSources :: ExtraSourceBuilder
 buildAsmSources _mbMainFile =
   buildExtraSources
     "Assembler Sources"
     Internal.componentAsmGhcOptions
     (asmSources . componentBuildInfo)
+
+buildCmmSources :: ExtraSourceBuilder
 buildCmmSources _mbMainFile =
   buildExtraSources
     "C-- Sources"
     Internal.componentCmmGhcOptions
     (cmmSources . componentBuildInfo)
 
+buildAutogenCmmSources :: ExtraSourceBuilder
+buildAutogenCmmSources _mbMainFile=
+  buildExtraSources
+    "C-- Generated Sources"
+    Internal.componentCmmGhcOptions
+    (autogenCmmSources . componentBuildInfo)
+
 -- | Create 'PreBuildComponentRules' for a given type of extra build sources
 -- which are compiled via a GHC invocation with the given options. Used to
 -- define built-in extra sources, such as, C, Cxx, Js, Asm, and Cmm sources.
 buildExtraSources
-  :: String
+  :: forall from
+   . Internal.SourcePath (ExtraSource from)
+  => String
   -- ^ String describing the extra sources being built, for printing.
   -> ( Verbosity
        -> LocalBuildInfo
        -> BuildInfo
        -> ComponentLocalBuildInfo
        -> SymbolicPath Pkg (Dir Artifacts)
-       -> SymbolicPath Pkg File
+       -> ExtraSource from
        -> GhcOptions
      )
   -- ^ Function to determine the @'GhcOptions'@ for the
   -- invocation of GHC when compiling these extra sources (e.g.
   -- @'Internal.componentCxxGhcOptions'@,
   -- @'Internal.componentCmmGhcOptions'@)
-  -> (Component -> [SymbolicPath Pkg File])
+  -> (Component -> [ExtraSource from])
   -- ^ View the extra sources of a component, typically from
   -- the build info (e.g. @'asmSources'@, @'cSources'@).
   -- @'Executable'@ components might additionally add the
@@ -189,8 +208,7 @@ buildExtraSources
             platform
             mbWorkDir
 
-        buildAction :: SymbolicPath Pkg File -> IO ()
-        buildAction sourceFile = do
+        buildAction extraSource = do
           let baseSrcOpts =
                 componentSourceGhcOptions
                   verbosity
@@ -198,7 +216,7 @@ buildExtraSources
                   bi
                   clbi
                   buildTargetDir
-                  sourceFile
+                  extraSource
               vanillaSrcOpts =
                 -- -fPIC is used in case you are using the repl
                 -- of a dynamically linked GHC
@@ -228,9 +246,9 @@ buildExtraSources
               odir = fromFlag (ghcOptObjDir vanillaSrcOpts)
 
               compileIfNeeded :: GhcOptions -> IO ()
-              compileIfNeeded opts = do
-                needsRecomp <- checkNeedsRecompilation mbWorkDir sourceFile opts
-                when needsRecomp $ runGhcProg opts
+              compileIfNeeded opts' = do
+                needsRecomp <- checkNeedsRecompilation mbWorkDir (Internal.sourcePath lbi extraSource) opts'
+                when needsRecomp $ runGhcProg opts'
 
           createDirectoryIfMissingVerbose verbosity True (i odir)
           case targetComponent targetInfo of
@@ -269,4 +287,4 @@ buildExtraSources
         else do
           info verbosity ("Building " ++ description ++ "...")
           traverse_ buildAction sources
-          return (toNubListR sources)
+          return (toNubListR (map (Internal.sourcePath lbi) sources))
diff --git a/Cabal/src/Distribution/Simple/GHC/Build/Link.hs b/Cabal/src/Distribution/Simple/GHC/Build/Link.hs
index e26e3890ba3..78f293e7656 100644
--- a/Cabal/src/Distribution/Simple/GHC/Build/Link.hs
+++ b/Cabal/src/Distribution/Simple/GHC/Build/Link.hs
@@ -121,6 +121,14 @@ linkOrLoadComponent
               PD.ldOptions bi
                 ++ [ "-static"
                    | withFullyStaticExe lbi
+                   -- MacOS can not link fully statically.
+                   -- Only kernels can be linked fully statically. Everything
+                   -- linking against libSystem must link dynamically. Thus even
+                   -- if we link all dependencies statically, we must not pass
+                   -- '-optl-static', as that will cause the linker to try and
+                   -- fail.
+                   , let Platform _hostArch hostOS = hostPlatform lbi
+                      in hostOS /= OSX
                    ]
                 -- Pass extra `ld-options` given
                 -- through to GHC's linker.
diff --git a/Cabal/src/Distribution/Simple/GHC/Internal.hs b/Cabal/src/Distribution/Simple/GHC/Internal.hs
index 6e27b41bc83..dd046fcbece 100644
--- a/Cabal/src/Distribution/Simple/GHC/Internal.hs
+++ b/Cabal/src/Distribution/Simple/GHC/Internal.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE RankNTypes #-}
 
 -----------------------------------------------------------------------------
@@ -47,6 +48,9 @@ module Distribution.Simple.GHC.Internal
   , ghcEnvironmentFileName
   , renderGhcEnvironmentFile
   , renderGhcEnvironmentFileEntry
+
+    -- * Paths
+  , SourcePath (..)
   ) where
 
 import Distribution.Compat.Prelude
@@ -311,7 +315,6 @@ getExtensions verbosity implInfo ghcProg = do
           then lines str
           else -- Older GHCs only gave us either Foo or NoFoo,
           -- so we have to work out the other one ourselves
-
             [ extStr''
             | extStr <- lines str
             , let extStr' = case extStr of
@@ -331,7 +334,6 @@ getExtensions verbosity implInfo ghcProg = do
           -- Since it was not a proper extension, it could
           -- not be turned off, hence we omit a
           -- DisableExtension entry here.
-
             (EnableExtension NondecreasingIndentation, Nothing)
               : extensions0
           else extensions0
@@ -357,21 +359,23 @@ includePaths lbi bi clbi odir =
          | dir <- mapMaybe (symbolicPathRelative_maybe . unsafeCoerceSymbolicPath) $ includeDirs bi
          ]
 
-componentCcGhcOptions
-  :: Verbosity
-  -> LocalBuildInfo
-  -> BuildInfo
-  -> ComponentLocalBuildInfo
-  -> SymbolicPath Pkg (Dir Artifacts)
-  -> SymbolicPath Pkg File
-  -> GhcOptions
-componentCcGhcOptions verbosity lbi bi clbi odir filename =
+type ExtraSourceGhcOptions pkg =
+  Verbosity
+    -> LocalBuildInfo
+    -> BuildInfo
+    -> ComponentLocalBuildInfo
+    -> SymbolicPath Pkg (Dir Artifacts)
+    -> ExtraSource pkg
+    -> GhcOptions
+
+componentCcGhcOptions :: SourcePath (ExtraSource pkg) => ExtraSourceGhcOptions pkg
+componentCcGhcOptions verbosity lbi bi clbi odir extraSource =
   mempty
     { -- Respect -v0, but don't crank up verbosity on GHC if
       -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
       ghcOptVerbosity = toFlag (min verbosity normal)
     , ghcOptMode = toFlag GhcModeCompile
-    , ghcOptInputFiles = toNubListR [filename]
+    , ghcOptInputFiles = toNubListR [sourcePath lbi extraSource]
     , ghcOptCppIncludePath = includePaths lbi bi clbi odir
     , ghcOptHideAllPackages = toFlag True
     , ghcOptPackageDBs = withPackageDB lbi
@@ -388,29 +392,23 @@ componentCcGhcOptions verbosity lbi bi clbi odir filename =
                 MaximalDebugInfo -> ["-g3"]
              )
           ++ ccOptions bi
+          ++ extraSourceOpts extraSource
     , ghcOptCcProgram =
         maybeToFlag $
           programPath
-            <$> lookupProgram gccProgram (withPrograms lbi)
+          <$> lookupProgram gccProgram (withPrograms lbi)
     , ghcOptObjDir = toFlag odir
     , ghcOptExtra = hcOptions GHC bi
     }
 
-componentCxxGhcOptions
-  :: Verbosity
-  -> LocalBuildInfo
-  -> BuildInfo
-  -> ComponentLocalBuildInfo
-  -> SymbolicPath Pkg (Dir Artifacts)
-  -> SymbolicPath Pkg File
-  -> GhcOptions
-componentCxxGhcOptions verbosity lbi bi clbi odir filename =
+componentCxxGhcOptions :: SourcePath (ExtraSource pkg) => ExtraSourceGhcOptions pkg
+componentCxxGhcOptions verbosity lbi bi clbi odir extraSource =
   mempty
     { -- Respect -v0, but don't crank up verbosity on GHC if
       -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
       ghcOptVerbosity = toFlag (min verbosity normal)
     , ghcOptMode = toFlag GhcModeCompile
-    , ghcOptInputFiles = toNubListR [filename]
+    , ghcOptInputFiles = toNubListR [sourcePath lbi extraSource]
     , ghcOptCppIncludePath = includePaths lbi bi clbi odir
     , ghcOptHideAllPackages = toFlag True
     , ghcOptPackageDBs = withPackageDB lbi
@@ -427,29 +425,23 @@ componentCxxGhcOptions verbosity lbi bi clbi odir filename =
                 MaximalDebugInfo -> ["-g3"]
              )
           ++ cxxOptions bi
+          ++ extraSourceOpts extraSource
     , ghcOptCcProgram =
         maybeToFlag $
           programPath
-            <$> lookupProgram gccProgram (withPrograms lbi)
+          <$> lookupProgram gccProgram (withPrograms lbi)
     , ghcOptObjDir = toFlag odir
     , ghcOptExtra = hcOptions GHC bi
     }
 
-componentAsmGhcOptions
-  :: Verbosity
-  -> LocalBuildInfo
-  -> BuildInfo
-  -> ComponentLocalBuildInfo
-  -> SymbolicPath Pkg (Dir Artifacts)
-  -> SymbolicPath Pkg File
-  -> GhcOptions
-componentAsmGhcOptions verbosity lbi bi clbi odir filename =
+componentAsmGhcOptions :: SourcePath (ExtraSource pkg) => ExtraSourceGhcOptions pkg
+componentAsmGhcOptions verbosity lbi bi clbi odir extraSource =
   mempty
     { -- Respect -v0, but don't crank up verbosity on GHC if
       -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
       ghcOptVerbosity = toFlag (min verbosity normal)
     , ghcOptMode = toFlag GhcModeCompile
-    , ghcOptInputFiles = toNubListR [filename]
+    , ghcOptInputFiles = toNubListR [sourcePath lbi extraSource]
     , ghcOptCppIncludePath = includePaths lbi bi clbi odir
     , ghcOptHideAllPackages = toFlag True
     , ghcOptPackageDBs = withPackageDB lbi
@@ -469,21 +461,14 @@ componentAsmGhcOptions verbosity lbi bi clbi odir filename =
     , ghcOptObjDir = toFlag odir
     }
 
-componentJsGhcOptions
-  :: Verbosity
-  -> LocalBuildInfo
-  -> BuildInfo
-  -> ComponentLocalBuildInfo
-  -> SymbolicPath Pkg (Dir Artifacts)
-  -> SymbolicPath Pkg File
-  -> GhcOptions
-componentJsGhcOptions verbosity lbi bi clbi odir filename =
+componentJsGhcOptions :: SourcePath (ExtraSource pkg) => ExtraSourceGhcOptions pkg
+componentJsGhcOptions verbosity lbi bi clbi odir extraSource =
   mempty
     { -- Respect -v0, but don't crank up verbosity on GHC if
       -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
       ghcOptVerbosity = toFlag (min verbosity normal)
     , ghcOptMode = toFlag GhcModeCompile
-    , ghcOptInputFiles = toNubListR [filename]
+    , ghcOptInputFiles = toNubListR [sourcePath lbi extraSource]
     , ghcOptCppIncludePath = includePaths lbi bi clbi odir
     , ghcOptHideAllPackages = toFlag True
     , ghcOptPackageDBs = withPackageDB lbi
@@ -577,21 +562,14 @@ toGhcOptimisation NoOptimisation = mempty -- TODO perhaps override?
 toGhcOptimisation NormalOptimisation = toFlag GhcNormalOptimisation
 toGhcOptimisation MaximumOptimisation = toFlag GhcMaximumOptimisation
 
-componentCmmGhcOptions
-  :: Verbosity
-  -> LocalBuildInfo
-  -> BuildInfo
-  -> ComponentLocalBuildInfo
-  -> SymbolicPath Pkg (Dir Artifacts)
-  -> SymbolicPath Pkg File
-  -> GhcOptions
-componentCmmGhcOptions verbosity lbi bi clbi odir filename =
+componentCmmGhcOptions :: SourcePath (ExtraSource pkg) => ExtraSourceGhcOptions pkg
+componentCmmGhcOptions verbosity lbi bi clbi odir extraSource =
   mempty
     { -- Respect -v0, but don't crank up verbosity on GHC if
       -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
       ghcOptVerbosity = toFlag (min verbosity normal)
     , ghcOptMode = toFlag GhcModeCompile
-    , ghcOptInputFiles = toNubListR [filename]
+    , ghcOptInputFiles = toNubListR [sourcePath lbi extraSource]
     , ghcOptCppIncludePath = includePaths lbi bi clbi odir
     , ghcOptCppOptions = cppOptions bi
     , ghcOptCppIncludes =
@@ -602,7 +580,7 @@ componentCmmGhcOptions verbosity lbi bi clbi odir filename =
     , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi
     , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi)
     , ghcOptDebugInfo = toFlag (withDebugInfo lbi)
-    , ghcOptExtra = cmmOptions bi
+    , ghcOptExtra = cmmOptions bi ++ extraSourceOpts extraSource
     , ghcOptObjDir = toFlag odir
     }
 
@@ -838,3 +816,12 @@ renderGhcEnvironmentFileEntry entry = case entry of
       UserPackageDB -> "user-package-db"
       SpecificPackageDB dbfile -> "package-db " ++ dbfile
   GhcEnvFileClearPackageDbStack -> "clear-package-db"
+
+class ExtraSourceClass e => SourcePath e where
+  sourcePath :: LocalBuildInfo -> e -> SymbolicPath Pkg 'File
+
+instance SourcePath (ExtraSource Pkg) where
+  sourcePath _ (ExtraSourcePkg f _) = f
+
+instance SourcePath (ExtraSource Build) where
+  sourcePath lbi (ExtraSourceBuild f _) = buildDir lbi </> f
diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs
index ca71857828e..967981ee045 100644
--- a/Cabal/src/Distribution/Simple/GHCJS.hs
+++ b/Cabal/src/Distribution/Simple/GHCJS.hs
@@ -60,6 +60,7 @@ import Distribution.Simple.BuildPaths
 import Distribution.Simple.Compiler
 import Distribution.Simple.Errors
 import Distribution.Simple.Flag
+import Distribution.Simple.GHC.Build.Utils (isCxx)
 import Distribution.Simple.GHC.EnvironmentParser
 import Distribution.Simple.GHC.ImplInfo
 import qualified Distribution.Simple.GHC.Internal as Internal
@@ -509,8 +510,6 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do
 
       -- See Note [Symbolic paths] in Distribution.Utils.Path
       i = interpretSymbolicPathLBI lbi
-      u :: SymbolicPathX allowAbs Pkg to -> FilePath
-      u = getSymbolicPath
 
   (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)
   let runGhcjsProg = runGHC verbosity ghcjsProg comp platform mbWorkDir
@@ -538,7 +537,7 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do
   -- modules?
   let cLikeFiles = fromNubListR $ toNubListR (cSources libBi) <> toNubListR (cxxSources libBi)
       jsSrcs = jsSources libBi
-      cObjs = map ((`replaceExtensionSymbolicPath` objExtension)) cLikeFiles
+      cObjs = map ((`replaceExtensionSymbolicPath` objExtension) . extraSourceFile) cLikeFiles
       baseOpts = componentGhcOptions verbosity lbi libBi clbi libTargetDir
       linkJsLibOpts =
         mempty
@@ -546,9 +545,9 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do
               [ "-link-js-lib"
               , getHSLibraryName uid
               , "-js-lib-outputdir"
-              , u libTargetDir
+              , getSymbolicPath libTargetDir
               ]
-                ++ map u jsSrcs
+                ++ foldMap (\e -> getSymbolicPath (extraSourceFile e) : extraSourceOpts e) jsSrcs
           }
       vanillaOptsNoJsLib =
         baseOpts
@@ -702,7 +701,7 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do
     info verbosity "Linking..."
     let cSharedObjs =
           map
-            ((`replaceExtensionSymbolicPath` ("dyn_" ++ objExtension)))
+            ((`replaceExtensionSymbolicPath` ("dyn_" ++ objExtension)) . extraSourceFile)
             (cSources libBi ++ cxxSources libBi)
         compiler_id = compilerId (compiler lbi)
         sharedLibFilePath = libTargetDir </> makeRelativePathEx (mkSharedLibName (hostPlatform lbi) compiler_id uid)
@@ -1115,8 +1114,8 @@ decodeMainIsArg arg
 --
 -- Used to correctly build and link sources.
 data BuildSources = BuildSources
-  { cSourcesFiles :: [SymbolicPath Pkg File]
-  , cxxSourceFiles :: [SymbolicPath Pkg File]
+  { cSourcesFiles :: [ExtraSource Pkg]
+  , cxxSourceFiles :: [ExtraSource Pkg]
   , inputSourceFiles :: [SymbolicPath Pkg File]
   , inputSourceModules :: [ModuleName]
   }
@@ -1162,12 +1161,12 @@ gbuildSources verbosity mbWorkDir pkgId specVer tmpDir bm =
               -- have no excuse anymore to keep doing it wrong... ;-)
               warn verbosity $
                 "Enabling workaround for Main module '"
-                  ++ prettyShow mainModName
-                  ++ "' listed in 'other-modules' illegally!"
+                ++ prettyShow mainModName
+                ++ "' listed in 'other-modules' illegally!"
 
               return
                 BuildSources
-                  { cSourcesFiles = cSources bnfo
+                  { cSourcesFiles =  cSources bnfo
                   , cxxSourceFiles = cxxSources bnfo
                   , inputSourceFiles = [main]
                   , inputSourceModules = filter (/= mainModName) $ exeModules exe
@@ -1182,11 +1181,11 @@ gbuildSources verbosity mbWorkDir pkgId specVer tmpDir bm =
                   }
         else
           let (csf, cxxsf)
-                | isCxx (getSymbolicPath main) = (cSources bnfo, main : cxxSources bnfo)
+                | isCxx (getSymbolicPath main) = (cSources bnfo, ExtraSourcePkg main [] : cxxSources bnfo)
                 -- if main is not a Haskell source
                 -- and main is not a C++ source
                 -- then we assume that it is a C source
-                | otherwise = (main : cSources bnfo, cxxSources bnfo)
+                | otherwise = (ExtraSourcePkg main [] : cSources bnfo, cxxSources bnfo)
            in return
                 BuildSources
                   { cSourcesFiles = csf
@@ -1204,9 +1203,6 @@ gbuildSources verbosity mbWorkDir pkgId specVer tmpDir bm =
         , inputSourceModules = foreignLibModules flib
         }
 
-    isCxx :: FilePath -> Bool
-    isCxx fp = elem (takeExtension fp) [".cpp", ".cxx", ".c++"]
-
 -- | FilePath has a Haskell extension: .hs or .lhs
 isHaskell :: FilePath -> Bool
 isHaskell fp = elem (takeExtension fp) [".hs", ".lhs"]
@@ -1267,8 +1263,8 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
       inputModules = inputSourceModules buildSources
       isGhcDynamic = isDynamic comp
       dynamicTooSupported = supportsDynamicToo comp
-      cObjs = map ((`replaceExtensionSymbolicPath` objExtension)) cSrcs
-      cxxObjs = map ((`replaceExtensionSymbolicPath` objExtension)) cxxSrcs
+      cObjs = map ((`replaceExtensionSymbolicPath` objExtension) . extraSourceFile) cSrcs
+      cxxObjs = map ((`replaceExtensionSymbolicPath` objExtension) . extraSourceFile) cxxSrcs
       needDynamic = gbuildNeedDynamic lbi bm
       needProfiling = withProfExe lbi
 
@@ -1470,7 +1466,7 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
             --       add a warning if this occurs.
             odir = fromFlag (ghcOptObjDir opts)
         createDirectoryIfMissingVerbose verbosity True (i odir)
-        needsRecomp <- checkNeedsRecompilation mbWorkDir filename opts
+        needsRecomp <- checkNeedsRecompilation mbWorkDir (extraSourceFile filename) opts
         when needsRecomp $
           runGhcProg opts
       | filename <- cxxSrcs
@@ -1512,7 +1508,7 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
               | otherwise = vanillaCcOpts
             odir = fromFlag (ghcOptObjDir opts)
         createDirectoryIfMissingVerbose verbosity True (i odir)
-        needsRecomp <- checkNeedsRecompilation mbWorkDir filename opts
+        needsRecomp <- checkNeedsRecompilation mbWorkDir (extraSourceFile filename) opts
         when needsRecomp $
           runGhcProg opts
       | filename <- cSrcs
diff --git a/Cabal/src/Distribution/Simple/PackageIndex.hs b/Cabal/src/Distribution/Simple/PackageIndex.hs
index a7d23962b72..7cb606a0e93 100644
--- a/Cabal/src/Distribution/Simple/PackageIndex.hs
+++ b/Cabal/src/Distribution/Simple/PackageIndex.hs
@@ -169,7 +169,6 @@ instance Semigroup (PackageIndex IPI.InstalledPackageInfo) where
 {-# NOINLINE invariant #-}
 invariant :: WithCallStack (InstalledPackageIndex -> Bool)
 invariant (PackageIndex pids pnames) =
-  -- trace (show pids' ++ "\n" ++ show pnames') $
   pids' == pnames'
   where
     pids' = map installedUnitId (Map.elems pids)
@@ -324,6 +323,10 @@ deleteUnitId ipkgid original@(PackageIndex pids pnames) =
         . List.deleteBy (\_ pkg -> installedUnitId pkg == ipkgid) undefined
 
 -- | Removes all packages with this source 'PackageId' from the index.
+--
+-- The Index maps unitids to instances, however we may have multiple unitids
+-- for the same package. Especially if for multiple compilers, as such we need
+-- to ensure we don't delete by name/version, but by name/version/compiler only.
 deleteSourcePackageId
   :: PackageId
   -> InstalledPackageIndex
@@ -334,17 +337,30 @@ deleteSourcePackageId pkgid original@(PackageIndex pids pnames) =
     Nothing -> original
     Just pvers -> case Map.lookup (packageVersion pkgid) pvers of
       Nothing -> original
-      Just pkgs ->
-        mkPackageIndex
-          (foldl' (flip (Map.delete . installedUnitId)) pids pkgs)
-          (deletePkgName pnames)
+      Just pkgs -> case [pkg | pkg <- pkgs, IPI.pkgCompiler pkg == pkgCompiler pkgid] of
+        [] -> original
+        pkgs' ->
+          mkPackageIndex
+            (foldl' (flip (Map.delete . installedUnitId)) pids pkgs')
+            (deletePkgName pnames)
+
   where
     deletePkgName =
       Map.update deletePkgVersion (packageName pkgid, LMainLibName)
 
+    deletePkgVersion :: Map Version [IPI.InstalledPackageInfo] -> Maybe (Map Version [IPI.InstalledPackageInfo])
     deletePkgVersion =
       (\m -> if Map.null m then Nothing else Just m)
-        . Map.delete (packageVersion pkgid)
+        . Map.update deletePkgInstances (packageVersion pkgid)
+
+    deletePkgInstance :: IPI.InstalledPackageInfo -> Maybe IPI.InstalledPackageInfo
+    deletePkgInstance ipi
+      | pkgCompiler pkgid /= pkgCompiler (IPI.sourcePackageId ipi) = Just ipi
+      | otherwise = Nothing
+
+    deletePkgInstances :: [IPI.InstalledPackageInfo] -> Maybe [IPI.InstalledPackageInfo]
+    deletePkgInstances xs = if null xs' then Nothing else Just xs'
+      where xs' = [x | x <- xs, pkgCompiler pkgid /= IPI.pkgCompiler x]
 
 -- | Removes all packages with this (case-sensitive) name from the index.
 --
diff --git a/Cabal/src/Distribution/Simple/Program/Db.hs b/Cabal/src/Distribution/Simple/Program/Db.hs
index c76b38e9923..bc5d0714aa3 100644
--- a/Cabal/src/Distribution/Simple/Program/Db.hs
+++ b/Cabal/src/Distribution/Simple/Program/Db.hs
@@ -67,6 +67,7 @@ module Distribution.Simple.Program.Db
   , ConfiguredProgs
   , updateUnconfiguredProgs
   , updateConfiguredProgs
+  , programDbSignature
   ) where
 
 import Distribution.Compat.Prelude
@@ -564,3 +565,17 @@ requireProgramVersion verbosity prog range programDb =
   join $
     either (dieWithException verbosity) return
       `fmap` lookupProgramVersion verbosity prog range programDb
+
+-- | Select the bits of a 'ProgramDb' to monitor for value changes.
+-- Use 'programsMonitorFiles' for the files to monitor.
+programDbSignature :: ProgramDb -> [ConfiguredProgram]
+programDbSignature progdb =
+  [ prog
+    { programMonitorFiles = []
+    , programOverrideEnv =
+        filter
+          ((/= "PATH") . fst)
+          (programOverrideEnv prog)
+    }
+  | prog <- configuredPrograms progdb
+  ]
diff --git a/Cabal/src/Distribution/Simple/Program/HcPkg.hs b/Cabal/src/Distribution/Simple/Program/HcPkg.hs
index a494bc63f02..8017850d16e 100644
--- a/Cabal/src/Distribution/Simple/Program/HcPkg.hs
+++ b/Cabal/src/Distribution/Simple/Program/HcPkg.hs
@@ -74,6 +74,7 @@ import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as LBS
 import qualified Data.List.NonEmpty as NE
 import qualified System.FilePath.Posix as FilePath.Posix
+import GHC.Stack (HasCallStack)
 
 -- | Information about the features and capabilities of an @hc-pkg@
 --   program.
@@ -277,7 +278,7 @@ hide hpi verbosity mbWorkDir packagedb pkgid =
 -- | Call @hc-pkg@ to get all the details of all the packages in the given
 -- package database.
 dump
-  :: HcPkgInfo
+  :: HasCallStack => HcPkgInfo
   -> Verbosity
   -> Maybe (SymbolicPath CWD (Dir from))
   -> PackageDBX (SymbolicPath from (Dir PkgDB))
@@ -292,9 +293,9 @@ dump hpi verbosity mbWorkDir packagedb = do
 
   case parsePackages output of
     Left ok -> return ok
-    _ -> dieWithException verbosity $ FailedToParseOutputDump (programId (hcPkgProgram hpi))
+    Right e -> dieWithException verbosity $ FailedToParseOutputDump (programId (hcPkgProgram hpi)) (unwords e)
 
-parsePackages :: LBS.ByteString -> Either [InstalledPackageInfo] [String]
+parsePackages :: HasCallStack => LBS.ByteString -> Either [InstalledPackageInfo] [String]
 parsePackages lbs0 =
   case traverse parseInstalledPackageInfo $ splitPkgs lbs0 of
     Right ok -> Left [setUnitId . maybe id mungePackagePaths (pkgRoot pkg) $ pkg | (_, pkg) <- ok]
@@ -380,7 +381,7 @@ mungePackagePaths pkgroot pkginfo =
 -- Older installed package info files did not have the installedUnitId
 -- field, so if it is missing then we fill it as the source package ID.
 -- NB: Internal libraries not supported.
-setUnitId :: InstalledPackageInfo -> InstalledPackageInfo
+setUnitId :: HasCallStack => InstalledPackageInfo -> InstalledPackageInfo
 setUnitId
   pkginfo@InstalledPackageInfo
     { installedUnitId = uid
@@ -389,7 +390,7 @@ setUnitId
     | unUnitId uid == "" =
         pkginfo
           { installedUnitId = mkLegacyUnitId pid
-          , installedComponentId_ = mkComponentId (prettyShow pid)
+          , installedComponentId_ = Just (mkComponentId (prettyShow pid))
           }
 setUnitId pkginfo = pkginfo
 
diff --git a/Cabal/src/Distribution/Simple/Register.hs b/Cabal/src/Distribution/Simple/Register.hs
index 0bd7312cbc4..9a9b313d994 100644
--- a/Cabal/src/Distribution/Simple/Register.hs
+++ b/Cabal/src/Distribution/Simple/Register.hs
@@ -90,6 +90,7 @@ import System.FilePath (isAbsolute)
 
 import qualified Data.ByteString.Lazy.Char8 as BS.Char8
 
+import GHC.Stack (HasCallStack)
 -- -----------------------------------------------------------------------------
 -- Registration
 
@@ -163,7 +164,7 @@ generateOne pkg lib lbi clbi regFlags =
     mbWorkDir = flagToMaybe $ setupWorkingDir common
 
 registerAll
-  :: PackageDescription
+  :: HasCallStack => PackageDescription
   -> LocalBuildInfo
   -> RegisterFlags
   -> [InstalledPackageInfo]
@@ -496,9 +497,10 @@ generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDi
   IPI.InstalledPackageInfo
     { IPI.sourcePackageId = packageId pkg
     , IPI.installedUnitId = componentUnitId clbi
-    , IPI.installedComponentId_ = componentComponentId clbi
+    , IPI.installedComponentId_ = Just (componentComponentId clbi)
     , IPI.instantiatedWith = expectLibraryComponent (maybeComponentInstantiatedWith clbi)
     , IPI.sourceLibName = libName lib
+    , IPI.unitFlags = flagAssignment lbi
     , IPI.compatPackageKey = expectLibraryComponent (maybeComponentCompatPackageKey clbi)
     , -- If GHC >= 8.4 we register with SDPX, otherwise with legacy license
       IPI.license =
@@ -553,6 +555,7 @@ generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDi
     , IPI.haddockHTMLs = [htmldir installDirs | hasModules]
     , IPI.pkgRoot = Nothing
     , IPI.libVisibility = libVisibility lib
+    , IPI.pkgCompiler = Nothing
     }
   where
     ghc84 = case compilerId $ compiler lbi of
diff --git a/Cabal/src/Distribution/Simple/SrcDist.hs b/Cabal/src/Distribution/Simple/SrcDist.hs
index 67f901bf7fb..4e751ed1bbb 100644
--- a/Cabal/src/Distribution/Simple/SrcDist.hs
+++ b/Cabal/src/Distribution/Simple/SrcDist.hs
@@ -566,11 +566,11 @@ allSourcesBuildInfo verbosity rip mbWorkDir bi pps modules = do
   return $
     sources
       ++ catMaybes bootFiles
-      ++ cSources bi
-      ++ cxxSources bi
-      ++ cmmSources bi
-      ++ asmSources bi
-      ++ jsSources bi
+      ++ map extraSourceFile (cSources bi)
+      ++ map extraSourceFile (cxxSources bi)
+      ++ map extraSourceFile (cmmSources bi)
+      ++ map extraSourceFile (asmSources bi)
+      ++ map extraSourceFile (jsSources bi)
   where
     nonEmpty' :: b -> ([a] -> b) -> [a] -> b
     nonEmpty' x _ [] = x
diff --git a/Cabal/src/Distribution/Types/PackageName/Magic.hs b/Cabal/src/Distribution/Types/PackageName/Magic.hs
index 022a62468b1..908e82bea6f 100644
--- a/Cabal/src/Distribution/Types/PackageName/Magic.hs
+++ b/Cabal/src/Distribution/Types/PackageName/Magic.hs
@@ -21,4 +21,4 @@ fakePackageCabalFileName = "fake-package.cabal"
 
 -- | 'fakePackageName' with 'version0'.
 fakePackageId :: PackageId
-fakePackageId = PackageIdentifier fakePackageName version0
+fakePackageId = PackageIdentifier fakePackageName version0 Nothing
diff --git a/Cabal/src/main_configure.hs b/Cabal/src/main_configure.hs
new file mode 100644
index 00000000000..577b8311e90
--- /dev/null
+++ b/Cabal/src/main_configure.hs
@@ -0,0 +1,4 @@
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMainWithHooks autoconfUserHooks
diff --git a/Cabal/src/main_simple.hs b/Cabal/src/main_simple.hs
new file mode 100644
index 00000000000..00bfe1fe441
--- /dev/null
+++ b/Cabal/src/main_simple.hs
@@ -0,0 +1,4 @@
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/cabal-install-solver/cabal-install-solver.cabal b/cabal-install-solver/cabal-install-solver.cabal
index 3f3b84644cf..c1145254b6c 100644
--- a/cabal-install-solver/cabal-install-solver.cabal
+++ b/cabal-install-solver/cabal-install-solver.cabal
@@ -95,6 +95,8 @@ library
     Distribution.Solver.Types.SolverId
     Distribution.Solver.Types.SolverPackage
     Distribution.Solver.Types.SourcePackage
+    Distribution.Solver.Types.Stage
+    Distribution.Solver.Types.Toolchain
     Distribution.Solver.Types.Variable
 
   build-depends:
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular.hs b/cabal-install-solver/src/Distribution/Solver/Modular.hs
index 9111b2d78d0..73bb9c5b20b 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular.hs
@@ -34,7 +34,7 @@ import Distribution.Solver.Modular.IndexConversion
 import Distribution.Solver.Modular.Log
          ( SolverFailure(..), displayLogMessages )
 import Distribution.Solver.Modular.Package
-         ( PN )
+         ( PN, showPI, I )
 import Distribution.Solver.Modular.RetryLog
 import Distribution.Solver.Modular.Solver
          ( SolverConfig(..), PruneAfterFirstSuccess(..), solve )
@@ -54,17 +54,44 @@ import Distribution.Simple.Setup
 import Distribution.Simple.Utils
          ( ordNubBy )
 import Distribution.Verbosity
+import Distribution.Solver.Modular.Configured (CP (..))
+import qualified Distribution.Solver.Types.ComponentDeps as ComponentDeps
+import Distribution.Pretty (Pretty (..), prettyShow)
+import Text.PrettyPrint (text, vcat, Doc, nest, ($+$))
+import Distribution.Solver.Types.OptionalStanza (showStanzas, optStanzaSetNull)
+import Distribution.Solver.Types.Toolchain ( Toolchains )
+import Distribution.Types.Flag (nullFlagAssignment)
 
 
+showCP :: CP QPN -> Doc
+showCP (CP qpi fa es ds) =
+    text "package:" <+> text (showPI qpi) $+$ nest 2 (
+    vcat
+    [ if nullFlagAssignment fa then mempty else text "flags:" <+> pretty fa
+    , if optStanzaSetNull es then mempty else text "stanzas:" <+> text (showStanzas es)
+    , vcat
+        [ text "component" <+> pretty c $+$
+          nest 2 (text "dependencies" $+$
+          nest 2 (vcat [ text (showPI dep) | dep <- deps]))
+        | (c, deps) <- ComponentDeps.toList ds
+        ]
+    ])
+
 -- | Ties the two worlds together: classic cabal-install vs. the modular
 -- solver. Performs the necessary translations before and after.
 modularResolver :: SolverConfig -> DependencyResolver loc
-modularResolver sc (Platform arch os) cinfo iidx sidx pkgConfigDB pprefs pcs pns =
-  uncurry postprocess <$> -- convert install plan
-  solve' sc cinfo idx pkgConfigDB pprefs gcs pns
-    where
+modularResolver sc toolchains biidx iidx sidx pkgConfigDB pprefs pcs pns = do
+    (assignment, revdepmap) <- solve' sc toolchains idx pkgConfigDB pprefs gcs pns
+    let cp = toCPs assignment revdepmap
+    Step (show (vcat (map showCP cp))) $
+        return $ postprocess assignment revdepmap
+  where
+      showIdx :: Index -> String
+      showIdx idx = unlines [prettyShow pn ++ ": " ++ show i
+                            | (pn, m) <- M.toList idx
+                            , (i, _info) <- M.toList (m :: Map I PInfo)]
       -- Indices have to be converted into solver-specific uniform index.
-      idx    = convPIs os arch cinfo gcs (shadowPkgs sc) (strongFlags sc) (solveExecutables sc) iidx sidx
+      idx    = convPIs toolchains gcs (shadowPkgs sc) (strongFlags sc) (solveExecutables sc) biidx iidx sidx
       -- Constraints have to be converted into a finite map indexed by PN.
       gcs    = M.fromListWith (++) (map pair pcs)
         where
@@ -74,7 +101,7 @@ modularResolver sc (Platform arch os) cinfo iidx sidx pkgConfigDB pprefs pcs pns
       -- package qualifiers, which means that linked packages become duplicates
       -- and can be removed.
       postprocess a rdm = ordNubBy nodeKey $
-                          map (convCP iidx sidx) (toCPs a rdm)
+                          map (convCP toolchains biidx iidx sidx) (toCPs a rdm)
 
       -- Helper function to extract the PN from a constraint.
       pcName :: PackageConstraint -> PN
@@ -114,21 +141,21 @@ modularResolver sc (Platform arch os) cinfo iidx sidx pkgConfigDB pprefs pcs pns
 -- complete, i.e., it shows the whole chain of dependencies from the user
 -- targets to the conflicting packages.
 solve' :: SolverConfig
-       -> CompilerInfo
+       -> Toolchains
        -> Index
        -> Maybe PkgConfigDb
        -> (PN -> PackagePreferences)
        -> Map PN [LabeledPackageConstraint]
        -> Set PN
        -> Progress String String (Assignment, RevDepMap)
-solve' sc cinfo idx pkgConfigDB pprefs gcs pns =
+solve' sc toolchains idx pkgConfigDB pprefs gcs pns =
     toProgress $ retry (runSolver printFullLog sc) createErrorMsg
   where
     runSolver :: Bool -> SolverConfig
               -> RetryLog String SolverFailure (Assignment, RevDepMap)
     runSolver keepLog sc' =
         displayLogMessages keepLog $
-        solve sc' cinfo idx pkgConfigDB pprefs gcs pns
+        solve sc' toolchains idx pkgConfigDB pprefs gcs pns
 
     createErrorMsg :: SolverFailure
                    -> RetryLog String String (Assignment, RevDepMap)
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Builder.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Builder.hs
index 5d196f4fd9f..9de9ea16ee2 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Builder.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Builder.hs
@@ -50,8 +50,7 @@ data BuildState = BS {
   index :: Index,                   -- ^ information about packages and their dependencies
   rdeps :: RevDepMap,               -- ^ set of all package goals, completed and open, with reverse dependencies
   open  :: [OpenGoal],              -- ^ set of still open goals (flag and package goals)
-  next  :: BuildType,               -- ^ kind of node to generate next
-  qualifyOptions :: QualifyOptions  -- ^ qualification options
+  next  :: BuildType                -- ^ kind of node to generate next
 }
 
 -- | Map of available linking targets.
@@ -105,7 +104,7 @@ scopedExtendOpen :: QPN -> FlaggedDeps PN -> FlagInfo ->
 scopedExtendOpen qpn fdeps fdefs s = extendOpen qpn gs s
   where
     -- Qualify all package names
-    qfdeps = qualifyDeps (qualifyOptions s) qpn fdeps
+    qfdeps = qualifyDeps qpn fdeps
     -- Introduce all package flags
     qfdefs = L.map (\ (fn, b) -> Flagged (FN qpn fn) b [] []) $ M.toList fdefs
     -- Combine new package and flag goals
@@ -255,7 +254,6 @@ buildTree idx (IndependentGoals ind) igs =
           , rdeps = M.fromList (L.map (\ qpn -> (qpn, []))              qpns)
           , open  = L.map topLevelGoal qpns
           , next  = Goals
-          , qualifyOptions = defaultQualifyOptions idx
           }
       , linkingState = M.empty
       }
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs b/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs
index 0e2e8ad5baa..32148c4dce8 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs
@@ -6,8 +6,6 @@ import Data.Maybe
 import Prelude hiding (pi)
 import Data.Either (partitionEithers)
 
-import Distribution.Package (UnitId, packageId)
-
 import qualified Distribution.Simple.PackageIndex as SI
 
 import Distribution.Solver.Modular.Configured
@@ -21,44 +19,51 @@ import           Distribution.Solver.Types.SolverId
 import           Distribution.Solver.Types.SolverPackage
 import           Distribution.Solver.Types.InstSolverPackage
 import           Distribution.Solver.Types.SourcePackage
+import           Distribution.Solver.Types.Toolchain
+import Control.Applicative ((<|>))
 
 -- | Converts from the solver specific result @CP QPN@ into
 -- a 'ResolverPackage', which can then be converted into
 -- the install plan.
-convCP :: SI.InstalledPackageIndex ->
-          CI.PackageIndex (SourcePackage loc) ->
-          CP QPN -> ResolverPackage loc
-convCP iidx sidx (CP qpi fa es ds) =
-  case convPI qpi of
-    Left  pi -> PreExisting $
+convCP :: Toolchains
+       -> SI.InstalledPackageIndex -- ^ build
+       -> SI.InstalledPackageIndex -- ^ host
+       -> CI.PackageIndex (SourcePackage loc)
+       -> CP QPN -> ResolverPackage loc
+convCP toolchains biidx iidx sidx (CP qpi fa es ds) =
+  case qpi of
+    -- Installed
+    (PI qpn (I _stage _ (Inst pi)))  ->
+      PreExisting $
                   InstSolverPackage {
-                    instSolverPkgIPI = fromJust $ SI.lookupUnitId iidx pi,
+                    instSolverQPN = qpn,
+                    instSolverPkgIPI = fromMaybe (error "convCP: lookupUnitId failed") $ (SI.lookupUnitId iidx pi) <|> (SI.lookupUnitId biidx pi),
                     instSolverPkgLibDeps = fmap fst ds',
                     instSolverPkgExeDeps = fmap snd ds'
                   }
-    Right pi -> Configured $
+    -- "In repo" i.e. a source package
+    (PI qpn@(Q _path pn) (I stage v InRepo)) ->
+      let pi = PackageIdentifier pn v (Just $ compilerIdFor stage toolchains) in
+      Configured $
                   SolverPackage {
-                      solverPkgSource = srcpkg,
+                      solverPkgQPN = qpn,
+                      solverPkgStage = stage,
+                      solverPkgSource = fromMaybe (error "convCP: lookupPackageId failed") $ CI.lookupPackageId sidx pi,
                       solverPkgFlags = fa,
                       solverPkgStanzas = es,
                       solverPkgLibDeps = fmap fst ds',
                       solverPkgExeDeps = fmap snd ds'
                     }
-      where
-        srcpkg = fromMaybe (error "convCP: lookupPackageId failed") $ CI.lookupPackageId sidx pi
   where
     ds' :: ComponentDeps ([SolverId] {- lib -}, [SolverId] {- exe -})
-    ds' = fmap (partitionEithers . map convConfId) ds
-
-convPI :: PI QPN -> Either UnitId PackageId
-convPI (PI _ (I _ (Inst pi))) = Left pi
-convPI pi                     = Right (packageId (either id id (convConfId pi)))
+    ds' = fmap (partitionEithers . map (convConfId toolchains)) ds
 
-convConfId :: PI QPN -> Either SolverId {- is lib -} SolverId {- is exe -}
-convConfId (PI (Q (PackagePath _ q) pn) (I v loc)) =
+convConfId :: Toolchains -> PI QPN -> Either SolverId {- is lib -} SolverId {- is exe -}
+convConfId toolchains (PI (Q (PackagePath _ q) pn) (I stage v loc)) =
     case loc of
-        Inst pi -> Left (PreExistingId sourceId pi)
-        _otherwise
+        Inst pi ->
+          Left (PreExistingId sourceId pi)
+        InRepo
           | QualExe _ pn' <- q
           -- NB: the dependencies of the executable are also
           -- qualified.  So the way to tell if this is an executable
@@ -67,6 +72,6 @@ convConfId (PI (Q (PackagePath _ q) pn) (I v loc)) =
           -- silly and didn't allow arbitrarily nested build-tools
           -- dependencies, so a shallow check works.
           , pn == pn' -> Right (PlannedId sourceId)
-          | otherwise    -> Left  (PlannedId sourceId)
+          | otherwise -> Left  (PlannedId sourceId)
   where
-    sourceId    = PackageIdentifier pn v
+    sourceId    = PackageIdentifier pn v (Just $ compilerIdFor stage toolchains)
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs
index 27debc9c6f0..78ac86c89f2 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs
@@ -1,34 +1,38 @@
 {-# LANGUAGE DeriveFunctor #-}
-{-# LANGUAGE RecordWildCards #-}
-module Distribution.Solver.Modular.Dependency (
-    -- * Variables
-    Var(..)
+
+module Distribution.Solver.Modular.Dependency
+  ( -- * Variables
+    Var (..)
   , showVar
   , varPN
+
     -- * Conflict sets
   , ConflictSet
   , ConflictMap
   , CS.showConflictSet
+
     -- * Constrained instances
-  , CI(..)
+  , CI (..)
+
     -- * Flagged dependencies
   , FlaggedDeps
-  , FlaggedDep(..)
-  , LDep(..)
-  , Dep(..)
-  , PkgComponent(..)
-  , ExposedComponent(..)
-  , DependencyReason(..)
+  , FlaggedDep (..)
+  , LDep (..)
+  , Dep (..)
+  , PkgComponent (..)
+  , ExposedComponent (..)
+  , DependencyReason (..)
   , showDependencyReason
   , flattenFlaggedDeps
-  , QualifyOptions(..)
   , qualifyDeps
   , unqualifyDeps
+
     -- * Reverse dependency map
   , RevDepMap
+
     -- * Goals
-  , Goal(..)
-  , GoalReason(..)
+  , Goal (..)
+  , GoalReason (..)
   , QGoalReason
   , goalToVar
   , varToConflictSet
@@ -39,21 +43,21 @@ module Distribution.Solver.Modular.Dependency (
   , dependencyReasonToConflictSetWithVersionConflict
   ) where
 
-import Prelude ()
 import qualified Data.Map as M
 import qualified Data.Set as S
 import Distribution.Solver.Compat.Prelude hiding (pi)
+import Prelude ()
 
-import Language.Haskell.Extension (Extension(..), Language(..))
+import Language.Haskell.Extension (Extension (..), Language (..))
 
-import Distribution.Solver.Modular.ConflictSet (ConflictSet, ConflictMap)
+import Distribution.Solver.Modular.ConflictSet (ConflictMap, ConflictSet)
+import qualified Distribution.Solver.Modular.ConflictSet as CS
 import Distribution.Solver.Modular.Flag
 import Distribution.Solver.Modular.Package
 import Distribution.Solver.Modular.Var
 import Distribution.Solver.Modular.Version
-import qualified Distribution.Solver.Modular.ConflictSet as CS
 
-import Distribution.Solver.Types.ComponentDeps (Component(..))
+import Distribution.Solver.Types.ComponentDeps (Component (..))
 import Distribution.Solver.Types.PackagePath
 import Distribution.Types.LibraryName
 import Distribution.Types.PkgconfigVersionRange
@@ -85,14 +89,14 @@ type FlaggedDeps qpn = [FlaggedDep qpn]
 
 -- | Flagged dependencies can either be plain dependency constraints,
 -- or flag-dependent dependency trees.
-data FlaggedDep qpn =
-    -- | Dependencies which are conditional on a flag choice.
+data FlaggedDep qpn
+  = -- | Dependencies which are conditional on a flag choice.
     Flagged (FN qpn) FInfo (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn)
-    -- | Dependencies which are conditional on whether or not a stanza
+  | -- | Dependencies which are conditional on whether or not a stanza
     -- (e.g., a test suite or benchmark) is enabled.
-  | Stanza  (SN qpn)       (TrueFlaggedDeps qpn)
-    -- | Dependencies which are always enabled, for the component 'comp'.
-  | Simple (LDep qpn) Component
+    Stanza (SN qpn) (TrueFlaggedDeps qpn)
+  | -- | Dependencies which are always enabled, for the component 'comp'.
+    Simple (LDep qpn) Component
 
 -- | Conservatively flatten out flagged dependencies
 --
@@ -102,10 +106,10 @@ flattenFlaggedDeps = concatMap aux
   where
     aux :: FlaggedDep qpn -> [(LDep qpn, Component)]
     aux (Flagged _ _ t f) = flattenFlaggedDeps t ++ flattenFlaggedDeps f
-    aux (Stanza  _   t)   = flattenFlaggedDeps t
-    aux (Simple d c)      = [(d, c)]
+    aux (Stanza _ t) = flattenFlaggedDeps t
+    aux (Simple d c) = [(d, c)]
 
-type TrueFlaggedDeps  qpn = FlaggedDeps qpn
+type TrueFlaggedDeps qpn = FlaggedDeps qpn
 type FalseFlaggedDeps qpn = FlaggedDeps qpn
 
 -- | A 'Dep' labeled with the reason it was introduced.
@@ -119,11 +123,16 @@ data LDep qpn = LDep (DependencyReason qpn) (Dep qpn)
 -- | A dependency (constraint) associates a package name with a constrained
 -- instance. It can also represent other types of dependencies, such as
 -- dependencies on language extensions.
-data Dep qpn = Dep (PkgComponent qpn) CI  -- ^ dependency on a package component
-             | Ext Extension              -- ^ dependency on a language extension
-             | Lang Language              -- ^ dependency on a language version
-             | Pkg PkgconfigName PkgconfigVersionRange  -- ^ dependency on a pkg-config package
-  deriving Functor
+data Dep qpn
+  = -- | dependency on a package component
+    Dep (PkgComponent qpn) CI
+  | -- | dependency on a language extension
+    Ext Extension
+  | -- | dependency on a language version
+    Lang Language
+  | -- | dependency on a pkg-config package
+    Pkg PkgconfigName PkgconfigVersionRange
+  deriving (Functor)
 
 -- | An exposed component within a package. This type is used to represent
 -- build-depends and build-tool-depends dependencies.
@@ -132,8 +141,8 @@ data PkgComponent qpn = PkgComponent qpn ExposedComponent
 
 -- | A component that can be depended upon by another package, i.e., a library
 -- or an executable.
-data ExposedComponent =
-    ExposedLib LibraryName
+data ExposedComponent
+  = ExposedLib LibraryName
   | ExposedExe UnqualComponentName
   deriving (Eq, Ord, Show)
 
@@ -147,43 +156,25 @@ data DependencyReason qpn = DependencyReason qpn (Map Flag FlagValue) (S.Set Sta
 -- | Print the reason that a dependency was introduced.
 showDependencyReason :: DependencyReason QPN -> String
 showDependencyReason (DependencyReason qpn flags stanzas) =
-    intercalate " " $
-        showQPN qpn
+  intercalate " " $
+    showQPN qpn
       : map (uncurry showFlagValue) (M.toList flags)
-     ++ map (\s -> showSBool s True) (S.toList stanzas)
-
--- | Options for goal qualification (used in 'qualifyDeps')
---
--- See also 'defaultQualifyOptions'
-data QualifyOptions = QO {
-    -- | Do we have a version of base relying on another version of base?
-    qoBaseShim :: Bool
-
-    -- Should dependencies of the setup script be treated as independent?
-  , qoSetupIndependent :: Bool
-  }
-  deriving Show
+      ++ map (\s -> showSBool s True) (S.toList stanzas)
 
 -- | Apply built-in rules for package qualifiers
 --
--- Although the behaviour of 'qualifyDeps' depends on the 'QualifyOptions',
--- it is important that these 'QualifyOptions' are _static_. Qualification
--- does NOT depend on flag assignment; in other words, it behaves the same no
--- matter which choices the solver makes (modulo the global 'QualifyOptions');
--- we rely on this in 'linkDeps' (see comment there).
---
 -- NOTE: It's the _dependencies_ of a package that may or may not be independent
 -- from the package itself. Package flag choices must of course be consistent.
-qualifyDeps :: QualifyOptions -> QPN -> FlaggedDeps PN -> FlaggedDeps QPN
-qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go
+qualifyDeps :: QPN -> FlaggedDeps PN -> FlaggedDeps QPN
+qualifyDeps (Q pp@(PackagePath ns q) pn) = go
   where
     go :: FlaggedDeps PN -> FlaggedDeps QPN
     go = map go1
 
     go1 :: FlaggedDep PN -> FlaggedDep QPN
     go1 (Flagged fn nfo t f) = Flagged (fmap (Q pp) fn) nfo (go t) (go f)
-    go1 (Stanza  sn     t)   = Stanza  (fmap (Q pp) sn)     (go t)
-    go1 (Simple dep comp)    = Simple (goLDep dep comp) comp
+    go1 (Stanza sn t) = Stanza (fmap (Q pp) sn) (go t)
+    go1 (Simple dep comp) = Simple (goLDep dep comp) comp
 
     -- Suppose package B has a setup dependency on package A.
     -- This will be recorded as something like
@@ -197,15 +188,14 @@ qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go
     goLDep (LDep dr dep) comp = LDep (fmap (Q pp) dr) (goD dep comp)
 
     goD :: Dep PN -> Component -> Dep QPN
-    goD (Ext  ext)    _    = Ext  ext
-    goD (Lang lang)   _    = Lang lang
-    goD (Pkg pkn vr)  _    = Pkg pkn vr
+    goD (Ext ext) _ = Ext ext
+    goD (Lang lang) _ = Lang lang
+    goD (Pkg pkn vr) _ = Pkg pkn vr
     goD (Dep dep@(PkgComponent qpn (ExposedExe _)) ci) _ =
-        Dep (Q (PackagePath ns (QualExe pn qpn)) <$> dep) ci
-    goD (Dep dep@(PkgComponent qpn (ExposedLib _)) ci) comp
-      | qBase qpn   = Dep (Q (PackagePath ns (QualBase  pn)) <$> dep) ci
-      | qSetup comp = Dep (Q (PackagePath ns (QualSetup pn)) <$> dep) ci
-      | otherwise   = Dep (Q (PackagePath ns inheritedQ    ) <$> dep) ci
+      Dep (Q (PackagePath ns (QualExe pn qpn)) <$> dep) ci
+    goD (Dep dep@(PkgComponent _qpn (ExposedLib _)) ci) comp
+      | comp == ComponentSetup = Dep (Q (PackagePath ns (QualSetup pn)) <$> dep) ci
+      | otherwise = Dep (Q (PackagePath ns inheritedQ) <$> dep) ci
 
     -- If P has a setup dependency on Q, and Q has a regular dependency on R, then
     -- we say that the 'Setup' qualifier is inherited: P has an (indirect) setup
@@ -216,18 +206,10 @@ qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go
     -- a detailed discussion.
     inheritedQ :: Qualifier
     inheritedQ = case q of
-                   QualSetup _  -> q
-                   QualExe _ _  -> q
-                   QualToplevel -> q
-                   QualBase _   -> QualToplevel
-
-    -- Should we qualify this goal with the 'Base' package path?
-    qBase :: PN -> Bool
-    qBase dep = qoBaseShim && unPackageName dep == "base"
-
-    -- Should we qualify this goal with the 'Setup' package path?
-    qSetup :: Component -> Bool
-    qSetup comp = qoSetupIndependent && comp == ComponentSetup
+      QualSetup _ -> q
+      QualExe _ _ -> q
+      QualToplevel -> q
+      QualBase _ -> QualToplevel
 
 -- | Remove qualifiers from set of dependencies
 --
@@ -244,8 +226,8 @@ unqualifyDeps = go
 
     go1 :: FlaggedDep QPN -> FlaggedDep PN
     go1 (Flagged fn nfo t f) = Flagged (fmap unq fn) nfo (go t) (go f)
-    go1 (Stanza  sn     t)   = Stanza  (fmap unq sn)     (go t)
-    go1 (Simple dep comp)    = Simple (goLDep dep) comp
+    go1 (Stanza sn t) = Stanza (fmap unq sn) (go t)
+    go1 (Simple dep comp) = Simple (goLDep dep) comp
 
     goLDep :: LDep QPN -> LDep PN
     goLDep (LDep dr dep) = LDep (fmap unq dr) (fmap unq dep)
@@ -271,8 +253,8 @@ data Goal qpn = Goal (Var qpn) (GoalReason qpn)
   deriving (Eq, Show, Functor)
 
 -- | Reason why a goal is being added to a goal set.
-data GoalReason qpn =
-    UserGoal                              -- introduced by a build target
+data GoalReason qpn
+  = UserGoal -- introduced by a build target
   | DependencyGoal (DependencyReason qpn) -- introduced by a package
   deriving (Eq, Show, Functor)
 
@@ -288,7 +270,7 @@ varToConflictSet = CS.singleton
 -- | Convert a 'GoalReason' to a 'ConflictSet' that can be used when the goal
 -- leads to a conflict.
 goalReasonToConflictSet :: GoalReason QPN -> ConflictSet
-goalReasonToConflictSet UserGoal            = CS.empty
+goalReasonToConflictSet UserGoal = CS.empty
 goalReasonToConflictSet (DependencyGoal dr) = dependencyReasonToConflictSet dr
 
 -- | Convert a 'GoalReason' to a 'ConflictSet' containing the reason that the
@@ -302,14 +284,14 @@ goalReasonToConflictSetWithConflict :: QPN -> GoalReason QPN -> ConflictSet
 goalReasonToConflictSetWithConflict goal (DependencyGoal (DependencyReason qpn flags stanzas))
   | M.null flags && S.null stanzas =
       CS.singletonWithConflict (P qpn) $ CS.GoalConflict goal
-goalReasonToConflictSetWithConflict _    gr = goalReasonToConflictSet gr
+goalReasonToConflictSetWithConflict _ gr = goalReasonToConflictSet gr
 
 -- | This function returns the solver variables responsible for the dependency.
 -- It drops the values chosen for flag and stanza variables, which are only
 -- needed for log messages.
 dependencyReasonToConflictSet :: DependencyReason QPN -> ConflictSet
 dependencyReasonToConflictSet (DependencyReason qpn flags stanzas) =
-    CS.fromList $ P qpn : flagVars ++ map stanzaToVar (S.toList stanzas)
+  CS.fromList $ P qpn : flagVars ++ map stanzaToVar (S.toList stanzas)
   where
     -- Filter out any flags that introduced the dependency with both values.
     -- They don't need to be included in the conflict set, because changing the
@@ -327,16 +309,19 @@ dependencyReasonToConflictSet (DependencyReason qpn flags stanzas) =
 -- This function currently only specifies the reason for the conflict in the
 -- simple case where the 'DependencyReason' does not involve any flags or
 -- stanzas. Otherwise, it falls back to calling 'dependencyReasonToConflictSet'.
-dependencyReasonToConflictSetWithVersionConstraintConflict :: QPN
-                                                           -> Ver
-                                                           -> DependencyReason QPN
-                                                           -> ConflictSet
 dependencyReasonToConflictSetWithVersionConstraintConflict
-    dependency excludedVersion dr@(DependencyReason qpn flags stanzas)
-  | M.null flags && S.null stanzas =
-    CS.singletonWithConflict (P qpn) $
-    CS.VersionConstraintConflict dependency excludedVersion
-  | otherwise = dependencyReasonToConflictSet dr
+  :: QPN
+  -> Ver
+  -> DependencyReason QPN
+  -> ConflictSet
+dependencyReasonToConflictSetWithVersionConstraintConflict
+  dependency
+  excludedVersion
+  dr@(DependencyReason qpn flags stanzas)
+    | M.null flags && S.null stanzas =
+        CS.singletonWithConflict (P qpn) $
+          CS.VersionConstraintConflict dependency excludedVersion
+    | otherwise = dependencyReasonToConflictSet dr
 
 -- | Convert a 'DependencyReason' to a 'ConflictSet' specifying that the
 -- conflict occurred because the conflict set variables introduced a version of
@@ -346,13 +331,16 @@ dependencyReasonToConflictSetWithVersionConstraintConflict
 -- This function currently only specifies the reason for the conflict in the
 -- simple case where the 'DependencyReason' does not involve any flags or
 -- stanzas. Otherwise, it falls back to calling 'dependencyReasonToConflictSet'.
-dependencyReasonToConflictSetWithVersionConflict :: QPN
-                                                 -> CS.OrderedVersionRange
-                                                 -> DependencyReason QPN
-                                                 -> ConflictSet
 dependencyReasonToConflictSetWithVersionConflict
-    pkgWithVersionConstraint constraint dr@(DependencyReason qpn flags stanzas)
-  | M.null flags && S.null stanzas =
-    CS.singletonWithConflict (P qpn) $
-    CS.VersionConflict pkgWithVersionConstraint constraint
-  | otherwise = dependencyReasonToConflictSet dr
+  :: QPN
+  -> CS.OrderedVersionRange
+  -> DependencyReason QPN
+  -> ConflictSet
+dependencyReasonToConflictSetWithVersionConflict
+  pkgWithVersionConstraint
+  constraint
+  dr@(DependencyReason qpn flags stanzas)
+    | M.null flags && S.null stanzas =
+        CS.singletonWithConflict (P qpn) $
+          CS.VersionConflict pkgWithVersionConstraint constraint
+    | otherwise = dependencyReasonToConflictSet dr
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Explore.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Explore.hs
index 90038a28f5c..8dfa9c88bf3 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Explore.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Explore.hs
@@ -268,9 +268,9 @@ exploreLog mbj enableBj fineGrainedConflicts (CountConflicts countConflicts) idx
     -- Skipping it is an optimization. If false, it returns a new conflict set
     -- to be merged with the previous one.
     couldResolveConflicts :: QPN -> POption -> S.Set CS.Conflict -> Maybe ConflictSet
-    couldResolveConflicts currentQPN@(Q _ pn) (POption i@(I v _) _) conflicts =
+    couldResolveConflicts currentQPN@(Q _ pn) (POption i@(I _stage v _) _) conflicts =
       let (PInfo deps _ _ _) = idx M.! pn M.! i
-          qdeps = qualifyDeps (defaultQualifyOptions idx) currentQPN deps
+          qdeps = qualifyDeps currentQPN deps
 
           couldBeResolved :: CS.Conflict -> Maybe ConflictSet
           couldBeResolved CS.OtherConflict = Nothing
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs
index 2f28d12de85..1f84e0ba161 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs
@@ -4,7 +4,6 @@ module Distribution.Solver.Modular.Index
     , ComponentInfo(..)
     , IsVisible(..)
     , IsBuildable(..)
-    , defaultQualifyOptions
     , mkIndex
     ) where
 
@@ -58,17 +57,3 @@ mkIndex xs = M.map M.fromList (groupMap (L.map (\ (pn, i, pi) -> (pn, (i, pi)))
 groupMap :: Ord a => [(a, b)] -> Map a [b]
 groupMap xs = M.fromListWith (flip (++)) (L.map (\ (x, y) -> (x, [y])) xs)
 
-defaultQualifyOptions :: Index -> QualifyOptions
-defaultQualifyOptions idx = QO {
-      qoBaseShim         = or [ dep == base
-                              | -- Find all versions of base ..
-                                Just is <- [M.lookup base idx]
-                                -- .. which are installed ..
-                              , (I _ver (Inst _), PInfo deps _comps _flagNfo _fr) <- M.toList is
-                                -- .. and flatten all their dependencies ..
-                              , (LDep _ (Dep (PkgComponent dep _) _ci), _comp) <- flattenFlaggedDeps deps
-                              ]
-    , qoSetupIndependent = True
-    }
-  where
-    base = mkPackageName "base"
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs
index 72d0b8193e3..f91b425f593 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs
@@ -14,6 +14,7 @@ import qualified Distribution.InstalledPackageInfo as IPI
 import Distribution.Compiler
 import Distribution.Package                          -- from Cabal
 import Distribution.Simple.BuildToolDepends          -- from Cabal
+import Distribution.Simple.Compiler (compilerInfo)
 import Distribution.Types.ExeDependency              -- from Cabal
 import Distribution.Types.PkgconfigDependency        -- from Cabal
 import Distribution.Types.ComponentName              -- from Cabal
@@ -23,6 +24,7 @@ import Distribution.Types.MungedPackageName          -- from Cabal
 import Distribution.PackageDescription               -- from Cabal
 import Distribution.PackageDescription.Configuration
 import qualified Distribution.Simple.PackageIndex as SI
+import Distribution.Simple.Compiler (compilerId)
 import Distribution.System
 
 import           Distribution.Solver.Types.ComponentDeps
@@ -34,6 +36,7 @@ import           Distribution.Solver.Types.PackageConstraint
 import qualified Distribution.Solver.Types.PackageIndex as CI
 import           Distribution.Solver.Types.Settings
 import           Distribution.Solver.Types.SourcePackage
+import           Distribution.Solver.Types.Toolchain ( Toolchains(..), Toolchain(..) )
 
 import Distribution.Solver.Modular.Dependency as D
 import Distribution.Solver.Modular.Flag as F
@@ -53,21 +56,26 @@ import Distribution.Solver.Modular.Version
 -- resolving these situations. However, the right thing to do is to
 -- fix the problem there, so for now, shadowing is only activated if
 -- explicitly requested.
-convPIs :: OS -> Arch -> CompilerInfo -> Map PN [LabeledPackageConstraint]
+convPIs :: Toolchains -> Map PN [LabeledPackageConstraint]
         -> ShadowPkgs -> StrongFlags -> SolveExecutables
-        -> SI.InstalledPackageIndex -> CI.PackageIndex (SourcePackage loc)
+        -> SI.InstalledPackageIndex -- ^ build
+        -> SI.InstalledPackageIndex -- ^ host
+        -> CI.PackageIndex (SourcePackage loc)
         -> Index
-convPIs os arch comp constraints sip strfl solveExes iidx sidx =
-  mkIndex $
-  convIPI' sip iidx ++ convSPI' os arch comp constraints strfl solveExes sidx
+convPIs toolchains constraints sip strfl solveExes biidx iidx sidx =
+  mkIndex $ bipis ++ hipis ++ spis
+  where bipis = convIPI' toolchains sip biidx
+        hipis = convIPI' toolchains sip iidx
+        ipis = bipis ++ hipis
+        spis = convSPI' toolchains constraints strfl solveExes sidx
 
 -- | Convert a Cabal installed package index to the simpler,
 -- more uniform index format of the solver.
-convIPI' :: ShadowPkgs -> SI.InstalledPackageIndex -> [(PN, I, PInfo)]
-convIPI' (ShadowPkgs sip) idx =
+convIPI' :: Toolchains -> ShadowPkgs -> SI.InstalledPackageIndex -> [(PN, I, PInfo)]
+convIPI' toolchains (ShadowPkgs sip) idx =
     -- apply shadowing whenever there are multiple installed packages with
     -- the same version
-    [ maybeShadow (convIP idx pkg)
+    [ maybeShadow (convIP toolchains idx pkg)
     -- IMPORTANT to get internal libraries. See
     -- Note [Index conversion with internal libraries]
     | (_, pkgs) <- SI.allPackagesBySourcePackageIdAndLibName idx
@@ -80,16 +88,20 @@ convIPI' (ShadowPkgs sip) idx =
     shadow x                                     = x
 
 -- | Extract/recover the package ID from an installed package info, and convert it to a solver's I.
-convId :: IPI.InstalledPackageInfo -> (PN, I)
-convId ipi = (pn, I ver $ Inst $ IPI.installedUnitId ipi)
-  where MungedPackageId mpn ver = mungedId ipi
+convId :: Toolchains -> IPI.InstalledPackageInfo -> (PN, I)
+convId toolchains ipi = (pn, I stage ver $ Inst $ IPI.installedUnitId ipi)
+  where MungedPackageId mpn ver compid = mungedId ipi
         -- HACK. See Note [Index conversion with internal libraries]
         pn = encodeCompatPackageName mpn
+        stage = case IPI.pkgCompiler ipi of
+          Just c | c == compilerId (toolchainCompiler (buildToolchain toolchains)) -> Build
+          Just c | c == compilerId (toolchainCompiler (hostToolchain toolchains)) -> Host
+          _ -> error "convId: unhandled compiler"
 
 -- | Convert a single installed package into the solver-specific format.
-convIP :: SI.InstalledPackageIndex -> IPI.InstalledPackageInfo -> (PN, I, PInfo)
-convIP idx ipi =
-  case traverse (convIPId (DependencyReason pn M.empty S.empty) comp idx) (IPI.depends ipi) of
+convIP :: Toolchains -> SI.InstalledPackageIndex -> IPI.InstalledPackageInfo -> (PN, I, PInfo)
+convIP toolchains idx ipi =
+  case traverse (convIPId toolchains (DependencyReason pn M.empty S.empty) comp idx) (IPI.depends ipi) of
         Left u    -> (pn, i, PInfo [] M.empty M.empty (Just (Broken u)))
         Right fds -> (pn, i, PInfo fds components M.empty Nothing)
  where
@@ -101,7 +113,7 @@ convIP idx ipi =
                     , compIsBuildable = IsBuildable True
                     }
 
-  (pn, i) = convId ipi
+  (pn, i) = convId toolchains ipi
 
   -- 'sourceLibName' is unreliable, but for now we only really use this for
   -- primary libs anyways
@@ -141,11 +153,11 @@ convIP idx ipi =
 -- May return Nothing if the package can't be found in the index. That
 -- indicates that the original package having this dependency is broken
 -- and should be ignored.
-convIPId :: DependencyReason PN -> Component -> SI.InstalledPackageIndex -> UnitId -> Either UnitId (FlaggedDep PN)
-convIPId dr comp idx ipid =
+convIPId :: Toolchains -> DependencyReason PN -> Component -> SI.InstalledPackageIndex -> UnitId -> Either UnitId (FlaggedDep PN)
+convIPId toolchains dr comp idx ipid =
   case SI.lookupUnitId idx ipid of
     Nothing  -> Left ipid
-    Just ipi -> let (pn, i) = convId ipi
+    Just ipi -> let (pn, i) = convId toolchains ipi
                     name = ExposedLib LMainLibName  -- TODO: Handle sub-libraries.
                 in  Right (D.Simple (LDep dr (Dep (PkgComponent pn name) (Fixed i))) comp)
                 -- NB: something we pick up from the
@@ -153,31 +165,34 @@ convIPId dr comp idx ipid =
 
 -- | Convert a cabal-install source package index to the simpler,
 -- more uniform index format of the solver.
-convSPI' :: OS -> Arch -> CompilerInfo -> Map PN [LabeledPackageConstraint]
+convSPI' :: Toolchains -> Map PN [LabeledPackageConstraint]
          -> StrongFlags -> SolveExecutables
          -> CI.PackageIndex (SourcePackage loc) -> [(PN, I, PInfo)]
-convSPI' os arch cinfo constraints strfl solveExes =
-    L.map (convSP os arch cinfo constraints strfl solveExes) . CI.allPackages
+convSPI' toolchains constraints strfl solveExes =
+    L.concatMap (convSP toolchains constraints strfl solveExes) . CI.allPackages
 
 -- | Convert a single source package into the solver-specific format.
-convSP :: OS -> Arch -> CompilerInfo -> Map PN [LabeledPackageConstraint]
-       -> StrongFlags -> SolveExecutables -> SourcePackage loc -> (PN, I, PInfo)
-convSP os arch cinfo constraints strfl solveExes (SourcePackage (PackageIdentifier pn pv) gpd _ _pl) =
-  let i = I pv InRepo
-      pkgConstraints = fromMaybe [] $ M.lookup pn constraints
-  in  (pn, i, convGPD os arch cinfo pkgConstraints strfl solveExes pn gpd)
+convSP :: Toolchains -> Map PN [LabeledPackageConstraint]
+       -> StrongFlags -> SolveExecutables -> SourcePackage loc -> [(PN, I, PInfo)]
+convSP toolchains constraints strfl solveExes (SourcePackage (PackageIdentifier pn pv _compid) gpd _ _pl) =
+  let pkgConstraints = fromMaybe [] $ M.lookup pn constraints
+  in  [(pn, I Host  pv InRepo, convGPD (hostToolchain toolchains)  pkgConstraints strfl solveExes pn gpd)
+      ,(pn, I Build pv InRepo, convGPD (buildToolchain toolchains) pkgConstraints strfl solveExes pn gpd)
+      ]
 
 -- We do not use 'flattenPackageDescription' or 'finalizePD'
 -- from 'Distribution.PackageDescription.Configuration' here, because we
 -- want to keep the condition tree, but simplify much of the test.
 
 -- | Convert a generic package description to a solver-specific 'PInfo'.
-convGPD :: OS -> Arch -> CompilerInfo -> [LabeledPackageConstraint]
+convGPD :: Toolchain -> [LabeledPackageConstraint]
         -> StrongFlags -> SolveExecutables -> PN -> GenericPackageDescription
         -> PInfo
-convGPD os arch cinfo constraints strfl solveExes pn
+convGPD toolchain constraints strfl solveExes pn
         (GenericPackageDescription pkg scannedVersion flags mlib sub_libs flibs exes tests benchs) =
   let
+    cinfo = compilerInfo (toolchainCompiler toolchain)
+    (Platform arch os) = toolchainPlatform toolchain
     fds  = flagInfo strfl flags
 
 
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Linking.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Linking.hs
index 15514472c80..ead3e10c6d4 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Linking.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Linking.hs
@@ -61,8 +61,6 @@ data ValidateState = VS {
     , vsLinks    :: Map QPN LinkGroup
     , vsFlags    :: FAssignment
     , vsStanzas  :: SAssignment
-    , vsQualifyOptions :: QualifyOptions
-
     -- Saved qualified dependencies. Every time 'validateLinking' makes a
     -- package choice, it qualifies the package's dependencies and saves them in
     -- this map. Then the qualified dependencies are available for subsequent
@@ -101,7 +99,7 @@ validateLinking index = (`runReader` initVS) . go
     goP qpn@(Q _pp pn) opt@(POption i _) r = do
       vs <- ask
       let PInfo deps _ _ _ = vsIndex vs ! pn ! i
-          qdeps            = qualifyDeps (vsQualifyOptions vs) qpn deps
+          qdeps            = qualifyDeps qpn deps
           newSaved         = M.insert qpn qdeps (vsSaved vs)
       case execUpdateState (pickPOption qpn opt qdeps) vs of
         Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)
@@ -129,7 +127,6 @@ validateLinking index = (`runReader` initVS) . go
       , vsLinks   = M.empty
       , vsFlags   = M.empty
       , vsStanzas = M.empty
-      , vsQualifyOptions = defaultQualifyOptions index
       , vsSaved   = M.empty
       }
 
@@ -275,8 +272,7 @@ linkDeps target = \deps -> do
 
     requalify :: FlaggedDeps QPN -> UpdateState (FlaggedDeps QPN)
     requalify deps = do
-      vs <- get
-      return $ qualifyDeps (vsQualifyOptions vs) target (unqualifyDeps deps)
+      return $ qualifyDeps target (unqualifyDeps deps)
 
 pickFlag :: QFN -> Bool -> UpdateState ()
 pickFlag qfn b = do
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs
index ccd0e4d4a70..3d2b3026c92 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DeriveFunctor #-}
 module Distribution.Solver.Modular.Package
   ( I(..)
+  , Stage(..)
   , Loc(..)
   , PackageId
   , PackageIdentifier(..)
@@ -11,8 +12,6 @@ module Distribution.Solver.Modular.Package
   , QPV
   , instI
   , makeIndependent
-  , primaryPP
-  , setupPP
   , showI
   , showPI
   , unPN
@@ -26,6 +25,7 @@ import Distribution.Pretty (prettyShow)
 
 import Distribution.Solver.Modular.Version
 import Distribution.Solver.Types.PackagePath
+import Distribution.Solver.Types.Stage (Stage (..))
 
 -- | A package name.
 type PN = PackageName
@@ -51,14 +51,18 @@ type PId = UnitId
 data Loc = Inst PId | InRepo
   deriving (Eq, Ord, Show)
 
+showStage :: Stage -> String
+showStage Build = "[build]"
+showStage Host  = "[host ]"
+
 -- | Instance. A version number and a location.
-data I = I Ver Loc
+data I = I Stage Ver Loc
   deriving (Eq, Ord, Show)
 
 -- | String representation of an instance.
 showI :: I -> String
-showI (I v InRepo)   = showVer v
-showI (I v (Inst uid)) = showVer v ++ "/installed" ++ extractPackageAbiHash uid
+showI (I s v InRepo)     = showStage s ++ showVer v
+showI (I s v (Inst uid)) = showStage s ++ showVer v ++ "/installed" ++ extractPackageAbiHash uid
   where
     extractPackageAbiHash xs =
       case first reverse $ break (=='-') $ reverse (prettyShow xs) of
@@ -74,31 +78,8 @@ showPI :: PI QPN -> String
 showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI i
 
 instI :: I -> Bool
-instI (I _ (Inst _)) = True
-instI _              = False
-
--- | Is the package in the primary group of packages.  This is used to
--- determine (1) if we should try to establish stanza preferences
--- for this goal, and (2) whether or not a user specified @--constraint@
--- should apply to this dependency (grep 'primaryPP' to see the
--- use sites).  In particular this does not include packages pulled in
--- as setup deps.
---
-primaryPP :: PackagePath -> Bool
-primaryPP (PackagePath _ns q) = go q
-  where
-    go QualToplevel    = True
-    go (QualBase  _)   = True
-    go (QualSetup _)   = False
-    go (QualExe _ _)   = False
-
--- | Is the package a dependency of a setup script.  This is used to
--- establish whether or not certain constraints should apply to this
--- dependency (grep 'setupPP' to see the use sites).
---
-setupPP :: PackagePath -> Bool
-setupPP (PackagePath _ns (QualSetup _)) = True
-setupPP (PackagePath _ns _)         = False
+instI (I _ _ (Inst _)) = True
+instI _                = False
 
 -- | Qualify a target package with its own name so that its dependencies are not
 -- required to be consistent with other targets.
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
index 9e0d5fb4d22..e17287324f5 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
@@ -15,6 +15,7 @@ module Distribution.Solver.Modular.Preference
     , onlyConstrained
     , sortGoals
     , pruneAfterFirstSuccess
+    , pruneHostFromSetup
     ) where
 
 import Prelude ()
@@ -72,7 +73,7 @@ addWeight :: (PN -> [Ver] -> POption -> Weight) -> EndoTreeTrav d c
 addWeight f = addWeights [f]
 
 version :: POption -> Ver
-version (POption (I v _) _) = v
+version (POption (I _ v _) _) = v
 
 -- | Prefer to link packages whenever possible.
 preferLinked :: EndoTreeTrav d c
@@ -139,8 +140,8 @@ preferPackagePreferences pcs =
 
     -- Prefer installed packages over non-installed packages.
     installed :: POption -> Weight
-    installed (POption (I _ (Inst _)) _) = 0
-    installed _                          = 1
+    installed (POption (I _ _ (Inst _)) _) = 0
+    installed _                            = 1
 
 -- | Traversal that tries to establish package stanza enable\/disable
 -- preferences. Works by reordering the branches of stanza choices.
@@ -184,7 +185,7 @@ processPackageConstraintP qpn c i (LabeledPackageConstraint (PackageConstraint s
     else r
   where
     go :: I -> PackageProperty -> Tree d c
-    go (I v _) (PackagePropertyVersion vr)
+    go (I _stage v _) (PackagePropertyVersion vr)
         | checkVR vr v  = r
         | otherwise     = Fail c (GlobalConstraintVersion vr src)
     go _       PackagePropertyInstalled
@@ -338,15 +339,36 @@ avoidReinstalls p = go
       | otherwise = PChoiceF qpn rdm gr cs
       where
         disableReinstalls =
-          let installed = [ v | (_, POption (I v (Inst _)) _, _) <- W.toList cs ]
+          let installed = [ v | (_, POption (I _stage v (Inst _)) _, _) <- W.toList cs ]
           in  W.mapWithKey (notReinstall installed) cs
 
-        notReinstall vs (POption (I v InRepo) _) _ | v `elem` vs =
+        notReinstall vs (POption (I _stage v InRepo) _) _ | v `elem` vs =
           Fail (varToConflictSet (P qpn)) CannotReinstall
         notReinstall _ _ x =
           x
     go x          = x
 
+-- | Ensure that Setup (Build time) dependencies only have Build dependencies
+-- available and that Host dependencies only have Host dependencies available.
+-- We also do not want to use InRepo dependencies for setup/build-depends. This
+-- easily leads to cycles.
+pruneHostFromSetup :: EndoTreeTrav d c
+pruneHostFromSetup = go
+  where
+    -- for Setup(.hs) and build-depends, we want to force Build packages.
+    go (PChoiceF qpn rdm gr cs) | (Q (PackagePath _ (QualSetup _)) _) <- qpn =
+      PChoiceF qpn rdm gr (W.filterKey (not . isHost) cs)
+    -- QualExe are build-depends. Structure is QualExe (comp) (build-depend).
+    go (PChoiceF qpn rdm gr cs) | (Q (PackagePath _ (QualExe _ _)) _) <- qpn =
+      PChoiceF qpn rdm gr (W.filterKey (not . isHost) cs)
+    -- everything else use Host packages.
+    go (PChoiceF qpn rdm gr cs) | (Q (PackagePath _ _) _) <- qpn =
+      PChoiceF qpn rdm gr (W.filterKey isHost cs)
+    go x = x
+
+    isHost :: POption -> Bool
+    isHost (POption (I s _v _l) _) = s == Host
+
 -- | Require all packages to be mentioned in a constraint or as a goal.
 onlyConstrained :: (PN -> Bool) -> EndoTreeTrav d QGoalReason
 onlyConstrained p = go
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
index b2c89fc1537..d519456143f 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
@@ -24,6 +24,7 @@ import Distribution.Solver.Types.PackagePreferences
 import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb)
 import Distribution.Solver.Types.LabeledPackageConstraint
 import Distribution.Solver.Types.Settings
+import Distribution.Solver.Types.Toolchain ( Toolchains(..), Toolchain(..), buildIsHost )
 import Distribution.Solver.Types.Variable
 
 import Distribution.Solver.Modular.Assignment
@@ -44,6 +45,9 @@ import Distribution.Solver.Modular.Tree
 import qualified Distribution.Solver.Modular.PSQ as PSQ
 
 import Distribution.Simple.Setup (BooleanFlag(..))
+import Distribution.Simple.Compiler (compilerInfo)
+
+import qualified Distribution.Solver.Modular.WeightedPSQ as W
 
 #ifdef DEBUG_TRACETREE
 import qualified Distribution.Solver.Modular.ConflictSet as CS
@@ -89,18 +93,19 @@ newtype PruneAfterFirstSuccess = PruneAfterFirstSuccess Bool
 -- before exploration.
 --
 solve :: SolverConfig                         -- ^ solver parameters
-      -> CompilerInfo
+      -> Toolchains
       -> Index                                -- ^ all available packages as an index
       -> Maybe PkgConfigDb                    -- ^ available pkg-config pkgs
       -> (PN -> PackagePreferences)           -- ^ preferences
       -> M.Map PN [LabeledPackageConstraint]  -- ^ global constraints
       -> S.Set PN                             -- ^ global goals
       -> RetryLog Message SolverFailure (Assignment, RevDepMap)
-solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
+solve sc toolchains idx pkgConfigDB userPrefs userConstraints userGoals =
   explorePhase      .
   traceTree "cycles.json" id .
   detectCycles      .
   traceTree "heuristics.json" id .
+  -- stageBuildDeps "post-pref: " .
   trav (
    heuristicsPhase  .
    preferencesPhase .
@@ -110,6 +115,9 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
   validationCata    .
   traceTree "pruned.json" id .
   trav prunePhase   .
+  -- stageBuildDeps "post-prune: " .
+  (if buildIsHost toolchains then id else trav P.pruneHostFromSetup) .
+  -- stageBuildDeps "build: " .
   traceTree "build.json" id $
   buildPhase
   where
@@ -137,7 +145,7 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
                        P.enforceManualFlags userConstraints
     validationCata   = P.enforceSingleInstanceRestriction .
                        validateLinking idx .
-                       validateTree cinfo idx pkgConfigDB
+                       validateTree (compilerInfo (toolchainCompiler (hostToolchain toolchains))) idx pkgConfigDB
     prunePhase       = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) .
                        (case onlyConstrained sc of
                           OnlyConstrainedAll ->
@@ -146,6 +154,32 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
                             id)
     buildPhase       = buildTree idx (independentGoals sc) (S.toList userGoals)
 
+    stageBuildDeps prefix = go
+      where go :: Tree d c -> Tree d c
+            -- For Setup we must use the build compiler, as the host compiler
+            -- may not be able to produce code that runs on the build machine.
+            go (PChoice qpn rdm gr cs) | (Q (PackagePath _ (QualSetup _)) _) <- qpn =
+              (PChoice qpn rdm gr (trace (prefix ++ show qpn ++ '\n':unlines (map (" - " ++) candidates)) (go <$> cs)))
+              where candidates = map show
+                              --  . filter (\(I _s _v l) -> l /= InRepo)
+                               . map (\(_w, (POption i _), _v) -> i) $ W.toList cs
+            -- Same for build-depends. These show up as QualExe (component) (build-depends).
+            go (PChoice qpn rdm gr cs) | (Q (PackagePath _ (QualExe _ _)) _) <- qpn =
+              (PChoice qpn rdm gr (trace (prefix ++ show qpn ++ '\n':unlines (map (" - " ++) candidates)) (go <$> cs)))
+              where candidates = map show
+                              --  . filter (\(I _s _v l) -> l /= InRepo)
+                               . map (\(_w, (POption i _), _v) -> i) $ W.toList cs
+            go (PChoice qpn rdm gr cs) =
+              (PChoice qpn rdm gr (go <$> cs))
+            go (FChoice qfn rdm gr t b d cs) =
+              FChoice qfn rdm gr t b d (go <$> cs)
+            go (SChoice qsn rdm gr t cs) =
+              SChoice qsn rdm gr t (go <$> cs)
+            go (GoalChoice rdm cs) =
+              GoalChoice rdm (go <$> cs)
+            go x@(Fail _ _) = x
+            go x@(Done _ _) = x
+
     allExplicit = M.keysSet userConstraints `S.union` userGoals
 
     pkgIsExplicit :: PN -> Bool
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs
index 4af149b31cf..e0ad47e32b8 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs
@@ -108,9 +108,7 @@ data ValidateState = VS {
 
   -- Map from package name to the components that are required from that
   -- package.
-  requiredComponents  :: Map QPN ComponentDependencyReasons,
-
-  qualifyOptions      :: QualifyOptions
+  requiredComponents  :: Map QPN ComponentDependencyReasons
 }
 
 newtype Validate a = Validate (Reader ValidateState a)
@@ -200,11 +198,10 @@ validate = go
       svd            <- asks saved -- obtain saved dependencies
       aComps         <- asks availableComponents
       rComps         <- asks requiredComponents
-      qo             <- asks qualifyOptions
       -- obtain dependencies and index-dictated exclusions introduced by the choice
       let (PInfo deps comps _ mfr) = idx ! pn ! i
       -- qualify the deps in the current scope
-      let qdeps = qualifyDeps qo qpn deps
+      let qdeps = qualifyDeps qpn deps
       -- the new active constraints are given by the instance we have chosen,
       -- plus the dependency information we have for that instance
       let newactives = extractAllDeps pfa psa qdeps
@@ -452,14 +449,14 @@ merge (MergedDepFixed comp1 vs1 i1) (PkgDep vs2 (PkgComponent p comp2) ci@(Fixed
            , ( ConflictingDep vs1 (PkgComponent p comp1) (Fixed i1)
              , ConflictingDep vs2 (PkgComponent p comp2) ci ) )
 
-merge (MergedDepFixed comp1 vs1 i@(I v _)) (PkgDep vs2 (PkgComponent p comp2) ci@(Constrained vr))
+merge (MergedDepFixed comp1 vs1 i@(I _stage v _)) (PkgDep vs2 (PkgComponent p comp2) ci@(Constrained vr))
   | checkVR vr v = Right $ MergedDepFixed comp1 vs1 i
   | otherwise    =
       Left ( createConflictSetForVersionConflict p v vs1 vr vs2
            , ( ConflictingDep vs1 (PkgComponent p comp1) (Fixed i)
              , ConflictingDep vs2 (PkgComponent p comp2) ci ) )
 
-merge (MergedDepConstrained vrOrigins) (PkgDep vs2 (PkgComponent p comp2) ci@(Fixed i@(I v _))) =
+merge (MergedDepConstrained vrOrigins) (PkgDep vs2 (PkgComponent p comp2) ci@(Fixed i@(I _stage v _))) =
     go vrOrigins -- I tried "reverse vrOrigins" here, but it seems to slow things down ...
   where
     go :: [VROrigin] -> Either (ConflictSet, (ConflictingDep, ConflictingDep)) MergedPkgDep
@@ -577,5 +574,4 @@ validateTree cinfo idx pkgConfigDb t = runValidate (validate t) VS {
   , pa                  = PA M.empty M.empty M.empty
   , availableComponents = M.empty
   , requiredComponents  = M.empty
-  , qualifyOptions      = defaultQualifyOptions idx
   }
diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/WeightedPSQ.hs b/cabal-install-solver/src/Distribution/Solver/Modular/WeightedPSQ.hs
index 94def7be196..747017148d7 100644
--- a/cabal-install-solver/src/Distribution/Solver/Modular/WeightedPSQ.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Modular/WeightedPSQ.hs
@@ -9,6 +9,7 @@ module Distribution.Solver.Modular.WeightedPSQ (
   , weights
   , isZeroOrOne
   , filter
+  , filterKey
   , lookup
   , mapWithKey
   , mapWeightsWithKey
@@ -34,6 +35,10 @@ newtype WeightedPSQ w k v = WeightedPSQ [(w, k, v)]
 filter :: (v -> Bool) -> WeightedPSQ k w v -> WeightedPSQ k w v
 filter p (WeightedPSQ xs) = WeightedPSQ (L.filter (p . triple_3) xs)
 
+-- | /O(N)/.
+filterKey :: (k -> Bool) -> WeightedPSQ w k v -> WeightedPSQ w k v
+filterKey p (WeightedPSQ xs) = WeightedPSQ (L.filter (p . triple_2) xs)
+
 -- | /O(1)/. Return @True@ if the @WeightedPSQ@ contains zero or one elements.
 isZeroOrOne :: WeightedPSQ w k v -> Bool
 isZeroOrOne (WeightedPSQ [])  = True
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs b/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs
index 139a6d2b33d..3306ac620de 100644
--- a/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Types/DependencyResolver.hs
@@ -16,6 +16,7 @@ import Distribution.Solver.Types.SourcePackage
 import Distribution.Simple.PackageIndex ( InstalledPackageIndex )
 import Distribution.Package ( PackageName )
 import Distribution.Compiler ( CompilerInfo )
+import Distribution.Solver.Types.Toolchain ( Toolchains )
 import Distribution.System ( Platform )
 
 -- | A dependency resolver is a function that works out an installation plan
@@ -26,9 +27,9 @@ import Distribution.System ( Platform )
 -- solving the package dependency problem and we want to make it easy to swap
 -- in alternatives.
 --
-type DependencyResolver loc = Platform
-                           -> CompilerInfo
-                           -> InstalledPackageIndex
+type DependencyResolver loc = Toolchains
+                           -> InstalledPackageIndex -- ^ build
+                           -> InstalledPackageIndex -- ^ host
                            -> PackageIndex (SourcePackage loc)
                            -> Maybe PkgConfigDb
                            -> (PackageName -> PackagePreferences)
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs b/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs
index 871a0dd15a9..868d1d9dfe2 100644
--- a/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs
@@ -8,6 +8,7 @@ import Prelude ()
 
 import Distribution.Package ( Package(..), HasMungedPackageId(..), HasUnitId(..) )
 import Distribution.Solver.Types.ComponentDeps ( ComponentDeps )
+import Distribution.Solver.Types.PackagePath (QPN)
 import Distribution.Solver.Types.SolverId
 import Distribution.Types.MungedPackageId
 import Distribution.Types.PackageId
@@ -17,6 +18,7 @@ import Distribution.InstalledPackageInfo (InstalledPackageInfo)
 -- | An 'InstSolverPackage' is a pre-existing installed package
 -- specified by the dependency solver.
 data InstSolverPackage = InstSolverPackage {
+      instSolverQPN :: QPN,
       instSolverPkgIPI :: InstalledPackageInfo,
       instSolverPkgLibDeps :: ComponentDeps [SolverId],
       instSolverPkgExeDeps :: ComponentDeps [SolverId]
@@ -29,8 +31,8 @@ instance Structured InstSolverPackage
 instance Package InstSolverPackage where
     packageId i =
         -- HACK! See Note [Index conversion with internal libraries]
-        let MungedPackageId mpn v = mungedId i
-        in PackageIdentifier (encodeCompatPackageName mpn) v
+        let MungedPackageId mpn v compid = mungedId i
+        in PackageIdentifier (encodeCompatPackageName mpn) v compid
 
 instance HasMungedPackageId InstSolverPackage where
     mungedId = mungedId . instSolverPkgIPI
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/PackageConstraint.hs b/cabal-install-solver/src/Distribution/Solver/Types/PackageConstraint.hs
index 06c5ae169fa..37c0d7dd1d7 100644
--- a/cabal-install-solver/src/Distribution/Solver/Types/PackageConstraint.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Types/PackageConstraint.hs
@@ -49,6 +49,9 @@ data ConstraintScope
      -- | The package with the specified name when it has a
      -- setup qualifier.
    | ScopeAnySetupQualifier PackageName
+     -- | The package with the specified name when it is a build-time
+     -- dependency. E.g. a Setup script or a build-depends.
+   | ScopeAnyBuildDepQualifier PackageName
      -- | The package with the specified name regardless of
      -- qualifier.
    | ScopeAnyQualifier PackageName
@@ -65,6 +68,7 @@ scopeToPackageName :: ConstraintScope -> PackageName
 scopeToPackageName (ScopeTarget pn) = pn
 scopeToPackageName (ScopeQualified _ pn) = pn
 scopeToPackageName (ScopeAnySetupQualifier pn) = pn
+scopeToPackageName (ScopeAnyBuildDepQualifier pn) = pn
 scopeToPackageName (ScopeAnyQualifier pn) = pn
 
 constraintScopeMatches :: ConstraintScope -> QPN -> Bool
@@ -74,6 +78,12 @@ constraintScopeMatches (ScopeTarget pn) (Q (PackagePath ns q) pn') =
   in namespaceMatches ns && q == QualToplevel && pn == pn'
 constraintScopeMatches (ScopeQualified q pn) (Q (PackagePath _ q') pn') =
     q == q' && pn == pn'
+constraintScopeMatches (ScopeAnyBuildDepQualifier pn) (Q pp pn') =
+  let setup (PackagePath _ (QualSetup _)) = True
+      setup _                             = False
+      build (PackagePath _ (QualExe _ _)) = True
+      build _                             = False
+  in (setup pp || build pp) && pn == pn'
 constraintScopeMatches (ScopeAnySetupQualifier pn) (Q pp pn') =
   let setup (PackagePath _ (QualSetup _)) = True
       setup _                             = False
@@ -83,6 +93,7 @@ constraintScopeMatches (ScopeAnyQualifier pn) (Q _ pn') = pn == pn'
 instance Pretty ConstraintScope where
   pretty (ScopeTarget pn) = pretty pn <<>> Disp.text "." <<>> pretty pn
   pretty (ScopeQualified q pn) = dispQualifier q <<>> pretty pn
+  pretty (ScopeAnyBuildDepQualifier pn) = Disp.text "build." <<>> pretty pn
   pretty (ScopeAnySetupQualifier pn) = Disp.text "setup." <<>> pretty pn
   pretty (ScopeAnyQualifier pn) = Disp.text "any." <<>> pretty pn
 
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/PackagePath.hs b/cabal-install-solver/src/Distribution/Solver/Types/PackagePath.hs
index 4fc4df25f97..05727ffe8c9 100644
--- a/cabal-install-solver/src/Distribution/Solver/Types/PackagePath.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Types/PackagePath.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DeriveGeneric #-}
 module Distribution.Solver.Types.PackagePath
     ( PackagePath(..)
     , Namespace(..)
@@ -18,7 +20,10 @@ import qualified Text.PrettyPrint as Disp
 -- | A package path consists of a namespace and a package path inside that
 -- namespace.
 data PackagePath = PackagePath Namespace Qualifier
-  deriving (Eq, Ord, Show)
+  deriving (Eq, Ord, Show, Generic)
+
+instance Binary PackagePath
+instance Structured PackagePath
 
 -- | Top-level namespace
 --
@@ -30,7 +35,10 @@ data Namespace =
 
     -- | A namespace for a specific build target
   | Independent PackageName
-  deriving (Eq, Ord, Show)
+  deriving (Eq, Ord, Show, Generic)
+
+instance Binary Namespace
+instance Structured Namespace
 
 -- | Pretty-prints a namespace. The result is either empty or
 -- ends in a period, so it can be prepended onto a qualifier.
@@ -68,7 +76,10 @@ data Qualifier =
     -- tracked only @pn2@, that would require us to pick only one
     -- version of an executable over the entire install plan.)
   | QualExe PackageName PackageName
-  deriving (Eq, Ord, Show)
+  deriving (Eq, Ord, Show, Generic)
+
+instance Binary Qualifier
+instance Structured Qualifier
 
 -- | Pretty-prints a qualifier. The result is either empty or
 -- ends in a period, so it can be prepended onto a package name.
@@ -87,11 +98,14 @@ dispQualifier (QualBase pn)  = pretty pn <<>> Disp.text "."
 
 -- | A qualified entity. Pairs a package path with the entity.
 data Qualified a = Q PackagePath a
-  deriving (Eq, Ord, Show)
+  deriving (Eq, Ord, Show, Generic)
 
 -- | Qualified package name.
 type QPN = Qualified PackageName
 
+instance Binary (Qualified PackageName)
+instance Structured (Qualified PackageName)
+
 -- | Pretty-prints a qualified package name.
 dispQPN :: QPN -> Disp.Doc
 dispQPN (Q (PackagePath ns qual) pn) =
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/SolverPackage.hs b/cabal-install-solver/src/Distribution/Solver/Types/SolverPackage.hs
index 186f140aefe..956bc5dff6e 100644
--- a/cabal-install-solver/src/Distribution/Solver/Types/SolverPackage.hs
+++ b/cabal-install-solver/src/Distribution/Solver/Types/SolverPackage.hs
@@ -10,8 +10,10 @@ import Distribution.Package ( Package(..) )
 import Distribution.PackageDescription ( FlagAssignment )
 import Distribution.Solver.Types.ComponentDeps ( ComponentDeps )
 import Distribution.Solver.Types.OptionalStanza
+import Distribution.Solver.Types.PackagePath (QPN)
 import Distribution.Solver.Types.SolverId
 import Distribution.Solver.Types.SourcePackage
+import Distribution.Solver.Types.Stage (Stage (..))
 
 -- | A 'SolverPackage' is a package specified by the dependency solver.
 -- It will get elaborated into a 'ConfiguredPackage' or even an
@@ -21,6 +23,8 @@ import Distribution.Solver.Types.SourcePackage
 -- but for symmetry we have the parameter.  (Maybe it can be removed.)
 --
 data SolverPackage loc = SolverPackage {
+        solverPkgQPN     :: QPN,
+        solverPkgStage   :: Stage,
         solverPkgSource  :: SourcePackage loc,
         solverPkgFlags   :: FlagAssignment,
         solverPkgStanzas :: OptionalStanzaSet,
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/Stage.hs b/cabal-install-solver/src/Distribution/Solver/Types/Stage.hs
new file mode 100644
index 00000000000..b2595a33089
--- /dev/null
+++ b/cabal-install-solver/src/Distribution/Solver/Types/Stage.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+module Distribution.Solver.Types.Stage
+  ( Stage (..)
+  ) where
+
+import Distribution.Compat.Prelude
+import Prelude ()
+
+data Stage
+  = -- | -- The system where the build is running
+    Build
+  | -- | -- The system where the built artifacts will run
+    Host
+  deriving (Eq, Ord, Read, Show, Generic, Typeable)
+
+instance Binary Stage
+instance Structured Stage
diff --git a/cabal-install-solver/src/Distribution/Solver/Types/Toolchain.hs b/cabal-install-solver/src/Distribution/Solver/Types/Toolchain.hs
new file mode 100644
index 00000000000..e965e54bee5
--- /dev/null
+++ b/cabal-install-solver/src/Distribution/Solver/Types/Toolchain.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE NamedFieldPuns #-}
+
+module Distribution.Solver.Types.Toolchain
+  ( Toolchain (..)
+  , Toolchains (..)
+  , toolchainFor
+  , compilerIdFor
+  , mkToolchainsWithHost
+  , buildIsHost
+  ) where
+
+import Distribution.Compat.Prelude
+import Prelude ()
+
+import Distribution.Simple.Compiler
+import Distribution.Simple.Program.Db
+import Distribution.Solver.Types.Stage (Stage (..))
+import Distribution.System
+
+---------------------------
+-- Toolchain
+--
+
+data Toolchain = Toolchain
+  { toolchainPlatform :: Platform
+  , toolchainCompiler :: Compiler
+  , toolchainProgramDb :: ProgramDb
+  }
+  deriving (Show, Generic, Typeable)
+
+-- TODO: review this
+instance Eq Toolchain where
+  lhs == rhs =
+    (((==) `on` toolchainPlatform) lhs rhs)
+      && (((==) `on` toolchainCompiler) lhs rhs)
+      && ((((==)) `on` (configuredPrograms . toolchainProgramDb)) lhs rhs)
+
+instance Binary Toolchain
+instance Structured Toolchain
+
+data Toolchains = Toolchains
+  { buildToolchain :: Toolchain
+  , hostToolchain :: Toolchain
+  }
+  deriving (Eq, Show, Generic, Typeable)
+
+toolchainFor :: Stage -> Toolchains -> Toolchain
+toolchainFor Build = buildToolchain
+toolchainFor Host = hostToolchain
+
+compilerIdFor :: Stage -> Toolchains -> CompilerId
+compilerIdFor stage = compilerId . toolchainCompiler . toolchainFor stage
+
+instance Binary Toolchains
+instance Structured Toolchains
+
+mkToolchainsWithHost :: Platform -> Compiler -> Toolchains
+mkToolchainsWithHost platform compiler =
+  Toolchains
+    { buildToolchain = Toolchain platform compiler (error "build program db missing")
+    , hostToolchain = Toolchain platform compiler (error "host program db missing")
+    }
+
+buildIsHost :: Toolchains -> Bool
+buildIsHost Toolchains {buildToolchain, hostToolchain} =
+  buildToolchain == hostToolchain
diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal
index 5aedb23aeed..c237c9d891e 100644
--- a/cabal-install/cabal-install.cabal
+++ b/cabal-install/cabal-install.cabal
@@ -141,6 +141,7 @@ library
         Distribution.Client.GlobalFlags
         Distribution.Client.Haddock
         Distribution.Client.HashValue
+        Distribution.Client.HookAccept
         Distribution.Client.HttpUtils
         Distribution.Client.IndexUtils
         Distribution.Client.IndexUtils.ActiveRepos
diff --git a/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs b/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs
index ce1d1665327..a6febfcca50 100644
--- a/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs
+++ b/cabal-install/src/Distribution/Client/BuildReports/Anonymous.hs
@@ -103,7 +103,7 @@ newBuildReport os' arch' comp pkgid flags deps result =
 
 cabalInstallID :: PackageIdentifier
 cabalInstallID =
-  PackageIdentifier (mkPackageName "cabal-install") cabalInstallVersion
+  PackageIdentifier (mkPackageName "cabal-install") cabalInstallVersion Nothing
 
 -------------------------------------------------------------------------------
 -- FieldGrammar
diff --git a/cabal-install/src/Distribution/Client/CmdExec.hs b/cabal-install/src/Distribution/Client/CmdExec.hs
index caa680a3a3a..c0f565738fb 100644
--- a/cabal-install/src/Distribution/Client/CmdExec.hs
+++ b/cabal-install/src/Distribution/Client/CmdExec.hs
@@ -55,7 +55,9 @@ import Distribution.Client.ProjectPlanning
   )
 import qualified Distribution.Client.ProjectPlanning as Planning
 import Distribution.Client.ProjectPlanning.Types
-  ( dataDirsEnvironmentForPlan
+  ( Toolchain (..)
+  , Toolchains (..)
+  , dataDirsEnvironmentForPlan
   )
 import Distribution.Client.Setup
   ( ConfigFlags (configCommonFlags)
@@ -170,7 +172,7 @@ execAction flags@NixStyleFlags{..} extraArgs globalFlags = do
 
   -- Some dependencies may have executables. Let's put those on the PATH.
   let extraPaths = pathAdditions baseCtx buildCtx
-      pkgProgs = pkgConfigCompilerProgs (elaboratedShared buildCtx)
+      pkgProgs = toolchainProgramDb $ buildToolchain $ pkgConfigToolchains (elaboratedShared buildCtx)
       extraEnvVars =
         dataDirsEnvironmentForPlan
           (distDirLayout baseCtx)
@@ -185,7 +187,7 @@ execAction flags@NixStyleFlags{..} extraArgs globalFlags = do
   -- point at the file.
   -- In case ghc is too old to support environment files,
   -- we pass the same info as arguments
-  let compiler = pkgConfigCompiler $ elaboratedShared buildCtx
+  let compiler = toolchainCompiler $ buildToolchain $ pkgConfigToolchains $ elaboratedShared buildCtx
       envFilesSupported = supportsPkgEnvFiles (getImplInfo compiler)
   case extraArgs of
     [] -> dieWithException verbosity SpecifyAnExecutable
@@ -243,7 +245,7 @@ matchCompilerPath elaboratedShared program =
   programPath program
     `elem` (programPath <$> configuredCompilers)
   where
-    configuredCompilers = configuredPrograms $ pkgConfigCompilerProgs elaboratedShared
+    configuredCompilers = configuredPrograms $ toolchainProgramDb $ buildToolchain $ pkgConfigToolchains elaboratedShared
 
 -- | Execute an action with a temporary .ghc.environment file reflecting the
 -- current environment. The action takes an environment containing the env
diff --git a/cabal-install/src/Distribution/Client/CmdFreeze.hs b/cabal-install/src/Distribution/Client/CmdFreeze.hs
index 29718b5d441..d6dae289354 100644
--- a/cabal-install/src/Distribution/Client/CmdFreeze.hs
+++ b/cabal-install/src/Distribution/Client/CmdFreeze.hs
@@ -142,6 +142,7 @@ freezeAction flags@NixStyleFlags{..} extraArgs globalFlags = do
   (_, elaboratedPlan, _, totalIndexState, activeRepos) <-
     rebuildInstallPlan
       verbosity
+      mempty
       distDirLayout
       cabalDirLayout
       projectConfig
diff --git a/cabal-install/src/Distribution/Client/CmdHaddock.hs b/cabal-install/src/Distribution/Client/CmdHaddock.hs
index 677589e3e35..4b35b1f9f2e 100644
--- a/cabal-install/src/Distribution/Client/CmdHaddock.hs
+++ b/cabal-install/src/Distribution/Client/CmdHaddock.hs
@@ -27,9 +27,6 @@ import Distribution.Client.ProjectConfig.Types
   , ProjectConfig (..)
   )
 import Distribution.Client.ProjectOrchestration
-import Distribution.Client.ProjectPlanning
-  ( ElaboratedSharedConfig (..)
-  )
 import Distribution.Client.Setup
   ( CommonSetupFlags (..)
   , ConfigFlags (..)
@@ -48,13 +45,6 @@ import Distribution.Simple.Command
   , usageAlternatives
   )
 import Distribution.Simple.Flag (Flag (..))
-import Distribution.Simple.Program.Builtin
-  ( haddockProgram
-  )
-import Distribution.Simple.Program.Db
-  ( addKnownProgram
-  , reconfigurePrograms
-  )
 import Distribution.Simple.Setup
   ( HaddockFlags (..)
   , fromFlagOrDefault
@@ -160,6 +150,7 @@ haddockAction relFlags targetStrings globalFlags = do
             projCtx{buildSettings = (buildSettings projCtx){buildSettingHaddockOpen = True}}
         | otherwise =
             projCtx
+
   absProjectConfig <- mkConfigAbsolute relProjectConfig
   let baseCtx = relBaseCtx{projectConfig = absProjectConfig}
 
@@ -188,28 +179,32 @@ haddockAction relFlags targetStrings globalFlags = do
               TargetActionHaddock
               targets
               elaboratedPlan
+
       return (elaboratedPlan', targets)
 
   printPlan verbosity baseCtx buildCtx
 
-  progs <-
-    reconfigurePrograms
-      verbosity
-      (haddockProgramPaths haddockFlags)
-      (haddockProgramArgs haddockFlags)
-      -- we need to insert 'haddockProgram' before we reconfigure it,
-      -- otherwise 'set
-      . addKnownProgram haddockProgram
-      . pkgConfigCompilerProgs
-      . elaboratedShared
-      $ buildCtx
+  -- TODO
+  -- progs <-
+  --   reconfigurePrograms
+  --     verbosity
+  --     (haddockProgramPaths haddockFlags)
+  --     (haddockProgramArgs haddockFlags)
+  --     -- we need to insert 'haddockProgram' before we reconfigure it,
+  --     -- otherwise 'set
+  --     . addKnownProgram haddockProgram
+  --     . pkgConfigCompilerProgs
+  --     . elaboratedShared
+  --     $ buildCtx
+
+  -- TODO
   let buildCtx' =
         buildCtx
-          { elaboratedShared =
-              (elaboratedShared buildCtx)
-                { pkgConfigCompilerProgs = progs
-                }
-          }
+  -- { elaboratedShared =
+  --     (elaboratedShared buildCtx)
+  --       { pkgConfigCompilerProgs = progs
+  --       }
+  -- }
 
   buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx'
   runProjectPostBuildPhase verbosity baseCtx buildCtx' buildOutcomes
diff --git a/cabal-install/src/Distribution/Client/CmdHaddockProject.hs b/cabal-install/src/Distribution/Client/CmdHaddockProject.hs
index 0635a77d68e..680e538513b 100644
--- a/cabal-install/src/Distribution/Client/CmdHaddockProject.hs
+++ b/cabal-install/src/Distribution/Client/CmdHaddockProject.hs
@@ -37,7 +37,9 @@ import Distribution.Client.ProjectPlanning
   , TargetAction (..)
   )
 import Distribution.Client.ProjectPlanning.Types
-  ( elabDistDirParams
+  ( Toolchain (..)
+  , Toolchains (..)
+  , elabDistDirParams
   )
 import Distribution.Client.ScriptUtils
   ( AcceptNoTargets (..)
@@ -72,14 +74,6 @@ import Distribution.Simple.Haddock (createHaddockIndex)
 import Distribution.Simple.InstallDirs
   ( toPathTemplate
   )
-import Distribution.Simple.Program.Builtin
-  ( haddockProgram
-  )
-import Distribution.Simple.Program.Db
-  ( addKnownProgram
-  , reconfigurePrograms
-  , requireProgramVersion
-  )
 import Distribution.Simple.Setup
   ( HaddockFlags (..)
   , HaddockProjectFlags (..)
@@ -100,8 +94,6 @@ import Distribution.Types.PackageDescription (PackageDescription (benchmarks, su
 import Distribution.Types.PackageId (pkgName)
 import Distribution.Types.PackageName (unPackageName)
 import Distribution.Types.UnitId (unUnitId)
-import Distribution.Types.Version (mkVersion)
-import Distribution.Types.VersionRange (orLaterVersion)
 import Distribution.Verbosity as Verbosity
   ( normal
   )
@@ -166,24 +158,26 @@ haddockProjectAction flags _extraArgs globalFlags = do
           pkgs :: [Either InstalledPackageInfo ElaboratedConfiguredPackage]
           pkgs = matchingPackages elaboratedPlan
 
-      progs <-
-        reconfigurePrograms
-          verbosity
-          (haddockProjectProgramPaths flags)
-          (haddockProjectProgramArgs flags)
-          -- we need to insert 'haddockProgram' before we reconfigure it,
-          -- otherwise 'set
-          . addKnownProgram haddockProgram
-          . pkgConfigCompilerProgs
-          $ sharedConfig
-      let sharedConfig' = sharedConfig{pkgConfigCompilerProgs = progs}
-
-      _ <-
-        requireProgramVersion
-          verbosity
-          haddockProgram
-          (orLaterVersion (mkVersion [2, 26, 1]))
-          progs
+      -- TODO
+      -- progs <-
+      --   reconfigurePrograms
+      --     verbosity
+      --     (haddockProjectProgramPaths flags)
+      --     (haddockProjectProgramArgs flags)
+      --     -- we need to insert 'haddockProgram' before we reconfigure it,
+      --     -- otherwise 'set
+      --     . addKnownProgram haddockProgram
+      --     . pkgConfigCompilerProgs
+      --     $ sharedConfig
+      -- let sharedConfig' = sharedConfig{pkgConfigCompilerProgs = progs}
+      let sharedConfig' = sharedConfig
+
+      -- _ <-
+      --   requireProgramVersion
+      --     verbosity
+      --     haddockProgram
+      --     (orLaterVersion (mkVersion [2, 26, 1]))
+      --     progs
 
       --
       -- Build project; we need to build dependencies.
@@ -301,7 +295,7 @@ haddockProjectAction flags _extraArgs globalFlags = do
                     packageDir =
                       storePackageDirectory
                         (cabalStoreDirLayout cabalLayout)
-                        (pkgConfigCompiler sharedConfig')
+                        (toolchainCompiler $ buildToolchain $ pkgConfigToolchains sharedConfig')
                         (elabUnitId package)
                     -- TODO: use `InstallDirTemplates`
                     docDir = packageDir </> "share" </> "doc" </> "html"
@@ -344,11 +338,12 @@ haddockProjectAction flags _extraArgs globalFlags = do
                     ]
               , haddockProjectUseUnicode = NoFlag
               }
+      -- NOTE: this lives in Cabal
       createHaddockIndex
         verbosity
-        (pkgConfigCompilerProgs sharedConfig')
-        (pkgConfigCompiler sharedConfig')
-        (pkgConfigPlatform sharedConfig')
+        (toolchainProgramDb $ buildToolchain $ pkgConfigToolchains sharedConfig')
+        (toolchainCompiler $ buildToolchain $ pkgConfigToolchains sharedConfig')
+        (toolchainPlatform $ buildToolchain $ pkgConfigToolchains sharedConfig')
         Nothing
         flags'
   where
diff --git a/cabal-install/src/Distribution/Client/CmdInstall.hs b/cabal-install/src/Distribution/Client/CmdInstall.hs
index 63cf59169a3..07a72119b75 100644
--- a/cabal-install/src/Distribution/Client/CmdInstall.hs
+++ b/cabal-install/src/Distribution/Client/CmdInstall.hs
@@ -467,7 +467,6 @@ installAction flags@NixStyleFlags{extraFlags, configFlags, installFlags, project
         fetchAndReadSourcePackages
           verbosity
           distDirLayout
-          (Just compiler)
           (projectConfigShared config)
           (projectConfigBuildOnly config)
           [ProjectPackageRemoteTarball uri | uri <- uris]
diff --git a/cabal-install/src/Distribution/Client/CmdListBin.hs b/cabal-install/src/Distribution/Client/CmdListBin.hs
index df16b98e1a2..44b78b0347c 100644
--- a/cabal-install/src/Distribution/Client/CmdListBin.hs
+++ b/cabal-install/src/Distribution/Client/CmdListBin.hs
@@ -205,7 +205,7 @@ listbinAction flags@NixStyleFlags{..} args globalFlags = do
           _ -> []
 
         plat :: Platform
-        plat = pkgConfigPlatform elaboratedSharedConfig
+        plat = toolchainPlatform $ buildToolchain $ pkgConfigToolchains elaboratedSharedConfig
 
         -- here and in PlanOutput,
         -- use binDirectoryFor?
diff --git a/cabal-install/src/Distribution/Client/CmdPath.hs b/cabal-install/src/Distribution/Client/CmdPath.hs
index d7587219665..ae70fbaf9ee 100644
--- a/cabal-install/src/Distribution/Client/CmdPath.hs
+++ b/cabal-install/src/Distribution/Client/CmdPath.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RecordWildCards #-}
 
 -- |
@@ -40,6 +41,7 @@ import Distribution.Client.ProjectConfig.Types
   )
 import Distribution.Client.ProjectOrchestration
 import Distribution.Client.ProjectPlanning
+import Distribution.Client.ProjectPlanning.Types (Toolchain (..), Toolchains (..))
 import Distribution.Client.RebuildMonad (runRebuild)
 import Distribution.Client.ScriptUtils
 import Distribution.Client.Setup
@@ -242,10 +244,11 @@ pathAction flags@NixStyleFlags{extraFlags = pathFlags', ..} cliTargetStrings glo
     if not $ fromFlagOrDefault False (pathCompiler pathFlags)
       then pure Nothing
       else do
-        (compiler, _, progDb) <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $ configureCompiler verbosity (distDirLayout baseCtx) (projectConfig baseCtx)
-        compilerProg <- requireCompilerProg verbosity compiler
-        (configuredCompilerProg, _) <- requireProgram verbosity compilerProg progDb
-        pure $ Just $ mkCompilerInfo configuredCompilerProg compiler
+        let projectRoot = distProjectRootDirectory (distDirLayout baseCtx)
+        Toolchains{buildToolchain} <- runRebuild projectRoot $ configureCompiler verbosity (distDirLayout baseCtx) (projectConfig baseCtx)
+        compilerProg <- requireCompilerProg verbosity (toolchainCompiler buildToolchain)
+        (configuredCompilerProg, _) <- requireProgram verbosity compilerProg (toolchainProgramDb buildToolchain)
+        pure $ Just $ mkCompilerInfo configuredCompilerProg (toolchainCompiler buildToolchain)
 
   paths <- for (fromFlagOrDefault [] $ pathDirectories pathFlags) $ \p -> do
     t <- getPathLocation baseCtx p
diff --git a/cabal-install/src/Distribution/Client/CmdRepl.hs b/cabal-install/src/Distribution/Client/CmdRepl.hs
index a75524bbca6..7570e11aa84 100644
--- a/cabal-install/src/Distribution/Client/CmdRepl.hs
+++ b/cabal-install/src/Distribution/Client/CmdRepl.hs
@@ -55,7 +55,9 @@ import Distribution.Client.ProjectPlanning
   , ElaboratedSharedConfig (..)
   )
 import Distribution.Client.ProjectPlanning.Types
-  ( elabOrderExeDependencies
+  ( Toolchain (..)
+  , Toolchains (..)
+  , elabOrderExeDependencies
   , showElaboratedInstallPlan
   )
 import Distribution.Client.ScriptUtils
@@ -344,7 +346,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
         -- especially in the no-project case.
         withInstallPlan (lessVerbose verbosity) baseCtx' $ \elaboratedPlan sharedConfig -> do
           -- targets should be non-empty map, but there's no NonEmptyMap yet.
-          targets <- validatedTargets (projectConfigShared (projectConfig ctx)) (pkgConfigCompiler sharedConfig) elaboratedPlan targetSelectors
+          targets <- validatedTargets (projectConfigShared (projectConfig ctx)) (toolchainCompiler $ buildToolchain $ pkgConfigToolchains sharedConfig) elaboratedPlan targetSelectors
 
           let
             (unitId, _) = fromMaybe (error "panic: targets should be non-empty") $ safeHead $ Map.toList targets
@@ -368,7 +370,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
         let ProjectBaseContext{..} = baseCtx''
 
         -- Recalculate with updated project.
-        targets <- validatedTargets (projectConfigShared projectConfig) (pkgConfigCompiler elaboratedShared') elaboratedPlan targetSelectors
+        targets <- validatedTargets (projectConfigShared projectConfig) (toolchainCompiler $ buildToolchain $ pkgConfigToolchains elaboratedShared') elaboratedPlan targetSelectors
 
         let
           elaboratedPlan' =
@@ -400,7 +402,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
               , targetsMap = targets
               }
 
-          ElaboratedSharedConfig{pkgConfigCompiler = compiler} = elaboratedShared'
+          ElaboratedSharedConfig{pkgConfigToolchains = Toolchains{hostToolchain = Toolchain{toolchainCompiler = compiler}}} = elaboratedShared'
 
           repl_flags = case originalComponent of
             Just oci -> generateReplFlags includeTransitive elaboratedPlan' oci
@@ -441,7 +443,7 @@ replAction flags@NixStyleFlags{extraFlags = r@ReplFlags{..}, ..} targetStrings g
         -- HACK: Just combine together all env overrides, placing the most common things last
 
         -- ghc program with overridden PATH
-        (ghcProg, _) <- requireProgram verbosity ghcProgram (pkgConfigCompilerProgs (elaboratedShared buildCtx'))
+        (ghcProg, _) <- requireProgram verbosity ghcProgram (toolchainProgramDb $ buildToolchain $ pkgConfigToolchains (elaboratedShared buildCtx'))
         let ghcProg' = ghcProg{programOverrideEnv = [("PATH", Just sp)]}
 
         -- Find what the unit files are, and start a repl based on all the response
diff --git a/cabal-install/src/Distribution/Client/CmdTarget.hs b/cabal-install/src/Distribution/Client/CmdTarget.hs
index c2edeeec89c..4c9cc59ec24 100644
--- a/cabal-install/src/Distribution/Client/CmdTarget.hs
+++ b/cabal-install/src/Distribution/Client/CmdTarget.hs
@@ -160,6 +160,7 @@ targetAction flags@NixStyleFlags{..} ts globalFlags = do
   (_, elaboratedPlan, _, _, _) <-
     rebuildInstallPlan
       verbosity
+      mempty
       distDirLayout
       cabalDirLayout
       projectConfig
diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs
index 419d7d603ea..71888779578 100644
--- a/cabal-install/src/Distribution/Client/Config.hs
+++ b/cabal-install/src/Distribution/Client/Config.hs
@@ -573,6 +573,10 @@ instance Semigroup SavedConfig where
               combineMonoid savedConfigureExFlags configAllowOlder
           , configWriteGhcEnvironmentFilesPolicy =
               combine configWriteGhcEnvironmentFilesPolicy
+          , configBuildHcFlavor = combine configBuildHcFlavor
+          , configBuildHcPath = combine configBuildHcPath
+          , configBuildHcPkg = combine configBuildHcPkg
+          , configBuildPackageDBs = lastNonEmpty configBuildPackageDBs
           }
         where
           combine = combine' savedConfigureExFlags
diff --git a/cabal-install/src/Distribution/Client/Configure.hs b/cabal-install/src/Distribution/Client/Configure.hs
index 048a7db963e..bc3c24f9245 100644
--- a/cabal-install/src/Distribution/Client/Configure.hs
+++ b/cabal-install/src/Distribution/Client/Configure.hs
@@ -67,6 +67,7 @@ import Distribution.Solver.Types.PkgConfigDb
   )
 import Distribution.Solver.Types.Settings
 import Distribution.Solver.Types.SourcePackage
+import Distribution.Solver.Types.Toolchain (mkToolchainsWithHost)
 
 import Distribution.Client.SavedFlags (readCommandFlags, writeCommandFlags)
 import Distribution.Package
@@ -463,6 +464,7 @@ planLocalPackage
           . setSolveExecutables (SolveExecutables False)
           . setSolverVerbosity verbosity
           $ standardInstallPolicy
+            mempty {- build pkgs -}
             installedPkgIndex
             -- NB: We pass in an *empty* source package database,
             -- because cabal configure assumes that all dependencies
@@ -470,7 +472,7 @@ planLocalPackage
             (SourcePackageDb mempty packagePrefs)
             [SpecificSourcePackage localPkg]
 
-    return (resolveDependencies platform (compilerInfo comp) pkgConfigDb resolverParams)
+    return (resolveDependencies (mkToolchainsWithHost platform comp) pkgConfigDb resolverParams)
 
 -- | Call an installer for an 'SourcePackage' but override the configure
 -- flags with the ones given by the 'ReadyPackage'. In particular the
diff --git a/cabal-install/src/Distribution/Client/Dependency.hs b/cabal-install/src/Distribution/Client/Dependency.hs
index d59bc611c44..878a99ca5e9 100644
--- a/cabal-install/src/Distribution/Client/Dependency.hs
+++ b/cabal-install/src/Distribution/Client/Dependency.hs
@@ -115,6 +115,7 @@ import Distribution.PackageDescription.Configuration
 import qualified Distribution.PackageDescription.Configuration as PD
 import Distribution.Simple.PackageIndex (InstalledPackageIndex)
 import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex
+import Distribution.Simple.Compiler (compilerInfo)
 import Distribution.Simple.Setup
   ( asBool
   )
@@ -153,6 +154,7 @@ import Distribution.Solver.Types.Settings
 import Distribution.Solver.Types.SolverId
 import Distribution.Solver.Types.SolverPackage
 import Distribution.Solver.Types.SourcePackage
+import Distribution.Solver.Types.Toolchain
 import Distribution.Solver.Types.Variable
 
 import Control.Exception
@@ -164,6 +166,8 @@ import Data.List
 import qualified Data.Map as Map
 import qualified Data.Set as Set
 
+import GHC.Stack (HasCallStack)
+
 -- ------------------------------------------------------------
 
 -- * High level planner policy
@@ -179,6 +183,7 @@ data DepResolverParams = DepResolverParams
   , depResolverPreferences :: [PackagePreference]
   , depResolverPreferenceDefault :: PackagesPreferenceDefault
   , depResolverInstalledPkgIndex :: InstalledPackageIndex
+  , depResolverBuildInstalledPkgIndex :: InstalledPackageIndex
   , depResolverSourcePkgIndex :: PackageIndex.PackageIndex UnresolvedSourcePackage
   , depResolverReorderGoals :: ReorderGoals
   , depResolverCountConflicts :: CountConflicts
@@ -275,15 +280,17 @@ showPackagePreference (PackageStanzasPreference pn st) =
 
 basicDepResolverParams
   :: InstalledPackageIndex
+  -> InstalledPackageIndex
   -> PackageIndex.PackageIndex UnresolvedSourcePackage
   -> DepResolverParams
-basicDepResolverParams installedPkgIndex sourcePkgIndex =
+basicDepResolverParams buildInstalledPkgIndex installedPkgIndex sourcePkgIndex=
   DepResolverParams
     { depResolverTargets = Set.empty
     , depResolverConstraints = []
     , depResolverPreferences = []
     , depResolverPreferenceDefault = PreferLatestForSelected
     , depResolverInstalledPkgIndex = installedPkgIndex
+    , depResolverBuildInstalledPkgIndex = buildInstalledPkgIndex
     , depResolverSourcePkgIndex = sourcePkgIndex
     , depResolverReorderGoals = ReorderGoals False
     , depResolverCountConflicts = CountConflicts True
@@ -447,7 +454,16 @@ dontInstallNonReinstallablePackages params =
         ConstraintSourceNonReinstallablePackage
       | pkgname <- nonReinstallablePackages
       ]
-
+dontInstallNonReinstallablePackagesForBuild :: DepResolverParams -> DepResolverParams
+dontInstallNonReinstallablePackagesForBuild params =
+  addConstraints extraConstraints params
+  where
+    extraConstraints =
+      [ LabeledPackageConstraint
+        (PackageConstraint (ScopeAnyBuildDepQualifier pkgname) PackagePropertyInstalled)
+        ConstraintSourceNonReinstallablePackage
+      | pkgname <- nonReinstallablePackages
+      ]
 -- | The set of non-reinstallable packages includes those which cannot be
 -- rebuilt using a GHC installation and Hackage-published source distribution.
 -- There are a few reasons why this might be true:
@@ -700,11 +716,13 @@ reinstallTargets params =
 
 -- | A basic solver policy on which all others are built.
 basicInstallPolicy
-  :: InstalledPackageIndex
+  :: InstalledPackageIndex -- ^ Build
+  -> InstalledPackageIndex -- ^ Host
   -> SourcePackageDb
   -> [PackageSpecifier UnresolvedSourcePackage]
   -> DepResolverParams
 basicInstallPolicy
+  binstalledPkgIndex
   installedPkgIndex
   (SourcePackageDb sourcePkgIndex sourcePkgPrefs)
   pkgSpecifiers =
@@ -721,6 +739,7 @@ basicInstallPolicy
       . addSourcePackages
         [pkg | SpecificSourcePackage pkg <- pkgSpecifiers]
       $ basicDepResolverParams
+        binstalledPkgIndex
         installedPkgIndex
         sourcePkgIndex
 
@@ -729,13 +748,15 @@ basicInstallPolicy
 --
 -- It extends the 'basicInstallPolicy' with a policy on setup deps.
 standardInstallPolicy
-  :: InstalledPackageIndex
+  :: InstalledPackageIndex -- ^ Build
+  -> InstalledPackageIndex -- ^ Host
   -> SourcePackageDb
   -> [PackageSpecifier UnresolvedSourcePackage]
   -> DepResolverParams
-standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers =
+standardInstallPolicy binstalledPkgIndex installedPkgIndex sourcePkgDb pkgSpecifiers =
   addDefaultSetupDependencies mkDefaultSetupDeps $
     basicInstallPolicy
+      binstalledPkgIndex
       installedPkgIndex
       sourcePkgDb
       pkgSpecifiers
@@ -782,14 +803,13 @@ runSolver = modularResolver
 -- a 'Progress' structure that can be unfolded to provide progress information,
 -- logging messages and the final result or an error.
 resolveDependencies
-  :: Platform
-  -> CompilerInfo
+  :: Toolchains
   -> Maybe PkgConfigDb
   -> DepResolverParams
   -> Progress String String SolverInstallPlan
-resolveDependencies platform comp pkgConfigDB params =
+resolveDependencies toolchains pkgConfigDB params =
   Step (showDepResolverParams finalparams) $
-    fmap (validateSolverResult platform comp indGoals) $
+    fmap (validateSolverResult toolchains indGoals) $
       runSolver
         ( SolverConfig
             reordGoals
@@ -808,8 +828,8 @@ resolveDependencies platform comp pkgConfigDB params =
             verbosity
             (PruneAfterFirstSuccess False)
         )
-        platform
-        comp
+        toolchains
+        binstalledPkgIndex
         installedPkgIndex
         sourcePkgIndex
         pkgConfigDB
@@ -818,12 +838,13 @@ resolveDependencies platform comp pkgConfigDB params =
         targets
   where
     finalparams@( DepResolverParams
-                    targets
+                    targets            -- depResolverTargets
                     constraints
                     prefs
                     defpref
                     installedPkgIndex
-                    sourcePkgIndex
+                    binstalledPkgIndex
+                    sourcePkgIndex     -- depResolverSourcePkgIndex
                     reordGoals
                     cntConflicts
                     fineGrained
@@ -841,7 +862,9 @@ resolveDependencies platform comp pkgConfigDB params =
                     verbosity
                   ) =
         if asBool (depResolverAllowBootLibInstalls params)
-          then params
+          then if buildIsHost toolchains
+            then params
+            else dontInstallNonReinstallablePackagesForBuild params
           else dontInstallNonReinstallablePackages params
 
     preferences :: PackageName -> PackagePreferences
@@ -909,18 +932,18 @@ interpretPackagesPreference selected defaultPref prefs =
 -- | Make an install plan from the output of the dep resolver.
 -- It checks that the plan is valid, or it's an error in the dep resolver.
 validateSolverResult
-  :: Platform
-  -> CompilerInfo
+  :: HasCallStack => Toolchains
   -> IndependentGoals
   -> [ResolverPackage UnresolvedPkgLoc]
   -> SolverInstallPlan
-validateSolverResult platform comp indepGoals pkgs =
-  case planPackagesProblems platform comp pkgs of
+validateSolverResult toolchains indepGoals pkgs =
+  case planPackagesProblems toolchains pkgs of
     [] -> case SolverInstallPlan.new indepGoals graph of
       Right plan -> plan
       Left problems -> error (formatPlanProblems problems)
     problems -> error (formatPkgProblems problems)
   where
+
     graph :: Graph.Graph (ResolverPackage UnresolvedPkgLoc)
     graph = Graph.fromDistinctList pkgs
 
@@ -960,14 +983,13 @@ showPlanPackageProblem (DuplicatePackageSolverId pid dups) =
     ++ " duplicate instances."
 
 planPackagesProblems
-  :: Platform
-  -> CompilerInfo
+  :: Toolchains
   -> [ResolverPackage UnresolvedPkgLoc]
   -> [PlanPackageProblem]
-planPackagesProblems platform cinfo pkgs =
+planPackagesProblems toolchains pkgs =
   [ InvalidConfiguredPackage pkg packageProblems
   | Configured pkg <- pkgs
-  , let packageProblems = configuredPackageProblems platform cinfo pkg
+  , let packageProblems = configuredPackageProblems toolchains pkg
   , not (null packageProblems)
   ]
     ++ [ DuplicatePackageSolverId (Graph.nodeKey aDup) dups
@@ -1016,14 +1038,12 @@ showPackageProblem (InvalidDep dep pkgid) =
 -- in the configuration given by the flag assignment, all the package
 -- dependencies are satisfied by the specified packages.
 configuredPackageProblems
-  :: Platform
-  -> CompilerInfo
+  :: Toolchains
   -> SolverPackage UnresolvedPkgLoc
   -> [PackageProblem]
 configuredPackageProblems
-  platform
-  cinfo
-  (SolverPackage pkg specifiedFlags stanzas specifiedDeps0 _specifiedExeDeps') =
+  toolchains
+  (SolverPackage _qpn _stage pkg specifiedFlags stanzas specifiedDeps0 _specifiedExeDeps') =
     [ DuplicateFlag flag
     | flag <- PD.findDuplicateFlagAssignments specifiedFlags
     ]
@@ -1061,7 +1081,7 @@ configuredPackageProblems
           (sort $ map fst (PD.unFlagAssignment specifiedFlags)) -- TODO
       packageSatisfiesDependency :: PackageIdentifier -> Dependency -> Bool
       packageSatisfiesDependency
-        (PackageIdentifier name version)
+        (PackageIdentifier name version _compid)
         (Dependency name' versionRange _) =
           assert (name == name') $
             version `withinRange` versionRange
@@ -1096,8 +1116,9 @@ configuredPackageProblems
           specifiedFlags
           compSpec
           (const Satisfied)
-          platform
-          cinfo
+          -- FIXME: HARDCODED HOST TOOLCHAIN here.
+          (toolchainPlatform (hostToolchain toolchains))
+          (compilerInfo (toolchainCompiler (hostToolchain toolchains)))
           []
           (srcpkgDescription pkg) of
           Right (resolvedPkg, _) ->
@@ -1144,6 +1165,7 @@ resolveWithoutDependencies
       prefs
       defpref
       installedPkgIndex
+      binstalledPkgIndex
       sourcePkgIndex
       _reorderGoals
       _countConflicts
diff --git a/cabal-install/src/Distribution/Client/Errors.hs b/cabal-install/src/Distribution/Client/Errors.hs
index ff9ad369bef..9d750b1548d 100644
--- a/cabal-install/src/Distribution/Client/Errors.hs
+++ b/cabal-install/src/Distribution/Client/Errors.hs
@@ -186,7 +186,9 @@ data CabalInstallException
   | MissingPackageList Repo.RemoteRepo
   | CmdPathAcceptsNoTargets
   | CmdPathCommandDoesn'tSupportDryRun
-  deriving (Show)
+  | HookAcceptUnknown FilePath FilePath String
+  | HookAcceptHashMismatch FilePath FilePath String String
+  deriving (Show, Typeable)
 
 exceptionCodeCabalInstall :: CabalInstallException -> Int
 exceptionCodeCabalInstall e = case e of
@@ -338,6 +340,8 @@ exceptionCodeCabalInstall e = case e of
   MissingPackageList{} -> 7160
   CmdPathAcceptsNoTargets{} -> 7161
   CmdPathCommandDoesn'tSupportDryRun -> 7163
+  HookAcceptUnknown{} -> 7164
+  HookAcceptHashMismatch{} -> 7165
 
 exceptionMessageCabalInstall :: CabalInstallException -> String
 exceptionMessageCabalInstall e = case e of
@@ -381,7 +385,7 @@ exceptionMessageCabalInstall e = case e of
   FindOpenProgramLocationErr err -> err
   PkgConfParseFailed perror ->
     "Couldn't parse the output of 'setup register --gen-pkg-config':"
-      ++ show perror
+      ++ perror
   ErrorPackingSdist err -> "Error packing sdist: " ++ err
   SdistException pkgIdentifier -> "sdist of " ++ prettyShow pkgIdentifier
   SpecifyAnExecutable -> "Please specify an executable to run"
@@ -860,6 +864,36 @@ exceptionMessageCabalInstall e = case e of
     "The 'path' command accepts no target arguments."
   CmdPathCommandDoesn'tSupportDryRun ->
     "The 'path' command doesn't support the flag '--dry-run'."
+  HookAcceptUnknown hsPath fpath hash ->
+    concat
+      [ "The following file does not appear in the hooks-security file.\n"
+      , "    hook file : "
+      , fpath
+      , "\n"
+      , "    file hash : "
+      , hash
+      , "\n"
+      , "After checking the contents of that file, it should be added to the\n"
+      , "hooks-security file with either AcceptAlways or better yet an AcceptHash.\n"
+      , "The hooks-security file is (probably) located at: "
+      , hsPath
+      ]
+  HookAcceptHashMismatch hsPath fpath expected actual ->
+    concat
+      [ "\nHook file hash mismatch for:\n"
+      , "    hook file    : "
+      , fpath
+      , "\n"
+      , "    expected hash: "
+      , expected
+      , "\n"
+      , "    actual hash  : "
+      , actual
+      , "\n"
+      , "The hook file should be inspected and if deemed ok, the hooks-security file updated.\n"
+      , "The hooks-security file is (probably) located at: "
+      , hsPath
+      ]
 
 instance Exception (VerboseException CabalInstallException) where
   displayException :: VerboseException CabalInstallException -> [Char]
diff --git a/cabal-install/src/Distribution/Client/Fetch.hs b/cabal-install/src/Distribution/Client/Fetch.hs
index 033d3a01e14..e66acf66cc6 100644
--- a/cabal-install/src/Distribution/Client/Fetch.hs
+++ b/cabal-install/src/Distribution/Client/Fetch.hs
@@ -38,6 +38,7 @@ import Distribution.Solver.Types.OptionalStanza
 import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb, readPkgConfigDb)
 import Distribution.Solver.Types.SolverPackage
 import Distribution.Solver.Types.SourcePackage
+import Distribution.Solver.Types.Toolchain (mkToolchainsWithHost)
 
 import Distribution.Client.Errors
 import Distribution.Package
@@ -174,8 +175,7 @@ planPackages
         installPlan <-
           foldProgress logMsg (dieWithException verbosity . PlanPackages . show) return $
             resolveDependencies
-              platform
-              (compilerInfo comp)
+              (mkToolchainsWithHost platform comp)
               pkgConfigDb
               resolverParams
 
@@ -220,7 +220,7 @@ planPackages
           -- already installed. Since we want to get the source packages of
           -- things we might have installed (but not have the sources for).
           . reinstallTargets
-          $ standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers
+          $ standardInstallPolicy mempty {-build-} installedPkgIndex sourcePkgDb pkgSpecifiers
 
       includeDependencies = fromFlag (fetchDeps fetchFlags)
       logMsg message rest = debug verbosity message >> rest
diff --git a/cabal-install/src/Distribution/Client/FetchUtils.hs b/cabal-install/src/Distribution/Client/FetchUtils.hs
index 62da386573d..6b29dd84bfc 100644
--- a/cabal-install/src/Distribution/Client/FetchUtils.hs
+++ b/cabal-install/src/Distribution/Client/FetchUtils.hs
@@ -60,6 +60,7 @@ import Distribution.Package
   ( PackageId
   , packageName
   , packageVersion
+  , pkgCompiler
   )
 import Distribution.Simple.Utils
   ( debug
@@ -395,7 +396,7 @@ waitAsyncFetchPackage verbosity downloadMap srcloc =
 packageFile :: Repo -> PackageId -> FilePath
 packageFile repo pkgid =
   packageDir repo pkgid
-    </> prettyShow pkgid
+    </> prettyShow pkgid{pkgCompiler = Nothing}
     <.> "tar.gz"
 
 -- | Generate the full path to the directory where the local cached copy of
diff --git a/cabal-install/src/Distribution/Client/Freeze.hs b/cabal-install/src/Distribution/Client/Freeze.hs
index a03b45b6a2d..e6a5190260f 100644
--- a/cabal-install/src/Distribution/Client/Freeze.hs
+++ b/cabal-install/src/Distribution/Client/Freeze.hs
@@ -52,6 +52,7 @@ import Distribution.Solver.Types.LabeledPackageConstraint
 import Distribution.Solver.Types.OptionalStanza
 import Distribution.Solver.Types.PkgConfigDb
 import Distribution.Solver.Types.SolverId
+import Distribution.Solver.Types.Toolchain (mkToolchainsWithHost)
 
 import Distribution.Client.Errors
 import Distribution.Package
@@ -212,8 +213,7 @@ planPackages
     installPlan <-
       foldProgress logMsg (dieWithException verbosity . FreezeException) return $
         resolveDependencies
-          platform
-          (compilerInfo comp)
+          (mkToolchainsWithHost platform comp)
           pkgConfigDb
           resolverParams
 
@@ -245,7 +245,7 @@ planPackages
                in LabeledPackageConstraint pc ConstraintSourceFreeze
             | pkgSpecifier <- pkgSpecifiers
             ]
-          $ standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers
+          $ standardInstallPolicy mempty {- build -} installedPkgIndex sourcePkgDb pkgSpecifiers
 
       logMsg message rest = debug verbosity message >> rest
 
diff --git a/cabal-install/src/Distribution/Client/Get.hs b/cabal-install/src/Distribution/Client/Get.hs
index 39ace2f2652..08029a53b8d 100644
--- a/cabal-install/src/Distribution/Client/Get.hs
+++ b/cabal-install/src/Distribution/Client/Get.hs
@@ -148,7 +148,7 @@ get verbosity repoCtxt globalFlags getFlags userTargets = do
     resolverParams :: SourcePackageDb -> [PackageSpecifier UnresolvedSourcePackage] -> DepResolverParams
     resolverParams sourcePkgDb pkgSpecifiers =
       -- TODO: add command-line constraint and preference args for unpack
-      standardInstallPolicy mempty sourcePkgDb pkgSpecifiers
+      standardInstallPolicy mempty mempty sourcePkgDb pkgSpecifiers
 
     onlyPkgDescr = fromFlagOrDefault False (getOnlyPkgDescr getFlags)
 
diff --git a/cabal-install/src/Distribution/Client/HashValue.hs b/cabal-install/src/Distribution/Client/HashValue.hs
index c5698f27f1e..12616e07eaa 100644
--- a/cabal-install/src/Distribution/Client/HashValue.hs
+++ b/cabal-install/src/Distribution/Client/HashValue.hs
@@ -4,6 +4,7 @@
 module Distribution.Client.HashValue
   ( HashValue
   , hashValue
+  , hashValueFromHex
   , truncateHash
   , showHashValue
   , readFileHashValue
@@ -51,6 +52,11 @@ instance Structured HashValue
 hashValue :: LBS.ByteString -> HashValue
 hashValue = HashValue . SHA256.hashlazy
 
+-- From a base16 encoded Bytestring to a HashValue with `Base16`'s
+-- error passing through.
+hashValueFromHex :: BS.ByteString -> Either String HashValue
+hashValueFromHex bs = HashValue <$> Base16.decode bs
+
 showHashValue :: HashValue -> String
 showHashValue (HashValue digest) = BS.unpack (Base16.encode digest)
 
diff --git a/cabal-install/src/Distribution/Client/HookAccept.hs b/cabal-install/src/Distribution/Client/HookAccept.hs
new file mode 100644
index 00000000000..fc7e5ae0460
--- /dev/null
+++ b/cabal-install/src/Distribution/Client/HookAccept.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Distribution.Client.HookAccept
+  ( HookAccept (..)
+  , assertHookHash
+  , loadHookHasheshMap
+  , parseHooks
+  ) where
+
+import Distribution.Client.Compat.Prelude
+
+import Data.ByteString.Char8 (ByteString)
+import qualified Data.ByteString.Char8 as BS
+
+import qualified Data.Map.Strict as Map
+
+import Distribution.Client.Config (getConfigFilePath)
+import Distribution.Client.Errors (CabalInstallException (..))
+import Distribution.Client.HashValue (HashValue, hashValueFromHex, readFileHashValue, showHashValue)
+import Distribution.Simple.Setup (Flag (..))
+import Distribution.Simple.Utils (dieWithException)
+import Distribution.Verbosity (normal)
+
+import System.FilePath (takeDirectory, (</>))
+
+data HookAccept
+  = AcceptAlways
+  | AcceptHash HashValue
+  deriving (Eq, Show, Generic)
+
+instance Monoid HookAccept where
+  mempty = AcceptAlways -- Should never be needed.
+  mappend = (<>)
+
+instance Semigroup HookAccept where
+  AcceptAlways <> AcceptAlways = AcceptAlways
+  AcceptAlways <> AcceptHash h = AcceptHash h
+  AcceptHash h <> AcceptAlways = AcceptHash h
+  AcceptHash h <> _ = AcceptHash h
+
+instance Binary HookAccept
+instance Structured HookAccept
+
+assertHookHash :: Map FilePath HookAccept -> FilePath -> IO ()
+assertHookHash m fpath = do
+  actualHash <- readFileHashValue fpath
+  hsPath <- getHooksSecurityFilePath NoFlag
+  case Map.lookup fpath m of
+    Nothing ->
+      dieWithException normal $
+        HookAcceptUnknown hsPath fpath (showHashValue actualHash)
+    Just AcceptAlways -> pure ()
+    Just (AcceptHash expectedHash) ->
+      when (actualHash /= expectedHash) $
+        dieWithException normal $
+          HookAcceptHashMismatch
+            hsPath
+            fpath
+            (showHashValue expectedHash)
+            (showHashValue actualHash)
+
+getHooksSecurityFilePath :: Flag FilePath -> IO FilePath
+getHooksSecurityFilePath configFileFlag = do
+  hfpath <- getConfigFilePath configFileFlag
+  pure $ takeDirectory hfpath </> "hooks-security"
+
+loadHookHasheshMap :: Flag FilePath -> IO (Map FilePath HookAccept)
+loadHookHasheshMap configFileFlag = do
+  hookFilePath <- getHooksSecurityFilePath configFileFlag
+  handleNotExists $ fmap parseHooks (BS.readFile hookFilePath)
+  where
+    handleNotExists :: IO (Map FilePath HookAccept) -> IO (Map FilePath HookAccept)
+    handleNotExists action = catchIO action $ \_ -> return mempty
+
+parseHooks :: ByteString -> Map FilePath HookAccept
+parseHooks = Map.fromList . map parse . cleanUp . BS.lines
+  where
+    cleanUp :: [ByteString] -> [ByteString]
+    cleanUp = filter (not . BS.null) . map rmComments
+
+    rmComments :: ByteString -> ByteString
+    rmComments = fst . BS.breakSubstring "--"
+
+parse :: ByteString -> (FilePath, HookAccept)
+parse bs =
+  case BS.words bs of
+    [fp, "AcceptAlways"] -> (BS.unpack fp, AcceptAlways)
+    [fp, "AcceptHash"] -> buildAcceptHash fp "00"
+    [fp, "AcceptHash", h] -> buildAcceptHash fp h
+    _ -> error $ "Not able to parse:" ++ show bs
+  where
+    buildAcceptHash :: ByteString -> ByteString -> (FilePath, HookAccept)
+    buildAcceptHash fp h =
+      case hashValueFromHex h of
+        Left err -> error $ "Distribution.Client.HookAccept.parse :" ++ err
+        Right hv -> (BS.unpack fp, AcceptHash hv)
diff --git a/cabal-install/src/Distribution/Client/IndexUtils.hs b/cabal-install/src/Distribution/Client/IndexUtils.hs
index 6027f5e53f3..489f8acfdb1 100644
--- a/cabal-install/src/Distribution/Client/IndexUtils.hs
+++ b/cabal-install/src/Distribution/Client/IndexUtils.hs
@@ -156,9 +156,11 @@ import Distribution.Client.Errors
 import qualified Hackage.Security.Client as Sec
 import qualified Hackage.Security.Util.Some as Sec
 
+import GHC.Stack (HasCallStack)
+
 -- | Reduced-verbosity version of 'Configure.getInstalledPackages'
 getInstalledPackages
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> Compiler
   -> PackageDBStackCWD
   -> ProgramDb
@@ -617,7 +619,7 @@ extractPkg verbosity entry blockNo = case Tar.entryContent entry of
           [pkgname, vers, _] -> case simpleParsec vers of
             Just ver -> Just . return $ Just (NormalPackage pkgid descr content blockNo)
               where
-                pkgid = PackageIdentifier (mkPackageName pkgname) ver
+                pkgid = PackageIdentifier (mkPackageName pkgname) ver Nothing
                 parsed = parseGenericPackageDescriptionMaybe (BS.toStrict content)
                 descr = case parsed of
                   Just d -> d
@@ -1269,10 +1271,10 @@ hashConsCache cache0 =
     go !pns !pvs (CachePackageId pid bno ts : rest) =
       CachePackageId pid' bno ts : go pns' pvs' rest
       where
-        !pid' = PackageIdentifier pn' pv'
+        !pid' = PackageIdentifier pn' pv' compid
         (!pn', !pns') = mapIntern pn pns
         (!pv', !pvs') = mapIntern pv pvs
-        PackageIdentifier pn pv = pid
+        PackageIdentifier pn pv compid = pid
     go pns pvs (x : xs) = x : go pns pvs xs
 
     mapIntern :: Ord k => k -> Map.Map k k -> (k, Map.Map k k)
@@ -1395,7 +1397,7 @@ read00IndexCacheEntry = \line ->
             (Just pkgname, Just pkgver, Just blockno) ->
               Just
                 ( CachePackageId
-                    (PackageIdentifier pkgname pkgver)
+                    (PackageIdentifier pkgname pkgver Nothing)
                     blockno
                     NoTimestamp
                 )
diff --git a/cabal-install/src/Distribution/Client/Install.hs b/cabal-install/src/Distribution/Client/Install.hs
index 635cd7e1689..6f7d3bdd8a9 100644
--- a/cabal-install/src/Distribution/Client/Install.hs
+++ b/cabal-install/src/Distribution/Client/Install.hs
@@ -141,6 +141,7 @@ import Distribution.Solver.Types.PkgConfigDb
   )
 import Distribution.Solver.Types.Settings
 import Distribution.Solver.Types.SourcePackage as SourcePackage
+import Distribution.Solver.Types.Toolchain (mkToolchainsWithHost)
 
 import Distribution.Client.ProjectConfig
 import Distribution.Client.Utils
@@ -585,8 +586,7 @@ planPackages
   pkgConfigDb
   pkgSpecifiers =
     resolveDependencies
-      platform
-      (compilerInfo comp)
+      (mkToolchainsWithHost platform comp)
       pkgConfigDb
       resolverParams
       >>= if onlyDeps then pruneInstallPlan pkgSpecifiers else return
@@ -650,6 +650,7 @@ planPackages
           -- doesn't understand how to install them
           . setSolveExecutables (SolveExecutables False)
           $ standardInstallPolicy
+            mempty {- build pkgs -}
             installedPkgIndex
             sourcePkgDb
             pkgSpecifiers
@@ -1101,7 +1102,7 @@ theSpecifiedPackage :: Package pkg => PackageSpecifier pkg -> Maybe PackageId
 theSpecifiedPackage pkgSpec =
   case pkgSpec of
     NamedPackage name [PackagePropertyVersion version] ->
-      PackageIdentifier name <$> trivialRange version
+      PackageIdentifier name <$> trivialRange version <*> Nothing
     NamedPackage _ _ -> Nothing
     SpecificSourcePackage pkg -> Just $ packageId pkg
   where
diff --git a/cabal-install/src/Distribution/Client/PackageHash.hs b/cabal-install/src/Distribution/Client/PackageHash.hs
index e8975b0fc57..0be2753dcf5 100644
--- a/cabal-install/src/Distribution/Client/PackageHash.hs
+++ b/cabal-install/src/Distribution/Client/PackageHash.hs
@@ -66,6 +66,8 @@ import qualified Data.ByteString.Lazy.Char8 as LBS
 import qualified Data.Map as Map
 import qualified Data.Set as Set
 
+import GHC.Stack (HasCallStack)
+
 -------------------------------
 -- Calculating package hashes
 --
@@ -76,7 +78,7 @@ import qualified Data.Set as Set
 -- Note that due to path length limitations on Windows, this function uses
 -- a different method on Windows that produces shorted package ids.
 -- See 'hashedInstalledPackageIdLong' vs 'hashedInstalledPackageIdShort'.
-hashedInstalledPackageId :: PackageHashInputs -> InstalledPackageId
+hashedInstalledPackageId :: HasCallStack => PackageHashInputs -> InstalledPackageId
 hashedInstalledPackageId
   | buildOS == Windows = hashedInstalledPackageIdShort
   | buildOS == OSX = hashedInstalledPackageIdVeryShort
@@ -91,11 +93,14 @@ hashedInstalledPackageIdLong :: PackageHashInputs -> InstalledPackageId
 hashedInstalledPackageIdLong
   pkghashinputs@PackageHashInputs{pkgHashPkgId, pkgHashComponent} =
     mkComponentId $
-      prettyShow pkgHashPkgId -- to be a bit user friendly
+        prettyShow compid
+        ++ "_"
+        ++ prettyShow pkgHashPkgId -- to be a bit user friendly
         ++ maybe "" displayComponent pkgHashComponent
         ++ "-"
         ++ showHashValue (hashPackageHashInputs pkghashinputs)
     where
+      PackageIdentifier name version (Just compid) = pkgHashPkgId
       displayComponent :: CD.Component -> String
       displayComponent CD.ComponentLib = ""
       displayComponent (CD.ComponentSubLib s) = "-l-" ++ prettyShow s
@@ -122,10 +127,12 @@ hashedInstalledPackageIdLong
 -- Truncating the hash size is disappointing but also technically ok. We
 -- rely on the hash primarily for collision avoidance not for any security
 -- properties (at least for now).
-hashedInstalledPackageIdShort :: PackageHashInputs -> InstalledPackageId
+hashedInstalledPackageIdShort :: HasCallStack => PackageHashInputs -> InstalledPackageId
 hashedInstalledPackageIdShort pkghashinputs@PackageHashInputs{pkgHashPkgId} =
   mkComponentId $
-    intercalate
+    prettyShow compid
+    ++
+    '_':intercalate
       "-"
       -- max length now 64
       [ truncateStr 14 (prettyShow name)
@@ -133,7 +140,7 @@ hashedInstalledPackageIdShort pkghashinputs@PackageHashInputs{pkgHashPkgId} =
       , showHashValue (truncateHash 20 (hashPackageHashInputs pkghashinputs))
       ]
   where
-    PackageIdentifier name version = pkgHashPkgId
+    PackageIdentifier name version (Just compid) = pkgHashPkgId
 
     -- Truncate a string, with a visual indication that it is truncated.
     truncateStr n s
@@ -147,8 +154,7 @@ hashedInstalledPackageIdShort pkghashinputs@PackageHashInputs{pkgHashPkgId} =
 --  @store/<libraryname>/libHS<libraryname>.dylib@
 -- where libraryname contains the libraries name, version and abi hash, but in
 --  @store/lib/libHS<very short libraryname>.dylib@
--- where the very short library name drops all vowels from the package name,
--- and truncates the hash to 4 bytes.
+-- where the very short library name truncates the hash to 4 bytes.
 --
 -- We therefore we only need one \@rpath entry to @store/lib@ instead of one
 -- \@rpath entry for each library. And the reduced library name saves some
@@ -163,17 +169,19 @@ hashedInstalledPackageIdShort pkghashinputs@PackageHashInputs{pkgHashPkgId} =
 -- libraries on macOS, such that the proxy libraries and the linked libraries
 -- stay under the load command limit, and the recursive linker is still able
 -- to link all of them.
-hashedInstalledPackageIdVeryShort :: PackageHashInputs -> InstalledPackageId
+hashedInstalledPackageIdVeryShort :: HasCallStack => PackageHashInputs -> InstalledPackageId
 hashedInstalledPackageIdVeryShort pkghashinputs@PackageHashInputs{pkgHashPkgId} =
   mkComponentId $
-    intercalate
+    prettyShow compid
+    ++
+    '_':intercalate
       "-"
-      [ filter (not . flip elem "aeiou") (prettyShow name)
+      [ prettyShow name
       , prettyShow version
       , showHashValue (truncateHash 4 (hashPackageHashInputs pkghashinputs))
       ]
   where
-    PackageIdentifier name version = pkgHashPkgId
+    PackageIdentifier name version (Just compid) = pkgHashPkgId
 
 -- | All the information that contributes to a package's hash, and thus its
 -- 'InstalledPackageId'.
diff --git a/cabal-install/src/Distribution/Client/ProjectBuilding.hs b/cabal-install/src/Distribution/Client/ProjectBuilding.hs
index 7bf6de869a5..c339d790880 100644
--- a/cabal-install/src/Distribution/Client/ProjectBuilding.hs
+++ b/cabal-install/src/Distribution/Client/ProjectBuilding.hs
@@ -220,7 +220,7 @@ rebuildTargetsDryRun distDirLayout@DistDirLayout{..} shared =
             else return (BuildStatusUnpack tarball)
       where
         srcdir :: FilePath
-        srcdir = distUnpackedSrcDirectory (packageId pkg)
+        srcdir = distUnpackedSrcDirectory ((packageId pkg){pkgCompiler = Nothing})
 
     dryRunLocalPkg
       :: ElaboratedConfiguredPackage
@@ -343,8 +343,7 @@ rebuildTargets
   storeDirLayout
   installPlan
   sharedPackageConfig@ElaboratedSharedConfig
-    { pkgConfigCompiler = compiler
-    , pkgConfigCompilerProgs = progdb
+    { pkgConfigToolchains = Toolchains{hostToolchain = Toolchain{toolchainCompiler = compiler, toolchainProgramDb = progdb}}
     }
   pkgsBuildStatus
   buildSettings@BuildTimeSettings
@@ -714,7 +713,7 @@ withTarballLocalDirectory
         let tmpdir = distTempDirectory
             builddir = relativeSymbolicPath $ makeRelativePathEx "dist"
          in withTempDirectory verbosity tmpdir "src" $ \unpackdir -> do
-              let srcdir = makeSymbolicPath $ unpackdir </> prettyShow pkgid
+              let srcdir = makeSymbolicPath $ unpackdir </> prettyShow (pkgid{pkgCompiler = Nothing})
               unpackPackageTarball
                 verbosity
                 tarball
@@ -728,7 +727,7 @@ withTarballLocalDirectory
       -- inplace there
       BuildInplaceOnly{} -> do
         let srcrootdir = distUnpackedSrcRootDirectory
-            srcdir = distUnpackedSrcDirectory pkgid
+            srcdir = distUnpackedSrcDirectory (pkgid{pkgCompiler = Nothing})
             builddir =
               makeSymbolicPath $
                 makeRelative (normalise srcdir) $
@@ -792,7 +791,7 @@ unpackPackageTarball verbosity tarball parentdir pkgid pkgTextOverride =
         </> pkgsubdir
         </> prettyShow pkgname
         <.> "cabal"
-    pkgsubdir = prettyShow pkgid
+    pkgsubdir = prettyShow (pkgid{pkgCompiler = Nothing})
     pkgname = packageName pkgid
 
 -- | This is a bit of a hacky workaround. A number of packages ship
diff --git a/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs b/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs
index e19c52157c0..c0bad4bc39f 100644
--- a/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs
+++ b/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs
@@ -30,6 +30,7 @@ module Distribution.Client.ProjectBuilding.UnpackedPackage
 import Distribution.Client.Compat.Prelude
 import Prelude ()
 
+import Distribution.Client.HookAccept (assertHookHash)
 import Distribution.Client.PackageHash (renderPackageHashInputs)
 import Distribution.Client.ProjectBuilding.Types
 import Distribution.Client.ProjectConfig
@@ -79,7 +80,9 @@ import Distribution.Simple.Command (CommandUI)
 import Distribution.Simple.Compiler
   ( PackageDBStackCWD
   , coercePackageDBStack
+  , showCompilerId
   )
+import Distribution.Solver.Types.Stage
 import qualified Distribution.Simple.InstallDirs as InstallDirs
 import Distribution.Simple.LocalBuildInfo
   ( ComponentName (..)
@@ -105,7 +108,7 @@ import qualified Data.ByteString.Lazy.Char8 as LBS.Char8
 import qualified Data.List.NonEmpty as NE
 
 import Control.Exception (ErrorCall, Handler (..), SomeAsyncException, assert, catches, onException)
-import System.Directory (canonicalizePath, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, removeFile)
+import System.Directory (canonicalizePath, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory, removeFile)
 import System.FilePath (dropDrive, normalise, takeDirectory, (<.>), (</>))
 import System.IO (Handle, IOMode (AppendMode), withFile)
 import System.Semaphore (SemaphoreName (..))
@@ -117,6 +120,8 @@ import Distribution.Compat.Directory (listDirectory)
 
 import Distribution.Client.ProjectBuilding.PackageFileMonitor
 
+import GHC.Stack (HasCallStack)
+
 -- | Each unpacked package is processed in the following phases:
 --
 -- * Configure phase
@@ -177,9 +182,7 @@ buildAndRegisterUnpackedPackage
   registerLock
   cacheLock
   pkgshared@ElaboratedSharedConfig
-    { pkgConfigCompiler = compiler
-    , pkgConfigCompilerProgs = progdb
-    }
+    { pkgConfigToolchains = toolchains }
   plan
   rpkg@(ReadyPackage pkg)
   srcdir
@@ -253,6 +256,10 @@ buildAndRegisterUnpackedPackage
 
     return ()
     where
+      (compiler, progdb) = case elabStage pkg of
+        Host -> (toolchainCompiler (hostToolchain toolchains), toolchainProgramDb (hostToolchain toolchains))
+        Build -> (toolchainCompiler (buildToolchain toolchains), toolchainProgramDb (buildToolchain toolchains))
+
       uid = installedUnitId rpkg
 
       comp_par_strat = case maybe_semaphore of
@@ -450,7 +457,8 @@ buildInplaceUnpackedPackage
   buildSettings@BuildTimeSettings{buildSettingHaddockOpen}
   registerLock
   cacheLock
-  pkgshared@ElaboratedSharedConfig{pkgConfigPlatform = Platform _ os}
+  pkgshared@ElaboratedSharedConfig
+    { pkgConfigToolchains = toolchains }
   plan
   rpkg@(ReadyPackage pkg)
   buildStatus
@@ -595,6 +603,10 @@ buildInplaceUnpackedPackage
         , buildResultLogFile = Nothing
         }
     where
+      Platform _ os = case elabStage pkg of
+        Host -> toolchainPlatform (hostToolchain toolchains)
+        Build -> toolchainPlatform (buildToolchain toolchains)
+
       dparams = elabDistDirParams pkgshared pkg
 
       packageFileMonitor = newPackageFileMonitor pkgshared distDirLayout dparams
@@ -656,9 +668,7 @@ buildAndInstallUnpackedPackage
   registerLock
   cacheLock
   pkgshared@ElaboratedSharedConfig
-    { pkgConfigCompiler = compiler
-    , pkgConfigPlatform = platform
-    }
+    { pkgConfigToolchains = toolchains }
   plan
   rpkg@(ReadyPackage pkg)
   srcdir
@@ -697,7 +707,48 @@ buildAndInstallUnpackedPackage
           runConfigure
         PBBuildPhase{runBuild} -> do
           noticeProgress ProgressBuilding
+          hooksDir <- (</> "cabalHooks") <$> getCurrentDirectory
+          -- run preBuildHook. If it returns with 0, we assume the build was
+          -- successful. If not, run the build.
+          preBuildHookFile <- canonicalizePath (hooksDir </> "preBuildHook")
+          hookExists <- doesFileExist preBuildHookFile
+          preCode <-
+            if hookExists
+              then do
+                assertHookHash (pkgConfigHookHashes pkgshared) preBuildHookFile
+                rawSystemExitCode
+                  verbosity
+                  (Just srcdir)
+                  preBuildHookFile
+                  [ (unUnitId $ installedUnitId rpkg)
+                  , (getSymbolicPath srcdir)
+                  , (getSymbolicPath builddir)
+                  ]
+                  Nothing
+                  `catchIO` (\_ -> pure (ExitFailure 10))
+              else pure ExitSuccess
+          -- Regardless of whether the preBuildHook exists or not, or whether it returned an
+          -- error or not, we want to run the build command.
+          -- If the preBuildHook downloads a cached version of the build products, the following
+          -- should be a NOOP.
           runBuild
+          -- not sure, if we want to care about a failed postBuildHook?
+          postBuildHookFile <- canonicalizePath (hooksDir </> "postBuildHook")
+          hookExists' <- doesFileExist postBuildHookFile
+          when hookExists' $ do
+            assertHookHash (pkgConfigHookHashes pkgshared) postBuildHookFile
+            void $
+              rawSystemExitCode
+                verbosity
+                (Just srcdir)
+                postBuildHookFile
+                [ (unUnitId $ installedUnitId rpkg)
+                , (getSymbolicPath srcdir)
+                , (getSymbolicPath builddir)
+                , show preCode
+                ]
+                Nothing
+                `catchIO` (\_ -> pure (ExitFailure 10))
         PBHaddockPhase{runHaddock} -> do
           noticeProgress ProgressHaddock
           runHaddock
@@ -712,7 +763,10 @@ buildAndInstallUnpackedPackage
                 | otherwise = do
                     assert
                       ( elabRegisterPackageDBStack pkg
-                          == storePackageDBStack compiler (elabPackageDbs pkg)
+                          == storePackageDBStack compiler (case elabStage pkg of
+                            Host -> elabPackageDbs pkg
+                            Build -> elabBuildPackageDbs pkg
+                            )
                       )
                       (return ())
                     _ <-
@@ -764,6 +818,10 @@ buildAndInstallUnpackedPackage
         , buildResultLogFile = mlogFile
         }
     where
+      (compiler, platform) = case elabStage pkg of
+          Host -> (toolchainCompiler (hostToolchain toolchains), toolchainPlatform (hostToolchain toolchains))
+          Build -> (toolchainCompiler (buildToolchain toolchains), toolchainPlatform (buildToolchain toolchains))
+
       uid = installedUnitId rpkg
       pkgid = packageId rpkg
 
@@ -774,13 +832,24 @@ buildAndInstallUnpackedPackage
           prettyShow pkgid
             ++ " (all, legacy fallback: "
             ++ unwords (map whyNotPerComponent $ NE.toList pkgWhyNotPerComponent)
+            ++ ", "
+            ++ dispcompiler (elabStage pkg)
+            -- ++ ", "
+            -- ++ show uid
             ++ ")"
         -- Packages built per component
         ElabComponent comp ->
           prettyShow pkgid
             ++ " ("
             ++ maybe "custom" prettyShow (compComponentName comp)
+            ++ ", "
+            ++ dispcompiler (elabStage pkg)
+            -- ++ ", "
+            -- ++ show uid
             ++ ")"
+      dispcompiler :: Stage -> String
+      dispcompiler Host = showCompilerId (toolchainCompiler (hostToolchain toolchains))
+      dispcompiler Build = showCompilerId (toolchainCompiler (buildToolchain toolchains))
 
       noticeProgress :: ProgressPhase -> IO ()
       noticeProgress phase =
@@ -932,7 +1001,7 @@ hasValidHaddockTargets ElaboratedConfiguredPackage{..}
         hasHaddocks = not (null (elabPkgDescription ^. componentModules name))
 
 withTempInstalledPackageInfoFile
-  :: Verbosity
+  :: HasCallStack => Verbosity
   -> FilePath
   -> (FilePath -> IO ())
   -> IO InstalledPackageInfo
@@ -946,15 +1015,15 @@ withTempInstalledPackageInfoFile verbosity tempdir action =
 
     readPkgConf "." pkgConfDest
   where
-    pkgConfParseFailed :: String -> IO a
+    pkgConfParseFailed :: HasCallStack => String -> IO a
     pkgConfParseFailed perror =
       dieWithException verbosity $ PkgConfParseFailed perror
 
-    readPkgConf :: FilePath -> FilePath -> IO InstalledPackageInfo
+    readPkgConf :: HasCallStack => FilePath -> FilePath -> IO InstalledPackageInfo
     readPkgConf pkgConfDir pkgConfFile = do
       pkgConfStr <- BS.readFile (pkgConfDir </> pkgConfFile)
       (warns, ipkg) <- case Installed.parseInstalledPackageInfo pkgConfStr of
-        Left perrors -> pkgConfParseFailed $ unlines $ NE.toList perrors
+        Left perrors -> pkgConfParseFailed $ unlines $ "While parsing:":LBS.Char8.unpack (LBS.Char8.fromStrict pkgConfStr):"encountered:":NE.toList perrors
         Right (warns, ipkg) -> return (warns, ipkg)
 
       unless (null warns) $
diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs
index b9f2cfed6c6..c211f26abca 100644
--- a/cabal-install/src/Distribution/Client/ProjectConfig.hs
+++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs
@@ -57,6 +57,7 @@ module Distribution.Client.ProjectConfig
   , BuildTimeSettings (..)
   , resolveBuildTimeSettings
   , resolveNumJobsSetting
+  , resolveProgramDb
 
     -- * Checking configuration
   , checkBadPerPackageCompilerPaths
@@ -175,6 +176,9 @@ import Distribution.Simple.InstallDirs
   )
 import Distribution.Simple.Program
   ( ConfiguredProgram (..)
+  , ProgramDb
+  , defaultProgramDb
+  , userSpecifyPaths
   )
 import Distribution.Simple.Setup
   ( Flag (Flag)
@@ -245,6 +249,7 @@ import System.IO
   )
 
 import Distribution.Deprecated.ProjectParseUtils (ProjectParseError (..), ProjectParseWarning)
+import Distribution.Simple.Program.Db (prependProgramSearchPath)
 import Distribution.Solver.Types.ProjectConfigPath
 
 ----------------------------------------
@@ -554,6 +559,12 @@ resolveNumJobsSetting projectConfigUseSemaphore projectConfigNumJobs =
       1 -> Serial
       n -> NumJobs (Just n)
 
+resolveProgramDb :: Verbosity -> PackageConfig -> IO ProgramDb
+resolveProgramDb verbosity pkgconf = do
+  let extraPath = fromNubList (packageConfigProgramPathExtra pkgconf)
+  progdb <- prependProgramSearchPath verbosity extraPath [] defaultProgramDb
+  return $ userSpecifyPaths (Map.toList (getMapLast (packageConfigProgramPaths pkgconf))) progdb
+
 ---------------------------------------------
 -- Reading and writing project config files
 --
@@ -787,6 +798,8 @@ defaultImplicitProjectConfig =
   mempty
     { -- We expect a package in the current directory.
       projectPackages = ["./*.cabal"]
+      -- By default we do not assume any special build packages.
+    , projectBuildPackages = []
     , projectConfigProvenance = Set.singleton Implicit
     }
 
@@ -1073,11 +1086,20 @@ findProjectPackages
   DistDirLayout{distProjectRootDirectory}
   ProjectConfig{..} = do
     requiredPkgs <- findPackageLocations True projectPackages
+    buildPkgs <- findPackageLocations True projectBuildPackages
     optionalPkgs <- findPackageLocations False projectPackagesOptional
     let repoPkgs = map ProjectPackageRemoteRepo projectPackagesRepo
         namedPkgs = map ProjectPackageNamed projectPackagesNamed
 
-    return (concat [requiredPkgs, optionalPkgs, repoPkgs, namedPkgs])
+    -- FIXME: We should _REALLY_ Tag the packages here somehow.
+    --        Right now we just slam together requiredPkgs and buildPkgs, ...
+    --        Maybe we can carry the Build/Host distinction in the
+    --        ProjectPackageLocation. Because we later on really want to make
+    --        sure we consider only buildPkgs for building with the Build
+    --        compiler, and all others with the Host compiler. For now we just
+    --        Assume both for both compilers, but this is not god.
+    -- XXX: FIXME!
+    return (concat [requiredPkgs, buildPkgs, optionalPkgs, repoPkgs, namedPkgs])
     where
       findPackageLocations :: Bool -> [String] -> Rebuild [ProjectPackageLocation]
       findPackageLocations required pkglocstr = do
@@ -1261,7 +1283,6 @@ mplusMaybeT ma mb = do
 fetchAndReadSourcePackages
   :: Verbosity
   -> DistDirLayout
-  -> Maybe Compiler
   -> ProjectConfigShared
   -> ProjectConfigBuildOnly
   -> [ProjectPackageLocation]
@@ -1269,7 +1290,6 @@ fetchAndReadSourcePackages
 fetchAndReadSourcePackages
   verbosity
   distDirLayout
-  compiler
   projectConfigShared
   projectConfigBuildOnly
   pkgLocations = do
@@ -1306,9 +1326,7 @@ fetchAndReadSourcePackages
       syncAndReadSourcePackagesRemoteRepos
         verbosity
         distDirLayout
-        compiler
         projectConfigShared
-        projectConfigBuildOnly
         (fromFlag (projectConfigOfflineMode projectConfigBuildOnly))
         [repo | ProjectPackageRemoteRepo repo <- pkgLocations]
 
@@ -1425,23 +1443,16 @@ fetchAndReadSourcePackageRemoteTarball
 syncAndReadSourcePackagesRemoteRepos
   :: Verbosity
   -> DistDirLayout
-  -> Maybe Compiler
   -> ProjectConfigShared
-  -> ProjectConfigBuildOnly
   -> Bool
   -> [SourceRepoList]
   -> Rebuild [PackageSpecifier (SourcePackage UnresolvedPkgLoc)]
 syncAndReadSourcePackagesRemoteRepos
   verbosity
   DistDirLayout{distDownloadSrcDirectory}
-  compiler
   ProjectConfigShared
     { projectConfigProgPathExtra
     }
-  ProjectConfigBuildOnly
-    { projectConfigUseSemaphore
-    , projectConfigNumJobs
-    }
   offlineMode
   repos = do
     repos' <-
@@ -1462,14 +1473,15 @@ syncAndReadSourcePackagesRemoteRepos
             ]
 
     let progPathExtra = fromNubList projectConfigProgPathExtra
+
     getConfiguredVCS <- delayInitSharedResources $ \repoType ->
       let vcs = Map.findWithDefault (error $ "Unknown VCS: " ++ prettyShow repoType) repoType knownVCSs
        in configureVCS verbosity progPathExtra vcs
 
-    concat
-      <$> rerunConcurrentlyIfChanged
+    x <-
+      rerunConcurrentlyIfChanged
         verbosity
-        (newJobControlFromParStrat verbosity compiler parStrat (Just maxNumFetchJobs))
+        (newParallelJobControl maxNumFetchJobs)
         [ ( monitor
           , repoGroup'
           , do
@@ -1487,8 +1499,9 @@ syncAndReadSourcePackagesRemoteRepos
                     [PackageSpecifier (SourcePackage UnresolvedPkgLoc)]
               monitor = newFileMonitor (pathStem <.> "cache")
         ]
+
+    return (concat x)
     where
-      parStrat = resolveNumJobsSetting projectConfigUseSemaphore projectConfigNumJobs
       syncRepoGroupAndReadSourcePackages
         :: VCS ConfiguredProgram
         -> FilePath
diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs
index 10858d5601d..bc8268d1506 100644
--- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs
+++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs
@@ -408,6 +408,7 @@ parseProjectSkeleton cacheDir httpTransport verbosity projectDir source (Project
 -- can redefine the parsers directly for the new types.
 data LegacyProjectConfig = LegacyProjectConfig
   { legacyPackages :: [String]
+  , legacyBuildPackages :: [String]
   , legacyPackagesOptional :: [String]
   , legacyPackagesRepo :: [SourceRepoList]
   , legacyPackagesNamed :: [PackageVersionConstraint]
@@ -613,6 +614,7 @@ convertLegacyProjectConfig :: LegacyProjectConfig -> ProjectConfig
 convertLegacyProjectConfig
   LegacyProjectConfig
     { legacyPackages
+    , legacyBuildPackages
     , legacyPackagesOptional
     , legacyPackagesRepo
     , legacyPackagesNamed
@@ -637,6 +639,7 @@ convertLegacyProjectConfig
     } =
     ProjectConfig
       { projectPackages = legacyPackages
+      , projectBuildPackages = legacyBuildPackages
       , projectPackagesOptional = legacyPackagesOptional
       , projectPackagesRepo = legacyPackagesRepo
       , projectPackagesNamed = legacyPackagesNamed
@@ -704,6 +707,11 @@ convertLegacyAllPackageFlags
 convertLegacyAllPackageFlags globalFlags configFlags configExFlags installFlags projectFlags projectConfigMultiRepl =
   ProjectConfigShared{..}
   where
+    projectConfigPackageDBs = (fmap . fmap) (interpretPackageDB Nothing) projectConfigPackageDBs_
+    projectConfigBuildPackageDBs = (fmap . fmap) (interpretPackageDB Nothing) projectConfigBuildPackageDBs_
+    projectConfigHookHashes = mempty -- :: Map FilePath HookAccept
+    projectConfigDistDir = fmap getSymbolicPath projectConfigAbsoluteDistDir
+
     GlobalFlags
       { globalConfigFile = projectConfigConfigFile
       , globalRemoteRepos = projectConfigRemoteRepos
@@ -713,8 +721,6 @@ convertLegacyAllPackageFlags globalFlags configFlags configExFlags installFlags
       , globalStoreDir = projectConfigStoreDir
       } = globalFlags
 
-    projectConfigPackageDBs = (fmap . fmap) (interpretPackageDB Nothing) projectConfigPackageDBs_
-
     ConfigFlags
       { configCommonFlags = commonFlags
       , configHcFlavor = projectConfigHcFlavor
@@ -730,8 +736,6 @@ convertLegacyAllPackageFlags globalFlags configFlags configExFlags installFlags
       { setupDistPref = projectConfigAbsoluteDistDir
       } = commonFlags
 
-    projectConfigDistDir = fmap getSymbolicPath projectConfigAbsoluteDistDir
-
     ConfigExFlags
       { configCabalVersion = projectConfigCabalVersion
       , configExConstraints = projectConfigConstraints
@@ -741,6 +745,10 @@ convertLegacyAllPackageFlags globalFlags configFlags configExFlags installFlags
       , configAllowNewer = projectConfigAllowNewer
       , configWriteGhcEnvironmentFilesPolicy =
         projectConfigWriteGhcEnvironmentFilesPolicy
+      , configBuildHcFlavor = projectConfigBuildHcFlavor
+      , configBuildHcPath = projectConfigBuildHcPath
+      , configBuildHcPkg = projectConfigBuildHcPkg
+      , configBuildPackageDBs = projectConfigBuildPackageDBs_
       } = configExFlags
 
     InstallFlags
@@ -931,6 +939,7 @@ convertToLegacyProjectConfig :: ProjectConfig -> LegacyProjectConfig
 convertToLegacyProjectConfig
   projectConfig@ProjectConfig
     { projectPackages
+    , projectBuildPackages
     , projectPackagesOptional
     , projectPackagesRepo
     , projectPackagesNamed
@@ -940,6 +949,7 @@ convertToLegacyProjectConfig
     } =
     LegacyProjectConfig
       { legacyPackages = projectPackages
+      , legacyBuildPackages = projectBuildPackages
       , legacyPackagesOptional = projectPackagesOptional
       , legacyPackagesRepo = projectPackagesRepo
       , legacyPackagesNamed = projectPackagesNamed
@@ -1010,7 +1020,7 @@ convertToLegacySharedConfig
           }
 
       configExFlags =
-        ConfigExFlags
+        mempty
           { configCabalVersion = projectConfigCabalVersion
           , configAppend = mempty
           , configBackup = mempty
@@ -1311,6 +1321,12 @@ legacyProjectConfigFieldDescrs constraintSrc =
       parsePackageLocationTokenQ
       legacyPackages
       (\v flags -> flags{legacyPackages = v})
+  , newLineListField
+      "build-packages"
+      (Disp.text . renderPackageLocationToken)
+      parsePackageLocationTokenQ
+      legacyBuildPackages
+      (\v flags -> flags{legacyBuildPackages = v})
   , newLineListField
       "optional-packages"
       (Disp.text . renderPackageLocationToken)
diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs
index 1a2b6ae2fa6..0f82fbcc12f 100644
--- a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs
+++ b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs
@@ -33,6 +33,7 @@ import Distribution.Client.BuildReports.Types
 import Distribution.Client.Dependency.Types
   ( PreSolver
   )
+import Distribution.Client.HookAccept (HookAccept (..))
 import Distribution.Client.Targets
   ( UserConstraint
   )
@@ -128,6 +129,11 @@ data ProjectConfig = ProjectConfig
   -- ^ Packages in this project, including local dirs, local .cabal files
   -- local and remote tarballs. When these are file globs, they must
   -- match at least one package.
+  , projectBuildPackages :: [String]
+  -- ^ Packages in this project, including local dirs, local .cabal files
+  -- local and remote tarballs considered only for build-time dependencies
+  -- (build-type: Custom orHooks; build-depends, ...). When these are file
+  -- globs, they must match at least one package.
   , projectPackagesOptional :: [String]
   -- ^ Like 'projectConfigPackageGlobs' but /optional/ in the sense that
   -- file globs are allowed to match nothing. The primary use case for
@@ -191,6 +197,9 @@ data ProjectConfigShared = ProjectConfigShared
   , projectConfigHcFlavor :: Flag CompilerFlavor
   , projectConfigHcPath :: Flag FilePath
   , projectConfigHcPkg :: Flag FilePath
+  , projectConfigBuildHcFlavor :: Flag CompilerFlavor
+  , projectConfigBuildHcPath :: Flag FilePath
+  , projectConfigBuildHcPkg :: Flag FilePath
   , projectConfigHaddockIndex :: Flag PathTemplate
   , -- Only makes sense for manual mode, not --local mode
     -- too much control!
@@ -198,6 +207,7 @@ data ProjectConfigShared = ProjectConfigShared
 
     projectConfigInstallDirs :: InstallDirs (Flag PathTemplate)
   , projectConfigPackageDBs :: [Maybe PackageDBCWD]
+  , projectConfigBuildPackageDBs :: [Maybe PackageDBCWD]
   , -- configuration used both by the solver and other phases
     projectConfigRemoteRepos :: NubList RemoteRepo
   -- ^ Available Hackage servers.
@@ -227,6 +237,7 @@ data ProjectConfigShared = ProjectConfigShared
   , projectConfigPreferOldest :: Flag PreferOldest
   , projectConfigProgPathExtra :: NubList FilePath
   , projectConfigMultiRepl :: Flag Bool
+  , projectConfigHookHashes :: Map FilePath HookAccept
   -- More things that only make sense for manual mode, not --local mode
   -- too much control!
   -- projectConfigShadowPkgs        :: Flag Bool,
diff --git a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs
index a14d43e4b99..6d91783326a 100644
--- a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs
+++ b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs
@@ -176,6 +176,8 @@ import qualified Data.List.NonEmpty as NE
 import qualified Data.Map as Map
 import qualified Data.Set as Set
 import Distribution.Client.Errors
+import Distribution.Client.HookAccept (loadHookHasheshMap)
+
 import Distribution.Package
 import Distribution.Simple.Command (commandShowOptions)
 import Distribution.Simple.Compiler
@@ -363,6 +365,8 @@ withInstallPlan
     , installedPackages
     }
   action = do
+    hookHashes <- loadHookHasheshMap (projectConfigConfigFile $ projectConfigShared projectConfig)
+
     -- Take the project configuration and make a plan for how to build
     -- everything in the project. This is independent of any specific targets
     -- the user has asked for.
@@ -370,6 +374,7 @@ withInstallPlan
     (elaboratedPlan, _, elaboratedShared, _, _) <-
       rebuildInstallPlan
         verbosity
+        hookHashes
         distDirLayout
         cabalDirLayout
         projectConfig
@@ -392,6 +397,8 @@ runProjectPreBuildPhase
     , installedPackages
     }
   selectPlanSubset = do
+    hookHashes <- loadHookHasheshMap (projectConfigConfigFile $ projectConfigShared projectConfig)
+
     -- Take the project configuration and make a plan for how to build
     -- everything in the project. This is independent of any specific targets
     -- the user has asked for.
@@ -399,6 +406,7 @@ runProjectPreBuildPhase
     (elaboratedPlan, _, elaboratedShared, _, _) <-
       rebuildInstallPlan
         verbosity
+        hookHashes
         distDirLayout
         cabalDirLayout
         projectConfig
@@ -517,7 +525,7 @@ runProjectPostBuildPhase
             AlwaysWriteGhcEnvironmentFiles -> True
             NeverWriteGhcEnvironmentFiles -> False
             WriteGhcEnvironmentFilesOnlyForGhc844AndNewer ->
-              let compiler = pkgConfigCompiler elaboratedShared
+              let compiler = toolchainCompiler $ buildToolchain $ pkgConfigToolchains elaboratedShared
                   ghcCompatVersion = compilerCompatVersion GHC compiler
                in maybe False (>= mkVersion [8, 4, 4]) ghcCompatVersion
 
@@ -651,7 +659,7 @@ resolveTargets
       checkTarget bt@(TargetPackage _ (ordNub -> [pkgid]) mkfilter)
         | Just ats <-
             fmap (maybe id filterTargetsKind mkfilter) $
-              Map.lookup pkgid availableTargetsByPackageId =
+                (Map.lookup pkgid availableTargetsByPackageId) =
             fmap (componentTargets WholeComponent) $
               selectPackageTargets bt ats
         | otherwise =
@@ -677,9 +685,17 @@ resolveTargets
           $ concat (Map.elems availableTargetsByPackageId)
       checkTarget (TargetComponent pkgid cname subtarget)
         | Just ats <-
-            Map.lookup
-              (pkgid, cname)
-              availableTargetsByPackageIdAndComponentName =
+            -- FIXME: this is stupid. We do not know what the target selectors HOST compiler is...
+            --        so we'll assume tere is only a _SINGLE_ match in the map if we ignore the pkgCompiler.
+            -- This lookup is now O(n) instead of O(log n).
+              (case [v | ((k,k'),v) <- Map.toList availableTargetsByPackageIdAndComponentName
+                      , k{pkgCompiler = Nothing} == pkgid
+                      , k' == cname] of
+                [match] -> Just match)
+              -- (Map.lookup
+              --   (pkgid, cname)
+              --   availableTargetsByPackageIdAndComponentName))
+                =
             fmap (componentTargets subtarget) $
               selectComponentTargets subtarget ats
         | Map.member pkgid availableTargetsByPackageId =
@@ -993,6 +1009,8 @@ printPlan
       showPkgAndReason (ReadyPackage elab) =
         unwords $
           filter (not . null) $
+            -- FIXME: ideally we'd like to display the compiler in there as well.
+            -- we do have access to elabStage, but the toolchain isn't around.
             [ " -"
             , if verbosity >= deafening
                 then prettyShow (installedUnitId elab)
@@ -1081,7 +1099,7 @@ printPlan
          in -- Not necessary to "escape" it, it's just for user output
             unwords . ("" :) $
               commandShowOptions
-                (Setup.configureCommand (pkgConfigCompilerProgs elaboratedShared))
+                (Setup.configureCommand (toolchainProgramDb $ buildToolchain $ pkgConfigToolchains elaboratedShared))
                 partialConfigureFlags
 
       showBuildStatus :: BuildStatus -> String
@@ -1113,7 +1131,9 @@ printPlan
       showBuildProfile =
         "Build profile: "
           ++ unwords
-            [ "-w " ++ (showCompilerId . pkgConfigCompiler) elaboratedShared
+            [ "-w " ++ (showCompilerId . toolchainCompiler . hostToolchain . pkgConfigToolchains) elaboratedShared
+            -- FIXME: this should only be shown if hostToolchain /= buildToolchain
+            , "-W " ++ (showCompilerId . toolchainCompiler . buildToolchain . pkgConfigToolchains) elaboratedShared
             , "-O"
                 ++ ( case globalOptimization <> localOptimization of -- if local is not set, read global
                       Setup.Flag NoOptimisation -> "0"
@@ -1126,8 +1146,8 @@ printPlan
 
 writeBuildReports :: BuildTimeSettings -> ProjectBuildContext -> ElaboratedInstallPlan -> BuildOutcomes -> IO ()
 writeBuildReports settings buildContext plan buildOutcomes = do
-  let plat@(Platform arch os) = pkgConfigPlatform . elaboratedShared $ buildContext
-      comp = pkgConfigCompiler . elaboratedShared $ buildContext
+  let plat@(Platform arch os) = toolchainPlatform . buildToolchain . pkgConfigToolchains . elaboratedShared $ buildContext
+      comp = toolchainCompiler . buildToolchain . pkgConfigToolchains . elaboratedShared $ buildContext
       getRepo (RepoTarballPackage r _ _) = Just r
       getRepo _ = Nothing
       fromPlanPackage (InstallPlan.Configured pkg) (Just result) =
diff --git a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs
index b6b5dc8dd79..a38a792481e 100644
--- a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs
+++ b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs
@@ -46,9 +46,7 @@ import Distribution.Simple.BuildPaths
   )
 import Distribution.Simple.Compiler
 import Distribution.Simple.GHC
-  ( GhcEnvironmentFileEntry (..)
-  , GhcImplInfo (supportsPkgEnvFiles)
-  , getImplInfo
+  ( GhcEnvironmentFileEntry (GhcEnvFileComment)
   , simpleGhcEnvironmentFile
   , writeGhcEnvironmentFile
   )
@@ -57,9 +55,9 @@ import Distribution.System
 import Distribution.Types.Version
   ( mkVersion
   )
-import Distribution.Utils.Path hiding
-  ( (<.>)
-  , (</>)
+import Distribution.Utils.Path
+  ( getSymbolicPath
+  , makeSymbolicPath
   )
 import Distribution.Verbosity
 
@@ -107,7 +105,7 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig =
     [ "cabal-version" J..= jdisplay cabalInstallVersion
     , "cabal-lib-version" J..= jdisplay cabalVersion
     , "compiler-id"
-        J..= (J.String . showCompilerId . pkgConfigCompiler)
+        J..= (J.String . showCompilerId . toolchainCompiler . hostToolchain . pkgConfigToolchains)
           elaboratedSharedConfig
     , "os" J..= jdisplay os
     , "arch" J..= jdisplay arch
@@ -115,7 +113,7 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig =
     ]
   where
     plat :: Platform
-    plat@(Platform arch os) = pkgConfigPlatform elaboratedSharedConfig
+    plat@(Platform arch os) = toolchainPlatform . hostToolchain . pkgConfigToolchains $ elaboratedSharedConfig
 
     installPlanToJ :: ElaboratedInstallPlan -> [J.Value]
     installPlanToJ = map planPackageToJ . InstallPlan.toList
@@ -799,7 +797,7 @@ createPackageEnvironment
   elaboratedPlan
   elaboratedShared
   buildStatus
-    | compilerFlavor (pkgConfigCompiler elaboratedShared) == GHC =
+    | compilerFlavor (toolchainCompiler $ hostToolchain $ pkgConfigToolchains elaboratedShared) == GHC =
         do
           envFileM <-
             writePlanGhcEnvironment
@@ -829,29 +827,30 @@ writePlanGhcEnvironment
 writePlanGhcEnvironment
   path
   elaboratedInstallPlan
-  ElaboratedSharedConfig
-    { pkgConfigCompiler = compiler
-    , pkgConfigPlatform = platform
-    }
-  postBuildStatus
-    | compilerFlavor compiler == GHC
-    , supportsPkgEnvFiles (getImplInfo compiler) =
-        -- TODO: check ghcjs compat
-        fmap Just $
-          writeGhcEnvironmentFile
+  elaboratedSharedConfig
+  postBuildStatus =
+    -- \| compilerFlavor compiler == GHC
+    -- , supportsPkgEnvFiles (getImplInfo compiler) =
+
+    -- TODO: check ghcjs compat
+    fmap Just $
+      writeGhcEnvironmentFile
+        path
+        -- FIXME
+        (toolchainPlatform (hostToolchain (pkgConfigToolchains elaboratedSharedConfig)))
+        -- FIXME
+        (compilerVersion (toolchainCompiler (hostToolchain (pkgConfigToolchains elaboratedSharedConfig))))
+        ( renderGhcEnvironmentFile
             path
-            platform
-            (compilerVersion compiler)
-            ( renderGhcEnvironmentFile
-                path
-                elaboratedInstallPlan
-                postBuildStatus
-            )
+            elaboratedInstallPlan
+            postBuildStatus
+        )
+
 -- TODO: [required eventually] support for writing user-wide package
 -- environments, e.g. like a global project, but we would not put the
 -- env file in the home dir, rather it lives under ~/.ghc/
 
-writePlanGhcEnvironment _ _ _ _ = return Nothing
+-- writePlanGhcEnvironment _ _ _ _ = return Nothing
 
 renderGhcEnvironmentFile
   :: FilePath
diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs
index c04bca730d7..8df269624f4 100644
--- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs
+++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs
@@ -95,6 +95,9 @@ module Distribution.Client.ProjectPlanning
   , binDirectories
   , storePackageInstallDirs
   , storePackageInstallDirs'
+
+    -- * Re-exports for backward compatibility
+  , programDbSignature
   ) where
 
 import Distribution.Client.Compat.Prelude
@@ -117,6 +120,7 @@ import Distribution.Client.Dependency
 import Distribution.Client.DistDirLayout
 import Distribution.Client.FetchUtils
 import Distribution.Client.HashValue
+import Distribution.Client.HookAccept (HookAccept)
 import Distribution.Client.HttpUtils
 import Distribution.Client.JobControl
 import Distribution.Client.PackageHash
@@ -163,6 +167,7 @@ import Distribution.Solver.Types.Settings
 import Distribution.Solver.Types.SolverId
 import Distribution.Solver.Types.SolverPackage
 import Distribution.Solver.Types.SourcePackage
+import Distribution.Solver.Types.Stage
 
 import Distribution.ModuleName
 import Distribution.Package
@@ -176,7 +181,7 @@ import Distribution.Simple.LocalBuildInfo
   )
 
 import Distribution.Simple.BuildWay
-import Distribution.Simple.PackageIndex (InstalledPackageIndex)
+import Distribution.Simple.PackageIndex (InstalledPackageIndex, allPackages)
 import Distribution.Simple.Program
 import Distribution.Simple.Program.Db
 import Distribution.Simple.Program.Find
@@ -193,6 +198,7 @@ import Distribution.Types.GivenComponent
 import Distribution.Types.LibraryName
 import qualified Distribution.Types.LocalBuildConfig as LBC
 import Distribution.Types.PackageVersionConstraint
+import qualified Distribution.Types.PackageId as PI
 import Distribution.Types.PkgconfigDependency
 import Distribution.Types.UnqualComponentName
 
@@ -215,8 +221,11 @@ import qualified Distribution.Simple.GHC as GHC
 import qualified Distribution.Simple.GHCJS as GHCJS
 import qualified Distribution.Simple.InstallDirs as InstallDirs
 import qualified Distribution.Simple.LocalBuildInfo as Cabal
+import qualified Distribution.Simple.PackageIndex as PI
 import qualified Distribution.Simple.Setup as Cabal
 import qualified Distribution.Solver.Types.ComponentDeps as CD
+-- import Distribution.Solver.Types.Stage
+import Distribution.Solver.Types.Toolchain
 
 import qualified Distribution.Compat.Graph as Graph
 
@@ -234,6 +243,9 @@ import Distribution.Solver.Types.ProjectConfigPath
 import System.FilePath
 import qualified Text.PrettyPrint as Disp
 
+import GHC.Stack (HasCallStack)
+-- import qualified Distribution.Solver.Types.PackageIndex as XXX
+
 -- | Check that an 'ElaboratedConfiguredPackage' actually makes
 -- sense under some 'ElaboratedSharedConfig'.
 sanityCheckElaboratedConfiguredPackage
@@ -397,14 +409,14 @@ rebuildProjectConfig
           let fetchCompiler = do
                 -- have to create the cache directory before configuring the compiler
                 liftIO $ createDirectoryIfMissingVerbose verbosity True distProjectCacheDirectory
-                (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout (fst (PD.ignoreConditions projectConfigSkeleton) <> cliConfig)
-                pure (os, arch, compiler)
+                Toolchains{hostToolchain = Toolchain{toolchainCompiler = compiler, toolchainPlatform = Platform arch os}} <- configureCompiler verbosity distDirLayout (fst (PD.ignoreConditions projectConfigSkeleton) <> cliConfig)
+                return (os, arch, compiler)
 
-          (projectConfig, compiler) <- instantiateProjectConfigSkeletonFetchingCompiler fetchCompiler mempty projectConfigSkeleton
+          (projectConfig, _compiler) <- instantiateProjectConfigSkeletonFetchingCompiler fetchCompiler mempty projectConfigSkeleton
           when (projectConfigDistDir (projectConfigShared $ projectConfig) /= NoFlag) $
             liftIO $
               warn verbosity "The builddir option is not supported in project and config files. It will be ignored."
-          localPackages <- phaseReadLocalPackages compiler (projectConfig <> cliConfig)
+          localPackages <- phaseReadLocalPackages (projectConfig <> cliConfig)
           return (projectConfig, localPackages)
 
     let configfiles =
@@ -436,11 +448,9 @@ rebuildProjectConfig
       -- NOTE: These are all packages mentioned in the project configuration.
       -- Whether or not they will be considered local to the project will be decided by `shouldBeLocal`.
       phaseReadLocalPackages
-        :: Maybe Compiler
-        -> ProjectConfig
+        :: ProjectConfig
         -> Rebuild [PackageSpecifier UnresolvedSourcePackage]
       phaseReadLocalPackages
-        compiler
         projectConfig@ProjectConfig
           { projectConfigShared
           , projectConfigBuildOnly
@@ -455,7 +465,6 @@ rebuildProjectConfig
           fetchAndReadSourcePackages
             verbosity
             distDirLayout
-            compiler
             projectConfigShared
             projectConfigBuildOnly
             pkgLocations
@@ -464,7 +473,7 @@ configureCompiler
   :: Verbosity
   -> DistDirLayout
   -> ProjectConfig
-  -> Rebuild (Compiler, Platform, ProgramDb)
+  -> Rebuild Toolchains
 configureCompiler
   verbosity
   DistDirLayout
@@ -476,9 +485,12 @@ configureCompiler
         { projectConfigHcFlavor
         , projectConfigHcPath
         , projectConfigHcPkg
+        , projectConfigBuildHcFlavor
+        , projectConfigBuildHcPath
+        , projectConfigBuildHcPkg
         }
     , projectConfigLocalPackages =
-      PackageConfig
+      projectConfigLocalPackages@PackageConfig
         { packageConfigProgramPaths
         , packageConfigProgramPathExtra
         }
@@ -499,37 +511,55 @@ configureCompiler
       )
       $ do
         liftIO $ info verbosity "Compiler settings changed, reconfiguring..."
-        let extraPath = fromNubList packageConfigProgramPathExtra
-        progdb <- liftIO $ prependProgramSearchPath verbosity extraPath [] defaultProgramDb
-        let progdb' = userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) progdb
-        result@(_, _, progdb'') <-
-          liftIO $
-            Cabal.configCompilerEx
-              hcFlavor
-              hcPath
-              hcPkg
-              progdb'
-              verbosity
 
-        -- Note that we added the user-supplied program locations and args
-        -- for /all/ programs, not just those for the compiler prog and
-        -- compiler-related utils. In principle we don't know which programs
-        -- the compiler will configure (and it does vary between compilers).
-        -- We do know however that the compiler will only configure the
-        -- programs it cares about, and those are the ones we monitor here.
-        monitorFiles (programsMonitorFiles progdb'')
-
-        -- Note: There is currently a bug here: we are dropping unconfigured
-        -- programs from the 'ProgramDb' when we re-use the cache created by
-        -- 'rerunIfChanged'.
-        --
-        -- See Note [Caching the result of configuring the compiler]
+        defdb <- liftIO $ resolveProgramDb verbosity projectConfigLocalPackages
+
+        buildToolchain <- do
+          (compiler, platform, progdb) <-
+            liftIO $
+              Cabal.configCompilerEx
+                buildHcFlavor
+                buildHcPath
+                buildHcPkg
+                defdb
+                verbosity
+
+          -- Note that we added the user-supplied program locations and args
+          -- for /all/ programs, not just those for the compiler prog and
+          -- compiler-related utils. In principle we don't know which programs
+          -- the compiler will configure (and it does vary between compilers).
+          -- We do know however that the compiler will only configure the
+          -- programs it cares about, and those are the ones we monitor here.
+          monitorFiles (programsMonitorFiles progdb)
+          return Toolchain{toolchainCompiler = compiler, toolchainPlatform = platform, toolchainProgramDb = progdb}
+
+        hostToolchain <- do
+          (compiler, platform, progdb) <-
+            liftIO $
+              Cabal.configCompilerEx
+                hcFlavor
+                hcPath
+                hcPkg
+                defdb
+                verbosity
 
-        return result
+          -- Note that we added the user-supplied program locations and args
+          -- for /all/ programs, not just those for the compiler prog and
+          -- compiler-related utils. In principle we don't know which programs
+          -- the compiler will configure (and it does vary between compilers).
+          -- We do know however that the compiler will only configure the
+          -- programs it cares about, and those are the ones we monitor here.
+          monitorFiles (programsMonitorFiles progdb)
+          return Toolchain{toolchainCompiler = compiler, toolchainPlatform = platform, toolchainProgramDb = progdb}
+
+        return Toolchains{buildToolchain, hostToolchain}
     where
       hcFlavor = flagToMaybe projectConfigHcFlavor
       hcPath = flagToMaybe projectConfigHcPath
       hcPkg = flagToMaybe projectConfigHcPkg
+      buildHcFlavor = flagToMaybe projectConfigBuildHcFlavor <|> flagToMaybe projectConfigHcFlavor
+      buildHcPath = flagToMaybe projectConfigBuildHcPath <|> flagToMaybe projectConfigHcPath
+      buildHcPkg = flagToMaybe projectConfigBuildHcPkg <|> flagToMaybe projectConfigHcPkg
 
 {- Note [Caching the result of configuring the compiler]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -589,6 +619,7 @@ Binary ProgramDb instance.
 --
 rebuildInstallPlan
   :: Verbosity
+  -> Map FilePath HookAccept
   -> DistDirLayout
   -> CabalDirLayout
   -> ProjectConfig
@@ -604,6 +635,7 @@ rebuildInstallPlan
   -- ^ @(improvedPlan, elaboratedPlan, _, _, _)@
 rebuildInstallPlan
   verbosity
+  hookHashes
   distDirLayout@DistDirLayout
     { distProjectRootDirectory
     , distProjectCacheFile
@@ -621,7 +653,7 @@ rebuildInstallPlan
         fileMonitorImprovedPlan
         -- react to changes in the project config,
         -- the package .cabal files and the path
-        (projectConfigMonitored, localPackages, progsearchpath)
+        (projectConfigMonitored, localPackages, progsearchpath, hookHashes)
         $ do
           -- And so is the elaborated plan that the improved plan based on
           (elaboratedPlan, elaboratedShared, totalIndexState, activeRepos) <-
@@ -631,14 +663,16 @@ rebuildInstallPlan
               ( projectConfigMonitored
               , localPackages
               , progsearchpath
+              , hookHashes
               )
               $ do
-                compilerEtc <- phaseConfigureCompiler projectConfig
-                _ <- phaseConfigurePrograms projectConfig compilerEtc
+                toolchains <- phaseConfigureToolchain projectConfig
+
+                -- _ <- phaseConfigurePrograms projectConfig compilerEtc
                 (solverPlan, pkgConfigDB, totalIndexState, activeRepos) <-
                   phaseRunSolver
                     projectConfig
-                    compilerEtc
+                    toolchains
                     localPackages
                     (fromMaybe mempty mbInstalledPackages)
                 ( elaboratedPlan
@@ -646,7 +680,7 @@ rebuildInstallPlan
                   ) <-
                   phaseElaboratePlan
                     projectConfig
-                    compilerEtc
+                    toolchains
                     pkgConfigDB
                     solverPlan
                     localPackages
@@ -674,10 +708,11 @@ rebuildInstallPlan
       -- This is moderately expensive and doesn't change that often so we cache
       -- it independently.
       --
-      phaseConfigureCompiler
+      phaseConfigureToolchain
         :: ProjectConfig
-        -> Rebuild (Compiler, Platform, ProgramDb)
-      phaseConfigureCompiler = configureCompiler verbosity distDirLayout
+        -> Rebuild Toolchains
+      phaseConfigureToolchain = do
+        configureCompiler verbosity distDirLayout
 
       -- Configuring other programs.
       --
@@ -691,19 +726,19 @@ rebuildInstallPlan
       -- all local packages, but that all the programs configured so far are the
       -- compiler program or related util programs.
       --
-      phaseConfigurePrograms
-        :: ProjectConfig
-        -> (Compiler, Platform, ProgramDb)
-        -> Rebuild ()
-      phaseConfigurePrograms projectConfig (_, _, compilerprogdb) = do
-        -- Users are allowed to specify program locations independently for
-        -- each package (e.g. to use a particular version of a pre-processor
-        -- for some packages). However they cannot do this for the compiler
-        -- itself as that's just not going to work. So we check for this.
-        liftIO $
-          checkBadPerPackageCompilerPaths
-            (configuredPrograms compilerprogdb)
-            (getMapMappend (projectConfigSpecificPackage projectConfig))
+      -- phaseConfigurePrograms
+      --   :: ProjectConfig
+      --   -> (Compiler, Platform, ProgramDb)
+      --   -> Rebuild ()
+      -- phaseConfigurePrograms projectConfig (_, _, compilerprogdb) = do
+      --   -- Users are allowed to specify program locations independently for
+      --   -- each package (e.g. to use a particular version of a pre-processor
+      --   -- for some packages). However they cannot do this for the compiler
+      --   -- itself as that's just not going to work. So we check for this.
+      --   liftIO $
+      --     checkBadPerPackageCompilerPaths
+      --       (configuredPrograms compilerprogdb)
+      --       (getMapMappend (projectConfigSpecificPackage projectConfig))
 
       -- TODO: [required eventually] find/configure other programs that the
       -- user specifies.
@@ -716,7 +751,7 @@ rebuildInstallPlan
       --
       phaseRunSolver
         :: ProjectConfig
-        -> (Compiler, Platform, ProgramDb)
+        -> Toolchains
         -> [PackageSpecifier UnresolvedSourcePackage]
         -> InstalledPackageIndex
         -> Rebuild (SolverInstallPlan, Maybe PkgConfigDb, IndexUtils.TotalIndexState, IndexUtils.ActiveRepos)
@@ -725,34 +760,92 @@ rebuildInstallPlan
           { projectConfigShared
           , projectConfigBuildOnly
           }
-        (compiler, platform, progdb)
-        localPackages
+        toolchains
+        localPackages_
         installedPackages =
           rerunIfChanged
             verbosity
             fileMonitorSolverPlan
             ( solverSettings
-            , localPackages
+            , localPackages_
             , localPackagesEnabledStanzas
-            , compiler
-            , platform
-            , programDbSignature progdb
+            , toolchains
+            , hookHashes
             )
             $ do
-              installedPkgIndex <-
+              -- InstalledPackageIndex
+              -- type InstalledPackageIndex = PackageIndex IPI.InstalledPackageInfo
+              -- data PackageIndex a = PackageIndex
+              -- { -- The primary index. Each InstalledPackageInfo record is uniquely identified
+              --   -- by its UnitId.
+              --   --
+              --   unitIdIndex :: !(Map UnitId a)
+              -- , -- This auxiliary index maps package names (case-sensitively) to all the
+              --   -- versions and instances of that package. This allows us to find all
+              --   -- versions satisfying a dependency.
+              --   --
+              --   -- It is a three-level index. The first level is the package name,
+              --   -- the second is the package version and the final level is instances
+              --   -- of the same package version. These are unique by UnitId
+              --   -- and are kept in preference order.
+              --   --
+              --   -- FIXME: Clarify what "preference order" means. Check that this invariant is
+              --   -- preserved. See #1463 for discussion.
+              --   packageIdIndex :: !(Map (PackageName, LibraryName) (Map Version [a]))
+              -- }
+              -- deriving (Eq, Generic, Show, Read)
+              --
+              -- let mapPkgIdx f = PI.fromList . map f . PI.allPackages
+              -- let updateIPI :: IPI.InstalledPackageInfo -> IPI.InstalledPackageInfo
+              --     updateIPI ipi = ipi {
+              --       IPI.sourcePackageId = (IPI.sourcePackageId ipi){ PI.pkgCompiler = IPI.pkgCompiler ipi }
+              --      }
+
+              hinstalledPkgIndex <-
+                -- mapPkgIdx updateIPI <$>
                 getInstalledPackages
                   verbosity
-                  compiler
-                  progdb
-                  platform
-                  corePackageDbs
-              (sourcePkgDb, tis, ar) <-
+                  (hostToolchain toolchains)
+                  (corePackageDbs Host)
+              -- this is an aweful hack, however `getInstalledPackages` is
+              -- terribly invovled everywhere so we'll have to do with this
+              -- for now. FIXME!
+              -- let hinstalledPkgIndex' = PI.fromList $ PI.allPackages hinstalledPkgIndex
+              binstalledPkgIndex <-
+                -- mapPkgIdx updateIPI <$>
+                getInstalledPackages
+                  verbosity
+                  (buildToolchain toolchains)
+                  -- FIXME: HACK
+                  -- if host and build compiler are the same, we want to get -package-db in here.
+                  (corePackageDbs $ if buildIsHost toolchains then Host else Build)
+
+              -- getSourcePackages
+              --   :: Verbosity
+              --   -> (forall a. (RepoContext -> IO a) -> IO a)
+              --   -> Maybe IndexUtils.TotalIndexState
+              --   -> Maybe IndexUtils.ActiveRepos
+              --   -> Rebuild (SourcePackageDb, IndexUtils.TotalIndexState, IndexUtils.ActiveRepos)
+
+              -- data SourcePackageDb = SourcePackageDb
+              --   { packageIndex :: PackageIndex UnresolvedSourcePackage
+              --   , packagePreferences :: Map PackageName VersionRange
+              --   }
+
+              -- NOTE: sourcePkgDbs is the stuff that we pull from Hackage
+              --       and similar Indices!
+              (sourcePkgDb_, tis, ar) <-
                 getSourcePackages
                   verbosity
                   withRepoCtx
                   (solverSettingIndexState solverSettings)
                   (solverSettingActiveRepos solverSettings)
-              pkgConfigDB <- getPkgConfigDb verbosity progdb
+
+              -- FIXME: we are duplicating the index here, bad for memory use :-/
+              let sourcePkgDb = sourcePkgDb_{packageIndex =  (addCompilerIdToSrcPkg (compilerIdFor Build toolchains) <$> packageIndex sourcePkgDb_)
+                                                          <> (addCompilerIdToSrcPkg (compilerIdFor Host  toolchains) <$> packageIndex sourcePkgDb_) }
+
+              pkgConfigDB <- getPkgConfigDb verbosity (toolchainProgramDb $ buildToolchain toolchains)
 
               -- TODO: [code cleanup] it'd be better if the Compiler contained the
               -- ConfiguredPrograms that it needs, rather than relying on the progdb
@@ -761,27 +854,52 @@ rebuildInstallPlan
 
               liftIO $ do
                 notice verbosity "Resolving dependencies..."
+                -- putStrLn "== installedPackages"
+                -- putStrLn $ unlines $ map (prettyShow . IPI.installedUnitId) $ PI.allPackages installedPackages
+                -- putStrLn "== binstalledPackages"
+                -- putStrLn $ unlines $ map (prettyShow . IPI.installedUnitId) $ PI.allPackages binstalledPkgIndex
+                -- putStrLn "== hinstalledPackages"
+                -- putStrLn $ unlines $ map (prettyShow . IPI.installedUnitId) $ PI.allPackages hinstalledPkgIndex
+                -- putStrLn "== localPackages"
+                -- putStrLn $ unlines . map (prettyShow . srcpkgPackageId) $ [pkg | SpecificSourcePackage pkg <- localPackages]
+                -- putStrLn $ unlines . take 20 $ map (prettyShow . srcpkgPackageId) (XXX.allPackages (packageIndex sourcePkgDb))
                 planOrError <-
                   foldProgress logMsg (pure . Left) (pure . Right) $
                     planPackages
                       verbosity
-                      compiler
-                      platform
+                      toolchains
                       solverSettings
-                      (installedPackages <> installedPkgIndex)
+                      binstalledPkgIndex
+                      (installedPackages <> hinstalledPkgIndex)
                       sourcePkgDb
                       pkgConfigDB
                       localPackages
                       localPackagesEnabledStanzas
                 case planOrError of
                   Left msg -> do
-                    reportPlanningFailure projectConfig compiler platform localPackages
+                    reportPlanningFailure projectConfig (hostToolchain toolchains) localPackages
                     dieWithException verbosity $ PhaseRunSolverErr msg
                   Right plan -> return (plan, pkgConfigDB, tis, ar)
           where
-            corePackageDbs :: PackageDBStackCWD
-            corePackageDbs =
-              Cabal.interpretPackageDbFlags False (projectConfigPackageDBs projectConfigShared)
+            -- FIXME: See FIXME in Distribution.Client.ProjectConfig wrt to buildPackages.
+            addCompilerToSourcePkg :: CompilerId -> [PackageSpecifier UnresolvedSourcePackage] -> [PackageSpecifier UnresolvedSourcePackage]
+            addCompilerToSourcePkg compilerId = map (addCompilerId compilerId)
+            addCompilerId :: CompilerId -> PackageSpecifier UnresolvedSourcePackage -> PackageSpecifier UnresolvedSourcePackage
+            addCompilerId compilerId (NamedPackage name props) = NamedPackage name props
+            addCompilerId compilerId (SpecificSourcePackage pkg) = SpecificSourcePackage (addCompilerIdToSrcPkg compilerId pkg)
+            addCompilerIdToSrcPkg :: CompilerId -> SourcePackage UnresolvedPkgLoc -> SourcePackage UnresolvedPkgLoc
+            addCompilerIdToSrcPkg compilerId pkg = pkg{srcpkgPackageId = (srcpkgPackageId pkg){pkgCompiler = Just compilerId}}
+            localPackages = (addCompilerToSourcePkg (compilerIdFor Build toolchains) localPackages_)
+                         <> (addCompilerToSourcePkg (compilerIdFor Host  toolchains) localPackages_)
+            corePackageDbs :: Stage -> PackageDBStackCWD
+            corePackageDbs stage =
+              Cabal.interpretPackageDbFlags False (packageDBs stage)
+
+            packageDBs Host = projectConfigPackageDBs projectConfigShared
+            packageDBs Build
+              | buildIsHost toolchains, null (projectConfigBuildPackageDBs projectConfigShared) = projectConfigPackageDBs projectConfigShared
+              | otherwise = projectConfigBuildPackageDBs projectConfigShared
+
 
             withRepoCtx :: (RepoContext -> IO a) -> IO a
             withRepoCtx =
@@ -791,7 +909,7 @@ rebuildInstallPlan
                 projectConfigBuildOnly
 
             solverSettings = resolveSolverSettings projectConfig
-            logMsg message rest = debugNoWrap verbosity message >> rest
+            logMsg message rest = infoNoWrap verbosity message >> rest
 
             localPackagesEnabledStanzas =
               Map.fromList
@@ -830,7 +948,7 @@ rebuildInstallPlan
       --
       phaseElaboratePlan
         :: ProjectConfig
-        -> (Compiler, Platform, ProgramDb)
+        -> Toolchains
         -> Maybe PkgConfigDb
         -> SolverInstallPlan
         -> [PackageSpecifier (SourcePackage (PackageLocation loc))]
@@ -846,10 +964,10 @@ rebuildInstallPlan
           , projectConfigSpecificPackage
           , projectConfigBuildOnly
           }
-        (compiler, platform, progdb)
+        toolchains
         pkgConfigDB
         solverPlan
-        localPackages = do
+        localPackages_ = do
           liftIO $ debug verbosity "Elaborating the install plan..."
 
           sourcePackageHashes <-
@@ -859,15 +977,14 @@ rebuildInstallPlan
               (packageLocationsSignature solverPlan)
               $ getPackageSourceHashes verbosity withRepoCtx solverPlan
 
-          defaultInstallDirs <- liftIO $ userInstallDirTemplates compiler
+          defaultInstallDirs <- liftIO $ userInstallDirTemplates (toolchainCompiler (hostToolchain toolchains))
           let installDirs = fmap Cabal.fromFlag $ (fmap Flag defaultInstallDirs) <> (projectConfigInstallDirs projectConfigShared)
           (elaboratedPlan, elaboratedShared) <-
             liftIO . runLogProgress verbosity $
               elaborateInstallPlan
                 verbosity
-                platform
-                compiler
-                progdb
+                hookHashes
+                toolchains
                 pkgConfigDB
                 distDirLayout
                 cabalStoreDirLayout
@@ -888,6 +1005,17 @@ rebuildInstallPlan
           liftIO $ debugNoWrap verbosity (showElaboratedInstallPlan instantiatedPlan)
           return (instantiatedPlan, elaboratedShared)
           where
+            -- FIXME: See FIXME in Distribution.Client.ProjectConfig wrt to buildPackages.
+            addCompilerToSourcePkg :: CompilerId -> [PackageSpecifier (SourcePackage (PackageLocation loc))] -> [PackageSpecifier (SourcePackage (PackageLocation loc))]
+            addCompilerToSourcePkg compilerId = map (addCompilerId compilerId)
+            addCompilerId :: CompilerId -> PackageSpecifier (SourcePackage (PackageLocation loc)) -> PackageSpecifier (SourcePackage (PackageLocation loc))
+            addCompilerId compilerId (NamedPackage name props) = NamedPackage name props
+            addCompilerId compilerId (SpecificSourcePackage pkg) = SpecificSourcePackage (f pkg)
+              where f :: SourcePackage (PackageLocation loc) -> SourcePackage (PackageLocation loc)
+                    f pkg = pkg{srcpkgPackageId = (srcpkgPackageId pkg){pkgCompiler = Just compilerId}}
+            localPackages = (addCompilerToSourcePkg (compilerIdFor Build toolchains) localPackages_)
+                         <> (addCompilerToSourcePkg (compilerIdFor Host  toolchains) localPackages_)
+
             withRepoCtx :: (RepoContext -> IO a) -> IO a
             withRepoCtx =
               projectConfigWithSolverRepoContext
@@ -926,10 +1054,11 @@ rebuildInstallPlan
         -> Rebuild ElaboratedInstallPlan
       phaseImprovePlan elaboratedPlan elaboratedShared = do
         liftIO $ debug verbosity "Improving the install plan..."
-        storePkgIdSet <- getStoreEntries cabalStoreDirLayout compiler
+        hstorePkgIdSet <- getStoreEntries cabalStoreDirLayout hcompiler
+        bstorePkgIdSet <- getStoreEntries cabalStoreDirLayout bcompiler
         let improvedPlan =
               improveInstallPlanWithInstalledPackages
-                storePkgIdSet
+                (hstorePkgIdSet `Set.union` bstorePkgIdSet)
                 elaboratedPlan
         liftIO $ debugNoWrap verbosity (showElaboratedInstallPlan improvedPlan)
         -- TODO: [nice to have] having checked which packages from the store
@@ -938,12 +1067,13 @@ rebuildInstallPlan
         -- matches up as expected, e.g. no dangling deps, files deleted.
         return improvedPlan
         where
-          compiler = pkgConfigCompiler elaboratedShared
+          hcompiler = toolchainCompiler (hostToolchain (pkgConfigToolchains elaboratedShared))
+          bcompiler = toolchainCompiler (buildToolchain (pkgConfigToolchains elaboratedShared))
 
 -- | If a 'PackageSpecifier' refers to a single package, return Just that
 -- package.
-reportPlanningFailure :: ProjectConfig -> Compiler -> Platform -> [PackageSpecifier UnresolvedSourcePackage] -> IO ()
-reportPlanningFailure projectConfig comp platform pkgSpecifiers =
+reportPlanningFailure :: ProjectConfig -> Toolchain -> [PackageSpecifier UnresolvedSourcePackage] -> IO ()
+reportPlanningFailure projectConfig Toolchain{toolchainCompiler = comp, toolchainPlatform = platform} pkgSpecifiers =
   when reportFailure $
     BuildReports.storeLocal
       (compilerInfo comp)
@@ -967,7 +1097,7 @@ reportPlanningFailure projectConfig comp platform pkgSpecifiers =
     theSpecifiedPackage pkgSpec =
       case pkgSpec of
         NamedPackage name [PackagePropertyVersion version] ->
-          PackageIdentifier name <$> trivialRange version
+          PackageIdentifier name <$> trivialRange version <*> Nothing
         NamedPackage _ _ -> Nothing
         SpecificSourcePackage pkg -> Just $ packageId pkg
     -- \| If a range includes only a single version, return Just that version.
@@ -991,44 +1121,28 @@ programsMonitorFiles progdb =
         (programPath prog)
   ]
 
--- | Select the bits of a 'ProgramDb' to monitor for value changes.
--- Use 'programsMonitorFiles' for the files to monitor.
-programDbSignature :: ProgramDb -> [ConfiguredProgram]
-programDbSignature progdb =
-  [ prog
-    { programMonitorFiles = []
-    , programOverrideEnv =
-        filter
-          ((/= "PATH") . fst)
-          (programOverrideEnv prog)
-    }
-  | prog <- configuredPrograms progdb
-  ]
-
 getInstalledPackages
   :: Verbosity
-  -> Compiler
-  -> ProgramDb
-  -> Platform
+  -> Toolchain
   -> PackageDBStackCWD
   -> Rebuild InstalledPackageIndex
-getInstalledPackages verbosity compiler progdb platform packagedbs = do
+getInstalledPackages verbosity Toolchain{toolchainCompiler, toolchainPlatform, toolchainProgramDb} packagedbs = do
   monitorFiles . map monitorFileOrDirectory
     =<< liftIO
       ( IndexUtils.getInstalledPackagesMonitorFiles
           verbosity
-          compiler
+          toolchainCompiler
           Nothing -- use ambient working directory
           (coercePackageDBStack packagedbs)
-          progdb
-          platform
+          toolchainProgramDb
+          toolchainPlatform
       )
   liftIO $
     IndexUtils.getInstalledPackages
       verbosity
-      compiler
+      toolchainCompiler
       packagedbs
-      progdb
+      toolchainProgramDb
 
 {-
 --TODO: [nice to have] use this but for sanity / consistency checking
@@ -1260,10 +1374,10 @@ getPackageSourceHashes verbosity withRepoCtx solverPlan = do
 
 planPackages
   :: Verbosity
-  -> Compiler
-  -> Platform
+  -> Toolchains
   -> SolverSettings
-  -> InstalledPackageIndex
+  -> InstalledPackageIndex -- ^ Build Installed Package Index
+  -> InstalledPackageIndex -- ^ Host Installed Package Index
   -> SourcePackageDb
   -> Maybe PkgConfigDb
   -> [PackageSpecifier UnresolvedSourcePackage]
@@ -1271,17 +1385,16 @@ planPackages
   -> Progress String String SolverInstallPlan
 planPackages
   verbosity
-  comp
-  platform
+  toolchains
   SolverSettings{..}
+  binstalledPkgIndex
   installedPkgIndex
   sourcePkgDb
   pkgConfigDB
   localPackages
   pkgStanzasEnable =
     resolveDependencies
-      platform
-      (compilerInfo comp)
+      toolchains
       pkgConfigDB
       resolverParams
     where
@@ -1324,7 +1437,9 @@ planPackages
           . removeLowerBounds solverSettingAllowOlder
           . removeUpperBounds solverSettingAllowNewer
           . addDefaultSetupDependencies
-            ( mkDefaultSetupDeps comp platform
+            ( mkDefaultSetupDeps
+                (toolchainCompiler (buildToolchain toolchains))
+                (toolchainPlatform (buildToolchain toolchains))
                 . PD.packageDescription
                 . srcpkgDescription
             )
@@ -1402,6 +1517,7 @@ planPackages
         -- Note: we don't use the standardInstallPolicy here, since that uses
         -- its own addDefaultSetupDependencies that is not appropriate for us.
         basicInstallPolicy
+          binstalledPkgIndex
           installedPkgIndex
           sourcePkgDb
           localPackages
@@ -1458,8 +1574,9 @@ planPackages
         | otherwise = mkVersion [1, 20]
         where
           isGHC = compFlav `elem` [GHC, GHCJS]
-          compFlav = compilerFlavor comp
-          compVer = compilerVersion comp
+          -- FIXME: We assume HOST here.
+          compFlav = compilerFlavor (toolchainCompiler (hostToolchain toolchains))
+          compVer = compilerVersion (toolchainCompiler (hostToolchain toolchains))
 
       -- As we can't predict the future, we also place a global upper
       -- bound on the lib:Cabal version we know how to interact with:
@@ -1584,10 +1701,9 @@ planPackages
 -- In theory should be able to make an elaborated install plan with a policy
 -- matching that of the classic @cabal install --user@ or @--global@
 elaborateInstallPlan
-  :: Verbosity
-  -> Platform
-  -> Compiler
-  -> ProgramDb
+  :: HasCallStack => Verbosity
+  -> Map FilePath HookAccept
+  -> Toolchains
   -> Maybe PkgConfigDb
   -> DistDirLayout
   -> StoreDirLayout
@@ -1602,9 +1718,8 @@ elaborateInstallPlan
   -> LogProgress (ElaboratedInstallPlan, ElaboratedSharedConfig)
 elaborateInstallPlan
   verbosity
-  platform
-  compiler
-  compilerprogdb
+  hookHashes
+  toolchains
   pkgConfigDB
   distDirLayout@DistDirLayout{..}
   storeDirLayout@StoreDirLayout{storePackageDBStack}
@@ -1621,10 +1736,9 @@ elaborateInstallPlan
     where
       elaboratedSharedConfig =
         ElaboratedSharedConfig
-          { pkgConfigPlatform = platform
-          , pkgConfigCompiler = compiler
-          , pkgConfigCompilerProgs = compilerprogdb
+          { pkgConfigToolchains = toolchains
           , pkgConfigReplOptions = mempty
+          , pkgConfigHookHashes = hookHashes
           }
 
       preexistingInstantiatedPkgs :: Map UnitId FullUnitId
@@ -1644,7 +1758,7 @@ elaborateInstallPlan
           f _ = Nothing
 
       elaboratedInstallPlan
-        :: LogProgress (InstallPlan.GenericInstallPlan IPI.InstalledPackageInfo ElaboratedConfiguredPackage)
+        :: HasCallStack => LogProgress (InstallPlan.GenericInstallPlan IPI.InstalledPackageInfo ElaboratedConfiguredPackage)
       elaboratedInstallPlan =
         flip InstallPlan.fromSolverInstallPlanWithProgress solverPlan $ \mapDep planpkg ->
           case planpkg of
@@ -1665,10 +1779,10 @@ elaborateInstallPlan
       -- NB: We don't INSTANTIATE packages at this point.  That's
       -- a post-pass.  This makes it simpler to compute dependencies.
       elaborateSolverToComponents
-        :: (SolverId -> [ElaboratedPlanPackage])
+        :: HasCallStack => (SolverId -> [ElaboratedPlanPackage])
         -> SolverPackage UnresolvedPkgLoc
         -> LogProgress [ElaboratedConfiguredPackage]
-      elaborateSolverToComponents mapDep spkg@(SolverPackage _ _ _ deps0 exe_deps0) =
+      elaborateSolverToComponents mapDep spkg@(SolverPackage _qpn _stage _ _ _ deps0 exe_deps0) =
         case mkComponentsGraph (elabEnabledSpec elab0) pd of
           Right g -> do
             let src_comps = componentsGraphToList g
@@ -1685,7 +1799,6 @@ elaborateInstallPlan
             let whyNotPerComp = why_not_per_component src_comps
             case NE.nonEmpty whyNotPerComp of
               Nothing -> do
-                elaborationWarnings
                 return comps
               Just notPerCompReasons -> do
                 checkPerPackageOk comps notPerCompReasons
@@ -1758,7 +1871,7 @@ elaborateInstallPlan
                   <+> fsep (punctuate comma $ map (text . whyNotPerComponent) $ toList reasons)
           -- TODO: Maybe exclude Backpack too
 
-          (elab0, elaborationWarnings) = elaborateSolverToCommon spkg
+          elab0 = elaborateSolverToCommon spkg
           pkgid = elabPkgSourceId elab0
           pd = elabPkgDescription elab0
 
@@ -1809,7 +1922,7 @@ elaborateInstallPlan
                     ++ " not implemented yet"
 
           buildComponent
-            :: ( ConfiguredComponentMap
+            :: HasCallStack => ( ConfiguredComponentMap
                , LinkedComponentMap
                , Map ComponentId FilePath
                )
@@ -1866,10 +1979,14 @@ elaborateInstallPlan
                       elab0
                         { elabPkgOrComp = ElabComponent $ elab_comp
                         }
+                    elabCompiler = toolchainCompiler (toolchainFor (elabStage elab0) toolchains)
+
                     cid = case elabBuildStyle elab0 of
                       BuildInplaceOnly{} ->
                         mkComponentId $
-                          prettyShow pkgid
+                          prettyShow (compilerId elabCompiler)
+                          ++
+                          '_':prettyShow pkgid
                             ++ "-inplace"
                             ++ ( case Cabal.componentNameString cname of
                                   Nothing -> ""
@@ -2058,6 +2175,8 @@ elaborateInstallPlan
       elaborateSolverToPackage
         pkgWhyNotPerComponent
         pkg@( SolverPackage
+                _qpn
+                _stage
                 (SourcePackage pkgid _gpd _srcloc _descOverride)
                 _flags
                 _stanzas
@@ -2069,11 +2188,17 @@ elaborateInstallPlan
           -- Knot tying: the final elab includes the
           -- pkgInstalledId, which is calculated by hashing many
           -- of the other fields of the elaboratedPackage.
-          elaborationWarnings
           return elab
           where
-            (elab0@ElaboratedConfiguredPackage{..}, elaborationWarnings) =
-              elaborateSolverToCommon pkg
+            elab =
+              elab1
+                { elabInstallDirs =
+                    computeInstallDirs
+                      storeDirLayout
+                      defaultInstallDirs
+                      elaboratedSharedConfig
+                      elab1
+                }
 
             elab1 =
               elab0
@@ -2084,23 +2209,21 @@ elaborateInstallPlan
                 , elabModuleShape = modShape
                 }
 
-            elab =
-              elab1
-                { elabInstallDirs =
-                    computeInstallDirs
-                      storeDirLayout
-                      defaultInstallDirs
-                      elaboratedSharedConfig
-                      elab1
-                }
+            elab0@ElaboratedConfiguredPackage{..} =
+              elaborateSolverToCommon pkg
+
 
             modShape = case find (matchElabPkg (== (CLibName LMainLibName))) comps of
               Nothing -> emptyModuleShape
               Just e -> Ty.elabModuleShape e
 
+            elabCompiler = toolchainCompiler (toolchainFor elabStage toolchains)
+
             pkgInstalledId
-              | shouldBuildInplaceOnly pkg =
-                  mkComponentId (prettyShow pkgid ++ "-inplace")
+              | shouldBuildInplaceOnly pkg = mkComponentId $
+                  prettyShow (compilerId elabCompiler)
+                  ++
+                  '_':prettyShow pkgid ++ "-inplace"
               | otherwise =
                   assert (isJust elabPkgSourceHash) $
                     hashedInstalledPackageId
@@ -2111,7 +2234,7 @@ elaborateInstallPlan
 
             -- Need to filter out internal dependencies, because they don't
             -- correspond to anything real anymore.
-            isExt confid = confSrcId confid /= pkgid
+            isExt confid = (confSrcId confid){pkgCompiler = Nothing} /= pkgid{pkgCompiler = Nothing}
             filterExt = filter isExt
 
             filterExt' :: [(ConfiguredId, a)] -> [(ConfiguredId, a)]
@@ -2158,16 +2281,18 @@ elaborateInstallPlan
 
       elaborateSolverToCommon
         :: SolverPackage UnresolvedPkgLoc
-        -> (ElaboratedConfiguredPackage, LogProgress ())
+        -> ElaboratedConfiguredPackage
       elaborateSolverToCommon
         pkg@( SolverPackage
+                _qpn
+                stage
                 (SourcePackage pkgid gdesc srcloc descOverride)
                 flags
                 stanzas
                 deps0
                 _exe_deps0
               ) =
-          (elaboratedPackage, wayWarnings pkgid)
+          elaboratedPackage
           where
             elaboratedPackage = ElaboratedConfiguredPackage{..}
 
@@ -2182,12 +2307,19 @@ elaborateInstallPlan
 
             elabIsCanonical = True
             elabPkgSourceId = pkgid
+
+            -- TODO: temporarily set everything to build on build
+            elabStage = stage
+            elabCompiler = toolchainCompiler (toolchainFor elabStage toolchains)
+            elabPlatform = toolchainPlatform (toolchainFor elabStage toolchains)
+            elabProgramDb = toolchainProgramDb (toolchainFor elabStage toolchains)
+
             elabPkgDescription = case PD.finalizePD
               flags
               elabEnabledSpec
               (const Satisfied)
-              platform
-              (compilerInfo compiler)
+              elabPlatform
+              (compilerInfo elabCompiler)
               []
               gdesc of
               Right (desc, _) -> desc
@@ -2243,15 +2375,18 @@ elaborateInstallPlan
                 else cp
 
             elabPkgSourceLocation = srcloc
-            elabPkgSourceHash = Map.lookup pkgid sourcePackageHashes
+            elabPkgSourceHash = case Map.lookup pkgid sourcePackageHashes of
+              Just h -> Just h
+              Nothing -> Nothing
             elabLocalToProject = isLocalToProject pkg
             elabBuildStyle =
               if shouldBuildInplaceOnly pkg
                 then BuildInplaceOnly OnDisk
                 else BuildAndInstall
             elabPackageDbs = projectConfigPackageDBs sharedPackageConfig
-            elabBuildPackageDBStack = buildAndRegisterDbs
-            elabRegisterPackageDBStack = buildAndRegisterDbs
+            elabBuildPackageDbs = projectConfigBuildPackageDBs sharedPackageConfig
+            elabBuildPackageDBStack = buildAndRegisterDbs stage
+            elabRegisterPackageDBStack = buildAndRegisterDbs stage
 
             elabSetupScriptStyle = packageSetupScriptStyle elabPkgDescription
             elabSetupScriptCliVersion =
@@ -2260,29 +2395,48 @@ elaborateInstallPlan
                 elabPkgDescription
                 libDepGraph
                 deps0
-            elabSetupPackageDBStack = buildAndRegisterDbs
 
-            elabInplaceBuildPackageDBStack = inplacePackageDbs
-            elabInplaceRegisterPackageDBStack = inplacePackageDbs
-            elabInplaceSetupPackageDBStack = inplacePackageDbs
+            -- This code is ... a bit nuts. We need to parameterise the DB stack
+            -- over the stage. (which is also assigned to elabStage). And now
+            -- we have inplace, core, ... other DBStacks, for the Setup however,
+            -- we _must_ force it to use the Build stage stack. As that's where
+            -- the setup dependencies will be found.
+            elabSetupPackageDBStack = buildAndRegisterDbs Build
+
+            inplacePackageDbs stage = corePackageDbs stage ++ [distPackageDB (compilerId (toolchainCompiler (toolchainFor stage toolchains)))]
+
+            corePackageDbs stage = storePackageDBStack (toolchainCompiler (toolchainFor stage toolchains)) (packageDBs stage)
 
-            buildAndRegisterDbs
-              | shouldBuildInplaceOnly pkg = inplacePackageDbs
-              | otherwise = corePackageDbs
+            packageDBs Host = projectConfigPackageDBs sharedPackageConfig
+            packageDBs Build
+              | buildIsHost toolchains, null (projectConfigBuildPackageDBs sharedPackageConfig) = projectConfigPackageDBs sharedPackageConfig
+              | otherwise = projectConfigBuildPackageDBs sharedPackageConfig
+
+            elabInplaceBuildPackageDBStack = inplacePackageDbs stage
+            elabInplaceRegisterPackageDBStack = inplacePackageDbs stage
+            elabInplaceSetupPackageDBStack = inplacePackageDbs stage
+
+            buildAndRegisterDbs stage
+              | shouldBuildInplaceOnly pkg = inplacePackageDbs stage
+              | otherwise = corePackageDbs stage
 
             elabPkgDescriptionOverride = descOverride
 
+            pkgsUseSharedLibrary :: Set PackageId
+            pkgsUseSharedLibrary =
+              packagesWithLibDepsDownwardClosedProperty (needsSharedLib elabCompiler)
+
             elabBuildOptions =
               LBC.BuildOptions
                 { withVanillaLib = perPkgOptionFlag pkgid True packageConfigVanillaLib -- TODO: [required feature]: also needs to be handled recursively
-                , withSharedLib = canBuildSharedLibs && pkgid `Set.member` pkgsUseSharedLibrary
+                , withSharedLib = canBuildSharedLibs elabCompiler && pkgid `Set.member` pkgsUseSharedLibrary
                 , withStaticLib = perPkgOptionFlag pkgid False packageConfigStaticLib
                 , withDynExe = perPkgOptionFlag pkgid False packageConfigDynExe
                 , withFullyStaticExe = perPkgOptionFlag pkgid False packageConfigFullyStaticExe
                 , withGHCiLib = perPkgOptionFlag pkgid False packageConfigGHCiLib -- TODO: [required feature] needs to default to enabled on windows still
                 , withProfExe = perPkgOptionFlag pkgid False packageConfigProf
-                , withProfLib = canBuildProfilingLibs && pkgid `Set.member` pkgsUseProfilingLibrary
-                , withProfLibShared = canBuildProfilingSharedLibs && pkgid `Set.member` pkgsUseProfilingLibraryShared
+                , withProfLib = canBuildProfilingLibs elabCompiler && pkgid `Set.member` pkgsUseProfilingLibrary
+                , withProfLibShared = canBuildProfilingSharedLibs elabCompiler && pkgid `Set.member` pkgsUseProfilingLibraryShared
                 , exeCoverage = perPkgOptionFlag pkgid False packageConfigCoverage
                 , libCoverage = perPkgOptionFlag pkgid False packageConfigCoverage
                 , withOptimization = perPkgOptionFlag pkgid NormalOptimisation packageConfigOptimization
@@ -2315,13 +2469,13 @@ elaborateInstallPlan
             elabProgramPaths =
               Map.fromList
                 [ (programId prog, programPath prog)
-                | prog <- configuredPrograms compilerprogdb
+                | prog <- configuredPrograms elabProgramDb
                 ]
                 <> perPkgOptionMapLast pkgid packageConfigProgramPaths
             elabProgramArgs =
               Map.fromList
                 [ (programId prog, args)
-                | prog <- configuredPrograms compilerprogdb
+                | prog <- configuredPrograms elabProgramDb
                 , let args = programOverrideArgs $ addHaddockIfDocumentationEnabled prog
                 , not (null args)
                 ]
@@ -2403,12 +2557,6 @@ elaborateInstallPlan
                 mempty
           perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig)
 
-      inplacePackageDbs =
-        corePackageDbs
-          ++ [distPackageDB (compilerId compiler)]
-
-      corePackageDbs = storePackageDBStack compiler (projectConfigPackageDBs sharedPackageConfig)
-
       -- For this local build policy, every package that lives in a local source
       -- dir (as opposed to a tarball), or depends on such a package, will be
       -- built inplace into a shared dist dir. Tarball packages that depend on
@@ -2419,13 +2567,18 @@ elaborateInstallPlan
           (packageId pkg)
           pkgsToBuildInplaceOnly
 
+
+      -- FIXME: This change is stupid, however the previous assumption is
+      -- that ALL pkgsLocalToProject somehow end up in the solverPlan. This
+      -- is not given, as we now have every local package listed twice. Once
+      -- for the Host, and once for the Build. Thus iterating over each individual
+      -- package _does_ work, but is rather idiotic.
       pkgsToBuildInplaceOnly :: Set PackageId
       pkgsToBuildInplaceOnly =
         Set.fromList $
           map packageId $
-            SolverInstallPlan.reverseDependencyClosure
-              solverPlan
-              (map PlannedId (Set.toList pkgsLocalToProject))
+            concat [SolverInstallPlan.reverseDependencyClosure solverPlan [PlannedId p]
+                   | p <- (Set.toList pkgsLocalToProject)]
 
       isLocalToProject :: Package pkg => pkg -> Bool
       isLocalToProject pkg =
@@ -2439,13 +2592,13 @@ elaborateInstallPlan
       -- TODO: localPackages is a misnomer, it's all project packages
       -- here is where we decide which ones will be local!
 
-      pkgsUseSharedLibrary :: Set PackageId
-      pkgsUseSharedLibrary =
-        packagesWithLibDepsDownwardClosedProperty needsSharedLib
+      pkgsUseProfilingLibrary :: Set PackageId
+      pkgsUseProfilingLibrary =
+        packagesWithLibDepsDownwardClosedProperty needsProfilingLib
 
-      needsSharedLib pkgid =
+      needsSharedLib compiler pkgid =
         fromMaybe
-          compilerShouldUseSharedLibByDefault
+          (compilerShouldUseSharedLibByDefault compiler)
           -- Case 1: --enable-shared or --disable-shared is passed explicitly, honour that.
           ( case pkgSharedLib of
               Just v -> Just v
@@ -2456,7 +2609,7 @@ elaborateInstallPlan
                   -- Case 3: If --enable-profiling is passed, then we are going to
                   -- build profiled dynamic, so no need for shared libraries.
                   case pkgProf of
-                    Just True -> if canBuildProfilingSharedLibs then Nothing else Just True
+                    Just True -> if canBuildProfilingSharedLibs compiler then Nothing else Just True
                     _ -> Just True
                 -- But don't necessarily turn off shared library generation if
                 -- --disable-executable-dynamic is passed. The shared objects might
@@ -2468,54 +2621,10 @@ elaborateInstallPlan
           pkgDynExe = perPkgOptionMaybe pkgid packageConfigDynExe
           pkgProf = perPkgOptionMaybe pkgid packageConfigProf
 
-      -- TODO: [code cleanup] move this into the Cabal lib. It's currently open
-      -- coded in Distribution.Simple.Configure, but should be made a proper
-      -- function of the Compiler or CompilerInfo.
-      compilerShouldUseSharedLibByDefault =
-        case compilerFlavor compiler of
-          GHC -> GHC.compilerBuildWay compiler == DynWay && canBuildSharedLibs
-          GHCJS -> GHCJS.isDynamic compiler
-          _ -> False
-
-      compilerShouldUseProfilingLibByDefault =
-        case compilerFlavor compiler of
-          GHC -> GHC.compilerBuildWay compiler == ProfWay && canBuildProfilingLibs
-          _ -> False
-
-      compilerShouldUseProfilingSharedLibByDefault =
-        case compilerFlavor compiler of
-          GHC -> GHC.compilerBuildWay compiler == ProfDynWay && canBuildProfilingSharedLibs
-          _ -> False
-
-      -- Returns False if we definitely can't build shared libs
-      canBuildWayLibs predicate = case predicate compiler of
-        Just can_build -> can_build
-        -- If we don't know for certain, just assume we can
-        -- which matches behaviour in previous cabal releases
-        Nothing -> True
-
-      canBuildSharedLibs = canBuildWayLibs dynamicSupported
-      canBuildProfilingLibs = canBuildWayLibs profilingVanillaSupported
-      canBuildProfilingSharedLibs = canBuildWayLibs profilingDynamicSupported
-
-      wayWarnings pkg = do
-        when
-          (needsProfilingLib pkg && not canBuildProfilingLibs)
-          (warnProgress (text "Compiler does not support building p libraries, profiling is disabled"))
-        when
-          (needsSharedLib pkg && not canBuildSharedLibs)
-          (warnProgress (text "Compiler does not support building dyn libraries, dynamic libraries are disabled"))
-        when
-          (needsProfilingLibShared pkg && not canBuildProfilingSharedLibs)
-          (warnProgress (text "Compiler does not support building p_dyn libraries, profiling dynamic libraries are disabled."))
-
-      pkgsUseProfilingLibrary :: Set PackageId
-      pkgsUseProfilingLibrary =
-        packagesWithLibDepsDownwardClosedProperty needsProfilingLib
-
-      needsProfilingLib pkg =
-        fromFlagOrDefault compilerShouldUseProfilingLibByDefault (profBothFlag <> profLibFlag)
+      needsProfilingLib pkg = fromFlagOrDefault False (profBothFlag <> profLibFlag)
         where
+          -- fromFlagOrDefault compilerShouldUseProfilingLibByDefault (profBothFlag <> profLibFlag)
+
           pkgid = packageId pkg
           profBothFlag = lookupPerPkgOption pkgid packageConfigProf
           profLibFlag = lookupPerPkgOption pkgid packageConfigProfLib
@@ -2524,24 +2633,26 @@ elaborateInstallPlan
       pkgsUseProfilingLibraryShared =
         packagesWithLibDepsDownwardClosedProperty needsProfilingLibShared
 
+      -- TODO: review this logic
       needsProfilingLibShared pkg =
-        fromMaybe
-          compilerShouldUseProfilingSharedLibByDefault
-          -- case 1: If --enable-profiling-shared is passed explicitly, honour that
-          ( case profLibSharedFlag of
-              Just v -> Just v
-              Nothing -> case pkgDynExe of
-                Just True ->
-                  case pkgProf of
-                    -- case 2: --enable-executable-dynamic + --enable-profiling
-                    -- turn on shared profiling libraries
-                    Just True -> if canBuildProfilingSharedLibs then Just True else Nothing
-                    _ -> Nothing
-                -- But don't necessarily turn off shared library generation is
-                -- --disable-executable-dynamic is passed. The shared objects might
-                -- be needed for something different.
-                _ -> Nothing
-          )
+        -- fromMaybe
+        --   compilerShouldUseProfilingSharedLibByDefault
+        --   -- case 1: If --enable-profiling-shared is passed explicitly, honour that
+        case profLibSharedFlag of
+          Just v -> v
+          Nothing -> case pkgDynExe of
+            Just True ->
+              case pkgProf of
+                -- case 2: --enable-executable-dynamic + --enable-profiling
+                -- turn on shared profiling libraries
+                -- Just True -> if canBuildProfilingSharedLibs then Just True else Nothing
+                Just True -> True
+                _ -> False
+            -- But don't necessarily turn off shared library generation is
+            -- --disable-executable-dynamic is passed. The shared objects might
+            -- be needed for something different.
+            -- _ -> Nothing
+            _ -> False
         where
           pkgid = packageId pkg
           profLibSharedFlag = perPkgOptionMaybe pkgid packageConfigProfShared
@@ -2571,6 +2682,51 @@ elaborateInstallPlan
             -- package config validation/resolution pass.
             ]
 
+-- TODO: [code cleanup] move this into the Cabal lib. It's currently open
+-- coded in Distribution.Simple.Configure, but should be made a proper
+-- function of the Compiler or CompilerInfo.
+compilerShouldUseSharedLibByDefault :: Compiler -> Bool
+compilerShouldUseSharedLibByDefault compiler =
+  case compilerFlavor compiler of
+    GHC -> GHC.compilerBuildWay compiler == DynWay && canBuildSharedLibs compiler
+    GHCJS -> GHCJS.isDynamic compiler
+    _ -> False
+
+-- -- TODO: [code cleanup] move this into the Cabal lib.
+-- compilerShouldUseProfilingLibByDefault :: Compiler -> Bool
+-- compilerShouldUseProfilingLibByDefault compiler =
+--   case compilerFlavor compiler of
+--     GHC -> GHC.compilerBuildWay compiler == ProfWay && canBuildProfilingLibs compiler
+--     _ -> False
+
+-- -- TODO: [code cleanup] move this into the Cabal lib.
+-- compilerShouldUseProfilingSharedLibByDefault :: Bool
+-- compilerShouldUseProfilingSharedLibByDefault =
+--   case compilerFlavor compiler of
+--     GHC -> GHC.compilerBuildWay compiler == ProfDynWay && canBuildProfilingSharedLibs compiler
+--     _ -> False
+
+-- Returns False if we definitely can't build shared libs
+-- TODO: [code cleanup] move this into the Cabal lib.
+canBuildWayLibs :: (t -> Maybe Bool) -> t -> Bool
+canBuildWayLibs predicate compiler = case predicate compiler of
+  Just can_build -> can_build
+  -- If we don't know for certain, just assume we can
+  -- which matches behaviour in previous cabal releases
+  Nothing -> True
+
+-- TODO: [code cleanup] move this into the Cabal lib.
+canBuildSharedLibs :: Compiler -> Bool
+canBuildSharedLibs = canBuildWayLibs dynamicSupported
+
+-- TODO: [code cleanup] move this into the Cabal lib.
+canBuildProfilingLibs :: Compiler -> Bool
+canBuildProfilingLibs = canBuildWayLibs profilingVanillaSupported
+
+-- TODO: [code cleanup] move this into the Cabal lib.
+canBuildProfilingSharedLibs :: Compiler -> Bool
+canBuildProfilingSharedLibs = canBuildWayLibs profilingDynamicSupported
+
 -- TODO: [nice to have] config consistency checking:
 -- + profiling libs & exes, exe needs lib, recursive
 -- + shared libs & exes, exe needs lib, recursive
@@ -2617,7 +2773,7 @@ matchElabPkg p elab =
 -- and 'ComponentName' to the 'ComponentId' that should be used
 -- in this case.
 mkCCMapping
-  :: ElaboratedPlanPackage
+  :: HasCallStack => ElaboratedPlanPackage
   -> (PackageName, Map ComponentName (AnnotatedId ComponentId))
 mkCCMapping =
   InstallPlan.foldPlanPackage
@@ -3847,8 +4003,10 @@ setupHsScriptOptions
             --   - if we commit to a Cabal version, the logic in
               Nothing
             else Just elabSetupScriptCliVersion
-      , useCompiler = Just pkgConfigCompiler
-      , usePlatform = Just pkgConfigPlatform
+      , -- for Setup.hs, we _always_ want to use the BUILD toolchain.
+        useCompiler = Just (toolchainCompiler $ buildToolchain $ pkgConfigToolchains)
+      , usePlatform = Just (toolchainPlatform $ buildToolchain $ pkgConfigToolchains)
+      , useProgramDb = toolchainProgramDb $ buildToolchain $ pkgConfigToolchains
       , usePackageDB = elabSetupPackageDBStack
       , usePackageIndex = Nothing
       , useDependencies =
@@ -3858,7 +4016,6 @@ setupHsScriptOptions
           ]
       , useDependenciesExclusive = True
       , useVersionMacros = elabSetupScriptStyle == SetupCustomExplicitDeps
-      , useProgramDb = pkgConfigCompilerProgs
       , useDistPref = builddir
       , useLoggingHandle = Nothing -- this gets set later
       , useWorkingDir = Just srcdir
@@ -3943,9 +4100,9 @@ computeInstallDirs storeDirLayout defaultInstallDirs elaboratedShared elab
       ( InstallDirs.absoluteInstallDirs
           (elabPkgSourceId elab)
           (elabUnitId elab)
-          (compilerInfo (pkgConfigCompiler elaboratedShared))
+          (compilerInfo (toolchainCompiler $ toolchainFor (elabStage elab) $ pkgConfigToolchains elaboratedShared))
           InstallDirs.NoCopyDest
-          (pkgConfigPlatform elaboratedShared)
+          (toolchainPlatform $ buildToolchain $ pkgConfigToolchains elaboratedShared)
           defaultInstallDirs
       )
         { -- absoluteInstallDirs sets these as 'undefined' but we have
@@ -3958,7 +4115,7 @@ computeInstallDirs storeDirLayout defaultInstallDirs elaboratedShared elab
       -- use special simplified install dirs
       storePackageInstallDirs'
         storeDirLayout
-        (pkgConfigCompiler elaboratedShared)
+        (toolchainCompiler $ toolchainFor (elabStage elab) $ pkgConfigToolchains elaboratedShared)
         (elabUnitId elab)
 
 -- TODO: [code cleanup] perhaps reorder this code
@@ -3979,7 +4136,7 @@ setupHsConfigureFlags
   mkSymbolicPath
   plan
   (ReadyPackage elab@ElaboratedConfiguredPackage{..})
-  sharedConfig@ElaboratedSharedConfig{..}
+  sharedConfig@ElaboratedSharedConfig{pkgConfigToolchains = Toolchains{buildToolchain}}
   configCommonFlags = do
     -- explicitly clear, then our package db stack
     -- TODO: [required eventually] have to do this differently for older Cabal versions
@@ -4049,7 +4206,7 @@ setupHsConfigureFlags
                 ["-hide-all-packages"]
                 elabProgramArgs
       configProgramPathExtra = toNubList elabProgramPathExtra
-      configHcFlavor = toFlag (compilerFlavor pkgConfigCompiler)
+      configHcFlavor = toFlag (compilerFlavor $ toolchainCompiler buildToolchain)
       configHcPath = mempty -- we use configProgramPaths instead
       configHcPkg = mempty -- we use configProgramPaths instead
       configDumpBuildInfo = toFlag elabDumpBuildInfo
@@ -4107,7 +4264,7 @@ setupHsConfigureFlags
       configUserInstall = mempty -- don't rely on defaults
       configPrograms_ = mempty -- never use, shouldn't exist
       configUseResponseFiles = mempty
-      configAllowDependingOnPrivateLibs = Flag $ not $ libraryVisibilitySupported pkgConfigCompiler
+      configAllowDependingOnPrivateLibs = Flag $ not $ libraryVisibilitySupported (toolchainCompiler buildToolchain)
       configIgnoreBuildTools = mempty
 
       cidToGivenComponent :: ConfiguredId -> GivenComponent
@@ -4282,13 +4439,13 @@ setupHsHaddockFlags
   -> Cabal.HaddockFlags
 setupHsHaddockFlags
   (ElaboratedConfiguredPackage{..})
-  (ElaboratedSharedConfig{..})
+  (ElaboratedSharedConfig{pkgConfigToolchains = Toolchains{buildToolchain}})
   _buildTimeSettings
   common =
     Cabal.HaddockFlags
       { haddockCommonFlags = common
       , haddockProgramPaths =
-          case lookupProgram haddockProgram pkgConfigCompilerProgs of
+          case lookupProgram haddockProgram (toolchainProgramDb buildToolchain) of
             Nothing -> mempty
             Just prg ->
               [
@@ -4368,7 +4525,7 @@ setupHsHaddockArgs elab =
 -- not replace installed packages with ghc-pkg.
 
 packageHashInputs
-  :: ElaboratedSharedConfig
+  :: HasCallStack => ElaboratedSharedConfig
   -> ElaboratedConfiguredPackage
   -> PackageHashInputs
 packageHashInputs
@@ -4426,11 +4583,11 @@ packageHashConfigInputs
   :: ElaboratedSharedConfig
   -> ElaboratedConfiguredPackage
   -> PackageHashConfigInputs
-packageHashConfigInputs shared@ElaboratedSharedConfig{..} pkg =
+packageHashConfigInputs shared@ElaboratedSharedConfig{pkgConfigToolchains = Toolchains{buildToolchain}} pkg =
   PackageHashConfigInputs
-    { pkgHashCompilerId = compilerId pkgConfigCompiler
-    , pkgHashCompilerABI = compilerAbiTag pkgConfigCompiler
-    , pkgHashPlatform = pkgConfigPlatform
+    { pkgHashCompilerId = compilerId (toolchainCompiler buildToolchain)
+    , pkgHashCompilerABI = compilerAbiTag (toolchainCompiler buildToolchain)
+    , pkgHashPlatform = toolchainPlatform buildToolchain
     , pkgHashFlagAssignment = elabFlagAssignment
     , pkgHashConfigureScriptArgs = elabConfigureScriptArgs
     , pkgHashVanillaLib = withVanillaLib
diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs
index 7ee5cb52f41..a4f22a25f2b 100644
--- a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs
+++ b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs
@@ -58,6 +58,10 @@ module Distribution.Client.ProjectPlanning.Types
   , isBenchComponentTarget
   , componentOptionalStanza
 
+    -- * Toolchain
+  , Toolchain (..)
+  , Toolchains (..)
+
     -- * Setup script
   , SetupScriptStyle (..)
   ) where
@@ -84,6 +88,7 @@ import Distribution.Client.Types
 import Distribution.Backpack
 import Distribution.Backpack.ModuleShape
 
+import Distribution.Client.HookAccept (HookAccept (..))
 import Distribution.Compat.Graph (IsNode (..))
 import Distribution.InstalledPackageInfo (InstalledPackageInfo)
 import Distribution.ModuleName (ModuleName)
@@ -107,9 +112,10 @@ import Distribution.Simple.Setup
   )
 import Distribution.Simple.Utils (ordNub)
 import Distribution.Solver.Types.ComponentDeps (ComponentDeps)
+import Distribution.Solver.Types.Stage
+import Distribution.Solver.Types.Toolchain
 import qualified Distribution.Solver.Types.ComponentDeps as CD
 import Distribution.Solver.Types.OptionalStanza
-import Distribution.System
 import Distribution.Types.ComponentRequestedSpec
 import qualified Distribution.Types.LocalBuildConfig as LBC
 import Distribution.Types.PackageDescription (PackageDescription (..))
@@ -183,13 +189,12 @@ showElaboratedInstallPlan = InstallPlan.showInstallPlan_gen showNode
 --      even platform and compiler could be different if we're building things
 --      like a server + client with ghc + ghcjs
 data ElaboratedSharedConfig = ElaboratedSharedConfig
-  { pkgConfigPlatform :: Platform
-  , pkgConfigCompiler :: Compiler -- TODO: [code cleanup] replace with CompilerInfo
-  , pkgConfigCompilerProgs :: ProgramDb
+  { pkgConfigToolchains :: Toolchains
   -- ^ The programs that the compiler configured (e.g. for GHC, the progs
   -- ghc & ghc-pkg). Once constructed, only the 'configuredPrograms' are
   -- used.
   , pkgConfigReplOptions :: ReplOptions
+  , pkgConfigHookHashes :: Map FilePath HookAccept
   }
   deriving (Show, Generic)
 
@@ -246,7 +251,9 @@ data ElaboratedConfiguredPackage = ElaboratedConfiguredPackage
   -- to disable. This tells us which ones we build by default, and
   -- helps with error messages when the user asks to build something
   -- they explicitly disabled.
-  --
+
+  , elabStage :: Stage
+
   -- TODO: The 'Bool' here should be refined into an ADT with three
   -- cases: NotRequested, ExplicitlyRequested and
   -- ImplicitlyRequested.  A stanza is explicitly requested if
@@ -261,6 +268,7 @@ data ElaboratedConfiguredPackage = ElaboratedConfiguredPackage
   -- just happen not to have any tests.  (But perhaps we should
   -- warn if ALL local packages don't have any tests.)
   , elabPackageDbs :: [Maybe PackageDBCWD]
+  , elabBuildPackageDbs :: [Maybe PackageDBCWD]
   , elabSetupPackageDBStack :: PackageDBStackCWD
   , elabBuildPackageDBStack :: PackageDBStackCWD
   , elabRegisterPackageDBStack :: PackageDBStackCWD
@@ -342,10 +350,10 @@ normaliseConfiguredPackage
   :: ElaboratedSharedConfig
   -> ElaboratedConfiguredPackage
   -> ElaboratedConfiguredPackage
-normaliseConfiguredPackage ElaboratedSharedConfig{pkgConfigCompilerProgs} pkg =
+normaliseConfiguredPackage ElaboratedSharedConfig{pkgConfigToolchains = Toolchains{buildToolchain}} pkg =
   pkg{elabProgramArgs = Map.mapMaybeWithKey lookupFilter (elabProgramArgs pkg)}
   where
-    knownProgramDb = addKnownPrograms builtinPrograms pkgConfigCompilerProgs
+    knownProgramDb = addKnownPrograms builtinPrograms (toolchainProgramDb buildToolchain)
 
     pkgDesc :: PackageDescription
     pkgDesc = elabPkgDescription pkg
@@ -538,8 +546,8 @@ elabDistDirParams shared elab =
     , distParamComponentName = case elabPkgOrComp elab of
         ElabComponent comp -> compComponentName comp
         ElabPackage _ -> Nothing
-    , distParamCompilerId = compilerId (pkgConfigCompiler shared)
-    , distParamPlatform = pkgConfigPlatform shared
+    , distParamCompilerId = compilerId $ toolchainCompiler $ hostToolchain $ pkgConfigToolchains shared
+    , distParamPlatform = toolchainPlatform $ hostToolchain $ pkgConfigToolchains shared
     , distParamOptimization = LBC.withOptimization $ elabBuildOptions elab
     }
 
diff --git a/cabal-install/src/Distribution/Client/ScriptUtils.hs b/cabal-install/src/Distribution/Client/ScriptUtils.hs
index 440de3c84ad..72950f36e0d 100644
--- a/cabal-install/src/Distribution/Client/ScriptUtils.hs
+++ b/cabal-install/src/Distribution/Client/ScriptUtils.hs
@@ -195,6 +195,7 @@ import qualified Data.ByteString.Char8 as BS
 import Data.ByteString.Lazy ()
 import qualified Data.Set as S
 import Distribution.Client.Errors
+import Distribution.Client.ProjectPlanning.Types (Toolchain (..), Toolchains (..))
 import Distribution.Utils.Path
   ( unsafeMakeSymbolicPath
   )
@@ -361,9 +362,9 @@ withContextAndSelectors noTargets kind flags@NixStyleFlags{..} targetStrings glo
       exists <- doesFileExist script
       if exists
         then do
-          ctx <- withGlobalConfig verbosity globalConfigFlag (scriptBaseCtx script)
+          baseCtx <- withGlobalConfig verbosity globalConfigFlag (scriptBaseCtx script)
 
-          let projectRoot = distProjectRootDirectory $ distDirLayout ctx
+          let projectRoot = distProjectRootDirectory $ distDirLayout baseCtx
           writeFile (projectRoot </> "scriptlocation") =<< canonicalizePath script
 
           scriptContents <- BS.readFile script
@@ -375,14 +376,14 @@ withContextAndSelectors noTargets kind flags@NixStyleFlags{..} targetStrings glo
               (fromNubList . projectConfigProgPathExtra $ projectConfigShared cliConfig)
               (flagToMaybe . projectConfigHttpTransport $ projectConfigBuildOnly cliConfig)
 
-          projectCfgSkeleton <- readProjectBlockFromScript verbosity httpTransport (distDirLayout ctx) (takeFileName script) scriptContents
+          projectCfgSkeleton <- readProjectBlockFromScript verbosity httpTransport (distDirLayout baseCtx) (takeFileName script) scriptContents
 
-          createDirectoryIfMissingVerbose verbosity True (distProjectCacheDirectory $ distDirLayout ctx)
-          (compiler, platform@(Platform arch os), _) <- runRebuild projectRoot $ configureCompiler verbosity (distDirLayout ctx) (fst (ignoreConditions projectCfgSkeleton) <> projectConfig ctx)
+          createDirectoryIfMissingVerbose verbosity True (distProjectCacheDirectory $ distDirLayout baseCtx)
+          Toolchains{buildToolchain = Toolchain{toolchainPlatform = platform@(Platform arch os), toolchainCompiler = compiler}} <- runRebuild projectRoot $ configureCompiler verbosity (distDirLayout baseCtx) (fst (ignoreConditions projectCfgSkeleton) <> projectConfig baseCtx)
 
           (projectCfg, _) <- instantiateProjectConfigSkeletonFetchingCompiler (pure (os, arch, compiler)) mempty projectCfgSkeleton
 
-          let ctx' = ctx & lProjectConfig %~ (<> projectCfg)
+          let ctx' = baseCtx & lProjectConfig %~ (<> projectCfg)
 
               build_dir = distBuildDirectory (distDirLayout ctx') $ (scriptDistDirParams script) ctx' compiler platform
               exePath = build_dir </> "bin" </> scriptExeFileName script
@@ -588,7 +589,7 @@ fakeProjectSourcePackage projectRoot = sourcePackage
 movedExePath :: UnqualComponentName -> DistDirLayout -> ElaboratedSharedConfig -> ElaboratedConfiguredPackage -> Maybe FilePath
 movedExePath selectedComponent distDirLayout elabShared elabConfigured = do
   exe <- find ((== selectedComponent) . exeName) . executables $ elabPkgDescription elabConfigured
-  let CompilerId flavor _ = (compilerId . pkgConfigCompiler) elabShared
+  let CompilerId flavor _ = (compilerId . toolchainCompiler . buildToolchain . pkgConfigToolchains) elabShared
   opts <- lookup flavor (perCompilerFlavorToList . options $ buildInfo exe)
   let projectRoot = distProjectRootDirectory distDirLayout
   fmap (projectRoot </>) . lookup "-o" $ reverse (zip opts (drop 1 opts))
diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs
index 78fcf7c2e1f..05e94256e64 100644
--- a/cabal-install/src/Distribution/Client/Setup.hs
+++ b/cabal-install/src/Distribution/Client/Setup.hs
@@ -162,7 +162,7 @@ import Distribution.ReadE
   )
 import Distribution.Simple.Command hiding (boolOpt, boolOpt')
 import qualified Distribution.Simple.Command as Command
-import Distribution.Simple.Compiler (Compiler, PackageDB, PackageDBStack)
+import Distribution.Simple.Compiler (Compiler, CompilerFlavor (..), PackageDB, PackageDBStack)
 import Distribution.Simple.Configure
   ( computeEffectiveProfiling
   , configCompilerAuxEx
@@ -920,6 +920,10 @@ data ConfigExFlags = ConfigExFlags
   , configAllowOlder :: Maybe AllowOlder
   , configWriteGhcEnvironmentFilesPolicy
       :: Flag WriteGhcEnvironmentFilesPolicy
+  , configBuildHcFlavor :: Flag CompilerFlavor
+  , configBuildHcPath :: Flag FilePath
+  , configBuildHcPkg :: Flag FilePath
+  , configBuildPackageDBs :: [Maybe PackageDB]
   }
   deriving (Eq, Show, Generic)
 
@@ -1047,6 +1051,44 @@ configureExOptions _showOrParseArgs src =
           writeGhcEnvironmentFilesPolicyParser
           writeGhcEnvironmentFilesPolicyPrinter
       )
+  , option
+      []
+      ["build-compiler"]
+      "build compiler"
+      configBuildHcFlavor
+      (\v flags -> flags{configBuildHcFlavor = v})
+      ( choiceOpt
+          [ (Flag GHC, ("g", ["ghc"]), "compile with GHC")
+          , (Flag GHCJS, ([], ["ghcjs"]), "compile with GHCJS")
+          ]
+      )
+  , option
+      "W"
+      ["with-build-compiler", "with-build-hc"]
+      "give the path to the compiler for the build toolchain"
+      configBuildHcPath
+      (\v flags -> flags{configBuildHcPath = v})
+      (reqArgFlag "PATH")
+  , option
+      ""
+      ["with-build-hc-pkg"]
+      "give the path to the package tool for the build toolchain"
+      configBuildHcPkg
+      (\v flags -> flags{configBuildHcPkg = v})
+      (reqArgFlag "PATH")
+  , option
+      ""
+      ["build-package-db"]
+      ( "Append the given package database to the list of package"
+          ++ " databases used (to satisfy dependencies and register into)."
+          ++ " May be a specific file, 'global' or 'user'. The initial list"
+          ++ " is ['global'], ['global', 'user'], or ['global', $sandbox],"
+          ++ " depending on context. Use 'clear' to reset the list to empty."
+          ++ " See the user guide for details."
+      )
+      configBuildPackageDBs
+      (\v flags -> flags{configBuildPackageDBs = v})
+      (reqArg' "DB" readPackageDbList showPackageDbList)
   ]
 
 writeGhcEnvironmentFilesPolicyParser :: ReadE (Flag WriteGhcEnvironmentFilesPolicy)
diff --git a/cabal-install/src/Distribution/Client/SetupWrapper.hs b/cabal-install/src/Distribution/Client/SetupWrapper.hs
index 78833af6d15..d46045c849a 100644
--- a/cabal-install/src/Distribution/Client/SetupWrapper.hs
+++ b/cabal-install/src/Distribution/Client/SetupWrapper.hs
@@ -1068,11 +1068,14 @@ getExternalSetupMethod verbosity options pkg bt = do
           debug verbosity "Setup executable needs to be updated, compiling..."
           (compiler, progdb, options'') <- configureCompiler options'
           pkgDbs <- traverse (traverse (makeRelativeToDirS mbWorkDir)) (coercePackageDBStack (usePackageDB options''))
-          let cabalPkgid = PackageIdentifier (mkPackageName "Cabal") cabalLibVersion
+          let cabalPkgid = PackageIdentifier (mkPackageName "Cabal") cabalLibVersion Nothing
               (program, extraOpts) =
                 case compilerFlavor compiler of
                   GHCJS -> (ghcjsProgram, ["-build-runner"])
-                  _ -> (ghcProgram, ["-threaded"])
+                  _ -> (ghcProgram, [])
+                    -- FIXME: don't enable -threaded unconditionnally: we may
+                    -- only have vanilla libraries (but maybe we don't have them
+                    -- either?)
               cabalDep =
                 maybe
                   []
@@ -1152,5 +1155,5 @@ getExternalSetupMethod verbosity options pkg bt = do
         return $ i setupProgFile
 
 isCabalPkgId, isBasePkgId :: PackageIdentifier -> Bool
-isCabalPkgId (PackageIdentifier pname _) = pname == mkPackageName "Cabal"
-isBasePkgId (PackageIdentifier pname _) = pname == mkPackageName "base"
+isCabalPkgId (PackageIdentifier pname _ _compid) = pname == mkPackageName "Cabal"
+isBasePkgId (PackageIdentifier pname _ _compid) = pname == mkPackageName "base"
diff --git a/cabal-install/src/Distribution/Client/SolverInstallPlan.hs b/cabal-install/src/Distribution/Client/SolverInstallPlan.hs
index 17dcf6d9398..883f9f38a91 100644
--- a/cabal-install/src/Distribution/Client/SolverInstallPlan.hs
+++ b/cabal-install/src/Distribution/Client/SolverInstallPlan.hs
@@ -218,7 +218,7 @@ showPlanProblem (PackageInconsistency name inconsistencies) =
       [ "  package "
         ++ prettyShow pkg
         ++ " requires "
-        ++ prettyShow (PackageIdentifier name ver)
+        ++ prettyShow (PackageIdentifier name ver Nothing)
       | (pkg, ver) <- inconsistencies
       ]
 showPlanProblem (PackageStateInvalid pkg pkg') =
@@ -360,6 +360,43 @@ setupRoots =
 -- Each element in the result is a package name along with the packages that
 -- depend on it and the versions they require. These are guaranteed to be
 -- distinct.
+
+-- type SolverPlanIndex = Graph (ResolverPackage UnresolvedPkgLoc)
+-- data ResolverPackage loc = PreExisting InstSolverPackage
+--                          | Configured  (SolverPackage loc)
+-- data InstSolverPackage = InstSolverPackage {
+--       instSolverQPN :: QPN,
+--       instSolverPkgIPI :: InstalledPackageInfo,
+--       instSolverPkgLibDeps :: ComponentDeps [SolverId],
+--       instSolverPkgExeDeps :: ComponentDeps [SolverId]
+--     }
+-- data SolverPackage loc = SolverPackage {
+--         solverPkgQPN     :: QPN,
+--         solverPkgStage   :: Stage,
+--         solverPkgSource  :: SourcePackage loc,
+--         solverPkgFlags   :: FlagAssignment,
+--         solverPkgStanzas :: OptionalStanzaSet,
+--         solverPkgLibDeps :: ComponentDeps [SolverId],
+--         solverPkgExeDeps :: ComponentDeps [SolverId]
+--     }
+-- newtype ComponentDeps a = ComponentDeps { unComponentDeps :: Map Component a }
+-- data SolverId = PreExistingId { solverSrcId :: PackageId, solverInstId :: UnitId }
+--               | PlannedId     { solverSrcId :: PackageId }
+--
+-- flatDeps :: ComponentDeps a -> a
+
+-- instance Package (ResolverPackage loc) where
+--   packageId (PreExisting ipkg)     = packageId ipkg
+--   packageId (Configured  spkg)     = packageId spkg
+
+-- resolverPackageLibDeps :: ResolverPackage loc -> CD.ComponentDeps [SolverId]
+-- resolverPackageLibDeps (PreExisting ipkg) = instSolverPkgLibDeps ipkg
+-- resolverPackageLibDeps (Configured spkg) = solverPkgLibDeps spkg
+
+-- resolverPackageExeDeps :: ResolverPackage loc -> CD.ComponentDeps [SolverId]
+-- resolverPackageExeDeps (PreExisting ipkg) = instSolverPkgExeDeps ipkg
+-- resolverPackageExeDeps (Configured spkg) = solverPkgExeDeps spkg
+
 dependencyInconsistencies'
   :: SolverPlanIndex
   -> [(PackageName, [(PackageIdentifier, Version)])]
diff --git a/cabal-install/src/Distribution/Client/SourceFiles.hs b/cabal-install/src/Distribution/Client/SourceFiles.hs
index 1166f333f3c..52b203d18c8 100644
--- a/cabal-install/src/Distribution/Client/SourceFiles.hs
+++ b/cabal-install/src/Distribution/Client/SourceFiles.hs
@@ -28,6 +28,7 @@ import Distribution.Types.BuildInfo
 import Distribution.Types.Component
 import Distribution.Types.ComponentRequestedSpec (ComponentRequestedSpec)
 import Distribution.Types.Executable
+import Distribution.Types.ExtraSource
 import Distribution.Types.ForeignLib
 import Distribution.Types.Library
 import Distribution.Types.PackageDescription
@@ -176,11 +177,11 @@ needBuildInfo pkg_descr bi modules = do
         matchDirFileGlobWithDie normal (\_ _ -> return []) (specVersion pkg_descr) (Just $ makeSymbolicPath root) fpath
   traverse_ needIfExists $
     concat
-      [ map getSymbolicPath $ cSources bi
-      , map getSymbolicPath $ cxxSources bi
-      , map getSymbolicPath $ jsSources bi
-      , map getSymbolicPath $ cmmSources bi
-      , map getSymbolicPath $ asmSources bi
+      [ map (getSymbolicPath . extraSourceFile) $ cSources bi
+      , map (getSymbolicPath . extraSourceFile) $ cxxSources bi
+      , map (getSymbolicPath . extraSourceFile) $ jsSources bi
+      , map (getSymbolicPath . extraSourceFile) $ cmmSources bi
+      , map (getSymbolicPath . extraSourceFile) $ asmSources bi
       , map getSymbolicPath $ expandedExtraSrcFiles
       ]
   for_ (fmap getSymbolicPath $ installIncludes bi) $ \f ->
diff --git a/cabal-install/src/Distribution/Client/Store.hs b/cabal-install/src/Distribution/Client/Store.hs
index dcf4c78d02c..5b82712b963 100644
--- a/cabal-install/src/Distribution/Client/Store.hs
+++ b/cabal-install/src/Distribution/Client/Store.hs
@@ -26,9 +26,11 @@ import Prelude ()
 import Distribution.Client.DistDirLayout
 import Distribution.Client.RebuildMonad
 
-import Distribution.Package (UnitId, mkUnitId)
+import Distribution.Package (UnitId, mkUnitId, addPrefixToUnitId, isPartialUnitId)
 import Distribution.Simple.Compiler (Compiler (..))
 
+import GHC.Stack (HasCallStack)
+
 import Distribution.Simple.Utils
   ( debug
   , info
@@ -132,18 +134,20 @@ doesStoreEntryExist StoreDirLayout{storePackageDirectory} compiler unitid =
 
 -- | Return the 'UnitId's of all packages\/components already installed in the
 -- store.
-getStoreEntries :: StoreDirLayout -> Compiler -> Rebuild (Set UnitId)
+getStoreEntries :: HasCallStack => StoreDirLayout -> Compiler -> Rebuild (Set UnitId)
 getStoreEntries StoreDirLayout{storeDirectory} compiler = do
   paths <- getDirectoryContentsMonitored (storeDirectory compiler)
   return $! mkEntries paths
   where
     mkEntries =
-      Set.delete (mkUnitId "package.db")
-        . Set.delete (mkUnitId "incoming")
-        . Set.fromList
-        . map mkUnitId
+      Set.fromList
+        . map (\pkg -> case mkUnitId pkg of
+            uid | isPartialUnitId uid -> addPrefixToUnitId (prettyShow (compilerId compiler)) uid
+                | otherwise -> uid)
         . filter valid
     valid ('.' : _) = False
+    valid "incoming" = False
+    valid "package.db" = False
     valid _ = True
 
 -- | The outcome of 'newStoreEntry': either the store entry was newly created
diff --git a/cabal-install/src/Distribution/Client/TargetSelector.hs b/cabal-install/src/Distribution/Client/TargetSelector.hs
index b31655c59c6..d74aff42e3e 100644
--- a/cabal-install/src/Distribution/Client/TargetSelector.hs
+++ b/cabal-install/src/Distribution/Client/TargetSelector.hs
@@ -72,6 +72,7 @@ import Distribution.PackageDescription
   , BenchmarkInterface (..)
   , BuildInfo (..)
   , Executable (..)
+  , ExtraSourceClass (..)
   , PackageDescription
   , TestSuite (..)
   , TestSuiteInterface (..)
@@ -1921,8 +1922,8 @@ collectKnownComponentInfo pkg =
     , cinfoSrcDirs = ordNub (map getSymbolicPath (hsSourceDirs bi))
     , cinfoModules = ordNub (componentModules c)
     , cinfoHsFiles = ordNub (componentHsFiles c)
-    , cinfoCFiles = ordNub (map getSymbolicPath $ cSources bi)
-    , cinfoJsFiles = ordNub (map getSymbolicPath $ jsSources bi)
+    , cinfoCFiles = ordNub (map (getSymbolicPath . extraSourceFile) $ cSources bi)
+    , cinfoJsFiles = ordNub (map (getSymbolicPath . extraSourceFile) $ jsSources bi)
     }
   | c <- pkgComponents pkg
   , let bi = componentBuildInfo c
diff --git a/cabal-install/src/Distribution/Client/Targets.hs b/cabal-install/src/Distribution/Client/Targets.hs
index bfa94b0da80..cef19b4d277 100644
--- a/cabal-install/src/Distribution/Client/Targets.hs
+++ b/cabal-install/src/Distribution/Client/Targets.hs
@@ -618,6 +618,8 @@ data UserConstraintScope
     UserQualified UserQualifier PackageName
   | -- | Scope that applies to the package when it has a setup qualifier.
     UserAnySetupQualifier PackageName
+  | -- | Scope that applies to all build packages only.
+    UserAnyBuildDepQualifier PackageName
   | -- | Scope that applies to the package when it has any qualifier.
     UserAnyQualifier PackageName
   deriving (Eq, Show, Generic)
@@ -634,6 +636,7 @@ fromUserConstraintScope :: UserConstraintScope -> ConstraintScope
 fromUserConstraintScope (UserQualified q pn) =
   ScopeQualified (fromUserQualifier q) pn
 fromUserConstraintScope (UserAnySetupQualifier pn) = ScopeAnySetupQualifier pn
+fromUserConstraintScope (UserAnyBuildDepQualifier pn) = ScopeAnyBuildDepQualifier pn
 fromUserConstraintScope (UserAnyQualifier pn) = ScopeAnyQualifier pn
 
 -- | Version of 'PackageConstraint' that the user can specify on
@@ -650,6 +653,7 @@ userConstraintPackageName (UserConstraint scope _) = scopePN scope
   where
     scopePN (UserQualified _ pn) = pn
     scopePN (UserAnyQualifier pn) = pn
+    scopePN (UserAnyBuildDepQualifier pn) = pn
     scopePN (UserAnySetupQualifier pn) = pn
 
 userToPackageConstraint :: UserConstraint -> PackageConstraint
@@ -698,6 +702,7 @@ instance Parsec UserConstraint where
           withDot :: PackageName -> m UserConstraintScope
           withDot pn
             | pn == mkPackageName "any" = UserAnyQualifier <$> parsec
+            | pn == mkPackageName "build" = UserAnyBuildDepQualifier <$> parsec
             | pn == mkPackageName "setup" = UserAnySetupQualifier <$> parsec
             | otherwise = P.unexpected $ "constraint scope: " ++ unPackageName pn
 
diff --git a/cabal-install/src/Distribution/Client/Types/AllowNewer.hs b/cabal-install/src/Distribution/Client/Types/AllowNewer.hs
index 0a5700174b8..6a6503aaf4c 100644
--- a/cabal-install/src/Distribution/Client/Types/AllowNewer.hs
+++ b/cabal-install/src/Distribution/Client/Types/AllowNewer.hs
@@ -109,7 +109,7 @@ relaxedDepStarP =
 
 -- continuation after package identifier
 relaxedDepPkgidP :: CabalParsing m => PackageIdentifier -> m RelaxedDep
-relaxedDepPkgidP pid@(PackageIdentifier pn v)
+relaxedDepPkgidP pid@(PackageIdentifier pn v _compid)
   | pn == mkPackageName "all"
   , v == nullVersion =
       RelaxedDep RelaxDepScopeAll <$ P.char ':' <*> modP <*> parsec
diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs
index 2b25a64b6be..c6c33735900 100644
--- a/cabal-install/tests/IntegrationTests2.hs
+++ b/cabal-install/tests/IntegrationTests2.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
@@ -70,7 +71,7 @@ import qualified Distribution.Simple.Flag as Flag
 import Distribution.Simple.Setup (CommonSetupFlags (..), HaddockFlags (..), HaddockProjectFlags (..), defaultCommonSetupFlags, defaultHaddockFlags, defaultHaddockProjectFlags, toFlag)
 import Distribution.System
 import Distribution.Text
-import Distribution.Utils.Path (unsafeMakeSymbolicPath)
+import Distribution.Utils.Path (FileOrDir (File), Pkg, SymbolicPath, unsafeMakeSymbolicPath)
 import Distribution.Version
 import IntegrationTests2.CPP
 
@@ -658,7 +659,10 @@ testTargetSelectorAmbiguous reportSubCase = do
 
     withCFiles :: Executable -> [FilePath] -> Executable
     withCFiles exe files =
-      exe{buildInfo = (buildInfo exe){cSources = map unsafeMakeSymbolicPath files}}
+      exe{buildInfo = (buildInfo exe){cSources = map (mkExtraSource . unsafeMakeSymbolicPath) files}}
+
+    mkExtraSource :: SymbolicPath Pkg File -> ExtraSource Pkg
+    mkExtraSource x = ExtraSourcePkg x []
 
     withHsSrcDirs :: Executable -> [FilePath] -> Executable
     withHsSrcDirs exe srcDirs =
@@ -1887,10 +1891,10 @@ testSetupScriptStyles config reportSubCase = do
 
   let isOSX (Platform _ OSX) = True
       isOSX _ = False
-      compilerVer = compilerVersion (pkgConfigCompiler sharedConfig)
+      compilerVer = compilerVersion (toolchainCompiler $ buildToolchain $ pkgConfigToolchains sharedConfig)
   -- Skip the Custom tests when the shipped Cabal library is buggy
   unless
-    ( (isOSX (pkgConfigPlatform sharedConfig) && (compilerVer < mkVersion [7, 10]))
+    ( (isOSX (toolchainPlatform $ buildToolchain $ pkgConfigToolchains sharedConfig) && (compilerVer < mkVersion [7, 10]))
         -- 9.10 ships Cabal 3.12.0.0 affected by #9940
         || (mkVersion [9, 10] <= compilerVer && compilerVer < mkVersion [9, 11])
     )
@@ -1904,7 +1908,7 @@ testSetupScriptStyles config reportSubCase = do
       removeFile (basedir </> testdir1 </> "marker")
 
       -- implicit deps implies 'Cabal < 2' which conflicts w/ GHC 8.2 or later
-      when (compilerVersion (pkgConfigCompiler sharedConfig) < mkVersion [8, 2]) $ do
+      when (compilerVersion (toolchainCompiler $ buildToolchain $ pkgConfigToolchains sharedConfig) < mkVersion [8, 2]) $ do
         reportSubCase (show SetupCustomImplicitDeps)
         (plan2, res2) <- executePlan =<< planProject testdir2 config
         pkg2 <- expectPackageInstalled plan2 res2 pkgidA
@@ -2193,6 +2197,7 @@ planProject testdir cliConfig = do
   (elaboratedPlan, _, elaboratedShared, _, _) <-
     rebuildInstallPlan
       verbosity
+      mempty
       distDirLayout
       cabalDirLayout
       projectConfig
@@ -2779,7 +2784,7 @@ testHaddockProjectDependencies config = do
   (_, _, sharedConfig) <- planProject testdir config
   -- `haddock-project` is only supported by `haddock-2.26.1` and above which is
   -- shipped with `ghc-9.4`
-  when (compilerVersion (pkgConfigCompiler sharedConfig) > mkVersion [9, 4]) $ do
+  when (compilerVersion (toolchainCompiler $ buildToolchain $ pkgConfigToolchains sharedConfig) > mkVersion [9, 4]) $ do
     let dir = basedir </> testdir
     cleanHaddockProject testdir
     withCurrentDirectory dir $ do
diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs
index bf69b20ee04..a77415fec49 100644
--- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs
+++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs
@@ -11,6 +11,7 @@
 module UnitTests.Distribution.Client.ProjectConfig (tests) where
 
 import Control.Monad
+import qualified Data.ByteString.Lazy.Char8 as LBS
 import Data.Either (isRight)
 import Data.Foldable (for_)
 import Data.List (intercalate, isPrefixOf, (\\))
@@ -26,6 +27,8 @@ import System.IO.Unsafe (unsafePerformIO)
 import Distribution.Deprecated.ParseUtils
 import qualified Distribution.Deprecated.ReadP as Parse
 
+import Distribution.Client.HashValue (hashValue)
+import Distribution.Client.HookAccept (HookAccept (..))
 import Distribution.Compiler
 import Distribution.Package
 import Distribution.PackageDescription
@@ -610,6 +613,9 @@ instance Arbitrary ProjectConfigShared where
     projectConfigHcFlavor <- arbitrary
     projectConfigHcPath <- arbitraryFlag arbitraryShortToken
     projectConfigHcPkg <- arbitraryFlag arbitraryShortToken
+    projectConfigHostHcFlavor <- arbitrary
+    projectConfigHostHcPath <- arbitraryFlag arbitraryShortToken
+    projectConfigHostHcPkg <- arbitraryFlag arbitraryShortToken
     projectConfigHaddockIndex <- arbitrary
     projectConfigInstallDirs <- fixInstallDirs <$> arbitrary
     projectConfigPackageDBs <- shortListOf 2 arbitrary
@@ -638,6 +644,7 @@ instance Arbitrary ProjectConfigShared where
     projectConfigPreferOldest <- arbitrary
     projectConfigProgPathExtra <- toNubList <$> listOf arbitraryShortToken
     projectConfigMultiRepl <- arbitrary
+    projectConfigHookHashes <- arbitrary
     return ProjectConfigShared{..}
     where
       arbitraryConstraints :: Gen [(UserConstraint, ConstraintSource)]
@@ -656,6 +663,9 @@ instance Arbitrary ProjectConfigShared where
         <*> shrinker projectConfigHcFlavor
         <*> shrinkerAla (fmap NonEmpty) projectConfigHcPath
         <*> shrinkerAla (fmap NonEmpty) projectConfigHcPkg
+        <*> shrinker projectConfigHostHcFlavor
+        <*> shrinkerAla (fmap NonEmpty) projectConfigHostHcPath
+        <*> shrinkerAla (fmap NonEmpty) projectConfigHostHcPkg
         <*> shrinker projectConfigHaddockIndex
         <*> shrinker projectConfigInstallDirs
         <*> shrinker projectConfigPackageDBs
@@ -684,6 +694,7 @@ instance Arbitrary ProjectConfigShared where
         <*> shrinker projectConfigPreferOldest
         <*> shrinker projectConfigProgPathExtra
         <*> shrinker projectConfigMultiRepl
+        <*> shrinker projectConfigHookHashes
     where
       preShrink_Constraints = map fst
       postShrink_Constraints = map (\uc -> (uc, projectConfigConstraintSource))
@@ -691,6 +702,9 @@ instance Arbitrary ProjectConfigShared where
 projectConfigConstraintSource :: ConstraintSource
 projectConfigConstraintSource = ConstraintSourceProjectConfig nullProjectConfigPath
 
+instance Arbitrary HookAccept where
+  arbitrary = elements [AcceptAlways, AcceptHash (hashValue $ LBS.pack "hash")]
+
 instance Arbitrary ProjectConfigProvenance where
   arbitrary = elements [Implicit, Explicit (ProjectConfigPath $ "cabal.project" :| [])]
 
diff --git a/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs b/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs
index 179fef5688a..42c38a7bc82 100644
--- a/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs
+++ b/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs
@@ -13,6 +13,8 @@ import Distribution.Solver.Types.Settings
 import Distribution.Client.BuildReports.Types
 import Distribution.Client.CmdInstall.ClientInstallFlags
 import Distribution.Client.Dependency.Types
+import Distribution.Client.HashValue (HashValue)
+import Distribution.Client.HookAccept (HookAccept (..))
 import Distribution.Client.IndexUtils.ActiveRepos
 import Distribution.Client.IndexUtils.IndexState
 import Distribution.Client.IndexUtils.Timestamp
@@ -45,6 +47,8 @@ instance ToExpr ProjectConfigPath
 instance ToExpr ConstraintSource
 instance ToExpr CountConflicts
 instance ToExpr FineGrainedConflicts
+instance ToExpr HashValue
+instance ToExpr HookAccept
 instance ToExpr IndependentGoals
 instance ToExpr InstallMethod
 instance ToExpr InstallOutcome
diff --git a/cabal-testsuite/PackageTests/ExtraSources/cbits/test.c b/cabal-testsuite/PackageTests/ExtraSources/cbits/test.c
new file mode 100644
index 00000000000..e31c5a9b7b5
--- /dev/null
+++ b/cabal-testsuite/PackageTests/ExtraSources/cbits/test.c
@@ -0,0 +1,3 @@
+#ifndef DOIT
+#error "It does not work"
+#endif
diff --git a/cabal-testsuite/PackageTests/ExtraSources/extra-sources.cabal b/cabal-testsuite/PackageTests/ExtraSources/extra-sources.cabal
new file mode 100644
index 00000000000..0d340f5c6bd
--- /dev/null
+++ b/cabal-testsuite/PackageTests/ExtraSources/extra-sources.cabal
@@ -0,0 +1,10 @@
+cabal-version: 3.4
+name: extra-sources
+version: 0
+build-type: Simple
+
+library
+  hs-source-dirs: src
+  build-depends: base
+  exposed-modules: MyLib
+  c-sources: cbits/test.c (-D DOIT=1)
diff --git a/cabal-testsuite/PackageTests/ExtraSources/setup.out b/cabal-testsuite/PackageTests/ExtraSources/setup.out
new file mode 100644
index 00000000000..43a3574bd1b
--- /dev/null
+++ b/cabal-testsuite/PackageTests/ExtraSources/setup.out
@@ -0,0 +1,5 @@
+# Setup configure
+Configuring extra-sources-0...
+# Setup build
+Preprocessing library for extra-sources-0...
+Building library for extra-sources-0...
diff --git a/cabal-testsuite/PackageTests/ExtraSources/setup.test.hs b/cabal-testsuite/PackageTests/ExtraSources/setup.test.hs
new file mode 100644
index 00000000000..9e2abcb188b
--- /dev/null
+++ b/cabal-testsuite/PackageTests/ExtraSources/setup.test.hs
@@ -0,0 +1,5 @@
+import Test.Cabal.Prelude
+
+main = setupTest $ do
+    setup "configure" []
+    setup "build" []
diff --git a/cabal-testsuite/PackageTests/ExtraSources/src/MyLib.hs b/cabal-testsuite/PackageTests/ExtraSources/src/MyLib.hs
new file mode 100644
index 00000000000..bcdf120b02c
--- /dev/null
+++ b/cabal-testsuite/PackageTests/ExtraSources/src/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib where
+
+someFunc :: IO ()
+someFunc = mempty
diff --git a/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs b/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs
index 6267a0f5684..1667106ac8b 100644
--- a/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs
+++ b/cabal-testsuite/src/Test/Cabal/OutputNormalizer.hs
@@ -115,11 +115,6 @@ normalizeOutput nenv =
         -- and replace it with:
         -- > "-package-id","<PACKAGEDEP>"
         --
-        -- Otherwise, output can not be properly normalized as on MacOs we remove
-        -- vowels from packages to make the names shorter.
-        -- E.g. "another-framework-0.8.1.1" -> "nthr-frmwrk-0.8.1.1"
-        --
-        -- This makes it impossible to have a stable package id, thus remove it completely.
         -- Check manually in your test-cases if the package-id needs to be verified.
       . resub ("\"-package-id\",\"([^\"]*)\"")
               "\"-package-id\",\"<PACKAGEDEP>\""
diff --git a/changelog.d/pr-10799 b/changelog.d/pr-10799
new file mode 100644
index 00000000000..3f37124aebb
--- /dev/null
+++ b/changelog.d/pr-10799
@@ -0,0 +1,15 @@
+synopsis: Add pre and post build hooks
+packages: cabal-install
+prs: #10799
+issues: #9892
+significance: significant
+
+description: {
+
+- Run a program (named "preBuildHook") before doing a package build and another program
+  (named "postBuildHook") after the package is built.
+- These programs are project local and need to be in the `cabalHooks` directory which is
+  in the same directory as the `cabal.project` file.
+- The absence of these programs will be ignored.
+- How to check and run these hooks securely is specified in the documentation.
+}
diff --git a/doc/build-hooks.rst b/doc/build-hooks.rst
new file mode 100644
index 00000000000..82e53693dc4
--- /dev/null
+++ b/doc/build-hooks.rst
@@ -0,0 +1,83 @@
+Build Hooks
+===========
+
+Build hooks are programs that are run before (pre-build hook) and
+after (post-build hook) a package (including package dependencies)
+is built. The hooks are completely generic and can even be absent
+(their absence is ignored). Regardless of the return code of the
+pre-build hook, the normal build is executed. In the case where
+the pre-build hook provides a pre-built version of what the build
+step would provide, the build step is still run, but should be
+little more than a NOOP.
+
+Build hooks are project local rather than global to the user
+because a single user may want to use one set of hooks in one
+project and another set of hooks (or even none at all) for another
+project.
+
+Since the hook files are project local (and hence likely to be committed
+to revision control), a naive implementation of these hooks would be
+a potential security issue. A solution to this potential security
+issue has been implemented and described in the Hook Security section
+below.
+
+
+Possible Use Cases
+------------------
+
+Possible use cases include:
+
+* Fine grained benchmarking of individual package build times.
+* Build product caching.
+
+
+Location of Hook Files
+----------------------
+
+The two hook files are `cabalHooks/preBuildHook` and
+`cabalHooks/postBuildHook` where the `cabalHooks` directory is in
+the same directory as the `cabal.project` file. On UNIX style
+systems, these hooks need to be marked as user executable programs.
+
+
+Hook Parameters Exit Codes
+--------------------------
+
+The pre-build hook is passed three parameters; the unit id (from cabal),
+the source directory and the build directory. The post-build hook is
+passed the same three parameters, plus the exit code of the pre-build
+hook.
+
+The exit codes for the two hooks are ignored by cabal apart from cabal
+capturing the exit code for the pre-build hook and passing it to the
+post-build hook.
+
+
+Hook Security
+-------------
+These hook files are generic executable programs and are stored local to
+the project. To prevent the running of malicious hook files, the
+hook files are only run, it they are mentioned in the `hooks-security`
+file which is located in the users home `.cabal` directory, usually
+either `$HOME/.cabal/` or `$HOME/.config/cabal/`.
+
+The `hooks-security` file should contain one entry per line. Blank lines
+are ignored, as are Haskell style single line comments (starts with "--"
+and goes until the end of the line). Each entry should contain the full
+hook file path, at least one space and either "AcceptAlways" or
+"AcceptHash" followed by at least one space and the hexadecimal encoded
+hash of the file. The `hooks-security` file is read once when `cabal` is
+started and the entries inserted into a `Map`.
+
+When `cabal` detects a "preBuildHook" or "postBuildHook" it looks up
+the full file path in the `Map`. If the path is not found in the `Map`,
+`cabal` will die with an error message suggesting that the hook file
+be manually inspected and if deemed safe, added to the `hooks-security`
+file.
+
+If the hook file path is in the `Map` and it was specified as
+"AcceptAlways" the hook will be run. If the `Map` entry is "AcceptHash"
+with a hash, the hash of the hook file will be calculated and compared
+against the supplied hash. If the hashes match, the hook will be run.
+If there is a hash mismatch, `cabal` will abort with an error message
+about the hash mismatch.
diff --git a/doc/index.rst b/doc/index.rst
index 4bd13c65d7a..a721b77ed63 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -20,6 +20,7 @@ Welcome to the Cabal User Guide
    how-to-run-in-windows
    how-to-use-backpack
    how-to-report-bugs
+   build-hooks
 
 .. toctree::
    :caption: Cabal Reference