diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,1 +1,1 @@
-Please see https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.12.1.0.md
+Please see https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-3.14.1.0.md
diff --git a/cabal-install-solver.cabal b/cabal-install-solver.cabal
--- a/cabal-install-solver.cabal
+++ b/cabal-install-solver.cabal
@@ -1,9 +1,9 @@
 cabal-version: 2.2
 name:          cabal-install-solver
-version:       3.12.1.0
-synopsis:      The command-line interface for Cabal and Hackage.
+version:       3.14.1.0
+synopsis:      The solver component of cabal-install
 description:
-  The solver component used in cabal-install command-line program
+  The solver component used in the cabal-install command-line program.
 
 homepage:      http://www.haskell.org/cabal/
 bug-reports:   https://github.com/haskell/cabal/issues
@@ -40,12 +40,9 @@
     -Wall -Wcompat -Wnoncanonical-monad-instances
     -fwarn-tabs -fwarn-incomplete-uni-patterns
 
-  if impl(ghc <8.8)
+  if impl(ghc < 8.8)
     ghc-options: -Wnoncanonical-monadfail-instances
 
-  if impl(ghc >=8.10)
-    ghc-options: -Wunused-packages
-
   exposed-modules:
     Distribution.Client.Utils.Assertion
 
@@ -102,10 +99,10 @@
 
   build-depends:
     , array         >=0.4      && <0.6
-    , base          >=4.11     && <4.21
+    , base          >=4.13     && <4.22
     , bytestring    >=0.10.6.0 && <0.13
-    , Cabal         ^>=3.12.1.0
-    , Cabal-syntax  ^>=3.12.1.0
+    , Cabal         ^>=3.14
+    , Cabal-syntax  ^>=3.14
     , containers    >=0.5.6.2  && <0.8
     , edit-distance ^>= 0.2.2
     , directory     >= 1.3.7.0  && < 1.4
@@ -134,10 +131,9 @@
      UnitTests.Distribution.Solver.Modular.MessageUtils
 
    build-depends:
-     , base        >= 4.11  && <4.21
-     , Cabal
+     , base        >= 4.13  && <4.22
      , Cabal-syntax
      , cabal-install-solver
      , tasty       >= 1.2.3 && <1.6
-     , tasty-quickcheck <0.11
+     , tasty-quickcheck <0.12
      , tasty-hunit >= 0.10
diff --git a/src/Distribution/Solver/Modular.hs b/src/Distribution/Solver/Modular.hs
--- a/src/Distribution/Solver/Modular.hs
+++ b/src/Distribution/Solver/Modular.hs
@@ -116,7 +116,7 @@
 solve' :: SolverConfig
        -> CompilerInfo
        -> Index
-       -> PkgConfigDb
+       -> Maybe PkgConfigDb
        -> (PN -> PackagePreferences)
        -> Map PN [LabeledPackageConstraint]
        -> Set PN
diff --git a/src/Distribution/Solver/Modular/Message.hs b/src/Distribution/Solver/Modular/Message.hs
--- a/src/Distribution/Solver/Modular/Message.hs
+++ b/src/Distribution/Solver/Modular/Message.hs
@@ -279,6 +279,7 @@
 showFR _ (UnsupportedExtension ext)       = " (conflict: requires " ++ showUnsupportedExtension ext ++ ")"
 showFR _ (UnsupportedLanguage lang)       = " (conflict: requires " ++ showUnsupportedLanguage lang ++ ")"
 showFR _ (MissingPkgconfigPackage pn vr)  = " (conflict: pkg-config package " ++ prettyShow pn ++ prettyShow vr ++ ", not found in the pkg-config database)"
+showFR _ (MissingPkgconfigProgram pn vr)  = " (pkg-config package " ++ prettyShow pn ++ prettyShow vr ++ " is needed but no pkg-config executable was found or querying it failed)"
 showFR _ (NewPackageDoesNotMatchExistingConstraint d) = " (conflict: " ++ showConflictingDep d ++ ")"
 showFR _ (ConflictingConstraints d1 d2)   = " (conflict: " ++ L.intercalate ", " (L.map showConflictingDep [d1, d2]) ++ ")"
 showFR _ (NewPackageIsMissingRequiredComponent comp dr) = " (does not contain " ++ showExposedComponent comp ++ ", which is required by " ++ showDependencyReason dr ++ ")"
