{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}

-- | cabal-install CLI command: freeze
module Distribution.Client.CmdFreeze
  ( freezeCommand
  , freezeAction
  ) where

import Distribution.Client.Compat.Prelude
import Prelude ()

import Distribution.Client.DistDirLayout
  ( DistDirLayout (distProjectFile)
  )
import Distribution.Client.IndexUtils (ActiveRepos, TotalIndexState, filterSkippedActiveRepos)
import qualified Distribution.Client.InstallPlan as InstallPlan
import Distribution.Client.NixStyleOptions
  ( NixStyleFlags (..)
  , defaultNixStyleFlags
  , nixStyleOptions
  )
import Distribution.Client.ProjectConfig
  ( ProjectConfig (..)
  , ProjectConfigShared (..)
  , writeProjectLocalFreezeConfig
  )
import Distribution.Client.ProjectOrchestration
import Distribution.Client.ProjectPlanning
import Distribution.Client.Targets
  ( UserConstraint (..)
  , UserConstraintScope (..)
  , UserQualifier (..)
  )
import Distribution.Solver.Types.ConstraintSource
  ( ConstraintSource (..)
  )
import Distribution.Solver.Types.PackageConstraint
  ( PackageProperty (..)
  )

import Distribution.Client.Setup
  ( CommonSetupFlags (setupVerbosity)
  , ConfigFlags (..)
  , GlobalFlags
  )
import Distribution.Package
  ( PackageName
  , packageName
  , packageVersion
  )
import Distribution.PackageDescription
  ( FlagAssignment
  , nullFlagAssignment
  )
import Distribution.Simple.Flag (Flag (..), fromFlagOrDefault)
import Distribution.Simple.Utils
  ( dieWithException
  , notice
  , wrapText
  )
import Distribution.Verbosity
  ( normal
  )
import Distribution.Version
  ( VersionRange
  , simplifyVersionRange
  , thisVersion
  , unionVersionRanges
  )

import qualified Data.Map as Map

import Distribution.Client.Errors
import Distribution.Simple.Command
  ( CommandUI (..)
  , usageAlternatives
  )

