From 3a05ea9a958d2354906043e27947b3157c6b293d Mon Sep 17 00:00:00 2001 From: Teo Camarasu Date: Thu, 26 Feb 2026 22:06:34 +0000 Subject: [PATCH] WIP; add prefer-version command --- .../src/Distribution/CabalSpecVersion.hs | 6 ++- .../src/Distribution/Solver/Types/Settings.hs | 29 ++++++++---- .../parser-tests/Tests/ParserTests.hs | 7 +-- .../src/Distribution/Client/Config.hs | 2 +- .../Distribution/Client/Dependency/Types.hs | 3 +- .../src/Distribution/Client/ProjectConfig.hs | 4 +- .../Client/ProjectConfig/FieldGrammar.hs | 11 ++++- .../Client/ProjectConfig/Legacy.hs | 5 ++- .../Distribution/Client/ProjectConfig/Lens.hs | 25 +++++++++-- .../Client/ProjectConfig/Types.hs | 4 +- .../Distribution/Client/ProjectPlanning.hs | 14 ++---- .../src/Distribution/Client/Setup.hs | 45 ++++++++++++------- .../Distribution/Client/ProjectConfig.hs | 13 ++++-- .../Distribution/Client/TreeDiffInstances.hs | 2 +- .../Distribution/Solver/Modular/DSL.hs | 4 +- .../Solver/Modular/DSL/TestCaseUtils.hs | 8 ++-- .../Distribution/Solver/Modular/QuickCheck.hs | 29 ++++++------ doc/cabal-project-description-file.rst | 21 ++++++++- 18 files changed, 156 insertions(+), 76 deletions(-) diff --git a/Cabal-syntax/src/Distribution/CabalSpecVersion.hs b/Cabal-syntax/src/Distribution/CabalSpecVersion.hs index 2e6cbc2b19d..8efebd98f46 100644 --- a/Cabal-syntax/src/Distribution/CabalSpecVersion.hs +++ b/Cabal-syntax/src/Distribution/CabalSpecVersion.hs @@ -36,6 +36,7 @@ data CabalSpecVersion CabalSpecV3_12 | CabalSpecV3_14 | CabalSpecV3_16 + | CabalSpecV3_18 deriving (Eq, Ord, Show, Read, Enum, Bounded, Data, Generic) instance Binary CabalSpecVersion @@ -46,6 +47,7 @@ instance NFData CabalSpecVersion where rnf = genericRnf -- -- @since 3.0.0.0 showCabalSpecVersion :: CabalSpecVersion -> String +showCabalSpecVersion CabalSpecV3_18 = "3.18" showCabalSpecVersion CabalSpecV3_16 = "3.16" showCabalSpecVersion CabalSpecV3_14 = "3.14" showCabalSpecVersion CabalSpecV3_12 = "3.12" @@ -69,13 +71,14 @@ showCabalSpecVersion CabalSpecV1_2 = "1.2" showCabalSpecVersion CabalSpecV1_0 = "1.0" cabalSpecLatest :: CabalSpecVersion -cabalSpecLatest = CabalSpecV3_16 +cabalSpecLatest = CabalSpecV3_18 -- | Parse 'CabalSpecVersion' from version digits. -- -- It may fail if for recent versions the version is not exact. cabalSpecFromVersionDigits :: [Int] -> Maybe CabalSpecVersion cabalSpecFromVersionDigits v + | v == [3, 18] = Just CabalSpecV3_18 | v == [3, 16] = Just CabalSpecV3_16 | v == [3, 14] = Just CabalSpecV3_14 | v == [3, 12] = Just CabalSpecV3_12 @@ -101,6 +104,7 @@ cabalSpecFromVersionDigits v -- | @since 3.4.0.0 cabalSpecToVersionDigits :: CabalSpecVersion -> [Int] +cabalSpecToVersionDigits CabalSpecV3_18 = [3, 18] cabalSpecToVersionDigits CabalSpecV3_16 = [3, 16] cabalSpecToVersionDigits CabalSpecV3_14 = [3, 14] cabalSpecToVersionDigits CabalSpecV3_12 = [3, 12] diff --git a/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs b/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs index 8d70237a33a..54e135d1cf0 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/Settings.hs @@ -3,7 +3,7 @@ module Distribution.Solver.Types.Settings ( ReorderGoals(..) , IndependentGoals(..) - , PreferOldest(..) + , PreferVersion(..) , MinimizeConflictSet(..) , AvoidReinstalls(..) , ShadowPkgs(..) @@ -41,8 +41,11 @@ newtype MinimizeConflictSet = MinimizeConflictSet Bool newtype IndependentGoals = IndependentGoals Bool deriving (BooleanFlag, Eq, Generic, Show) -newtype PreferOldest = PreferOldest Bool - deriving (BooleanFlag, Eq, Generic, Show) +data PreferVersion = + PreferOldest + | PreferLatest + | PreferLatestExceptInstalled + deriving (Eq, Generic, Show) newtype AvoidReinstalls = AvoidReinstalls Bool deriving (BooleanFlag, Eq, Generic, Show) @@ -73,7 +76,7 @@ instance Binary ReorderGoals instance Binary CountConflicts instance Binary FineGrainedConflicts instance Binary IndependentGoals -instance Binary PreferOldest +instance Binary PreferVersion instance Binary MinimizeConflictSet instance Binary AvoidReinstalls instance Binary ShadowPkgs @@ -86,7 +89,7 @@ instance Structured ReorderGoals instance Structured CountConflicts instance Structured FineGrainedConflicts instance Structured IndependentGoals -instance Structured PreferOldest +instance Structured PreferVersion instance Structured MinimizeConflictSet instance Structured AvoidReinstalls instance Structured ShadowPkgs @@ -99,7 +102,7 @@ instance NFData ReorderGoals instance NFData CountConflicts instance NFData FineGrainedConflicts instance NFData IndependentGoals -instance NFData PreferOldest +instance NFData PreferVersion instance NFData MinimizeConflictSet instance NFData AvoidReinstalls instance NFData ShadowPkgs @@ -111,6 +114,11 @@ instance Pretty OnlyConstrained where pretty OnlyConstrainedAll = PP.text "all" pretty OnlyConstrainedNone = PP.text "none" +instance Pretty PreferVersion where + pretty PreferOldest = PP.text "oldest" + pretty PreferLatest = PP.text "latest" + pretty PreferLatestExceptInstalled = PP.text "legacy" + instance Parsec OnlyConstrained where parsec = P.choice [ P.string "all" >> return OnlyConstrainedAll @@ -135,8 +143,13 @@ instance Parsec StrongFlags where instance Parsec AllowBootLibInstalls where parsec = AllowBootLibInstalls <$> parsec -instance Parsec PreferOldest where - parsec = PreferOldest <$> parsec +instance Parsec PreferVersion where + parsec = P.choice + [ P.string "oldest" >> return PreferOldest + , P.try (P.string "latest" >> return PreferLatest) + -- TODO: think about naming + , P.string "legacy" >> return PreferLatestExceptInstalled + ] instance Parsec IndependentGoals where parsec = IndependentGoals <$> parsec diff --git a/cabal-install/parser-tests/Tests/ParserTests.hs b/cabal-install/parser-tests/Tests/ParserTests.hs index d9ab2f5247f..64a99594dd7 100644 --- a/cabal-install/parser-tests/Tests/ParserTests.hs +++ b/cabal-install/parser-tests/Tests/ParserTests.hs @@ -7,6 +7,7 @@ module Tests.ParserTests (parserTests) where import Control.Monad.IO.Class ( MonadIO (liftIO) ) +import GHC.Stack (HasCallStack) import Data.Either (fromRight) import qualified Data.Map as Map import Data.Maybe (fromJust) @@ -44,7 +45,7 @@ import Distribution.Solver.Types.Settings , IndependentGoals (..) , MinimizeConflictSet (..) , OnlyConstrained (..) - , PreferOldest (..) + , PreferVersion (..) , ReorderGoals (..) , StrongFlags (..) ) @@ -227,7 +228,7 @@ testProjectConfigShared = do projectConfigOnlyConstrained = Flag OnlyConstrainedAll projectConfigPerComponent = Flag True projectConfigIndependentGoals = Flag (IndependentGoals True) - projectConfigPreferOldest = Flag (PreferOldest True) + projectConfigPreferVersion = Flag PreferOldest projectConfigProgPathExtra = toNubList ["/foo/bar", "/baz/quux"] projectConfigMultiRepl = toFlag True @@ -577,7 +578,7 @@ readConfig testSubDir projectFileName = do readProjectFileSkeletonLegacy verbosity httpTransport distDirLayout extensionName extensionDescription return (parsec, legacy) -assertConfigEquals :: (Eq a, Show a) => a -> ProjectConfigSkeleton -> ProjectConfigSkeleton -> (ProjectConfigSkeleton -> a) -> Assertion +assertConfigEquals :: (Eq a, Show a, HasCallStack) => a -> ProjectConfigSkeleton -> ProjectConfigSkeleton -> (ProjectConfigSkeleton -> a) -> Assertion assertConfigEquals expected config configLegacy access = do assertEqual "Expectation does not match result of Legacy parser" expected actualLegacy assertEqual "Parsed Config does not match expected" expected actual diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 0653b449504..6b4554b2800 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -427,7 +427,7 @@ instance Semigroup SavedConfig where , installFineGrainedConflicts = combine installFineGrainedConflicts , installMinimizeConflictSet = combine installMinimizeConflictSet , installIndependentGoals = combine installIndependentGoals - , installPreferOldest = combine installPreferOldest + , installPreferVersion = combine installPreferVersion , installShadowPkgs = combine installShadowPkgs , installStrongFlags = combine installStrongFlags , installAllowBootLibInstalls = combine installAllowBootLibInstalls diff --git a/cabal-install/src/Distribution/Client/Dependency/Types.hs b/cabal-install/src/Distribution/Client/Dependency/Types.hs index 269519a474b..37956ae9b5d 100644 --- a/cabal-install/src/Distribution/Client/Dependency/Types.hs +++ b/cabal-install/src/Distribution/Client/Dependency/Types.hs @@ -46,11 +46,12 @@ data PackagesPreferenceDefault -- installed version. -- -- * This is the standard policy for upgrade. + -- * This is enabled by --prefer-version=latest for install PreferAllLatest | -- | Always prefer the oldest version irrespective of any existing -- installed version or packages explicitly requested. -- - -- * This is enabled by --prefer-oldest. + -- * This is enabled by --prefer-version=oldest for install. PreferAllOldest | -- | Always prefer the installed versions over ones that would need to be -- installed. Secondarily, prefer latest versions (eg the latest installed diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 32d8048b2b5..96aa391460a 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -392,7 +392,7 @@ resolveSolverSettings solverSettingIndexState = flagToMaybe projectConfigIndexState solverSettingActiveRepos = flagToMaybe projectConfigActiveRepos solverSettingIndependentGoals = fromFlag projectConfigIndependentGoals - solverSettingPreferOldest = fromFlag projectConfigPreferOldest + solverSettingPreferVersion = fromFlag projectConfigPreferVersion -- solverSettingShadowPkgs = fromFlag projectConfigShadowPkgs -- solverSettingReinstall = fromFlag projectConfigReinstall -- solverSettingAvoidReinstalls = fromFlag projectConfigAvoidReinstalls @@ -415,7 +415,7 @@ resolveSolverSettings , projectConfigAllowBootLibInstalls = Flag (AllowBootLibInstalls False) , projectConfigOnlyConstrained = Flag OnlyConstrainedNone , projectConfigIndependentGoals = Flag (IndependentGoals False) - , projectConfigPreferOldest = Flag (PreferOldest False) + , projectConfigPreferVersion = Flag PreferLatestExceptInstalled -- projectConfigShadowPkgs = Flag False, -- projectConfigReinstall = Flag False, -- projectConfigAvoidReinstalls = Flag False, diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs b/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs index 591bf0ba03d..ea4eb673e0f 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/FieldGrammar.hs @@ -21,6 +21,8 @@ import Distribution.Simple.InstallDirs import Distribution.Solver.Types.ConstraintSource (ConstraintSource (..)) import Distribution.Solver.Types.ProjectConfigPath import Distribution.Types.PackageVersionConstraint (PackageVersionConstraint (..)) +import Data.Bool (bool) +import Distribution.Solver.Types.Settings (PreferVersion(..)) projectConfigFieldGrammar :: ProjectConfigPath -> [String] -> ParsecFieldGrammar' ProjectConfig projectConfigFieldGrammar source knownPrograms = @@ -105,7 +107,7 @@ projectConfigSharedFieldGrammar source = <*> optionalFieldDef "reject-unconstrained-dependencies" L.projectConfigOnlyConstrained mempty <*> optionalFieldDef "per-component" L.projectConfigPerComponent mempty <*> optionalFieldDef "independent-goals" L.projectConfigIndependentGoals mempty - <*> optionalFieldDef "prefer-oldest" L.projectConfigPreferOldest mempty + <*> packageConfigPreferVersion <*> monoidalFieldAla "extra-prog-path-shared-only" (alaNubList' FSep FilePathNT) L.projectConfigProgPathExtra <*> optionalFieldDef "multi-repl" L.projectConfigMultiRepl mempty @@ -263,3 +265,10 @@ packageConfigCoverageGrammar = <$> optionalFieldDef "coverage" L.packageConfigCoverage mempty <*> optionalFieldDef "library-coverage" L.packageConfigCoverage mempty ^^^ deprecatedSince CabalSpecV1_22 "Please use 'coverage' field instead." + +packageConfigPreferVersion :: ParsecFieldGrammar ProjectConfigShared (Flag PreferVersion) +packageConfigPreferVersion = + mappend <$> (fmap (bool PreferLatestExceptInstalled PreferOldest) <$> (optionalFieldDef "prefer-oldest" L.projectConfigPreferOldest mempty) + ^^^ deprecatedSince CabalSpecV1_22 "Please use 'coverage' field instead." + ) + <*> (optionalFieldDef "prefer-version" L.projectConfigPreferVersion mempty) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 80f6812dc4f..73308c8dc2d 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -760,7 +760,7 @@ convertLegacyAllPackageFlags globalFlags configFlags configExFlags installFlags , installMinimizeConflictSet = projectConfigMinimizeConflictSet , installPerComponent = projectConfigPerComponent , installIndependentGoals = projectConfigIndependentGoals - , installPreferOldest = projectConfigPreferOldest + , installPreferVersion = projectConfigPreferVersion , -- installShadowPkgs = projectConfigShadowPkgs, installStrongFlags = projectConfigStrongFlags , installAllowBootLibInstalls = projectConfigAllowBootLibInstalls @@ -1044,7 +1044,7 @@ convertToLegacySharedConfig , installFineGrainedConflicts = projectConfigFineGrainedConflicts , installMinimizeConflictSet = projectConfigMinimizeConflictSet , installIndependentGoals = projectConfigIndependentGoals - , installPreferOldest = projectConfigPreferOldest + , installPreferVersion = projectConfigPreferVersion , installShadowPkgs = mempty -- projectConfigShadowPkgs, , installStrongFlags = projectConfigStrongFlags , installAllowBootLibInstalls = projectConfigAllowBootLibInstalls @@ -1506,6 +1506,7 @@ legacySharedConfigFieldDescrs constraintSrc = , "minimize-conflict-set" , "independent-goals" , "prefer-oldest" + , "prefer-version" , "strong-flags" , "allow-boot-library-installs" , "reject-unconstrained-dependencies" diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs index 03e05835cd6..5884f7dd660 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Lens.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE PatternSynonyms #-} module Distribution.Client.ProjectConfig.Lens where import Distribution.Client.BuildReports.Types (ReportLevel (..)) @@ -36,8 +37,11 @@ import Distribution.Simple.InstallDirs import Distribution.Simple.Setup ( DumpBuildInfo (..) , Flag + , pattern Flag + , pattern NoFlag , HaddockTarget (..) , TestShowDetails (..) + , flagToMaybe ) import Distribution.Solver.Types.ConstraintSource (ConstraintSource) import Distribution.Solver.Types.Settings @@ -47,7 +51,7 @@ import Distribution.Solver.Types.Settings , IndependentGoals (..) , MinimizeConflictSet (..) , OnlyConstrained (..) - , PreferOldest (..) + , PreferVersion (..) , ReorderGoals (..) , StrongFlags (..) ) @@ -292,10 +296,25 @@ projectConfigOnlyConstrained :: Lens' ProjectConfigShared (Flag OnlyConstrained) projectConfigOnlyConstrained f s = fmap (\x -> s{T.projectConfigOnlyConstrained = x}) (f (T.projectConfigOnlyConstrained s)) {-# INLINEABLE projectConfigOnlyConstrained #-} -projectConfigPreferOldest :: Lens' ProjectConfigShared (Flag PreferOldest) -projectConfigPreferOldest f s = fmap (\x -> s{T.projectConfigPreferOldest = x}) (f (T.projectConfigPreferOldest s)) +projectConfigPreferOldest :: Lens' ProjectConfigShared (Flag Bool) +projectConfigPreferOldest f s = fmap (\x -> s{T.projectConfigPreferVersion = fromBool x}) (f (toBool $ T.projectConfigPreferVersion s)) + where + toBool :: Flag PreferVersion -> Flag Bool + toBool mpv = case flagToMaybe mpv of + Just PreferOldest -> Flag True + Just PreferLatestExceptInstalled -> Flag False + _ -> NoFlag + fromBool :: Flag Bool -> Flag PreferVersion + fromBool mpo = case flagToMaybe mpo of + Just True -> Flag PreferOldest + Just False -> Flag PreferLatestExceptInstalled + _ -> NoFlag {-# INLINEABLE projectConfigPreferOldest #-} +projectConfigPreferVersion :: Lens' ProjectConfigShared (Flag PreferVersion) +projectConfigPreferVersion f s = fmap (\x -> s{T.projectConfigPreferVersion = x}) (f (T.projectConfigPreferVersion s)) +{-# INLINEABLE projectConfigPreferVersion #-} + projectConfigProgPathExtra :: Lens' ProjectConfigShared (NubList FilePath) projectConfigProgPathExtra f s = fmap (\x -> s{T.projectConfigProgPathExtra = x}) (f (T.projectConfigProgPathExtra s)) {-# INLINEABLE projectConfigProgPathExtra #-} diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs index 14069f57a0b..a02c0c801ba 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs @@ -229,7 +229,7 @@ data ProjectConfigShared = ProjectConfigShared , projectConfigOnlyConstrained :: Flag OnlyConstrained , projectConfigPerComponent :: Flag Bool , projectConfigIndependentGoals :: Flag IndependentGoals - , projectConfigPreferOldest :: Flag PreferOldest + , projectConfigPreferVersion :: Flag PreferVersion , projectConfigProgPathExtra :: NubList FilePath , projectConfigMultiRepl :: Flag Bool -- More things that only make sense for manual mode, not --local mode @@ -469,7 +469,7 @@ data SolverSettings = SolverSettings , solverSettingIndexState :: Maybe TotalIndexState , solverSettingActiveRepos :: Maybe ActiveRepos , solverSettingIndependentGoals :: IndependentGoals - , solverSettingPreferOldest :: PreferOldest + , solverSettingPreferVersion :: PreferVersion -- Things that only make sense for manual mode, not --local mode -- too much control! -- solverSettingShadowPkgs :: Bool, diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 968b915ee2a..80a5c751208 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -1353,18 +1353,12 @@ planPackages . setAllowBootLibInstalls solverSettingAllowBootLibInstalls . setOnlyConstrained solverSettingOnlyConstrained . setSolverVerbosity verbosity - -- TODO: [required eventually] decide if we need to prefer - -- installed for global packages, or prefer latest even for - -- global packages. Perhaps should be configurable but with a - -- different name than "upgrade-dependencies". . setPreferenceDefault - ( if Cabal.asBool solverSettingPreferOldest - then PreferAllOldest - else PreferLatestForSelected + ( case solverSettingPreferVersion of + PreferOldest -> PreferAllOldest + PreferLatest -> PreferAllLatest + PreferLatestExceptInstalled -> PreferLatestForSelected ) - {-(if solverSettingUpgradeDeps - then PreferAllLatest - else PreferLatestForSelected)-} . removeLowerBounds solverSettingAllowOlder . removeUpperBounds solverSettingAllowNewer diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs index 81f2d69ff0a..8d6b806575e 100644 --- a/cabal-install/src/Distribution/Client/Setup.hs +++ b/cabal-install/src/Distribution/Client/Setup.hs @@ -244,6 +244,7 @@ import Control.Exception import Data.List ( deleteFirstsBy ) +import Data.Functor (($>)) import System.FilePath ( () ) @@ -1410,7 +1411,7 @@ data FetchFlags = FetchFlags , fetchFineGrainedConflicts :: Flag FineGrainedConflicts , fetchMinimizeConflictSet :: Flag MinimizeConflictSet , fetchIndependentGoals :: Flag IndependentGoals - , fetchPreferOldest :: Flag PreferOldest + , fetchPreferVersion :: Flag PreferVersion , fetchShadowPkgs :: Flag ShadowPkgs , fetchStrongFlags :: Flag StrongFlags , fetchAllowBootLibInstalls :: Flag AllowBootLibInstalls @@ -1433,7 +1434,7 @@ defaultFetchFlags = , fetchFineGrainedConflicts = Flag (FineGrainedConflicts True) , fetchMinimizeConflictSet = Flag (MinimizeConflictSet False) , fetchIndependentGoals = Flag (IndependentGoals False) - , fetchPreferOldest = Flag (PreferOldest False) + , fetchPreferVersion = Flag PreferLatestExceptInstalled , fetchShadowPkgs = Flag (ShadowPkgs False) , fetchStrongFlags = Flag (StrongFlags False) , fetchAllowBootLibInstalls = Flag (AllowBootLibInstalls False) @@ -1516,8 +1517,8 @@ fetchCommand = (\v flags -> flags{fetchMinimizeConflictSet = v}) fetchIndependentGoals (\v flags -> flags{fetchIndependentGoals = v}) - fetchPreferOldest - (\v flags -> flags{fetchPreferOldest = v}) + fetchPreferVersion + (\v flags -> flags{fetchPreferVersion = v}) fetchShadowPkgs (\v flags -> flags{fetchShadowPkgs = v}) fetchStrongFlags @@ -1545,7 +1546,7 @@ data FreezeFlags = FreezeFlags , freezeFineGrainedConflicts :: Flag FineGrainedConflicts , freezeMinimizeConflictSet :: Flag MinimizeConflictSet , freezeIndependentGoals :: Flag IndependentGoals - , freezePreferOldest :: Flag PreferOldest + , freezePreferVersion :: Flag PreferVersion , freezeShadowPkgs :: Flag ShadowPkgs , freezeStrongFlags :: Flag StrongFlags , freezeAllowBootLibInstalls :: Flag AllowBootLibInstalls @@ -1566,7 +1567,7 @@ defaultFreezeFlags = , freezeFineGrainedConflicts = Flag (FineGrainedConflicts True) , freezeMinimizeConflictSet = Flag (MinimizeConflictSet False) , freezeIndependentGoals = Flag (IndependentGoals False) - , freezePreferOldest = Flag (PreferOldest False) + , freezePreferVersion = Flag PreferLatestExceptInstalled , freezeShadowPkgs = Flag (ShadowPkgs False) , freezeStrongFlags = Flag (StrongFlags False) , freezeAllowBootLibInstalls = Flag (AllowBootLibInstalls False) @@ -1638,8 +1639,8 @@ freezeCommand = (\v flags -> flags{freezeMinimizeConflictSet = v}) freezeIndependentGoals (\v flags -> flags{freezeIndependentGoals = v}) - freezePreferOldest - (\v flags -> flags{freezePreferOldest = v}) + freezePreferVersion + (\v flags -> flags{freezePreferVersion = v}) freezeShadowPkgs (\v flags -> flags{freezeShadowPkgs = v}) freezeStrongFlags @@ -2241,7 +2242,7 @@ data InstallFlags = InstallFlags , installFineGrainedConflicts :: Flag FineGrainedConflicts , installMinimizeConflictSet :: Flag MinimizeConflictSet , installIndependentGoals :: Flag IndependentGoals - , installPreferOldest :: Flag PreferOldest + , installPreferVersion :: Flag PreferVersion , installShadowPkgs :: Flag ShadowPkgs , installStrongFlags :: Flag StrongFlags , installAllowBootLibInstalls :: Flag AllowBootLibInstalls @@ -2286,7 +2287,7 @@ defaultInstallFlags = , installFineGrainedConflicts = Flag (FineGrainedConflicts True) , installMinimizeConflictSet = Flag (MinimizeConflictSet False) , installIndependentGoals = Flag (IndependentGoals False) - , installPreferOldest = Flag (PreferOldest False) + , installPreferVersion = Flag PreferLatestExceptInstalled , installShadowPkgs = Flag (ShadowPkgs False) , installStrongFlags = Flag (StrongFlags False) , installAllowBootLibInstalls = Flag (AllowBootLibInstalls False) @@ -2646,8 +2647,8 @@ installOptions showOrParseArgs = (\v flags -> flags{installMinimizeConflictSet = v}) installIndependentGoals (\v flags -> flags{installIndependentGoals = v}) - installPreferOldest - (\v flags -> flags{installPreferOldest = v}) + installPreferVersion + (\v flags -> flags{installPreferVersion = v}) installShadowPkgs (\v flags -> flags{installShadowPkgs = v}) installStrongFlags @@ -3599,8 +3600,8 @@ optionSolverFlags -> (Flag MinimizeConflictSet -> flags -> flags) -> (flags -> Flag IndependentGoals) -> (Flag IndependentGoals -> flags -> flags) - -> (flags -> Flag PreferOldest) - -> (Flag PreferOldest -> flags -> flags) + -> (flags -> Flag PreferVersion) + -> (Flag PreferVersion -> flags -> flags) -> (flags -> Flag ShadowPkgs) -> (Flag ShadowPkgs -> flags -> flags) -> (flags -> Flag StrongFlags) @@ -3687,9 +3688,21 @@ optionSolverFlags [] ["prefer-oldest"] "Prefer the oldest (instead of the latest) versions of packages available. Useful to determine lower bounds in the build-depends section." - (fmap asBool . getpo) - (setpo . fmap PreferOldest) + ((\x -> if x == Flag PreferOldest then Flag True else NoFlag) . getpo) + (setpo . (\x -> x $> PreferOldest)) (yesNoOpt showOrParseArgs) + , option + [] + ["prefer-version"] + "" -- TODO ... + (getpo) + (setpo) + (reqArg "oldest|latest|legacy" + ( parsecToReadEErr show --(unexpectMsgString) + --(\err -> "Error parsing prefer-version: " ++ err) + (toFlag `fmap` parsec) + ) + (flagToList . fmap prettyShow)) , option [] ["shadow-installed-packages"] diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index caee779671b..b5e928885b3 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -636,7 +636,7 @@ instance Arbitrary ProjectConfigShared where projectConfigOnlyConstrained <- arbitrary projectConfigPerComponent <- arbitrary projectConfigIndependentGoals <- arbitrary - projectConfigPreferOldest <- arbitrary + projectConfigPreferVersion <- arbitrary projectConfigProgPathExtra <- toNubList <$> listOf arbitraryShortToken projectConfigMultiRepl <- arbitrary return ProjectConfigShared{..} @@ -683,7 +683,7 @@ instance Arbitrary ProjectConfigShared where <*> shrinker projectConfigOnlyConstrained <*> shrinker projectConfigPerComponent <*> shrinker projectConfigIndependentGoals - <*> shrinker projectConfigPreferOldest + <*> shrinker projectConfigPreferVersion <*> shrinker projectConfigProgPathExtra <*> shrinker projectConfigMultiRepl where @@ -1046,8 +1046,13 @@ instance Arbitrary MinimizeConflictSet where instance Arbitrary IndependentGoals where arbitrary = IndependentGoals <$> arbitrary -instance Arbitrary PreferOldest where - arbitrary = PreferOldest <$> arbitrary +instance Arbitrary PreferVersion where + arbitrary = + oneof + [ pure PreferOldest + , pure PreferLatest + , pure PreferLatestExceptInstalled + ] instance Arbitrary StrongFlags where arbitrary = StrongFlags <$> arbitrary diff --git a/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs b/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs index ef4f9fb7c9f..2db2f1aabd2 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/TreeDiffInstances.hs @@ -57,7 +57,7 @@ instance ToExpr OverwritePolicy instance ToExpr PackageConfig instance ToExpr (PackageDBX FilePath) instance ToExpr PackageProperty -instance ToExpr PreferOldest +instance ToExpr PreferVersion instance ToExpr PreSolver instance ToExpr ProjectConfig instance ToExpr ProjectConfigBuildOnly diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL.hs index 20e52b9b139..ab667143345 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL.hs @@ -793,7 +793,7 @@ exResolve -> FineGrainedConflicts -> MinimizeConflictSet -> IndependentGoals - -> PreferOldest + -> PreferVersion -> ReorderGoals -> AllowBootLibInstalls -> OnlyConstrained @@ -863,7 +863,7 @@ exResolve setFineGrainedConflicts fineGrainedConflicts $ setMinimizeConflictSet minimizeConflictSet $ setIndependentGoals indepGoals $ - (if asBool prefOldest then setPreferenceDefault PreferAllOldest else id) $ + (if prefOldest == PreferOldest then setPreferenceDefault PreferAllOldest else id) $ setReorderGoals reorder $ setMaxBackjumps mbj $ setAllowBootLibInstalls allowBootLibInstalls $ diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs index bfe563947cd..d3c340bf6bf 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs @@ -73,7 +73,7 @@ independentGoals test = test{testIndepGoals = IndependentGoals True} -- | Combinator to turn on --prefer-oldest preferOldest :: SolverTest -> SolverTest -preferOldest test = test{testPreferOldest = PreferOldest True} +preferOldest test = test{testPreferVersion = PreferOldest} allowBootLibInstalls :: SolverTest -> SolverTest allowBootLibInstalls test = @@ -130,7 +130,7 @@ data SolverTest = SolverTest , testFineGrainedConflicts :: FineGrainedConflicts , testMinimizeConflictSet :: MinimizeConflictSet , testIndepGoals :: IndependentGoals - , testPreferOldest :: PreferOldest + , testPreferVersion :: PreferVersion , testAllowBootLibInstalls :: AllowBootLibInstalls , testOnlyConstrained :: OnlyConstrained , testEnableBackjumping :: EnableBackjumping @@ -234,7 +234,7 @@ mkTestExtLangPC exts langs mPkgConfigDb db label targets result = , testFineGrainedConflicts = FineGrainedConflicts True , testMinimizeConflictSet = MinimizeConflictSet False , testIndepGoals = IndependentGoals False - , testPreferOldest = PreferOldest False + , testPreferVersion = PreferLatestExceptInstalled , testAllowBootLibInstalls = AllowBootLibInstalls False , testOnlyConstrained = OnlyConstrainedNone , testEnableBackjumping = EnableBackjumping True @@ -267,7 +267,7 @@ runTest SolverTest{..} = askOption $ \(OptionShowSolverLog showSolverLog) -> testFineGrainedConflicts testMinimizeConflictSet testIndepGoals - testPreferOldest + testPreferVersion (ReorderGoals False) testAllowBootLibInstalls testOnlyConstrained diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs index 2e1f5ba39db..a5711f6f021 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs @@ -79,7 +79,7 @@ tests = (ReorderGoals False) (CountConflicts True) indepGoals - (PreferOldest False) + PreferLatestExceptInstalled (getBlind <$> goalOrder) targets = testTargets test targets2 = case targetOrder of @@ -100,7 +100,7 @@ tests = reorderGoals (CountConflicts True) indep - (PreferOldest False) + PreferLatestExceptInstalled Nothing in counterexample (showResults r1 r2) $ noneReachedBackjumpLimit [r1, r2] ==> @@ -116,7 +116,7 @@ tests = reorderGoals (CountConflicts True) indepGoals - (PreferOldest False) + PreferLatestExceptInstalled Nothing in counterexample (showResults r1 r2) $ noneReachedBackjumpLimit [r1, r2] ==> @@ -132,15 +132,15 @@ tests = reorderGoals (CountConflicts True) indepGoals - (PreferOldest False) + PreferLatestExceptInstalled Nothing in counterexample (showResults r1 r2) $ noneReachedBackjumpLimit [r1, r2] ==> isRight (resultPlan r1) === isRight (resultPlan r2) , testPropertyWithSeed "prefer oldest does not affect solvability" $ \test reorderGoals indepGoals -> - let r1 = solve' (PreferOldest True) test - r2 = solve' (PreferOldest False) test + let r1 = solve' PreferOldest test + r2 = solve' PreferLatestExceptInstalled test solve' prefOldest = solve (EnableBackjumping True) @@ -173,7 +173,7 @@ tests = reorderGoals (CountConflicts False) indepGoals - (PreferOldest False) + PreferLatestExceptInstalled Nothing in counterexample (showResults r1 r2) $ noneReachedBackjumpLimit [r1, r2] ==> @@ -190,7 +190,7 @@ tests = reorderGoals (CountConflicts False) indepGoals - (PreferOldest False) + PreferLatestExceptInstalled Nothing in counterexample (showResults r1 r2) $ noneReachedBackjumpLimit [r1, r2] ==> @@ -231,7 +231,7 @@ solve -> ReorderGoals -> CountConflicts -> IndependentGoals - -> PreferOldest + -> PreferVersion -> Maybe VarOrdering -> SolverTest -> Result @@ -532,10 +532,13 @@ instance Arbitrary IndependentGoals where shrink (IndependentGoals indep) = [IndependentGoals False | indep] -instance Arbitrary PreferOldest where - arbitrary = PreferOldest <$> arbitrary - - shrink (PreferOldest prefOldest) = [PreferOldest False | prefOldest] +instance Arbitrary PreferVersion where + arbitrary = + oneof + [ pure PreferOldest + , pure PreferLatest + , pure PreferLatestExceptInstalled + ] instance Arbitrary Component where arbitrary = diff --git a/doc/cabal-project-description-file.rst b/doc/cabal-project-description-file.rst index adae4514b14..c56e9c06e2b 100644 --- a/doc/cabal-project-description-file.rst +++ b/doc/cabal-project-description-file.rst @@ -1973,17 +1973,34 @@ Most users generally won't need these. --no-prefer-oldest :synopsis: Prefer the oldest versions of packages available. :since: 3.10 + :deprecated: :default: False By default, when solver has a choice of multiple versions of the same package, it will first try to derive a build plan with the latest - version. This flag switches the behaviour, making the solver - to prefer the oldest packages available. + version, except if it can use a version from the global package database. + This flag switches the behaviour, making the solver to prefer the oldest packages available. The primary use case is to help users in establishing lower bounds of upstream dependencies. The command line variant of this field is ``--(no-)prefer-oldest``. +.. cfg-field:: prefer-version: (oldest|latest|latest-except-installed) + --prefer-version=(oldest|latest|latest-except-installed) + :synopsis: Specify how to pick package versions + :since: 3.18 + + :default: latest-except-installed + + By default, when solver has a choice of multiple versions of the same + package, it will first try to derive a build plan with the latest version, + except if it can use a version from the global package database. This flag + switches the behaviour, allowing the solver to prefer the latest packages + (irrespective of the global package DB) or the oldest packages available. + + The primary use case of picking the oldest package is to help users in + establishing lower bounds of upstream dependencies. + .. include:: references.inc