diff --git a/cabal-install/src/Distribution/Client/Configure.hs b/cabal-install/src/Distribution/Client/Configure.hs
index 2cbe16096a456e7d1c89be2fae4fea793356f05b..554785ff847fe8aea35e13859a929cde04d9b6b1 100644
--- a/cabal-install/src/Distribution/Client/Configure.hs
+++ b/cabal-install/src/Distribution/Client/Configure.hs
@@ -154,6 +154,9 @@ configure verbosity packageDBs repoCtxt comp platform progdb
         (fromFlagOrDefault
            (useDistPref defaultSetupScriptOptions)
            (configDistPref configFlags))
+        (fromFlagOrDefault
+            (setupConfigDynamic defaultSetupScriptOptions)
+            (configDynExe configFlags))
         (chooseCabalVersion
            configExFlags
            (flagToMaybe (configCabalVersion configExFlags)))
@@ -167,6 +170,7 @@ configureSetupScript :: PackageDBStack
                      -> Platform
                      -> ProgramDb
                      -> FilePath
+                     -> Bool
                      -> VersionRange
                      -> Maybe Lock
                      -> Bool
@@ -178,6 +182,7 @@ configureSetupScript packageDBs
                      platform
                      progdb
                      distPref
+                     dynExe
                      cabalVersion
                      lock
                      forceExternal
@@ -209,6 +214,7 @@ configureSetupScript packageDBs
     , useDependenciesExclusive = not defaultSetupDeps && isJust explicitSetupDeps
     , useVersionMacros         = not defaultSetupDeps && isJust explicitSetupDeps
     , isInteractive            = False
+    , setupConfigDynamic       = dynExe
     }
   where
     -- When we are compiling a legacy setup script without an explicit
diff --git a/cabal-install/src/Distribution/Client/Install.hs b/cabal-install/src/Distribution/Client/Install.hs
index 2baa8af9e49c01f70fe834f5819209bcf11f42d7..a53c7ded1aa046c1273aa3bf8ebf6d6259eb0167 100644
--- a/cabal-install/src/Distribution/Client/Install.hs
+++ b/cabal-install/src/Distribution/Client/Install.hs
@@ -1059,6 +1059,7 @@ performInstallations verbosity
         platform
         progdb
         distPref
+        (fromFlagOrDefault (setupConfigDynamic defaultSetupScriptOptions) $ configDynExe configFlags)
         (chooseCabalVersion configExFlags (libVersion miscOptions))
         (Just lock)
         parallelInstall
diff --git a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs
index c9243c310e0c53d846fa015d4d44c8aa37327ead..fde7ea8b97a99a30aa5d319bf45be93eac34399a 100644
--- a/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs
+++ b/cabal-install/src/Distribution/Client/ProjectPlanOutput.hs
@@ -272,9 +272,9 @@ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig =
     comp2str = prettyShow
 
     style2str :: Bool -> BuildStyle -> String
+    style2str _     BuildAndInstall  = "global"
     style2str True  _                = "local"
     style2str False BuildInplaceOnly = "inplace"
-    style2str False BuildAndInstall  = "global"
 
     jdisplay :: Pretty a => a -> J.Value
     jdisplay = J.String . prettyShow
diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs
index 8f391c9ec994c242024bd7d3c40ce306abe53481..ec43732937701a662e89964c2af18de527732fbc 100644
--- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs
+++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs
@@ -668,6 +668,7 @@ rebuildInstallPlan verbosity
                          projectConfigAllPackages,
                          projectConfigLocalPackages,
                          projectConfigSpecificPackage,
+                         projectPackagesNamed,
                          projectConfigBuildOnly
                        }
                        (compiler, platform, progdb) pkgConfigDB
@@ -692,6 +693,7 @@ rebuildInstallPlan verbosity
                 localPackages
                 sourcePackageHashes
                 defaultInstallDirs
+                projectPackagesNamed
                 projectConfigShared
                 projectConfigAllPackages
                 projectConfigLocalPackages
