diff --git a/cabal-install.cabal b/cabal-install.cabal
--- a/cabal-install.cabal
+++ b/cabal-install.cabal
@@ -1,7 +1,7 @@
 Cabal-Version:      3.0
 
 Name:               cabal-install
-Version:            3.14.1.1
+Version:            3.14.2.0
 Synopsis:           The command-line interface for Cabal and Hackage.
 Description:
     The \'cabal\' command-line program simplifies the process of managing
@@ -55,7 +55,7 @@
     build-depends: base >=4.13 && <4.22
 
 common cabal-dep
-    build-depends: Cabal ^>=3.14
+    build-depends: Cabal >=3.14.2 && <3.15
 
 common cabal-syntax-dep
     build-depends: Cabal-syntax ^>=3.14
@@ -221,7 +221,7 @@
     build-depends:
         async      >= 2.0      && < 2.3,
         array      >= 0.4      && < 0.6,
-        base16-bytestring >= 0.1.1 && < 1.1.0.0,
+        base16-bytestring >= 0.1.1 && < 1.1,
         binary     >= 0.7.3    && < 0.9,
         bytestring >= 0.10.6.0 && < 0.13,
         containers >= 0.5.6.2  && < 0.8,
@@ -236,7 +236,7 @@
         network-uri >= 2.6.0.2 && < 2.7,
         pretty     >= 1.1      && < 1.2,
         process    >= 1.2.3.0  && < 1.7,
-        random     >= 1.2      && < 1.3,
+        random     >= 1.2      && < 1.4,
         stm        >= 2.0      && < 2.6,
         tar        >= 0.5.0.3  && < 0.7,
         time       >= 1.5.0.1  && < 1.15,
@@ -244,7 +244,7 @@
         hackage-security >= 0.6.2.0 && < 0.7,
         text       >= 1.2.3    && < 1.3 || >= 2.0 && < 2.2,
         parsec     >= 3.1.13.0 && < 3.2,
-        open-browser >= 0.2.1.0 && < 0.3,
+        open-browser >= 0.2.1.0 && < 0.4,
         regex-base  >= 0.94.0.0 && <0.95,
         regex-posix >= 0.96.0.0 && <0.97,
         safe-exceptions >= 0.1.7.0 && < 0.2,
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,5 +1,8 @@
 -*-change-log-*-
 
+3.14.2.0 Mikolaj Konarski <mikolaj@well-typed.com> April 2025
+	* See https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.14.2.0.md
+
 3.14.1.0 Hécate <hecate+github@glitchbra.in> November 2024
 	* See https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.14.1.0.md
 
diff --git a/src/Distribution/Client/Check.hs b/src/Distribution/Client/Check.hs
--- a/src/Distribution/Client/Check.hs
+++ b/src/Distribution/Client/Check.hs
@@ -1,9 +1,3 @@
-{-# LANGUAGE CPP #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
-
 -- |
 -- Module      :  Distribution.Client.Check
 -- Copyright   :  (c) Lennart Kolmodin 2008
diff --git a/src/Distribution/Client/CmdOutdated.hs b/src/Distribution/Client/CmdOutdated.hs
--- a/src/Distribution/Client/CmdOutdated.hs
+++ b/src/Distribution/Client/CmdOutdated.hs
@@ -1,10 +1,5 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RecordWildCards #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Distribution.Client.CmdOutdated
diff --git a/src/Distribution/Client/CmdPath.hs b/src/Distribution/Client/CmdPath.hs
--- a/src/Distribution/Client/CmdPath.hs
+++ b/src/Distribution/Client/CmdPath.hs
@@ -1,11 +1,6 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RecordWildCards #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Distribution.Client.CmdPath
diff --git a/src/Distribution/Client/CmdTest.hs b/src/Distribution/Client/CmdTest.hs
--- a/src/Distribution/Client/CmdTest.hs
+++ b/src/Distribution/Client/CmdTest.hs
@@ -33,7 +33,8 @@
   )
 import Distribution.Client.ProjectOrchestration
 import Distribution.Client.Setup
-  ( ConfigFlags (..)
+  ( CommonSetupFlags (..)
+  , ConfigFlags (..)
   , GlobalFlags (..)
   )
 import Distribution.Client.TargetProblem
@@ -66,7 +67,6 @@
 import qualified System.Exit (exitSuccess)
 
 import Distribution.Client.Errors
-import Distribution.Client.Setup (CommonSetupFlags (..))
 import GHC.Environment
   ( getFullArgs
   )
diff --git a/src/Distribution/Client/CmdUpdate.hs b/src/Distribution/Client/CmdUpdate.hs
--- a/src/Distribution/Client/CmdUpdate.hs
+++ b/src/Distribution/Client/CmdUpdate.hs
@@ -1,6 +1,3 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
diff --git a/src/Distribution/Client/Config.hs b/src/Distribution/Client/Config.hs
--- a/src/Distribution/Client/Config.hs
+++ b/src/Distribution/Client/Config.hs
@@ -49,7 +49,10 @@
   ) where
 
 import Distribution.Client.Compat.Prelude