freezeCommand :: CommandUI (NixStyleFlags ())
freezeCommand :: CommandUI (NixStyleFlags ())
freezeCommand =
  CommandUI
    { commandName :: String
commandName = String
"v2-freeze"
    , commandSynopsis :: String
commandSynopsis = String
"Freeze dependencies."
    , commandUsage :: String -> String
commandUsage = String -> [String] -> String -> String
usageAlternatives String
"v2-freeze" [String
"[FLAGS]"]
    , commandDescription :: Maybe (String -> String)
commandDescription = (String -> String) -> Maybe (String -> String)
forall a. a -> Maybe a
Just ((String -> String) -> Maybe (String -> String))
-> (String -> String) -> Maybe (String -> String)
forall a b. (a -> b) -> a -> b
$ \String
_ ->
        String -> String
wrapText (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$
          String
"The project configuration is frozen so that it will be reproducible "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"in future.\n\n"
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"The precise dependency configuration for the project is written to "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"the 'cabal.project.freeze' file (or '$project_file.freeze' if "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"'--project-file' is specified). This file extends the configuration "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"from the 'cabal.project' file and thus is used as the project "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"configuration for all other commands (such as 'v2-build', "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"'v2-repl' etc).\n\n"
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"The freeze file can be kept in source control. To make small "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"adjustments it may be edited manually, or to make bigger changes "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"you may wish to delete the file and re-freeze. For more control, "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"one approach is to try variations using 'v2-build --dry-run' with "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"solver flags such as '--constraint=\"pkg < 1.2\"' and once you have "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"a satisfactory solution to freeze it using the 'v2-freeze' command "
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"with the same set of flags."
    , commandNotes :: Maybe (String -> String)
commandNotes = (String -> String) -> Maybe (String -> String)
forall a. a -> Maybe a
Just ((String -> String) -> Maybe (String -> String))
-> (String -> String) -> Maybe (String -> String)
forall a b. (a -> b) -> a -> b
$ \String
pname ->
        String
"Examples:\n"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"  "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
pname
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" v2-freeze\n"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"    Freeze the configuration of the current project\n\n"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"  "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
pname
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" v2-build --dry-run --constraint=\"aeson < 1\"\n"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"    Check what a solution with the given constraints would look like\n"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"  "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
pname
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" v2-freeze --constraint=\"aeson < 1\"\n"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"    Freeze a solution using the given constraints\n"
    , commandDefaultFlags :: NixStyleFlags ()
commandDefaultFlags = () -> NixStyleFlags ()
forall a. a -> NixStyleFlags a
defaultNixStyleFlags ()
    , commandOptions :: ShowOrParseArgs -> [OptionField (NixStyleFlags ())]
commandOptions = (ShowOrParseArgs -> [OptionField ()])
-> ShowOrParseArgs -> [OptionField (NixStyleFlags ())]
forall a.
(ShowOrParseArgs -> [OptionField a])
-> ShowOrParseArgs -> [OptionField (NixStyleFlags a)]
nixStyleOptions ([OptionField ()] -> ShowOrParseArgs -> [OptionField ()]
forall a b. a -> b -> a
const [])
    }

-- | To a first approximation, the @freeze@ command runs the first phase of
-- the @build@ command where we bring the install plan up to date, and then
-- based on the install plan we write out a @cabal.project.freeze@ config file.
--
-- For more details on how this works, see the module
-- "Distribution.Client.ProjectOrchestration"
freezeAction :: NixStyleFlags () -> [String] -> GlobalFlags -> IO ()
freezeAction :: NixStyleFlags () -> [String] -> GlobalFlags -> IO ()
freezeAction flags :: NixStyleFlags ()
flags@NixStyleFlags{()
TestFlags
HaddockFlags
ConfigFlags
BenchmarkFlags
ProjectFlags
InstallFlags
ConfigExFlags
configFlags :: ConfigFlags
configExFlags :: ConfigExFlags
installFlags :: InstallFlags
haddockFlags :: HaddockFlags
testFlags :: TestFlags
benchmarkFlags :: BenchmarkFlags
projectFlags :: ProjectFlags
extraFlags :: ()
configFlags :: forall a. NixStyleFlags a -> ConfigFlags
configExFlags :: forall a. NixStyleFlags a -> ConfigExFlags
installFlags :: forall a. NixStyleFlags a -> InstallFlags
haddockFlags :: forall a. NixStyleFlags a -> HaddockFlags
testFlags :: forall a. NixStyleFlags a -> TestFlags
benchmarkFlags :: forall a. NixStyleFlags a -> BenchmarkFlags
projectFlags :: forall a. NixStyleFlags a -> ProjectFlags
extraFlags :: forall a. NixStyleFlags a -> a
..} [String]
extraArgs GlobalFlags
globalFlags = do
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([String] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
extraArgs) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
    Verbosity -> CabalInstallException -> IO ()
forall a1 a.
(HasCallStack, Show a1, Typeable a1,
 Exception (VerboseException a1)) =>
Verbosity -> a1 -> IO a
dieWithException Verbosity
verbosity (CabalInstallException -> IO ()) -> CabalInstallException -> IO ()
forall a b. (a -> b) -> a -> b
$
      [String] -> CabalInstallException
FreezeAction [String]
extraArgs

  ProjectBaseContext
    { DistDirLayout
distDirLayout :: DistDirLayout
distDirLayout :: ProjectBaseContext -> DistDirLayout
distDirLayout
    , CabalDirLayout
cabalDirLayout :: CabalDirLayout
cabalDirLayout :: ProjectBaseContext -> CabalDirLayout
cabalDirLayout
    , ProjectConfig
projectConfig :: ProjectConfig
projectConfig :: ProjectBaseContext -> ProjectConfig
projectConfig
    , [PackageSpecifier UnresolvedSourcePackage]
localPackages :: [PackageSpecifier UnresolvedSourcePackage]
localPackages :: ProjectBaseContext -> [PackageSpecifier UnresolvedSourcePackage]
localPackages
    , BuildTimeSettings
buildSettings :: BuildTimeSettings
buildSettings :: ProjectBaseContext -> BuildTimeSettings
buildSettings
    } <-
    Verbosity
-> ProjectConfig -> CurrentCommand -> IO ProjectBaseContext
establishProjectBaseContext Verbosity
verbosity ProjectConfig
cliConfig CurrentCommand
OtherCommand

  (ElaboratedInstallPlan
_, ElaboratedInstallPlan
elaboratedPlan, ElaboratedSharedConfig
_, TotalIndexState
totalIndexState, ActiveRepos
activeRepos) <-
    Verbosity
-> DistDirLayout
-> CabalDirLayout
-> ProjectConfig
-> [PackageSpecifier UnresolvedSourcePackage]
-> Maybe InstalledPackageIndex
-> IO
     (ElaboratedInstallPlan, ElaboratedInstallPlan,
      ElaboratedSharedConfig, TotalIndexState, ActiveRepos)
rebuildInstallPlan
      Verbosity
verbosity
      DistDirLayout
distDirLayout
      CabalDirLayout
cabalDirLayout
      ProjectConfig
projectConfig
      [PackageSpecifier UnresolvedSourcePackage]
localPackages
      Maybe InstalledPackageIndex
forall a. Maybe a
Nothing

  let freezeConfig :: ProjectConfig
freezeConfig = ElaboratedInstallPlan
-> TotalIndexState -> ActiveRepos -> ProjectConfig
projectFreezeConfig ElaboratedInstallPlan
elaboratedPlan TotalIndexState
totalIndexState ActiveRepos
activeRepos
      dryRun :: Bool
dryRun =
        BuildTimeSettings -> Bool
buildSettingDryRun BuildTimeSettings
buildSettings
          Bool -> Bool -> Bool
|| BuildTimeSettings -> Bool
buildSettingOnlyDownload BuildTimeSettings
buildSettings

  if Bool
dryRun
    then Verbosity -> String -> IO ()
notice Verbosity
verbosity String
"Freeze file not written due to flag(s)"
    else do
      DistDirLayout -> ProjectConfig -> IO ()
writeProjectLocalFreezeConfig DistDirLayout
distDirLayout ProjectConfig
freezeConfig
      Verbosity -> String -> IO ()
notice Verbosity
verbosity (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$
        String
"Wrote freeze file: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (DistDirLayout -> String -> String
distProjectFile DistDirLayout
distDirLayout String
"freeze")
  where
    verbosity :: Verbosity
verbosity = Verbosity -> Flag Verbosity -> Verbosity
forall a. a -> Flag a -> a
fromFlagOrDefault Verbosity
normal (CommonSetupFlags -> Flag Verbosity
setupVerbosity (CommonSetupFlags -> Flag Verbosity)
-> CommonSetupFlags -> Flag Verbosity
forall a b. (a -> b) -> a -> b
$ ConfigFlags -> CommonSetupFlags
configCommonFlags ConfigFlags
configFlags)
    cliConfig :: ProjectConfig
cliConfig =
      GlobalFlags
-> NixStyleFlags () -> ClientInstallFlags -> ProjectConfig
forall a.
GlobalFlags
-> NixStyleFlags a -> ClientInstallFlags -> ProjectConfig
commandLineFlagsToProjectConfig
        GlobalFlags
globalFlags
        NixStyleFlags ()
flags
        ClientInstallFlags
forall a. Monoid a => a
mempty -- ClientInstallFlags, not needed here

-- | Given the install plan, produce a config value with constraints that
-- freezes the versions of packages used in the plan.
projectFreezeConfig
  :: ElaboratedInstallPlan
  -> TotalIndexState
  -> ActiveRepos
  -> ProjectConfig
projectFreezeConfig :: ElaboratedInstallPlan
-> TotalIndexState -> ActiveRepos -> ProjectConfig
projectFreezeConfig ElaboratedInstallPlan
elaboratedPlan TotalIndexState
totalIndexState ActiveRepos
activeRepos0 =
  ProjectConfig
forall a. Monoid a => a
mempty
    { projectConfigShared =
        mempty
          { projectConfigConstraints =
              concat (Map.elems (projectFreezeConstraints elaboratedPlan))
          , projectConfigIndexState = Flag totalIndexState
          , projectConfigActiveRepos = Flag activeRepos
          }
    }
  where
    activeRepos :: ActiveRepos
    activeRepos :: ActiveRepos
activeRepos = ActiveRepos -> ActiveRepos
filterSkippedActiveRepos ActiveRepos
activeRepos0

-- | Given the install plan, produce solver constraints that will ensure the
-- solver picks the same solution again in future in different environments.
projectFreezeConstraints
  :: ElaboratedInstallPlan
  -> Map PackageName [(UserConstraint, ConstraintSource)]
projectFreezeConstraints :: ElaboratedInstallPlan
-> Map PackageName [(UserConstraint, ConstraintSource)]
projectFreezeConstraints ElaboratedInstallPlan
plan =
  --
  -- TODO: [required eventually] this is currently an underapproximation
  -- since the constraints language is not expressive enough to specify the
  -- precise solution. See https://github.com/haskell/cabal/issues/3502.
  --
  -- For the moment we deal with multiple versions in the solution by using
  -- constraints that allow either version. Also, we do not include any
  -- /version/ constraints for packages that are local to the project (e.g.
  -- if the solution has two instances of Cabal, one from the local project
  -- and one pulled in as a setup deps then we exclude all constraints on
  -- Cabal, not just the constraint for the local instance since any
  -- constraint would apply to both instances). We do however keep flag
  -- constraints of local packages.
  --
  Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName [(UserConstraint, ConstraintSource)]
deleteLocalPackagesVersionConstraints
    (([(UserConstraint, ConstraintSource)]
 -> [(UserConstraint, ConstraintSource)]
 -> [(UserConstraint, ConstraintSource)])
-> Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName [(UserConstraint, ConstraintSource)]
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith [(UserConstraint, ConstraintSource)]
-> [(UserConstraint, ConstraintSource)]
-> [(UserConstraint, ConstraintSource)]
forall a. [a] -> [a] -> [a]
(++) Map PackageName [(UserConstraint, ConstraintSource)]
versionConstraints Map PackageName [(UserConstraint, ConstraintSource)]
flagConstraints)
  where
    versionConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]
    versionConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]