@@ -1350,6 +1352,7 @@ elaborateInstallPlan
   -> [PackageSpecifier (SourcePackage (PackageLocation loc))]
   -> Map PackageId PackageSourceHash
   -> InstallDirs.InstallDirTemplates
+  -> [PackageVersionConstraint]
   -> ProjectConfigShared
   -> PackageConfig
   -> PackageConfig
@@ -1361,6 +1364,7 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB
                      solverPlan localPackages
                      sourcePackageHashes
                      defaultInstallDirs
+                     extraPackages
                      sharedPackageConfig
                      allPackagesConfig
                      localPackagesConfig
@@ -2031,15 +2035,21 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB
       $ map packageId
       $ SolverInstallPlan.reverseDependencyClosure
           solverPlan
-          (map PlannedId (Set.toList pkgsLocalToProject))
+          (map PlannedId (Set.toList pkgsInplaceToProject))
 
     isLocalToProject :: Package pkg => pkg -> Bool
     isLocalToProject pkg = Set.member (packageId pkg)
                                       pkgsLocalToProject
 
+    pkgsInplaceToProject :: Set PackageId
+    pkgsInplaceToProject =
+        Set.fromList (catMaybes (map shouldBeLocal localPackages))
+        --TODO: localPackages is a misnomer, it's all project packages
+        -- here is where we decide which ones will be local!
+
     pkgsLocalToProject :: Set PackageId
     pkgsLocalToProject =
-        Set.fromList (catMaybes (map shouldBeLocal localPackages))
+        Set.fromList (catMaybes (map (isInLocal extraPackages) localPackages))
         --TODO: localPackages is a misnomer, it's all project packages
         -- here is where we decide which ones will be local!
 
@@ -2108,6 +2118,28 @@ shouldBeLocal (SpecificSourcePackage pkg) = case srcpkgSource pkg of
     LocalUnpackedPackage _ -> Just (packageId pkg)
     _                      -> Nothing
 
+-- Used to determine which packages are affected by local package configuration
+-- flags like ‘--enable-shared --enable-executable-dynamic --disable-library-vanilla’.
+isInLocal :: [PackageVersionConstraint] -> PackageSpecifier (SourcePackage (PackageLocation loc)) -> Maybe PackageId
+isInLocal _              NamedPackage{}              = Nothing
+isInLocal _extraPackages (SpecificSourcePackage pkg) = case srcpkgSource pkg of
+    LocalUnpackedPackage _ -> Just (packageId pkg)
+    -- LocalTarballPackage is matched here too, because otherwise ‘sdistize’
+    -- produces for ‘localPackages’ in the ‘ProjectBaseContext’ a
+    -- LocalTarballPackage, and ‘shouldBeLocal’ will make flags like
+    -- ‘--disable-library-vanilla’ have no effect for a typical
+    -- ‘cabal install --lib --enable-shared enable-executable-dynamic --disable-library-vanilla’,
+    -- as these flags would apply to local packages, but the sdist would
+    -- erroneously not get categorized as a local package, so the flags would be
+    -- ignored and produce a package with an unchanged hash.
+    LocalTarballPackage  _ -> Just (packageId pkg)
+    -- TODO: the docs say ‘extra-packages’ is implemented in cabal project
+    -- files.  We can fix that here by checking that the version range matches.
+    --RemoteTarballPackage    _ -> _
+    --RepoTarballPackage      _ -> _
+    --RemoteSourceRepoPackage _ -> _
+    _                      -> Nothing
+
 -- | Given a 'ElaboratedPlanPackage', report if it matches a 'ComponentName'.
 matchPlanPkg :: (ComponentName -> Bool) -> ElaboratedPlanPackage -> Bool
 matchPlanPkg p = InstallPlan.foldPlanPackage (p . ipiComponentName) (matchElabPkg p)
@@ -3387,7 +3419,8 @@ setupHsScriptOptions (ReadyPackage elab@ElaboratedConfiguredPackage{..})
       useWin32CleanHack        = False,   --TODO: [required eventually]
       forceExternalSetupMethod = isParallelBuild,
       setupCacheLock           = Just cacheLock,