-import Distribution.Compat.Environment (lookupEnv)
+import Distribution.Compat.Environment
+  ( getEnvironment
+  , lookupEnv
+  )
 import Prelude ()
 
 import Language.Haskell.Extension (Language (Haskell2010))
@@ -126,9 +129,6 @@
   ( cabalInstallVersion
   )
 import qualified Distribution.Compat.CharParsing as P
-import Distribution.Compat.Environment
-  ( getEnvironment
-  )
 import Distribution.Compiler
   ( CompilerFlavor (..)
   , defaultCompilerFlavor
@@ -226,7 +226,8 @@
   , renameFile
   )
 import System.FilePath
-  ( takeDirectory
+  ( normalise
+  , takeDirectory
   , (<.>)
   , (</>)
   )
@@ -1679,7 +1680,12 @@
     -- Note: the trailing colon is important
     "file+noindex:" -> do
       let uri = remoteRepoURI repo0
-      return $ Left $ LocalRepo reponame (uriPath uri) (uriFragment uri == "#shared-cache")
+      return $
+        Left $
+          LocalRepo
+            reponame
+            (normalise (uriPath uri))
+            (uriFragment uri == "#shared-cache")
     _ -> do
       let repo = repo0{remoteRepoName = reponame}
 
diff --git a/src/Distribution/Client/Configure.hs b/src/Distribution/Client/Configure.hs
--- a/src/Distribution/Client/Configure.hs
+++ b/src/Distribution/Client/Configure.hs
@@ -1,9 +1,4 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Distribution.Client.Configure
diff --git a/src/Distribution/Client/FileMonitor.hs b/src/Distribution/Client/FileMonitor.hs
--- a/src/Distribution/Client/FileMonitor.hs
+++ b/src/Distribution/Client/FileMonitor.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DeriveFunctor #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
diff --git a/src/Distribution/Client/GenBounds.hs b/src/Distribution/Client/GenBounds.hs
--- a/src/Distribution/Client/GenBounds.hs
+++ b/src/Distribution/Client/GenBounds.hs
@@ -1,9 +1,3 @@
-{-# LANGUAGE CPP #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
-
 -- |
 -- Module      :  Distribution.Client.GenBounds
 -- Copyright   :  (c) Doug Beardsley 2015
diff --git a/src/Distribution/Client/GlobalFlags.hs b/src/Distribution/Client/GlobalFlags.hs
--- a/src/Distribution/Client/GlobalFlags.hs
+++ b/src/Distribution/Client/GlobalFlags.hs
@@ -57,7 +57,8 @@
   , uriScheme
   )
 import System.FilePath
-  ( (</>)
+  ( isAbsolute
+  , (</>)
   )
 
 import qualified Distribution.Client.Security.DNS as Sec.DNS
@@ -69,8 +70,6 @@
 import qualified Hackage.Security.Util.Path as Sec
 import qualified Hackage.Security.Util.Pretty as Sec
 
-import qualified System.FilePath.Posix as FilePath.Posix
-
 -- ------------------------------------------------------------
 
 -- * Global flags
@@ -192,7 +191,7 @@
   ignoreExpiry
   extraPaths = \callback -> do
     for_ localNoIndexRepos $ \local ->
-      unless (FilePath.Posix.isAbsolute (localRepoPath local)) $
+      unless (isAbsolute (localRepoPath local)) $
         warn verbosity $
           "file+noindex " ++ unRepoName (localRepoName local) ++ " repository path is not absolute; this is fragile, and not recommended"
 
diff --git a/src/Distribution/Client/IndexUtils.hs b/src/Distribution/Client/IndexUtils.hs
--- a/src/Distribution/Client/IndexUtils.hs
+++ b/src/Distribution/Client/IndexUtils.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE GADTs #-}
@@ -8,10 +7,6 @@
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
-
 -- |
 -- Module      :  Distribution.Client.IndexUtils
 -- Copyright   :  (c) Duncan Coutts 2008
@@ -461,7 +456,7 @@
             RepoSecure{..} -> warn verbosity $ exceptionMessageCabalInstall $ MissingPackageList repoRemote
             RepoLocalNoIndex local _ ->
               warn verbosity $
-                "Error during construction of local+noindex "
+                "Error during construction of file+noindex "
                   ++ unRepoName (localRepoName local)
                   ++ " repository index: "
                   ++ show e
@@ -521,7 +516,7 @@
     then action
     else
       if localNoIndex index
-        then return () -- TODO: don't update cache for local+noindex repositories
+        then return () -- TODO: don't update cache for file+noindex repositories
         else do
           indexTime <- getModTime $ indexFile index
           cacheTime <- getModTime $ cacheFile index
@@ -1120,12 +1115,14 @@
 -- 'dieWithException's if it fails again). Throws IOException if any arise.
 readNoIndexCache :: Verbosity -> Index -> IO NoIndexCache
 readNoIndexCache verbosity index = do
-  cacheOrFail <- readNoIndexCache' index
+  cacheOrFail <- readNoIndexCache' verbosity index
   case cacheOrFail of
     Left msg -> do
       warn verbosity $
         concat
-          [ "Parsing the index cache failed ("
+          [ "Parsing the index cache for repo \""
+          , unRepoName (repoName repo)
+          , "\" failed ("
           , msg
           , "). "
           , "Trying to regenerate the index cache..."
@@ -1133,10 +1130,12 @@
 
       updatePackageIndexCacheFile verbosity index
 
-      either (dieWithException verbosity . CorruptedIndexCache) return =<< readNoIndexCache' index
+      either (dieWithException verbosity . CorruptedIndexCache) return =<< readNoIndexCache' verbosity index
 
     -- we don't hash cons local repository cache, they are hopefully small
     Right res -> return res
+  where
+    RepoIndex _ctxt repo = index
 
 -- | Read the 'Index' cache from the filesystem. Throws IO exceptions
 -- if any arise and returns Left on invalid input.
@@ -1147,8 +1146,12 @@
   | otherwise =
       Right . read00IndexCache <$> BSS.readFile (cacheFile index)
 
-readNoIndexCache' :: Index -> IO (Either String NoIndexCache)
-readNoIndexCache' index = structuredDecodeFileOrFail (cacheFile index)
+readNoIndexCache' :: Verbosity -> Index -> IO (Either String NoIndexCache)
+readNoIndexCache' verbosity index = do
+  exists <- doesFileExist (cacheFile index)
+  if exists
+    then structuredDecodeFileOrFail (cacheFile index)
+    else updatePackageIndexCacheFile verbosity index >> readNoIndexCache' verbosity index
 
 -- | Write the 'Index' cache to the filesystem
 writeIndexCache :: Index -> Cache -> IO ()
diff --git a/src/Distribution/Client/InstallSymlink.hs b/src/Distribution/Client/InstallSymlink.hs
--- a/src/Distribution/Client/InstallSymlink.hs
+++ b/src/Distribution/Client/InstallSymlink.hs
@@ -1,11 +1,5 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RecordWildCards #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Distribution.Client.InstallSymlink
diff --git a/src/Distribution/Client/Main.hs b/src/Distribution/Client/Main.hs
--- a/src/Distribution/Client/Main.hs
+++ b/src/Distribution/Client/Main.hs
@@ -1,11 +1,6 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Main
diff --git a/src/Distribution/Client/Manpage.hs b/src/Distribution/Client/Manpage.hs
--- a/src/Distribution/Client/Manpage.hs
+++ b/src/Distribution/Client/Manpage.hs
@@ -1,7 +1,3 @@
-{-# LANGUAGE CPP #-}
-
------------------------------------------------------------------------------
-
 -- |
 -- Module      :  Distribution.Client.Manpage
 -- Copyright   :  (c) Maciek Makowski 2015
diff --git a/src/Distribution/Client/ProjectBuilding.hs b/src/Distribution/Client/ProjectBuilding.hs
--- a/src/Distribution/Client/ProjectBuilding.hs
+++ b/src/Distribution/Client/ProjectBuilding.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE ConstraintKinds #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE NamedFieldPuns #-}
diff --git a/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs b/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs
--- a/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs
+++ b/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs
@@ -447,7 +447,7 @@
     -- TODO: [code cleanup] there is duplication between the
     --      distdirlayout and the builddir here builddir is not
     --      enough, we also need the per-package cachedir
-    createDirectoryIfMissingVerbose verbosity True $ getSymbolicPath builddir
+    createDirectoryIfMissingVerbose verbosity True $ interpretSymbolicPath (Just srcdir) builddir
     createDirectoryIfMissingVerbose
       verbosity
       True
diff --git a/src/Distribution/Client/ProjectConfig.hs b/src/Distribution/Client/ProjectConfig.hs
--- a/src/Distribution/Client/ProjectConfig.hs
+++ b/src/Distribution/Client/ProjectConfig.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE RecordWildCards #-}
diff --git a/src/Distribution/Client/ProjectConfig/Legacy.hs b/src/Distribution/Client/ProjectConfig/Legacy.hs
--- a/src/Distribution/Client/ProjectConfig/Legacy.hs
+++ b/src/Distribution/Client/ProjectConfig/Legacy.hs
@@ -39,7 +39,7 @@
 
 import Distribution.Client.ProjectConfig.Types
 import Distribution.Client.Types.AllowNewer (AllowNewer (..), AllowOlder (..))
-import Distribution.Client.Types.Repo (LocalRepo (..), RemoteRepo (..), emptyRemoteRepo)
+import Distribution.Client.Types.Repo (LocalRepo (..), RemoteRepo (..), emptyRemoteRepo, normaliseFileNoIndexURI)
 import Distribution.Client.Types.RepoName (RepoName (..), unRepoName)
 import Distribution.Client.Types.SourceRepo (SourceRepoList, sourceRepositoryPackageGrammar)
 
@@ -173,7 +173,7 @@
   , option
   , reqArg'
   )
-import Distribution.System (Arch, OS)
+import Distribution.System (Arch, OS, buildOS)
 import Distribution.Types.PackageVersionConstraint
   ( PackageVersionConstraint
   )
@@ -185,7 +185,7 @@
 import qualified Data.ByteString.Char8 as BS
 import qualified Data.Map as Map
 import qualified Data.Set as Set
-import Network.URI (URI (..), parseURI)
+import Network.URI (URI (..), nullURIAuth, parseURI)
 import System.Directory (createDirectoryIfMissing, makeAbsolute)
 import System.FilePath (isAbsolute, isPathSeparator, makeValid, splitFileName, (</>))
 import Text.PrettyPrint
@@ -2043,7 +2043,14 @@
     localToRemote :: LocalRepo -> RemoteRepo
     localToRemote (LocalRepo name path sharedCache) =
       (emptyRemoteRepo name)
-        { remoteRepoURI = URI "file+noindex:" Nothing path "" (if sharedCache then "#shared-cache" else "")
+        { remoteRepoURI =
+            normaliseFileNoIndexURI buildOS $
+              URI
+                "file+noindex:"
+                (Just nullURIAuth)
+                path
+                ""
+                (if sharedCache then "#shared-cache" else "")
         }
 
 -------------------------------
diff --git a/src/Distribution/Client/ProjectPlanning.hs b/src/Distribution/Client/ProjectPlanning.hs
--- a/src/Distribution/Client/ProjectPlanning.hs
+++ b/src/Distribution/Client/ProjectPlanning.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveFunctor #-}
@@ -100,7 +99,18 @@
   ) where
 
 import Distribution.Client.Compat.Prelude
-import Text.PrettyPrint (render)
+import Text.PrettyPrint
+  ( colon
+  , comma
+  , fsep
+  , hang
+  , punctuate
+  , quotes
+  , render
+  , text
+  , vcat
+  , ($$)
+  )
 import Prelude ()
 
 import Distribution.Client.Config
@@ -220,7 +230,6 @@
 import Distribution.Client.Errors
 import Distribution.Solver.Types.ProjectConfigPath
 import System.FilePath
-import Text.PrettyPrint (colon, comma, fsep, hang, punctuate, quotes, text, vcat, ($$))
 import qualified Text.PrettyPrint as Disp
 
 -- | Check that an 'ElaboratedConfiguredPackage' actually makes
@@ -461,6 +470,7 @@
         { projectConfigHcFlavor
         , projectConfigHcPath
         , projectConfigHcPkg
+        , projectConfigProgPathExtra
         }
     , projectConfigLocalPackages =
       PackageConfig
@@ -471,6 +481,7 @@
     let fileMonitorCompiler = newFileMonitor $ distProjectCacheFile "compiler"
 
     progsearchpath <- liftIO $ getSystemSearchPath
+
     rerunIfChanged
       verbosity
       fileMonitorCompiler
@@ -483,37 +494,78 @@
       )
       $ 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
-        (comp, plat, progdb'') <-
+        progdb <-
           liftIO $
+            -- Add paths in the global config
+            prependProgramSearchPath verbosity (fromNubList projectConfigProgPathExtra) [] defaultProgramDb
+              -- Add paths in the local config
+              >>= prependProgramSearchPath verbosity (fromNubList packageConfigProgramPathExtra) []
+              >>= pure . userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths))
+        result@(_, _, progdb') <-
+          liftIO $
             Cabal.configCompilerEx
               hcFlavor
               hcPath
               hcPkg
-              progdb'
+              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'')
+        monitorFiles (programsMonitorFiles progdb')
 
-        -- Configure the unconfigured programs in the program database,
-        -- as we can't serialise unconfigured programs.
-        -- See also #2241 and #9840.
-        finalProgDb <- liftIO $ configureAllKnownPrograms verbosity 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]
 