diff --git a/src/Distribution/Solver/Modular/Solver.hs b/src/Distribution/Solver/Modular/Solver.hs
--- a/src/Distribution/Solver/Modular/Solver.hs
+++ b/src/Distribution/Solver/Modular/Solver.hs
@@ -91,7 +91,7 @@
 solve :: SolverConfig                         -- ^ solver parameters
       -> CompilerInfo
       -> Index                                -- ^ all available packages as an index
-      -> PkgConfigDb                          -- ^ available pkg-config pkgs
+      -> Maybe PkgConfigDb                    -- ^ available pkg-config pkgs
       -> (PN -> PackagePreferences)           -- ^ preferences
       -> M.Map PN [LabeledPackageConstraint]  -- ^ global constraints
       -> S.Set PN                             -- ^ global goals
diff --git a/src/Distribution/Solver/Modular/Tree.hs b/src/Distribution/Solver/Modular/Tree.hs
--- a/src/Distribution/Solver/Modular/Tree.hs
+++ b/src/Distribution/Solver/Modular/Tree.hs
@@ -102,6 +102,7 @@
 data FailReason = UnsupportedExtension Extension
                 | UnsupportedLanguage Language
                 | MissingPkgconfigPackage PkgconfigName PkgconfigVersionRange
+                | MissingPkgconfigProgram PkgconfigName PkgconfigVersionRange
                 | NewPackageDoesNotMatchExistingConstraint ConflictingDep
                 | ConflictingConstraints ConflictingDep ConflictingDep
                 | NewPackageIsMissingRequiredComponent ExposedComponent (DependencyReason QPN)