-      isInteractive            = False
+      isInteractive            = False,
+      setupConfigDynamic       = elabDynExe
     }
 
 
diff --git a/cabal-install/src/Distribution/Client/SetupWrapper.hs b/cabal-install/src/Distribution/Client/SetupWrapper.hs
index e4885ed07c639639200aeb2a805145725f7cbdf2..239e1a37908f9d3d36d635663f882fb432fb6497 100644
--- a/cabal-install/src/Distribution/Client/SetupWrapper.hs
+++ b/cabal-install/src/Distribution/Client/SetupWrapper.hs
@@ -71,7 +71,7 @@ import Distribution.Simple.BuildPaths
 import Distribution.Simple.Command
          ( CommandUI(..), commandShowOptions )
 import Distribution.Simple.Program.GHC
-         ( GhcMode(..), GhcOptions(..), renderGhcOptions )
+         ( GhcMode(..), GhcDynLinkMode(..), GhcOptions(..), renderGhcOptions )
 import qualified Distribution.Simple.PackageIndex as PackageIndex
 import Distribution.Simple.PackageIndex (InstalledPackageIndex)
 import qualified Distribution.InstalledPackageInfo as IPI
@@ -249,7 +249,12 @@ data SetupScriptOptions = SetupScriptOptions {
     -- | Is the task we are going to run an interactive foreground task,
     -- or an non-interactive background task? Based on this flag we
     -- decide whether or not to delegate ctrl+c to the spawned task
-    isInteractive            :: Bool
+    isInteractive            :: Bool,
+
+    -- Also track build output artifact configuration.
+
+    -- | Pass `-dynamic` to `ghc` for dynamic rather than static linking.
+    setupConfigDynamic       :: Bool
   }
 
 defaultSetupScriptOptions :: SetupScriptOptions
@@ -272,7 +277,8 @@ defaultSetupScriptOptions = SetupScriptOptions {
     useWin32CleanHack        = False,
     forceExternalSetupMethod = False,
     setupCacheLock           = Nothing,
-    isInteractive            = False
+    isInteractive            = False,
+    setupConfigDynamic       = False
   }
 
 workingDir :: SetupScriptOptions -> FilePath
@@ -840,6 +846,9 @@ getExternalSetupMethod verbosity options pkg bt = do
               -- --ghc-option=-v instead!
               ghcOptVerbosity       = Flag (min verbosity normal)
             , ghcOptMode            = Flag GhcModeMake
+            , ghcOptDynLinkMode     = case setupConfigDynamic options'' of
+                                      True  -> Flag GhcDynamicOnly
+                                      False -> Flag GhcStaticOnly
             , ghcOptInputFiles      = toNubListR [setupHs]
             , ghcOptOutputFile      = Flag setupProgFile
             , ghcOptObjDir          = Flag setupDir
diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs
index 0bdf1e964a69c380a235a0a7c54502b800896391..35e4b61b53ba77e6de8e3e0c02b30df128c2b04f 100644
--- a/cabal-install/tests/IntegrationTests2.hs
+++ b/cabal-install/tests/IntegrationTests2.hs
@@ -1591,7 +1591,7 @@ testProgramOptionsLocal config0 = do
                 (Just [ghcFlag])
                 (getProgArgs localPackages "q")
     assertEqual "p"
-                Nothing
+                (Just [ghcFlag])
                 (getProgArgs localPackages "p")
   where
     testdir = "regression/program-options"
diff --git a/changelog.d/pr-8623 b/changelog.d/pr-8623
new file mode 100644
index 0000000000000000000000000000000000000000..29c9c3bb2c5a85b65c61413b982db933be62c7b6
--- /dev/null
+++ b/changelog.d/pr-8623
@@ -0,0 +1,7 @@
+synopsis: Fix project-local flags being ignored
+packages: cabal-install
+prs: #8623
+description: {
+    Fix some cases of configuration flags being dropped, e.g. with `v2-install`
+    and `--enable-shared --enable-executable-dynamic --disable-library-vanilla`.
+}