-        return (comp, plat, finalProgDb)
+        return result
     where
       hcFlavor = flagToMaybe projectConfigHcFlavor
       hcPath = flagToMaybe projectConfigHcPath
       hcPkg = flagToMaybe projectConfigHcPkg
 
+{- Note [Caching the result of configuring the compiler]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We can't straightforwardly cache anything that contains a 'ProgramDb', because
+the 'Binary' instance for 'ProgramDb' discards all unconfigured programs.
+See that instance, as well as 'restoreProgramDb', for a few more details.
+
+This means that if we try to cache the result of configuring the compiler (which
+contains a 'ProgramDb'):
+
+ - On the first run, we will obtain a 'ProgramDb' which may contain several
+   unconfigured programs. In particular, configuring GHC will add tools such
+   as `ar` and `ld` as unconfigured programs to the 'ProgramDb', with custom
+   logic for finding their location based on the location of the GHC binary.
+ - On subsequent runs, if we use the cache created by 'rerunIfChanged', we will
+   deserialise the 'ProgramDb' from disk, which means it won't include any
+   unconfigured programs, which might mean we are unable to find 'ar' or 'ld'.
+
+This is not currently a huge problem because, in the Cabal library, we eagerly
+re-run the configureCompiler step (thus recovering any lost information), but
+this is wasted work that we should stop doing in Cabal, given that cabal-install
+has already figured out all the necessary information about the compiler.
+
+To fix this bug, we can't simply eagerly configure all unconfigured programs,
+as was originally attempted, for a couple of reasons:
+
+ - it does more work than necessary, by configuring programs that we may not
+   end up needing,
+ - it means that we prioritise system executables for built-in build tools
+   (such as `alex` and `happy`), instead of using the proper version for a
+   package or package component, as specified by a `build-tool-depends` stanza
+   or by package-level `extra-prog-path` arguments.
+   This lead to bug reports #10633 and #10692.
+
+See #9840 for more information about the problems surrounding the lossly
+Binary ProgramDb instance.
+-}
+
 ------------------------------------------------------------------------------
 
 -- * Deciding what to do: making an 'ElaboratedInstallPlan'
@@ -2265,13 +2317,16 @@
                 ]
                 <> perPkgOptionMapLast pkgid packageConfigProgramPaths
             elabProgramArgs =
-              Map.fromList
-                [ (programId prog, args)
-                | prog <- configuredPrograms compilerprogdb
-                , let args = programOverrideArgs $ addHaddockIfDocumentationEnabled prog
-                , not (null args)
-                ]
-                <> perPkgOptionMapMappend pkgid packageConfigProgramArgs
+              Map.unionWith
+                (++)
+                ( Map.fromList
+                    [ (programId prog, args)
+                    | prog <- configuredPrograms compilerprogdb
+                    , let args = programOverrideArgs $ addHaddockIfDocumentationEnabled prog
+                    , not (null args)
+                    ]
+                )
+                (perPkgOptionMapMappend pkgid packageConfigProgramArgs)
             elabProgramPathExtra = perPkgOptionNubList pkgid packageConfigProgramPathExtra
             elabConfigureScriptArgs = perPkgOptionList pkgid packageConfigConfigureArgs
             elabExtraLibDirs = perPkgOptionList pkgid packageConfigExtraLibDirs
diff --git a/src/Distribution/Client/Run.hs b/src/Distribution/Client/Run.hs
--- a/src/Distribution/Client/Run.hs
+++ b/src/Distribution/Client/Run.hs
@@ -28,13 +28,14 @@
   , TestSuite (..)
   )
 import Distribution.Simple (PackageDBX (..))