diff --git a/src/Distribution/Solver/Modular/Validate.hs b/src/Distribution/Solver/Modular/Validate.hs
--- a/src/Distribution/Solver/Modular/Validate.hs
+++ b/src/Distribution/Solver/Modular/Validate.hs
@@ -90,7 +90,7 @@
 data ValidateState = VS {
   supportedExt        :: Extension -> Bool,
   supportedLang       :: Language  -> Bool,
-  presentPkgs         :: PkgconfigName -> PkgconfigVersionRange  -> Bool,
+  presentPkgs         :: Maybe (PkgconfigName -> PkgconfigVersionRange  -> Bool),
   index               :: Index,
 
   -- Saved, scoped, dependencies. Every time 'validate' makes a package choice,
@@ -383,7 +383,7 @@
 -- or the successfully extended assignment.
 extend :: (Extension -> Bool)            -- ^ is a given extension supported
        -> (Language  -> Bool)            -- ^ is a given language supported
-       -> (PkgconfigName -> PkgconfigVersionRange -> Bool) -- ^ is a given pkg-config requirement satisfiable
+       -> Maybe (PkgconfigName -> PkgconfigVersionRange -> Bool) -- ^ is a given pkg-config requirement satisfiable
        -> [LDep QPN]
        -> PPreAssignment
        -> Either Conflict PPreAssignment
@@ -398,8 +398,10 @@
       if langSupported lang then Right a
                             else Left (dependencyReasonToConflictSet dr, UnsupportedLanguage lang)
     extendSingle a (LDep dr (Pkg pn vr))  =
-      if pkgPresent pn vr then Right a
-                          else Left (dependencyReasonToConflictSet dr, MissingPkgconfigPackage pn vr)
+      case (\f -> f pn vr) <$> pkgPresent of
+        Just True -> Right a
+        Just False -> Left (dependencyReasonToConflictSet dr, MissingPkgconfigPackage pn vr)
+        Nothing -> Left (dependencyReasonToConflictSet dr, MissingPkgconfigProgram pn vr)
     extendSingle a (LDep dr (Dep dep@(PkgComponent qpn _) ci)) =
       let mergedDep = M.findWithDefault (MergedDepConstrained []) qpn a
       in  case (\ x -> M.insert qpn x a) <$> merge mergedDep (PkgDep dr dep ci) of
@@ -561,7 +563,7 @@
 
 
 -- | Interface.
-validateTree :: CompilerInfo -> Index -> PkgConfigDb -> Tree d c -> Tree d c
+validateTree :: CompilerInfo -> Index -> Maybe PkgConfigDb -> Tree d c -> Tree d c
 validateTree cinfo idx pkgConfigDb t = runValidate (validate t) VS {
     supportedExt        = maybe (const True) -- if compiler has no list of extensions, we assume everything is supported
                                 (\ es -> let s = S.fromList es in \ x -> S.member x s)
@@ -569,7 +571,7 @@
   , supportedLang       = maybe (const True)
                                 (flip L.elem) -- use list lookup because language list is small and no Ord instance
                                 (compilerInfoLanguages  cinfo)
-  , presentPkgs         = pkgConfigPkgIsPresent pkgConfigDb
+  , presentPkgs         = pkgConfigPkgIsPresent <$> pkgConfigDb
   , index               = idx
   , saved               = M.empty
   , pa                  = PA M.empty M.empty M.empty
diff --git a/src/Distribution/Solver/Types/ConstraintSource.hs b/src/Distribution/Solver/Types/ConstraintSource.hs
--- a/src/Distribution/Solver/Types/ConstraintSource.hs
+++ b/src/Distribution/Solver/Types/ConstraintSource.hs
@@ -41,6 +41,10 @@
   -- from Cabal >= 3.11
   | ConstraintSourceMultiRepl
 
+  -- | Constraint introduced by --enable-profiling-shared, which requires features
+  -- from Cabal >= 3.13
+  | ConstraintSourceProfiledDynamic
+
   -- | The source of the constraint is not specified.
   | ConstraintSourceUnknown
 
@@ -72,6 +76,8 @@
     "config file, command line flag, or user target"
 showConstraintSource ConstraintSourceMultiRepl =
     "--enable-multi-repl"
+showConstraintSource ConstraintSourceProfiledDynamic =
+    "--enable-profiling-shared"
 showConstraintSource ConstraintSourceUnknown = "unknown source"
 showConstraintSource ConstraintSetupCabalMinVersion =
     "minimum version of Cabal used by Setup.hs"
diff --git a/src/Distribution/Solver/Types/DependencyResolver.hs b/src/Distribution/Solver/Types/DependencyResolver.hs
--- a/src/Distribution/Solver/Types/DependencyResolver.hs
+++ b/src/Distribution/Solver/Types/DependencyResolver.hs
@@ -30,7 +30,7 @@
                            -> CompilerInfo
                            -> InstalledPackageIndex
                            -> PackageIndex (SourcePackage loc)
-                           -> PkgConfigDb
+                           -> Maybe PkgConfigDb
                            -> (PackageName -> PackagePreferences)
                            -> [LabeledPackageConstraint]
                            -> Set PackageName
diff --git a/src/Distribution/Solver/Types/PkgConfigDb.hs b/src/Distribution/Solver/Types/PkgConfigDb.hs
--- a/src/Distribution/Solver/Types/PkgConfigDb.hs
+++ b/src/Distribution/Solver/Types/PkgConfigDb.hs
@@ -47,15 +47,12 @@
 import Distribution.Verbosity                   (Verbosity)
 
 -- | The list of packages installed in the system visible to
--- @pkg-config@. This is an opaque datatype, to be constructed with
--- `readPkgConfigDb` and queried with `pkgConfigPkgPresent`.
-data PkgConfigDb =  PkgConfigDb (M.Map PkgconfigName (Maybe PkgconfigVersion))
-                 -- ^ If an entry is `Nothing`, this means that the
-                 -- package seems to be present, but we don't know the
-                 -- exact version (because parsing of the version
-                 -- number failed).
-                 | NoPkgConfigDb
-                 -- ^ For when we could not run pkg-config successfully.
+-- @pkg-config@.
+--
+-- If an entry is `Nothing`, this means that the package seems to be present,
+-- but we don't know the exact version (because parsing of the version number
+-- failed).
+newtype PkgConfigDb = PkgConfigDb (M.Map PkgconfigName (Maybe PkgconfigVersion))
      deriving (Show, Generic, Typeable)
 
 instance Binary PkgConfigDb
@@ -64,11 +61,11 @@
 -- | Query pkg-config for the list of installed packages, together
 -- with their versions. Return a `PkgConfigDb` encapsulating this
 -- information.
-readPkgConfigDb :: Verbosity -> ProgramDb -> IO PkgConfigDb
+readPkgConfigDb :: Verbosity -> ProgramDb -> IO (Maybe PkgConfigDb)
 readPkgConfigDb verbosity progdb = handle ioErrorHandler $ do
     mpkgConfig <- needProgram verbosity pkgConfigProgram progdb
     case mpkgConfig of
-      Nothing             -> noPkgConfig "Cannot find pkg-config program"
+      Nothing             -> noPkgConfig "cannot find pkg-config program"
       Just (pkgConfig, _) -> do
         -- To prevent malformed Unicode in the descriptions from crashing cabal,
         -- read without interpreting any encoding first. (#9608)
@@ -106,7 +103,7 @@
                        (programInvocation pkgConfig ("--modversion" : pkgNames))
         let pkgVersions = lines outs
         if exitCode == ExitSuccess && length pkgVersions == length pkgNames
-          then (return . pkgConfigDbFromList . zip pkgNames) pkgVersions
+          then (return . Just . pkgConfigDbFromList . zip pkgNames) pkgVersions
           else
           -- if there's a single broken pc file the above fails, so we fall back
           -- into calling it individually
@@ -116,17 +113,17 @@
           -- requested one, we fall back to querying one by one.
           do
             info verbosity ("call to pkg-config --modversion on all packages failed. Falling back to querying pkg-config individually on each package")
-            pkgConfigDbFromList . catMaybes <$> mapM (getIndividualVersion pkgConfig) pkgNames
+            Just . pkgConfigDbFromList . catMaybes <$> mapM (getIndividualVersion pkgConfig) pkgNames
   where
     -- For when pkg-config invocation fails (possibly because of a
     -- too long command line).
     noPkgConfig extra = do
-        info verbosity ("Failed to query pkg-config, Cabal will continue"
-                        ++ " without solving for pkg-config constraints: "
+        info verbosity ("Warning: Failed to query pkg-config, Cabal will backtrack "
+                        ++ "if a package from pkg-config is requested. Error message: "
                         ++ extra)
-        return NoPkgConfigDb
+        return Nothing
 
-    ioErrorHandler :: IOException -> IO PkgConfigDb
+    ioErrorHandler :: IOException -> IO (Maybe PkgConfigDb)
     ioErrorHandler e = noPkgConfig (show e)
 
     getIndividualVersion :: ConfiguredProgram -> String -> IO (Maybe (String, String))
@@ -162,26 +159,13 @@
       Nothing       -> False    -- Package not present in the DB.
       Just Nothing  -> True     -- Package present, but version unknown.
       Just (Just v) -> withinPkgconfigVersionRange v vr
--- If we could not read the pkg-config database successfully we fail.
--- The plan found by the solver can't be executed later, because pkg-config itself
--- is going to be called in the build phase to get the library location for linking
--- so even if there is a library, it would need to be passed manual flags anyway.
-pkgConfigPkgIsPresent NoPkgConfigDb _ _ = False
 
-
-
 -- | Query the version of a package in the @pkg-config@ database.
 -- @Nothing@ indicates the package is not in the database, while
 -- @Just Nothing@ indicates that the package is in the database,
 -- but its version is not known.
 pkgConfigDbPkgVersion :: PkgConfigDb -> PkgconfigName -> Maybe (Maybe PkgconfigVersion)
 pkgConfigDbPkgVersion (PkgConfigDb db) pn = M.lookup pn db
--- NB: Since the solver allows solving to succeed if there is
--- NoPkgConfigDb, we should report that we *guess* that there
--- is a matching pkg-config configuration, but that we just
--- don't know about it.
-pkgConfigDbPkgVersion NoPkgConfigDb _ = Just Nothing
-
 
 -- | Query pkg-config for the locations of pkg-config's package files. Use this
 -- to monitor for changes in the pkg-config DB.
diff --git a/src/Distribution/Solver/Types/ProjectConfigPath.hs b/src/Distribution/Solver/Types/ProjectConfigPath.hs
--- a/src/Distribution/Solver/Types/ProjectConfigPath.hs
+++ b/src/Distribution/Solver/Types/ProjectConfigPath.hs
@@ -16,6 +16,7 @@
 
     -- * Checks and Normalization
     , isCyclicConfigPath
+    , isTopLevelConfigPath
     , canonicalizeConfigPath
     ) where
 
@@ -137,6 +138,11 @@
 -- the project root directory.
 isCyclicConfigPath :: ProjectConfigPath -> Bool
 isCyclicConfigPath (ProjectConfigPath p) = length p /= length (NE.nub p)
+
+-- | Check if the project config path is top-level, meaning it was not included by
+-- some other project config.
+isTopLevelConfigPath :: ProjectConfigPath -> Bool
+isTopLevelConfigPath (ProjectConfigPath p) = NE.length p == 1
 
 -- | Prepends the path of the importee to the importer path.
 consProjectConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath
