diff --git a/cabal-install/Distribution/Client/Dependency.hs b/cabal-install/Distribution/Client/Dependency.hs index 8f20e7a4f9c05747720ecbfe30a03014f8cfe380..10f2c0243dd729edc818b0364bea4a4217049f8a 100644 --- a/cabal-install/Distribution/Client/Dependency.hs +++ b/cabal-install/Distribution/Client/Dependency.hs @@ -587,13 +587,12 @@ interpretPackagesPreference :: Set PackageName -> (PackageName -> PackagePreferences) interpretPackagesPreference selected defaultPref prefs = \pkgname -> PackagePreferences (versionPref pkgname) (installPref pkgname) - where versionPref pkgname = - fromMaybe anyVersion (Map.lookup pkgname versionPrefs) - versionPrefs = Map.fromList - [ (pkgname, pref) - | PackageVersionPreference pkgname pref <- prefs ] + fromMaybe [anyVersion] (Map.lookup pkgname versionPrefs) + versionPrefs = Map.fromListWith (++) + [(pkgname, [pref]) + | PackageVersionPreference pkgname pref <- prefs] installPref pkgname = fromMaybe (installPrefDefault pkgname) (Map.lookup pkgname installPrefs) @@ -818,7 +817,8 @@ resolveWithoutDependencies (DepResolverParams targets constraints . InstalledPackageIndex.lookupSourcePackageId installedPkgIndex . packageId - versionPref pkg = packageVersion pkg `withinRange` preferredVersions + versionPref pkg = length . filter (packageVersion pkg `withinRange`) $ + preferredVersions packageConstraints :: PackageName -> VersionRange packageConstraints pkgname = diff --git a/cabal-install/Distribution/Client/Dependency/Modular/Preference.hs b/cabal-install/Distribution/Client/Dependency/Modular/Preference.hs index 22ac151f55e07f0745f90ac20efd3693fb1cb1a6..90385bfcce4f59b920c0999daad60da9e7529925 100644 --- a/cabal-install/Distribution/Client/Dependency/Modular/Preference.hs +++ b/cabal-install/Distribution/Client/Dependency/Modular/Preference.hs @@ -74,11 +74,13 @@ preferLinked = trav go cmpL (Just _) (Just _) = EQ --- | Ordering that treats preferred versions as greater than non-preferred --- versions. -preferredVersionsOrdering :: VR -> Ver -> Ver -> Ordering -preferredVersionsOrdering vr v1 v2 = - compare (checkVR vr v1) (checkVR vr v2) +-- | Ordering that treats versions satisfying more preferred ranges as greater +-- than versions satisfying less preferred ranges. +preferredVersionsOrdering :: [VR] -> Ver -> Ver -> Ordering +preferredVersionsOrdering vrs v1 v2 = compare (check v1) (check v2) + where + check v = Prelude.length . Prelude.filter (==True) . + Prelude.map (flip checkVR v) $ vrs -- | Traversal that tries to establish package preferences (not constraints). -- Works by reordering choice nodes. @@ -86,8 +88,8 @@ preferPackagePreferences :: (PN -> PackagePreferences) -> Tree a -> Tree a preferPackagePreferences pcs = packageOrderFor (const True) preference where preference pn i1@(I v1 _) i2@(I v2 _) = - let PackagePreferences vr ipref = pcs pn - in preferredVersionsOrdering vr v1 v2 `mappend` -- combines lexically + let PackagePreferences vrs ipref = pcs pn + in preferredVersionsOrdering vrs v1 v2 `mappend` -- combines lexically locationsOrdering ipref i1 i2 -- Note that we always rank installed before uninstalled, and later diff --git a/cabal-install/Distribution/Client/Dependency/TopDown.hs b/cabal-install/Distribution/Client/Dependency/TopDown.hs index e7bb9f1077bceea71ec7fcc4810f65eb4fd7db4a..f6f7823331b443bc783fe67042677f342b43000d 100644 --- a/cabal-install/Distribution/Client/Dependency/TopDown.hs +++ b/cabal-install/Distribution/Client/Dependency/TopDown.hs @@ -127,7 +127,9 @@ explore pref (ChoiceNode _ choices) = where isInstalled (SourceOnly _) = False isInstalled _ = True - isPreferred p = packageVersion p `withinRange` preferredVersions + isPreferred p = length . filter (packageVersion p `withinRange`) $ + preferredVersions + (PackagePreferences preferredVersions packageInstalledPreference) = pref pkgname @@ -669,9 +671,11 @@ finaliseSelectedPackages pref selected constraints = -- version constraints. TODO: distinguish hacks from prefs bounded = boundedAbove versionRange isPreferred p - | bounded = True -- any constant will do - | otherwise = packageVersion p `withinRange` preferredVersions + | bounded = boundedRank -- this is a dummy constant + | otherwise = length . filter (packageVersion p `withinRange`) $ + preferredVersions where (PackagePreferences preferredVersions _) = pref (packageName p) + boundedRank = 0 -- any value will do boundedAbove :: VersionRange -> Bool boundedAbove vr = case asVersionIntervals vr of diff --git a/cabal-install/Distribution/Client/Dependency/Types.hs b/cabal-install/Distribution/Client/Dependency/Types.hs index 9b643cfe5a84e38f013e7758116e777c33a218a8..a1821b679b02a64b65192eb8221481376148a83f 100644 --- a/cabal-install/Distribution/Client/Dependency/Types.hs +++ b/cabal-install/Distribution/Client/Dependency/Types.hs @@ -32,6 +32,7 @@ module Distribution.Client.Dependency.Types ( ConstraintSource(..), unlabelPackageConstraint, showConstraintSource + ) where #if !MIN_VERSION_base(4,8,0) @@ -156,17 +157,18 @@ showPackageConstraint (PackageConstraintStanzas pn ss) = showStanza TestStanzas = "test" showStanza BenchStanzas = "bench" --- | A per-package preference on the version. It is a soft constraint that the +-- | Per-package preferences on the version. It is a soft constraint that the -- 'DependencyResolver' should try to respect where possible. It consists of --- a 'InstalledPreference' which says if we prefer versions of packages --- that are already installed. It also has a 'PackageVersionPreference' which --- is a suggested constraint on the version number. The resolver should try to --- use package versions that satisfy the suggested version constraint. +-- an 'InstalledPreference' which says if we prefer versions of packages +-- that are already installed. It also has (possibly multiple) +-- 'PackageVersionPreference's which are suggested constraints on the version +-- number. The resolver should try to use package versions that satisfy +-- the maximum number of the suggested version constraints. -- -- It is not specified if preferences on some packages are more important than -- others. -- -data PackagePreferences = PackagePreferences VersionRange InstalledPreference +data PackagePreferences = PackagePreferences [VersionRange] InstalledPreference -- | Whether we prefer an installed version of a package or simply the latest -- version. diff --git a/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs b/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs index a5345fe7083be401b9733dcc923a9c2f83f90dc7..3c6ce7d2aa830b3f241672b2d0b5a3cae1806ce4 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs @@ -2,7 +2,10 @@ -- | DSL for testing the modular solver module UnitTests.Distribution.Client.Dependency.Modular.DSL ( ExampleDependency(..) + , ExPreference(..) , ExampleDb + , ExampleVersionRange + , ExamplePkgVersion , exAv , exInst , exResolve @@ -84,6 +87,7 @@ type ExamplePkgVersion = Int type ExamplePkgHash = String -- for example "installed" packages type ExampleFlagName = String type ExampleTestName = String +type ExampleVersionRange = C.VersionRange data ExampleDependency = -- | Simple dependency on any version @@ -104,6 +108,7 @@ data ExampleDependency = -- | Dependency on a language version | ExLang Language +data ExPreference = ExPref String ExampleVersionRange data ExampleAvailable = ExAv { exAvName :: ExamplePkgName @@ -304,8 +309,9 @@ exResolve :: ExampleDb -> [Language] -> [ExamplePkgName] -> Bool + -> [ExPreference] -> ([String], Either String CI.InstallPlan.InstallPlan) -exResolve db exts langs targets indepGoals = runProgress $ +exResolve db exts langs targets indepGoals prefs = runProgress $ resolveDependencies C.buildPlatform compiler Modular @@ -325,15 +331,17 @@ exResolve db exts langs targets indepGoals = runProgress $ packageIndex = exAvIdx avai , packagePreferences = Map.empty } - enableTests = map (\p -> PackageConstraintStanzas + enableTests = fmap (\p -> PackageConstraintStanzas (C.PackageName p) [TestStanzas]) (exDbPkgs db) - targets' = map (\p -> NamedPackage (C.PackageName p) []) targets - params = addConstraints (map toLpc enableTests) - $ (standardInstallPolicy instIdx avaiIdx targets') { - depResolverIndependentGoals = indepGoals + targets' = fmap (\p -> NamedPackage (C.PackageName p) []) targets + params = addPreferences (fmap toPref prefs) + $ addConstraints (fmap toLpc enableTests) + $ (standardInstallPolicy instIdx avaiIdx targets') { + depResolverIndependentGoals = indepGoals } toLpc pc = LabeledPackageConstraint pc ConstraintSourceUnknown + toPref (ExPref n v) = PackageVersionPreference (C.PackageName n) v extractInstallPlan :: CI.InstallPlan.InstallPlan -> [(ExamplePkgName, ExamplePkgVersion)] diff --git a/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/Solver.hs index e7692d41b7eb7270ba2672e686d889c5f116bde2..be94ea7473900485ee78ba9d7b7055ab633bbf9a 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/Solver.hs @@ -8,6 +8,9 @@ import Data.Maybe (isNothing) import Data.Proxy import Data.Typeable +import qualified Data.Version as V +import qualified Distribution.Version as V + -- test-framework import Test.Tasty as TF import Test.Tasty.HUnit (testCase, assertEqual, assertBool) @@ -85,9 +88,26 @@ tests = [ , runTest $ mkTestLangs [Haskell98] dbLangs1 "unsupportedIndirect" ["B"] Nothing , runTest $ mkTestLangs [Haskell98, Haskell2010, UnknownLanguage "Haskell3000"] dbLangs1 "supportedUnknown" ["C"] (Just [("A",1),("B",1),("C",1)]) ] + + , testGroup "Soft Constraints" [ + runTest $ soft [ ExPref "A" $ mkvrThis 1] $ mkTest db13 "selectPreferredVersionSimple" ["A"] (Just [("A", 1)]) + , runTest $ soft [ ExPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionSimple2" ["A"] (Just [("A", 2)]) + , runTest $ soft [ ExPref "A" $ mkvrOrEarlier 2 + , ExPref "A" $ mkvrOrEarlier 1] $ mkTest db13 "selectPreferredVersionMultiple" ["A"] (Just [("A", 1)]) + , runTest $ soft [ ExPref "A" $ mkvrOrEarlier 1 + , ExPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionMultiple2" ["A"] (Just [("A", 1)]) + , runTest $ soft [ ExPref "A" $ mkvrThis 1 + , ExPref "A" $ mkvrThis 2] $ mkTest db13 "selectPreferredVersionMultiple3" ["A"] (Just [("A", 2)]) + , runTest $ soft [ ExPref "A" $ mkvrThis 1 + , ExPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionMultiple4" ["A"] (Just [("A", 1)]) + ] ] where - indep test = test { testIndepGoals = True } + indep test = test { testIndepGoals = True } + soft prefs test = test { testSoftConstraints = prefs } + mkvrThis = V.thisVersion . makeV + mkvrOrEarlier = V.orEarlierVersion . makeV + makeV v = V.Version [v,0,0] [] {------------------------------------------------------------------------------- Solver tests @@ -98,6 +118,7 @@ data SolverTest = SolverTest { , testTargets :: [String] , testResult :: Maybe [(String, Int)] , testIndepGoals :: Bool + , testSoftConstraints :: [ExPreference] , testDb :: ExampleDb , testSupportedExts :: [Extension] , testSupportedLangs :: [Language] @@ -138,6 +159,7 @@ mkTestExtLang exts langs db label targets result = SolverTest { , testTargets = targets , testResult = result , testIndepGoals = False + , testSoftConstraints = [] , testDb = db , testSupportedExts = exts , testSupportedLangs = langs @@ -146,7 +168,8 @@ mkTestExtLang exts langs db label targets result = SolverTest { runTest :: SolverTest -> TF.TestTree runTest SolverTest{..} = askOption $ \(OptionShowSolverLog showSolverLog) -> testCase testLabel $ do - let (_msgs, result) = exResolve testDb testSupportedExts testSupportedLangs testTargets testIndepGoals + let (_msgs, result) = exResolve testDb testSupportedExts testSupportedLangs + testTargets testIndepGoals testSoftConstraints when showSolverLog $ mapM_ putStrLn _msgs case result of Left err -> assertBool ("Unexpected error:\n" ++ err) (isNothing testResult) @@ -387,6 +410,13 @@ db12 = , Right $ exAv "E" 1 [ExFix "base" 4, ExFix "syb" 2] ] +db13 :: ExampleDb +db13 = [ + Right $ exAv "A" 1 [] + , Right $ exAv "A" 2 [] + , Right $ exAv "A" 3 [] + ] + dbExts1 :: ExampleDb dbExts1 = [ Right $ exAv "A" 1 [ExExt (EnableExtension RankNTypes)] @@ -403,7 +433,6 @@ dbLangs1 = [ , Right $ exAv "C" 1 [ExLang (UnknownLanguage "Haskell3000"), ExAny "B"] ] - {------------------------------------------------------------------------------- Test options -------------------------------------------------------------------------------}