-import Distribution.Simple.Build (addInternalBuildTools)
+import Distribution.Simple.Build (addInternalBuildToolsFixed)
 import Distribution.Simple.BuildPaths (exeExtension)
 import Distribution.Simple.Compiler (CompilerFlavor (..), compilerFlavor)
 import Distribution.Simple.Flag (fromFlag)
 import Distribution.Simple.LocalBuildInfo
   ( ComponentName (..)
   , LocalBuildInfo (..)
+  , absoluteWorkingDirLBI
   , buildDir
   , depLibraryPaths
   , interpretSymbolicPathLBI
@@ -59,7 +60,6 @@
 import qualified Distribution.Simple.GHCJS as GHCJS
 
 import Distribution.Client.Errors
-import Distribution.Compat.Environment (getEnvironment)
 import Distribution.Utils.Path
 
 -- | Return the executable to run and any extra arguments that should be
@@ -143,6 +143,7 @@
 -- | Run a given executable.
 run :: Verbosity -> LocalBuildInfo -> Executable -> [String] -> IO ()
 run verbosity lbi exe exeArgs = do
+  curDir <- absoluteWorkingDirLBI lbi
   let distPref = fromFlag $ configDistPref $ configFlags lbi
       buildPref = buildDir lbi
       pkg_descr = localPkgDescr lbi
@@ -154,7 +155,8 @@
           { withPackageDB = withPackageDB lbi ++ [SpecificPackageDB internalPkgDb]
           , -- Include any build-tool-depends on build tools internal to the current package.
             withPrograms =
-              addInternalBuildTools
+              addInternalBuildToolsFixed
+                (Just curDir)
                 pkg_descr
                 lbi
                 (buildInfo exe)
@@ -178,13 +180,11 @@
             return (p, [])
 
   -- Compute the appropriate environment for running the executable
-  existingEnv <- getEnvironment
   let progDb = withPrograms lbiForExe
       pathVar = progSearchPath progDb
       envOverrides = progOverrideEnv progDb
   newPath <- programSearchPathAsPATHVar pathVar
-  overrideEnv <- fromMaybe [] <$> getEffectiveEnvironment ([("PATH", Just newPath)] ++ envOverrides)
-  let env = overrideEnv ++ existingEnv
+  env <- getFullEnvironment ([("PATH", Just newPath)] ++ envOverrides)
 
   -- Add (DY)LD_LIBRARY_PATH if needed
   env' <-
diff --git a/src/Distribution/Client/Sandbox.hs b/src/Distribution/Client/Sandbox.hs
--- a/src/Distribution/Client/Sandbox.hs
+++ b/src/Distribution/Client/Sandbox.hs
@@ -1,11 +1,6 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE RankNTypes #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Distribution.Client.Sandbox
diff --git a/src/Distribution/Client/Security/HTTP.hs b/src/Distribution/Client/Security/HTTP.hs
--- a/src/Distribution/Client/Security/HTTP.hs
+++ b/src/Distribution/Client/Security/HTTP.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE RecordWildCards #-}
diff --git a/src/Distribution/Client/SetupWrapper.hs b/src/Distribution/Client/SetupWrapper.hs
--- a/src/Distribution/Client/SetupWrapper.hs
+++ b/src/Distribution/Client/SetupWrapper.hs
@@ -143,7 +143,10 @@
   , renderGhcOptions
   )
 import Distribution.Simple.Setup
-  ( Flag (..), CommonSetupFlags (..), GlobalFlags (..)
+  ( CommonSetupFlags (..)
+  , Flag (..)
+  , GlobalFlags (..)
+  , globalCommand
   )
 import Distribution.Simple.Utils
   ( cabalVersion
@@ -175,7 +178,6 @@
 
 import Data.List (foldl1')
 import qualified Data.Map.Lazy as Map
-import Distribution.Simple.Setup (globalCommand)
 import Distribution.Client.Compat.ExecutablePath (getExecutablePath)
 import Distribution.Compat.Process (proc)
 import System.Directory (doesFileExist)
diff --git a/src/Distribution/Client/Store.hs b/src/Distribution/Client/Store.hs
--- a/src/Distribution/Client/Store.hs
+++ b/src/Distribution/Client/Store.hs
@@ -47,8 +47,7 @@
 import Lukko
 #else
 import System.IO (openFile, IOMode(ReadWriteMode), hClose)
-import GHC.IO.Handle.Lock (hLock, hTryLock, LockMode(ExclusiveLock))
-import GHC.IO.Handle.Lock (hUnlock)
+import GHC.IO.Handle.Lock (LockMode (ExclusiveLock), hLock, hTryLock, hUnlock)
 #endif
 
 -- $concurrency
diff --git a/src/Distribution/Client/Targets.hs b/src/Distribution/Client/Targets.hs
--- a/src/Distribution/Client/Targets.hs
+++ b/src/Distribution/Client/Targets.hs
@@ -1,13 +1,6 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE DeriveFoldable #-}
-{-# LANGUAGE DeriveFunctor #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DeriveTraversable #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 -- Module      :  Distribution.Client.Targets
diff --git a/src/Distribution/Client/Types/Repo.hs b/src/Distribution/Client/Types/Repo.hs
--- a/src/Distribution/Client/Types/Repo.hs
+++ b/src/Distribution/Client/Types/Repo.hs
@@ -15,6 +15,9 @@
   , repoName
   , isRepoRemote
   , maybeRepoRemote
+
+    -- * Windows
+  , normaliseFileNoIndexURI
   ) where
 
 import Distribution.Client.Compat.Prelude
@@ -23,6 +26,7 @@
 import Network.URI (URI (..), nullURI, parseAbsoluteURI, uriToString)
 
 import Distribution.Simple.Utils (toUTF8BS)
+import Distribution.System (OS (Windows))
 
 import Distribution.Client.HashValue (hashValue, showHashValue, truncateHash)
 
@@ -32,6 +36,9 @@
 
 import Distribution.Client.Types.RepoName
 
+import qualified System.FilePath.Posix as Posix
+import qualified System.FilePath.Windows as Windows
+
 -------------------------------------------------------------------------------
 -- Remote repository
 -------------------------------------------------------------------------------
@@ -190,3 +197,41 @@
 repoName (RepoLocalNoIndex r _) = localRepoName r
 repoName (RepoRemote r _) = remoteRepoName r
 repoName (RepoSecure r _) = remoteRepoName r
+
+-------------------------------------------------------------------------------
+
+-- * Windows utils
+
+-------------------------------------------------------------------------------
+
+-- | When on Windows, we need to convert the paths in URIs to be POSIX-style.
+--
+-- >>> import Network.URI
+-- >>> normaliseFileNoIndexURI Windows (URI "file+noindex:" (Just nullURIAuth) "C:\\dev\\foo" "" "")
+-- file+noindex:C:/dev/foo
+--
+-- Other formats of file paths are not understood by @network-uri@:
+--
+-- >>> import Network.URI
+-- >>> uriPath <$> parseURI "file+noindex://C:/foo.txt"
+-- Just "/foo.txt"
+-- >>> parseURI "file+noindex://C:\foo.txt"
+-- Nothing
+-- >>> uriPath <$> parseURI "file+noindex:///C:/foo.txt"
+-- Just "/C:/foo.txt"
+-- >>> uriPath <$> parseURI "file+noindex:C:/foo.txt"
+-- Just "C:/foo.txt"
+--
+-- Out of the ones above, only the last one can be used from anywhere in the
+-- system.
+normaliseFileNoIndexURI :: OS -> URI -> URI
+normaliseFileNoIndexURI os uri@(URI scheme _auth path query fragment)
+  | "file+noindex:" <- scheme
+  , Windows <- os =
+      URI scheme Nothing (asPosixPath path) query fragment
+  | otherwise = uri
+  where
+    asPosixPath p =
+      -- We don't use 'isPathSeparator' because @Windows.isPathSeparator
+      -- Posix.pathSeparator == True@.
+      [if x == Windows.pathSeparator then Posix.pathSeparator else x | x <- p]
diff --git a/src/Distribution/Deprecated/ParseUtils.hs b/src/Distribution/Deprecated/ParseUtils.hs
--- a/src/Distribution/Deprecated/ParseUtils.hs
+++ b/src/Distribution/Deprecated/ParseUtils.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE Rank2Types #-}
 -----------------------------------------------------------------------------
 -- This module is meant to be local-only to Distribution...
diff --git a/src/Distribution/Deprecated/ReadP.hs b/src/Distribution/Deprecated/ReadP.hs
--- a/src/Distribution/Deprecated/ReadP.hs
+++ b/src/Distribution/Deprecated/ReadP.hs
@@ -1,9 +1,4 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE GADTs #-}
-
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
 
 -- |
 --
diff --git a/tests/IntegrationTests2.hs b/tests/IntegrationTests2.hs
--- a/tests/IntegrationTests2.hs
+++ b/tests/IntegrationTests2.hs
@@ -45,24 +45,31 @@
 import qualified Distribution.Client.CmdHaddock        as CmdHaddock
 import qualified Distribution.Client.CmdListBin        as CmdListBin
 
+import qualified Distribution.Client.CmdHaddockProject as CmdHaddockProject
+import Distribution.Client.Config (SavedConfig (savedGlobalFlags), createDefaultConfigFile, loadConfig)
+import Distribution.Client.GlobalFlags
+  ( GlobalFlags
+  , defaultGlobalFlags
+  , globalNix
+  )
+import Distribution.Client.Setup (globalCommand, globalStoreDir)
+import Distribution.ModuleName (ModuleName)
 import Distribution.Package
 import Distribution.PackageDescription
 import Distribution.InstalledPackageInfo (InstalledPackageInfo)
-import Distribution.Simple.Setup (toFlag, HaddockFlags(..), defaultHaddockFlags)
-import Distribution.Client.Setup (globalCommand)
-import Distribution.Client.Config (loadConfig, SavedConfig(savedGlobalFlags), createDefaultConfigFile)
+import Distribution.Simple.Setup
+ ( toFlag,
+ HaddockFlags(..),
+ defaultHaddockFlags,
+ HaddockProjectFlags(..),
+ defaultHaddockProjectFlags )
 import Distribution.Simple.Compiler
 import Distribution.Simple.Command
 import qualified Distribution.Simple.Flag as Flag
 import Distribution.System
 import Distribution.Version
-import Distribution.ModuleName (ModuleName)
 import Distribution.Text
 import Distribution.Utils.Path (unsafeMakeSymbolicPath)
-import qualified Distribution.Client.CmdHaddockProject as CmdHaddockProject
-import Distribution.Client.Setup (globalStoreDir)
-import Distribution.Client.GlobalFlags (defaultGlobalFlags)
-import Distribution.Simple.Setup (HaddockProjectFlags(..), defaultHaddockProjectFlags)
 
 import qualified Data.Map as Map
 import qualified Data.Set as Set
@@ -83,10 +90,9 @@
 import Data.Tagged (Tagged(..))
 
 import qualified Data.ByteString as BS
-import Distribution.Client.GlobalFlags (GlobalFlags, globalNix)
+import Data.Maybe (fromJust)
 import Distribution.Simple.Flag (Flag (Flag, NoFlag))
 import Distribution.Types.ParStrat
-import Data.Maybe (fromJust)
 
 #if !MIN_VERSION_directory(1,2,7)
 removePathForcibly :: FilePath -> IO ()
diff --git a/tests/UnitTests/Distribution/Client/FileMonitor.hs b/tests/UnitTests/Distribution/Client/FileMonitor.hs
--- a/tests/UnitTests/Distribution/Client/FileMonitor.hs
+++ b/tests/UnitTests/Distribution/Client/FileMonitor.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE CPP #-}
-
 module UnitTests.Distribution.Client.FileMonitor (tests) where
 
 import Distribution.Parsec (simpleParsec)
diff --git a/tests/UnitTests/Distribution/Client/Glob.hs b/tests/UnitTests/Distribution/Client/Glob.hs
--- a/tests/UnitTests/Distribution/Client/Glob.hs
+++ b/tests/UnitTests/Distribution/Client/Glob.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 module UnitTests.Distribution.Client.Glob (tests) where
diff --git a/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/tests/UnitTests/Distribution/Client/ProjectConfig.hs
--- a/tests/UnitTests/Distribution/Client/ProjectConfig.hs
+++ b/tests/UnitTests/Distribution/Client/ProjectConfig.hs
@@ -33,6 +33,7 @@
 import Distribution.Simple.Program.Db
 import Distribution.Simple.Program.Types
 import Distribution.Simple.Utils (toUTF8BS)
+import Distribution.System (OS (Windows), buildOS)
 import Distribution.Types.PackageVersionConstraint
 import Distribution.Version
 
@@ -1016,7 +1017,10 @@
   arbitrary =
     LocalRepo
       <$> arbitrary
-      <*> elements ["/tmp/foo", "/tmp/bar"] -- TODO: generate valid absolute paths
+      <*> elements
+        ( (if buildOS == Windows then map (normalise . ("C:" ++)) else id)
+            ["/tmp/foo", "/tmp/bar"]
+        ) -- TODO: generate valid absolute paths
       <*> arbitrary
 
 instance Arbitrary PreSolver where
diff --git a/tests/UnitTests/Distribution/Client/UserConfig.hs b/tests/UnitTests/Distribution/Client/UserConfig.hs
--- a/tests/UnitTests/Distribution/Client/UserConfig.hs
+++ b/tests/UnitTests/Distribution/Client/UserConfig.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE CPP #-}
-
 module UnitTests.Distribution.Client.UserConfig
   ( tests
   ) where
diff --git a/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs b/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs
--- a/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs
+++ b/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE StandaloneDeriving #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
diff --git a/tests/UnitTests/Distribution/Solver/Types/OptionalStanza.hs b/tests/UnitTests/Distribution/Solver/Types/OptionalStanza.hs
--- a/tests/UnitTests/Distribution/Solver/Types/OptionalStanza.hs
+++ b/tests/UnitTests/Distribution/Solver/Types/OptionalStanza.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE CPP #-}
-
 module UnitTests.Distribution.Solver.Types.OptionalStanza
   ( tests
   ) where