versionConstraints =
      (PackageName
 -> VersionRange -> [(UserConstraint, ConstraintSource)])
-> Map PackageName VersionRange
-> Map PackageName [(UserConstraint, ConstraintSource)]
forall k a b. (k -> a -> b) -> Map k a -> Map k b
Map.mapWithKey
        ( \PackageName
p VersionRange
v ->
            [
              ( UserConstraintScope -> PackageProperty -> UserConstraint
UserConstraint (PackageName -> UserConstraintScope
UserAnyQualifier PackageName
p) (VersionRange -> PackageProperty
PackagePropertyVersion VersionRange
v)
              , ConstraintSource
ConstraintSourceFreeze
              )
            ]
        )
        Map PackageName VersionRange
versionRanges

    versionRanges :: Map PackageName VersionRange
    versionRanges :: Map PackageName VersionRange
versionRanges =
      (VersionRange -> VersionRange)
-> Map PackageName VersionRange -> Map PackageName VersionRange
forall a b k. (a -> b) -> Map k a -> Map k b
Map.map VersionRange -> VersionRange
simplifyVersionRange (Map PackageName VersionRange -> Map PackageName VersionRange)
-> Map PackageName VersionRange -> Map PackageName VersionRange
forall a b. (a -> b) -> a -> b
$
        (VersionRange -> VersionRange -> VersionRange)
-> [(PackageName, VersionRange)] -> Map PackageName VersionRange
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith VersionRange -> VersionRange -> VersionRange
unionVersionRanges ([(PackageName, VersionRange)] -> Map PackageName VersionRange)
-> [(PackageName, VersionRange)] -> Map PackageName VersionRange
forall a b. (a -> b) -> a -> b
$
          [ (InstalledPackageInfo -> PackageName
forall pkg. Package pkg => pkg -> PackageName
packageName InstalledPackageInfo
pkg, Version -> VersionRange
thisVersion (InstalledPackageInfo -> Version
forall pkg. Package pkg => pkg -> Version
packageVersion InstalledPackageInfo
pkg))
          | InstallPlan.PreExisting InstalledPackageInfo
pkg <- ElaboratedInstallPlan
-> [GenericPlanPackage
      InstalledPackageInfo ElaboratedConfiguredPackage]
forall ipkg srcpkg.
GenericInstallPlan ipkg srcpkg -> [GenericPlanPackage ipkg srcpkg]
InstallPlan.toList ElaboratedInstallPlan
plan
          ]
            [(PackageName, VersionRange)]
-> [(PackageName, VersionRange)] -> [(PackageName, VersionRange)]
forall a. [a] -> [a] -> [a]
++ [ (ElaboratedConfiguredPackage -> PackageName
forall pkg. Package pkg => pkg -> PackageName
packageName ElaboratedConfiguredPackage
pkg, Version -> VersionRange
thisVersion (ElaboratedConfiguredPackage -> Version
forall pkg. Package pkg => pkg -> Version
packageVersion ElaboratedConfiguredPackage
pkg))
               | InstallPlan.Configured ElaboratedConfiguredPackage
pkg <- ElaboratedInstallPlan
-> [GenericPlanPackage
      InstalledPackageInfo ElaboratedConfiguredPackage]
forall ipkg srcpkg.
GenericInstallPlan ipkg srcpkg -> [GenericPlanPackage ipkg srcpkg]
InstallPlan.toList ElaboratedInstallPlan
plan
               ]

    flagConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]
    flagConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]
flagConstraints =
      (PackageName
 -> FlagAssignment -> [(UserConstraint, ConstraintSource)])
-> Map PackageName FlagAssignment
-> Map PackageName [(UserConstraint, ConstraintSource)]
forall k a b. (k -> a -> b) -> Map k a -> Map k b
Map.mapWithKey
        ( \PackageName
p FlagAssignment
f ->
            [
              ( UserConstraintScope -> PackageProperty -> UserConstraint
UserConstraint (UserQualifier -> PackageName -> UserConstraintScope
UserQualified UserQualifier
UserQualToplevel PackageName
p) (FlagAssignment -> PackageProperty
PackagePropertyFlags FlagAssignment
f)
              , ConstraintSource
ConstraintSourceFreeze
              )
            ]
        )
        Map PackageName FlagAssignment
flagAssignments

    flagAssignments :: Map PackageName FlagAssignment
    flagAssignments :: Map PackageName FlagAssignment
flagAssignments =
      [(PackageName, FlagAssignment)] -> Map PackageName FlagAssignment
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
        [ (PackageName
pkgname, FlagAssignment
flags)
        | InstallPlan.Configured ElaboratedConfiguredPackage
elab <- ElaboratedInstallPlan
-> [GenericPlanPackage
      InstalledPackageInfo ElaboratedConfiguredPackage]
forall ipkg srcpkg.
GenericInstallPlan ipkg srcpkg -> [GenericPlanPackage ipkg srcpkg]
InstallPlan.toList ElaboratedInstallPlan
plan
        , let flags :: FlagAssignment
flags = ElaboratedConfiguredPackage -> FlagAssignment
elabFlagAssignment ElaboratedConfiguredPackage
elab
              pkgname :: PackageName
pkgname = ElaboratedConfiguredPackage -> PackageName
forall pkg. Package pkg => pkg -> PackageName
packageName ElaboratedConfiguredPackage
elab
        , Bool -> Bool
not (FlagAssignment -> Bool
nullFlagAssignment FlagAssignment
flags)
        ]

    -- As described above, remove the version constraints on local packages,
    -- but leave any flag constraints.
    deleteLocalPackagesVersionConstraints
      :: Map PackageName [(UserConstraint, ConstraintSource)]
      -> Map PackageName [(UserConstraint, ConstraintSource)]
    deleteLocalPackagesVersionConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName [(UserConstraint, ConstraintSource)]
deleteLocalPackagesVersionConstraints =
      (PackageName
 -> ()
 -> [(UserConstraint, ConstraintSource)]
 -> Maybe [(UserConstraint, ConstraintSource)])
-> (Map PackageName ()
    -> Map PackageName [(UserConstraint, ConstraintSource)])
-> (Map PackageName [(UserConstraint, ConstraintSource)]
    -> Map PackageName [(UserConstraint, ConstraintSource)])
-> Map PackageName ()
-> Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName [(UserConstraint, ConstraintSource)]
forall k a b c.
Ord k =>
(k -> a -> b -> Maybe c)
-> (Map k a -> Map k c)
-> (Map k b -> Map k c)
-> Map k a
-> Map k b
-> Map k c
Map.mergeWithKey
        ( \PackageName
_pkgname () [(UserConstraint, ConstraintSource)]
constraints ->
            case ((UserConstraint, ConstraintSource) -> Bool)
-> [(UserConstraint, ConstraintSource)]
-> [(UserConstraint, ConstraintSource)]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool)
-> ((UserConstraint, ConstraintSource) -> Bool)
-> (UserConstraint, ConstraintSource)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UserConstraint -> Bool
isVersionConstraint (UserConstraint -> Bool)
-> ((UserConstraint, ConstraintSource) -> UserConstraint)
-> (UserConstraint, ConstraintSource)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UserConstraint, ConstraintSource) -> UserConstraint
forall a b. (a, b) -> a
fst) [(UserConstraint, ConstraintSource)]
constraints of
              [] -> Maybe [(UserConstraint, ConstraintSource)]
forall a. Maybe a
Nothing
              [(UserConstraint, ConstraintSource)]
constraints' -> [(UserConstraint, ConstraintSource)]
-> Maybe [(UserConstraint, ConstraintSource)]
forall a. a -> Maybe a
Just [(UserConstraint, ConstraintSource)]
constraints'
        )
        (Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName ()
-> Map PackageName [(UserConstraint, ConstraintSource)]
forall a b. a -> b -> a
const Map PackageName [(UserConstraint, ConstraintSource)]
forall k a. Map k a
Map.empty)
        Map PackageName [(UserConstraint, ConstraintSource)]
-> Map PackageName [(UserConstraint, ConstraintSource)]
forall a. a -> a
id
        Map PackageName ()
localPackages

    isVersionConstraint :: UserConstraint -> Bool
isVersionConstraint (UserConstraint UserConstraintScope
_ (PackagePropertyVersion VersionRange
_)) = Bool
True
    isVersionConstraint UserConstraint
_ = Bool
False

    localPackages :: Map PackageName ()
    localPackages :: Map PackageName ()
localPackages =
      [(PackageName, ())] -> Map PackageName ()
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
        [ (ElaboratedConfiguredPackage -> PackageName
forall pkg. Package pkg => pkg -> PackageName
packageName ElaboratedConfiguredPackage
elab, ())
        | InstallPlan.Configured ElaboratedConfiguredPackage
elab <- ElaboratedInstallPlan
-> [GenericPlanPackage
      InstalledPackageInfo ElaboratedConfiguredPackage]
forall ipkg srcpkg.
GenericInstallPlan ipkg srcpkg -> [GenericPlanPackage ipkg srcpkg]
InstallPlan.toList ElaboratedInstallPlan
plan
        , ElaboratedConfiguredPackage -> Bool
elabLocalToProject ElaboratedConfiguredPackage
elab
        ]