packages feed

cabal-install 0.10.2 → 0.14.0

raw patch · 56 files changed

+5158/−1259 lines, 56 filesdep +mtldep ~Cabaldep ~arraydep ~filepathnew-uploader

Dependencies added: mtl

Dependency ranges changed: Cabal, array, filepath, old-time, pretty, process, time, unix

Files

Distribution/Client/BuildReports/Anonymous.hs view
@@ -56,9 +56,9 @@  import qualified Distribution.Compat.ReadP as Parse          ( ReadP, pfail, munch1, skipSpaces )-import qualified Text.PrettyPrint.HughesPJ as Disp+import qualified Text.PrettyPrint as Disp          ( Doc, render, char, text )-import Text.PrettyPrint.HughesPJ+import Text.PrettyPrint          ( (<+>), (<>) )  import Data.List@@ -112,6 +112,7 @@    | SetupFailed    | ConfigureFailed    | BuildFailed+   | TestsFailed    | InstallFailed    | InstallOk   deriving Eq@@ -122,7 +123,7 @@ new :: OS -> Arch -> CompilerId -- -> Version     -> ConfiguredPackage -> BR.BuildResult     -> BuildReport-new os' arch' comp (ConfiguredPackage pkg flags deps) result =+new os' arch' comp (ConfiguredPackage pkg flags _ deps) result =   BuildReport {     package               = packageId pkg,     os                    = os',@@ -143,6 +144,7 @@       Left  (BR.UnpackFailed    _) -> UnpackFailed       Left  (BR.ConfigureFailed _) -> ConfigureFailed       Left  (BR.BuildFailed     _) -> BuildFailed+      Left  (BR.TestsFailed     _) -> TestsFailed       Left  (BR.InstallFailed   _) -> InstallFailed       Right (BR.BuildOk       _ _) -> InstallOk     convertDocsOutcome = case result of@@ -151,9 +153,9 @@       Right (BR.BuildOk BR.DocsFailed _)    -> Failed       Right (BR.BuildOk BR.DocsOk _)        -> Ok     convertTestsOutcome = case result of+      Left  (BR.TestsFailed _)              -> Failed       Left _                                -> NotTried       Right (BR.BuildOk _ BR.TestsNotTried) -> NotTried-      Right (BR.BuildOk _ BR.TestsFailed)   -> Failed       Right (BR.BuildOk _ BR.TestsOk)       -> Ok  cabalInstallID :: PackageIdentifier@@ -280,6 +282,7 @@   disp SetupFailed     = Disp.text "SetupFailed"   disp ConfigureFailed = Disp.text "ConfigureFailed"   disp BuildFailed     = Disp.text "BuildFailed"+  disp TestsFailed     = Disp.text "TestsFailed"   disp InstallFailed   = Disp.text "InstallFailed"   disp InstallOk       = Disp.text "InstallOk" @@ -294,6 +297,7 @@       "SetupFailed"      -> return SetupFailed       "ConfigureFailed"  -> return ConfigureFailed       "BuildFailed"      -> return BuildFailed+      "TestsFailed"      -> return TestsFailed       "InstallFailed"    -> return InstallFailed       "InstallOk"        -> return InstallOk       _                  -> Parse.pfail
Distribution/Client/BuildReports/Storage.hs view
@@ -116,12 +116,12 @@                 -> Maybe (BuildReport, Repo) fromPlanPackage (Platform arch os) comp planPackage = case planPackage of -  InstallPlan.Installed pkg@(ConfiguredPackage (AvailablePackage {-                          packageSource = RepoTarballPackage repo _ _ }) _ _) result+  InstallPlan.Installed pkg@(ConfiguredPackage (SourcePackage {+                          packageSource = RepoTarballPackage repo _ _ }) _ _ _) result     -> Just $ (BuildReport.new os arch comp pkg (Right result), repo) -  InstallPlan.Failed pkg@(ConfiguredPackage (AvailablePackage {-                       packageSource = RepoTarballPackage repo _ _ }) _ _) result+  InstallPlan.Failed pkg@(ConfiguredPackage (SourcePackage {+                       packageSource = RepoTarballPackage repo _ _ }) _ _ _) result     -> Just $ (BuildReport.new os arch comp pkg (Left result), repo)    _ -> Nothing
Distribution/Client/BuildReports/Types.hs view
@@ -19,7 +19,7 @@  import qualified Distribution.Compat.ReadP as Parse          ( pfail, munch1 )-import qualified Text.PrettyPrint.HughesPJ as Disp+import qualified Text.PrettyPrint as Disp          ( text )  import Data.Char as Char
Distribution/Client/BuildReports/Upload.hs view
@@ -29,10 +29,8 @@ type BuildLog = String  uploadReports :: URI -> [(BuildReport, Maybe BuildLog)]-              -> BrowserAction (HandleStream String) ()               ->  BrowserAction (HandleStream BuildLog) ()-uploadReports uri reports auth = do-  auth+uploadReports uri reports = do   forM_ reports $ \(report, mbBuildLog) -> do      buildId <- postBuildReport uri report      case mbBuildLog of
Distribution/Client/Config.hs view
@@ -37,16 +37,19 @@          , ReportFlags(..), reportCommand          , showRepo, parseRepo ) +import Distribution.Simple.Compiler+         ( OptimisationLevel(..) ) import Distribution.Simple.Setup          ( ConfigFlags(..), configureOptions, defaultConfigFlags          , installDirsOptions-         , Flag, toFlag, flagToMaybe, fromFlagOrDefault )+         , Flag(..), toFlag, flagToMaybe, fromFlagOrDefault ) import Distribution.Simple.InstallDirs          ( InstallDirs(..), defaultInstallDirs          , PathTemplate, toPathTemplate ) import Distribution.ParseUtils          ( FieldDescr(..), liftField-         , ParseResult(..), locatedErrorMsg, showPWarning+         , ParseResult(..), PError(..), PWarning(..)+         , locatedErrorMsg, showPWarning          , readFields, warning, lineNo          , simpleField, listField, parseFilePathQ, parseTokenQ ) import qualified Distribution.ParseUtils as ParseUtils@@ -76,9 +79,9 @@ import qualified Data.Map as Map import qualified Distribution.Compat.ReadP as Parse          ( option )-import qualified Text.PrettyPrint.HughesPJ as Disp+import qualified Text.PrettyPrint as Disp          ( Doc, render, text, colon, vcat, empty, isEmpty, nest )-import Text.PrettyPrint.HughesPJ+import Text.PrettyPrint          ( (<>), (<+>), ($$), ($+$) ) import System.Directory          ( createDirectoryIfMissing, getAppUserDataDirectory )@@ -343,7 +346,7 @@    ++ toSavedConfig liftConfigFlag        (configureOptions ParseArgs)-       (["builddir", "configure-option"] ++ map fieldName installDirsFields)+       (["builddir", "configure-option", "constraint"] ++ map fieldName installDirsFields)          --FIXME: this is only here because viewAsFieldDescr gives us a parser         -- that only recognises 'ghc' etc, the case-sensitive flag names, not@@ -351,6 +354,31 @@        [simpleField "compiler"           (fromFlagOrDefault Disp.empty . fmap Text.disp) (optional Text.parse)           configHcFlavor (\v flags -> flags { configHcFlavor = v })+        -- TODO: The following is a temporary fix. The "optimization" field is+        -- OptArg, and viewAsFieldDescr fails on that. Instead of a hand-written+        -- hackaged parser and printer, we should handle this case properly in+        -- the library.+       ,liftField configOptimization (\v flags -> flags { configOptimization = v }) $+        let name = "optimization" in+        FieldDescr name+          (\f -> case f of+                   Flag NoOptimisation      -> Disp.text "False"+                   Flag NormalOptimisation  -> Disp.text "True"+                   Flag MaximumOptimisation -> Disp.text "2"+                   _                        -> Disp.empty)+          (\line str _ -> case () of+           _ |  str == "False" -> ParseOk [] (Flag NoOptimisation)+             |  str == "True"  -> ParseOk [] (Flag NormalOptimisation)+             |  str == "0"     -> ParseOk [] (Flag NoOptimisation)+             |  str == "1"     -> ParseOk [] (Flag NormalOptimisation)+             |  str == "2"     -> ParseOk [] (Flag MaximumOptimisation)+             | lstr == "false" -> ParseOk [caseWarning] (Flag NoOptimisation)+             | lstr == "true"  -> ParseOk [caseWarning] (Flag NormalOptimisation)+             | otherwise       -> ParseFailed (NoParse name line)+             where+               lstr = lowercase str+               caseWarning = PWarning $+                 "The '" ++ name ++ "' field is case sensitive, use 'True' or 'False'.")        ]    ++ toSavedConfig liftConfigExFlag@@ -359,7 +387,7 @@    ++ toSavedConfig liftInstallFlag        (installOptions ParseArgs)-       ["dry-run", "reinstall", "only"] []+       ["dry-run", "only"] []    ++ toSavedConfig liftUploadFlag        (commandOptions uploadCommand ParseArgs)@@ -367,7 +395,11 @@    ++ toSavedConfig liftReportFlag        (commandOptions reportCommand ParseArgs)-       ["verbose"] []+       ["verbose", "username", "password"] []+       --FIXME: this is a hack, hiding the username and password.+       -- But otherwise it masks the upload ones. Either need to+       -- share the options or make then distinct. In any case+       -- they should probably be per-server.    where     toSavedConfig lift options exclusions replacements =
Distribution/Client/Configure.hs view
@@ -18,20 +18,22 @@ import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.InstallPlan (InstallPlan) import Distribution.Client.IndexUtils as IndexUtils-         ( getAvailablePackages, getInstalledPackages )+         ( getSourcePackages, getInstalledPackages ) import Distribution.Client.Setup          ( ConfigExFlags(..), configureCommand, filterConfigureFlags )-import Distribution.Client.Types as Available+import Distribution.Client.Types as Source import Distribution.Client.SetupWrapper          ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions )+import Distribution.Client.Targets+         ( userToPackageConstraint )  import Distribution.Simple.Compiler          ( CompilerId(..), Compiler(compilerId)          , PackageDB(..), PackageDBStack ) import Distribution.Simple.Program (ProgramConfiguration ) import Distribution.Simple.Setup-         ( ConfigFlags(..), toFlag, flagToMaybe, fromFlagOrDefault )-import Distribution.Client.PackageIndex (PackageIndex)+         ( ConfigFlags(..), fromFlag, toFlag, flagToMaybe, fromFlagOrDefault )+import Distribution.Simple.PackageIndex (PackageIndex) import Distribution.Simple.Utils          ( defaultPackageDesc ) import Distribution.Package@@ -64,11 +66,11 @@ configure verbosity packageDBs repos comp conf   configFlags configExFlags extraArgs = do -  installed <- getInstalledPackages verbosity comp packageDBs conf-  available <- getAvailablePackages verbosity repos+  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+  sourcePkgDb       <- getSourcePackages    verbosity repos    progress <- planLocalPackage verbosity comp configFlags configExFlags-                               installed available+                               installedPkgIndex sourcePkgDb    notice verbosity "Resolving dependencies..."   maybePlan <- foldProgress logMsg (return . Left) (return . Right)@@ -76,15 +78,15 @@   case maybePlan of     Left message -> do       info verbosity message-      setupWrapper verbosity (setupScriptOptions installed) Nothing+      setupWrapper verbosity (setupScriptOptions installedPkgIndex) Nothing         configureCommand (const configFlags) extraArgs      Right installPlan -> case InstallPlan.ready installPlan of-      [pkg@(ConfiguredPackage (AvailablePackage _ _ (LocalUnpackedPackage _)) _ _)] ->+      [pkg@(ConfiguredPackage (SourcePackage _ _ (LocalUnpackedPackage _)) _ _ _)] ->         configurePackage verbosity           (InstallPlan.planPlatform installPlan)           (InstallPlan.planCompiler installPlan)-          (setupScriptOptions installed)+          (setupScriptOptions installedPkgIndex)           configFlags pkg extraArgs        _ -> die $ "internal error: configure install plan should have exactly "@@ -95,15 +97,8 @@       useCabalVersion  = maybe anyVersion thisVersion                          (flagToMaybe (configCabalVersion configExFlags)),       useCompiler      = Just comp,-      -- Hack: we typically want to allow the UserPackageDB for finding the-      -- Cabal lib when compiling any Setup.hs even if we're doing a global-      -- install. However we also allow looking in a specific package db.-      usePackageDB     = if UserPackageDB `elem` packageDBs-                           then packageDBs-                           else packageDBs ++ [UserPackageDB],-      usePackageIndex  = if UserPackageDB `elem` packageDBs-                           then Just index-                           else Nothing,+      usePackageDB     = packageDBs',+      usePackageIndex  = index',       useProgramConfig = conf,       useDistPref      = fromFlagOrDefault                            (useDistPref defaultSetupScriptOptions)@@ -111,6 +106,16 @@       useLoggingHandle = Nothing,       useWorkingDir    = Nothing     }+      where+        -- Hack: we typically want to allow the UserPackageDB for finding the+        -- Cabal lib when compiling any Setup.hs even if we're doing a global+        -- install. However we also allow looking in a specific package db.+        (packageDBs', index') =+          case packageDBs of+            (GlobalPackageDB:dbs) | UserPackageDB `notElem` dbs+                -> (GlobalPackageDB:UserPackageDB:dbs, Nothing)+            -- but if the user is using an odd db stack, don't touch it+            dbs -> (dbs, Just index)      logMsg message rest = debug verbosity message >> rest @@ -119,20 +124,25 @@ -- planLocalPackage :: Verbosity -> Compiler                  -> ConfigFlags -> ConfigExFlags-                 -> PackageIndex InstalledPackage-                 -> AvailablePackageDb+                 -> PackageIndex+                 -> SourcePackageDb                  -> IO (Progress String String InstallPlan)-planLocalPackage verbosity comp configFlags configExFlags installed-  (AvailablePackageDb _ availablePrefs) = do+planLocalPackage verbosity comp configFlags configExFlags installedPkgIndex+  (SourcePackageDb _ packagePrefs) = do   pkg <- readPackageDescription verbosity =<< defaultPackageDesc verbosity+  solver <- chooseSolver verbosity (fromFlag $ configSolver configExFlags) (compilerId comp)    let -- We create a local package and ask to resolve a dependency on it-      localPkg = AvailablePackage {-        packageInfoId                = packageId pkg,-        Available.packageDescription = pkg,-        packageSource                = LocalUnpackedPackage "."+      localPkg = SourcePackage {+        packageInfoId             = packageId pkg,+        Source.packageDescription = pkg,+        packageSource             = LocalUnpackedPackage "."       } +      testsEnabled = fromFlagOrDefault False $ configTests configFlags+      benchmarksEnabled =+        fromFlagOrDefault False $ configBenchmarks configFlags+       resolverParams =            addPreferences@@ -142,23 +152,33 @@          . addConstraints             -- version constraints from the config file or command line-            [ PackageVersionConstraint name ver-            | Dependency name ver <- configConstraints configFlags ]+            -- TODO: should warn or error on constraints that are not on direct deps+            -- or flag constraints not on the package in question.+            (map userToPackageConstraint (configExConstraints configExFlags))          . addConstraints             -- package flags from the config file or command line-            [ PackageFlagsConstraint (packageName pkg)+            [ PackageConstraintFlags (packageName pkg)                                      (configConfigurationsFlags configFlags) ] +        . addConstraints+            -- '--enable-tests' and '--enable-benchmarks' constraints from+            -- command line+            [ PackageConstraintStanzas (packageName pkg) $ concat+                [ if testsEnabled then [TestStanzas] else []+                , if benchmarksEnabled then [BenchStanzas] else []+                ]+            ]+         $ standardInstallPolicy-            installed-            (AvailablePackageDb mempty availablePrefs)+            installedPkgIndex+            (SourcePackageDb mempty packagePrefs)             [SpecificSourcePackage localPkg] -  return (resolveDependencies buildPlatform (compilerId comp) resolverParams)+  return (resolveDependencies buildPlatform (compilerId comp) solver resolverParams)  --- | Call an installer for an 'AvailablePackage' but override the configure+-- | Call an installer for an 'SourcePackage' but override the configure -- flags with the ones given by the 'ConfiguredPackage'. In particular the -- 'ConfiguredPackage' specifies an exact 'FlagAssignment' and exactly -- versioned package dependencies. So we ignore any previous partial flag@@ -172,7 +192,7 @@                  -> [String]                  -> IO () configurePackage verbosity platform comp scriptOptions configFlags-  (ConfiguredPackage (AvailablePackage _ gpkg _) flags deps) extraArgs =+  (ConfiguredPackage (SourcePackage _ gpkg _) flags stanzas deps) extraArgs =    setupWrapper verbosity     scriptOptions (Just pkg) configureCommand configureFlags extraArgs@@ -181,11 +201,13 @@     configureFlags   = filterConfigureFlags configFlags {       configConfigurationsFlags = flags,       configConstraints         = map thisPackageVersion deps,-      configVerbosity           = toFlag verbosity+      configVerbosity           = toFlag verbosity,+      configBenchmarks          = toFlag (BenchStanzas `elem` stanzas),+      configTests               = toFlag (TestStanzas `elem` stanzas)     }      pkg = case finalizePackageDescription flags            (const True)-           platform comp [] gpkg of+           platform comp [] (enableStanzas stanzas gpkg) of       Left _ -> error "finalizePackageDescription ConfiguredPackage failed"       Right (desc, _) -> desc
Distribution/Client/Dependency.hs view
@@ -14,6 +14,7 @@ ----------------------------------------------------------------------------- module Distribution.Client.Dependency (     -- * The main package dependency resolver+    chooseSolver,     resolveDependencies,     Progress(..),     foldProgress,@@ -42,44 +43,57 @@     addConstraints,     addPreferences,     setPreferenceDefault,-    addAvailablePackages,-    hideInstalledPackagesSpecific,+    setReorderGoals,+    setIndependentGoals,+    setAvoidReinstalls,+    setShadowPkgs,+    setMaxBackjumps,+    addSourcePackages,+    hideInstalledPackagesSpecificByInstalledPackageId,+    hideInstalledPackagesSpecificBySourcePackageId,     hideInstalledPackagesAllVersions,   ) where -import Distribution.Client.Dependency.TopDown (topDownResolver)+import Distribution.Client.Dependency.TopDown+         ( topDownResolver )+import Distribution.Client.Dependency.Modular+         ( modularResolver, SolverConfig(..) ) import qualified Distribution.Client.PackageIndex as PackageIndex-import Distribution.Client.PackageIndex (PackageIndex)+import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.InstallPlan (InstallPlan) import Distribution.Client.Types-         ( AvailablePackageDb(AvailablePackageDb)-         , AvailablePackage(..), InstalledPackage )+         ( SourcePackageDb(SourcePackageDb)+         , SourcePackage(..) ) import Distribution.Client.Dependency.Types-         ( DependencyResolver, PackageConstraint(..)+         ( PreSolver(..), Solver(..), DependencyResolver, PackageConstraint(..)          , PackagePreferences(..), InstalledPreference(..)+         , PackagesPreferenceDefault(..)          , Progress(..), foldProgress ) import Distribution.Client.Targets+import qualified Distribution.InstalledPackageInfo as Installed import Distribution.Package          ( PackageName(..), PackageId, Package(..), packageVersion-         , Dependency(Dependency))+         , InstalledPackageId, Dependency(Dependency)) import Distribution.Version-         ( VersionRange, anyVersion, withinRange, simplifyVersionRange )+         ( Version(..), VersionRange, anyVersion, withinRange, simplifyVersionRange ) import Distribution.Compiler-         ( CompilerId(..) )+         ( CompilerId(..), CompilerFlavor(..) ) import Distribution.System          ( Platform )-import Distribution.Simple.Utils (comparing)+import Distribution.Simple.Utils+         ( comparing, warn, info ) import Distribution.Text          ( display )+import Distribution.Verbosity+         ( Verbosity )  import Data.List (maximumBy, foldl')-import Data.Maybe (fromMaybe, isJust)+import Data.Maybe (fromMaybe) import qualified Data.Map as Map import qualified Data.Set as Set import Data.Set (Set) - -- ------------------------------------------------------------ -- * High level planner policy -- ------------------------------------------------------------@@ -93,36 +107,16 @@        depResolverConstraints       :: [PackageConstraint],        depResolverPreferences       :: [PackagePreference],        depResolverPreferenceDefault :: PackagesPreferenceDefault,-       depResolverInstalled         :: PackageIndex InstalledPackage,-       depResolverAvailable         :: PackageIndex AvailablePackage+       depResolverInstalledPkgIndex :: InstalledPackageIndex.PackageIndex,+       depResolverSourcePkgIndex    :: PackageIndex.PackageIndex SourcePackage,+       depResolverReorderGoals      :: Bool,+       depResolverIndependentGoals  :: Bool,+       depResolverAvoidReinstalls   :: Bool,+       depResolverShadowPkgs        :: Bool,+       depResolverMaxBackjumps      :: Maybe Int      }  --- | Global policy for all packages to say if we prefer package versions that--- are already installed locally or if we just prefer the latest available.----data PackagesPreferenceDefault =--     -- | Always prefer the latest version irrespective of any existing-     -- installed version.-     ---     -- * This is the standard policy for upgrade.-     ---     PreferAllLatest--     -- | Always prefer the installed versions over ones that would need to be-     -- installed. Secondarily, prefer latest versions (eg the latest installed-     -- version or if there are none then the latest available version).-   | PreferAllInstalled--     -- | Prefer the latest version for packages that are explicitly requested-     -- but prefers the installed version for any other packages.-     ---     -- * This is the standard policy for install.-     ---   | PreferLatestForSelected-- -- | A package selection preference for a particular package. -- -- Preferences are soft constraints that the dependency resolver should try to@@ -137,17 +131,22 @@      -- | If we prefer versions of packages that are already installed.    | PackageInstalledPreference PackageName InstalledPreference -basicDepResolverParams :: PackageIndex InstalledPackage-                       -> PackageIndex AvailablePackage+basicDepResolverParams :: InstalledPackageIndex.PackageIndex+                       -> PackageIndex.PackageIndex SourcePackage                        -> DepResolverParams-basicDepResolverParams installed available =+basicDepResolverParams installedPkgIndex sourcePkgIndex =     DepResolverParams {        depResolverTargets           = [],        depResolverConstraints       = [],        depResolverPreferences       = [],        depResolverPreferenceDefault = PreferLatestForSelected,-       depResolverInstalled         = installed,-       depResolverAvailable         = available+       depResolverInstalledPkgIndex = installedPkgIndex,+       depResolverSourcePkgIndex    = sourcePkgIndex,+       depResolverReorderGoals      = False,+       depResolverIndependentGoals  = False,+       depResolverAvoidReinstalls   = False,+       depResolverShadowPkgs        = False,+       depResolverMaxBackjumps      = Nothing      }  addTargets :: [PackageName]@@ -180,12 +179,42 @@       depResolverPreferenceDefault = preferenceDefault     } +setReorderGoals :: Bool -> DepResolverParams -> DepResolverParams+setReorderGoals b params =+    params {+      depResolverReorderGoals = b+    }++setIndependentGoals :: Bool -> DepResolverParams -> DepResolverParams+setIndependentGoals b params =+    params {+      depResolverIndependentGoals = b+    }++setAvoidReinstalls :: Bool -> DepResolverParams -> DepResolverParams+setAvoidReinstalls b params =+    params {+      depResolverAvoidReinstalls = b+    }++setShadowPkgs :: Bool -> DepResolverParams -> DepResolverParams+setShadowPkgs b params =+    params {+      depResolverShadowPkgs = b+    }++setMaxBackjumps :: Maybe Int -> DepResolverParams -> DepResolverParams+setMaxBackjumps n params =+    params {+      depResolverMaxBackjumps = n+    }+ dontUpgradeBasePackage :: DepResolverParams -> DepResolverParams dontUpgradeBasePackage params =     addConstraints extraConstraints params   where     extraConstraints =-      [ PackageInstalledConstraint pkgname+      [ PackageConstraintInstalled pkgname       | all (/=PackageName "base") (depResolverTargets params)       , pkgname <-  [ PackageName "base", PackageName "ghc-prim" ]       , isInstalled pkgname ]@@ -193,45 +222,59 @@     -- below when there are no targets and thus no dep on base.     -- Need to refactor contraints separate from needing packages.     isInstalled = not . null-                . PackageIndex.lookupPackageName (depResolverInstalled params)+                . InstalledPackageIndex.lookupPackageName+                                 (depResolverInstalledPkgIndex params) -addAvailablePackages :: [AvailablePackage]-                     -> DepResolverParams -> DepResolverParams-addAvailablePackages pkgs params =+addSourcePackages :: [SourcePackage]+                  -> DepResolverParams -> DepResolverParams+addSourcePackages pkgs params =     params {-      depResolverAvailable = foldl (flip PackageIndex.insert)-                                   (depResolverAvailable params) pkgs+      depResolverSourcePkgIndex =+        foldl (flip PackageIndex.insert)+              (depResolverSourcePkgIndex params) pkgs     } -hideInstalledPackagesSpecific :: [PackageId]-                              -> DepResolverParams -> DepResolverParams-hideInstalledPackagesSpecific pkgids params =+hideInstalledPackagesSpecificByInstalledPackageId :: [InstalledPackageId]+                                                     -> DepResolverParams -> DepResolverParams+hideInstalledPackagesSpecificByInstalledPackageId pkgids params =     --TODO: this should work using exclude constraints instead     params {-      depResolverInstalled = foldl' (flip PackageIndex.deletePackageId)-                                    (depResolverInstalled params) pkgids+      depResolverInstalledPkgIndex =+        foldl' (flip InstalledPackageIndex.deleteInstalledPackageId)+               (depResolverInstalledPkgIndex params) pkgids     } +hideInstalledPackagesSpecificBySourcePackageId :: [PackageId]+                                                  -> DepResolverParams -> DepResolverParams+hideInstalledPackagesSpecificBySourcePackageId pkgids params =+    --TODO: this should work using exclude constraints instead+    params {+      depResolverInstalledPkgIndex =+        foldl' (flip InstalledPackageIndex.deleteSourcePackageId)+               (depResolverInstalledPkgIndex params) pkgids+    }+ hideInstalledPackagesAllVersions :: [PackageName]                                  -> DepResolverParams -> DepResolverParams hideInstalledPackagesAllVersions pkgnames params =     --TODO: this should work using exclude constraints instead     params {-      depResolverInstalled =-        foldl' (flip PackageIndex.deletePackageName)-               (depResolverInstalled params) pkgnames+      depResolverInstalledPkgIndex =+        foldl' (flip InstalledPackageIndex.deletePackageName)+               (depResolverInstalledPkgIndex params) pkgnames     }   hideBrokenInstalledPackages :: DepResolverParams -> DepResolverParams hideBrokenInstalledPackages params =-    hideInstalledPackagesSpecific pkgids params+    hideInstalledPackagesSpecificByInstalledPackageId pkgids params   where-    pkgids = map packageId-           . PackageIndex.reverseDependencyClosure (depResolverInstalled params)-           . map (packageId . fst)-           . PackageIndex.brokenPackages-           $ depResolverInstalled params+    pkgids = map Installed.installedPackageId+           . InstalledPackageIndex.reverseDependencyClosure+                            (depResolverInstalledPkgIndex params)+           . map (Installed.installedPackageId . fst)+           . InstalledPackageIndex.brokenPackages+           $ depResolverInstalledPkgIndex params   upgradeDependencies :: DepResolverParams -> DepResolverParams@@ -243,16 +286,17 @@     hideInstalledPackagesAllVersions (depResolverTargets params) params  -standardInstallPolicy :: PackageIndex InstalledPackage-                      -> AvailablePackageDb-                      -> [PackageSpecifier AvailablePackage]+standardInstallPolicy :: InstalledPackageIndex.PackageIndex+                      -> SourcePackageDb+                      -> [PackageSpecifier SourcePackage]                       -> DepResolverParams standardInstallPolicy-    installed (AvailablePackageDb available availablePrefs) pkgSpecifiers+    installedPkgIndex (SourcePackageDb sourcePkgIndex sourcePkgPrefs)+    pkgSpecifiers    = addPreferences       [ PackageVersionPreference name ver-      | (name, ver) <- Map.toList availablePrefs ]+      | (name, ver) <- Map.toList sourcePkgPrefs ]    . addConstraints       (concatMap pkgSpecifierConstraints pkgSpecifiers)@@ -260,23 +304,35 @@   . addTargets       (map pkgSpecifierTarget pkgSpecifiers) -  . hideInstalledPackagesSpecific+  . hideInstalledPackagesSpecificBySourcePackageId       [ packageId pkg | SpecificSourcePackage pkg <- pkgSpecifiers ] -  . addAvailablePackages+  . addSourcePackages       [ pkg  | SpecificSourcePackage pkg <- pkgSpecifiers ]    $ basicDepResolverParams-      installed available+      installedPkgIndex sourcePkgIndex   -- ------------------------------------------------------------ -- * Interface to the standard resolver -- ------------------------------------------------------------ -defaultResolver :: DependencyResolver-defaultResolver = topDownResolver+chooseSolver :: Verbosity -> PreSolver -> CompilerId -> IO Solver+chooseSolver _         AlwaysTopDown _                = return TopDown+chooseSolver _         AlwaysModular _                = return Modular+chooseSolver verbosity Choose        (CompilerId f v) = do+  let chosenSolver | f == GHC && v <= Version [7] [] = TopDown+                   | otherwise                       = Modular+      msg TopDown = warn verbosity "Falling back to topdown solver for GHC < 7."+      msg Modular = info verbosity "Choosing modular solver."+  msg chosenSolver+  return chosenSolver +runSolver :: Solver -> SolverConfig -> DependencyResolver+runSolver TopDown = const topDownResolver -- TODO: warn about unsuported options+runSolver Modular = modularResolver+ -- | Run the dependency solver. -- -- Since this is potentially an expensive operation, the result is wrapped in a@@ -285,26 +341,40 @@ -- resolveDependencies :: Platform                     -> CompilerId+                    -> Solver                     -> DepResolverParams                     -> Progress String String InstallPlan      --TODO: is this needed here? see dontUpgradeBasePackage-resolveDependencies platform comp params+resolveDependencies platform comp _solver params   | null (depResolverTargets params)   = return (mkInstallPlan platform comp []) -resolveDependencies platform comp params =+resolveDependencies platform comp  solver params =      fmap (mkInstallPlan platform comp)-  $ defaultResolver platform comp installed available-                    preferences constraints targets+  $ runSolver solver (SolverConfig reorderGoals indGoals noReinstalls shadowing maxBkjumps)+                     platform comp installedPkgIndex sourcePkgIndex+                     preferences constraints targets   where     DepResolverParams       targets constraints       prefs defpref-      installed available = dontUpgradeBasePackage-                          . hideBrokenInstalledPackages-                          $ params+      installedPkgIndex+      sourcePkgIndex+      reorderGoals+      indGoals+      noReinstalls+      shadowing+      maxBkjumps      = dontUpgradeBasePackage+                      -- TODO:+                      -- The modular solver can properly deal with broken packages+                      -- and won't select them. So the 'hideBrokenInstalledPackages'+                      -- function should be moved into a module that is specific+                      -- to the Topdown solver.+                      . (if solver /= Modular then hideBrokenInstalledPackages+                                              else id)+                      $ params      preferences = interpretPackagesPreference                     (Set.fromList targets) defpref prefs@@ -316,8 +386,8 @@ mkInstallPlan :: Platform               -> CompilerId               -> [InstallPlan.PlanPackage] -> InstallPlan-mkInstallPlan platform comp pkgs =-  case InstallPlan.new platform comp (PackageIndex.fromList pkgs) of+mkInstallPlan platform comp pkgIndex =+  case InstallPlan.new platform comp (PackageIndex.fromList pkgIndex) of     Right plan     -> plan     Left  problems -> error $ unlines $         "internal error: could not construct a valid install plan."@@ -375,12 +445,13 @@ -- It simply means preferences for installed packages will be ignored. -- resolveWithoutDependencies :: DepResolverParams-                           -> Either [ResolveNoDepsError] [AvailablePackage]+                           -> Either [ResolveNoDepsError] [SourcePackage] resolveWithoutDependencies (DepResolverParams targets constraints-                                prefs defpref installed available) =+                              prefs defpref installedPkgIndex sourcePkgIndex+                              _reorderGoals _indGoals _avoidReinstalls _shadowing _maxBjumps) =     collectEithers (map selectPackage targets)   where-    selectPackage :: PackageName -> Either ResolveNoDepsError AvailablePackage+    selectPackage :: PackageName -> Either ResolveNoDepsError SourcePackage     selectPackage pkgname       | null choices = Left  $! ResolveUnsatisfiable pkgname requiredVersions       | otherwise    = Right $! maximumBy bestByPrefs choices@@ -389,7 +460,8 @@         -- Constraints         requiredVersions = packageConstraints pkgname         pkgDependency    = Dependency pkgname requiredVersions-        choices          = PackageIndex.lookupDependency available pkgDependency+        choices          = PackageIndex.lookupDependency sourcePkgIndex+                                                         pkgDependency          -- Preferences         PackagePreferences preferredVersions preferInstalled@@ -399,7 +471,8 @@                           (installPref pkg, versionPref pkg, packageVersion pkg)         installPref   = case preferInstalled of           PreferLatest    -> const False-          PreferInstalled -> isJust . PackageIndex.lookupPackageId installed+          PreferInstalled -> not . null . InstalledPackageIndex.lookupSourcePackageId+                                                     installedPkgIndex                            . packageId         versionPref   pkg = packageVersion pkg `withinRange` preferredVersions @@ -408,7 +481,7 @@       Map.findWithDefault anyVersion pkgname packageVersionConstraintMap     packageVersionConstraintMap =       Map.fromList [ (name, range)-                   | PackageVersionConstraint name range <- constraints ]+                   | PackageConstraintVersion name range <- constraints ]      packagePreferences :: PackageName -> PackagePreferences     packagePreferences = interpretPackagesPreference
+ Distribution/Client/Dependency/Modular.hs view
@@ -0,0 +1,58 @@+module Distribution.Client.Dependency.Modular+         ( modularResolver, SolverConfig(..)) where++-- Here, we try to map between the external cabal-install solver+-- interface and the internal interface that the solver actually+-- expects. There are a number of type conversions to perform: we+-- have to convert the package indices to the uniform index used+-- by the solver; we also have to convert the initial constraints;+-- and finally, we have to convert back the resulting install+-- plan.++import Data.Map as M+         ( fromListWith )+import Distribution.Client.Dependency.Modular.Assignment+         ( Assignment, toCPs )+import Distribution.Client.Dependency.Modular.Dependency+         ( RevDepMap )+import Distribution.Client.Dependency.Modular.ConfiguredConversion+         ( convCP )+import Distribution.Client.Dependency.Modular.IndexConversion+         ( convPIs )+import Distribution.Client.Dependency.Modular.Log+         ( logToProgress )+import Distribution.Client.Dependency.Modular.Package+         ( PN )+import Distribution.Client.Dependency.Modular.Solver+         ( SolverConfig(..), solve )+import Distribution.Client.Dependency.Types+         ( DependencyResolver, PackageConstraint(..) )+import Distribution.Client.InstallPlan+         ( PlanPackage )+import Distribution.System+         ( Platform(..) )++-- | Ties the two worlds together: classic cabal-install vs. the modular+-- solver. Performs the necessary translations before and after.+modularResolver :: SolverConfig -> DependencyResolver+modularResolver sc (Platform arch os) cid iidx sidx pprefs pcs pns =+  fmap (uncurry postprocess)      $ -- convert install plan+  logToProgress (maxBackjumps sc) $ -- convert log format into progress format+  solve sc idx pprefs gcs pns+    where+      -- Indices have to be converted into solver-specific uniform index.+      idx    = convPIs os arch cid (shadowPkgs sc) iidx sidx+      -- Constraints have to be converted into a finite map indexed by PN.+      gcs    = M.fromListWith (++) (map (\ pc -> (pcName pc, [pc])) pcs)++      -- Results have to be converted into an install plan.+      postprocess :: Assignment -> RevDepMap -> [PlanPackage]+      postprocess a rdm = map (convCP iidx sidx) (toCPs a rdm)++      -- Helper function to extract the PN from a constraint.+      pcName :: PackageConstraint -> PN+      pcName (PackageConstraintVersion   pn _) = pn+      pcName (PackageConstraintInstalled pn  ) = pn+      pcName (PackageConstraintSource    pn  ) = pn+      pcName (PackageConstraintFlags     pn _) = pn+      pcName (PackageConstraintStanzas   pn _) = pn
+ Distribution/Client/Dependency/Modular/Assignment.hs view
@@ -0,0 +1,146 @@+module Distribution.Client.Dependency.Modular.Assignment where++import Control.Applicative+import Control.Monad+import Data.Array as A+import Data.List as L+import Data.Map as M+import Data.Maybe+import Data.Graph+import Prelude hiding (pi)++import Distribution.PackageDescription (FlagAssignment) -- from Cabal+import Distribution.Client.Types (OptionalStanza)++import Distribution.Client.Dependency.Modular.Configured+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Version++-- | A (partial) package assignment. Qualified package names+-- are associated with instances.+type PAssignment    = Map QPN I++-- | A (partial) package preassignment. Qualified package names+-- are associated with constrained instances. Constrained instances+-- record constraints about the instances that can still be chosen,+-- and in the extreme case fix a concrete instance.+type PPreAssignment = Map QPN (CI QPN)+type FAssignment    = Map QFN Bool+type SAssignment    = Map QSN Bool++-- | A (partial) assignment of variables.+data Assignment = A PAssignment FAssignment SAssignment+  deriving (Show, Eq)++-- | A preassignment comprises knowledge about variables, but not+-- necessarily fixed values.+data PreAssignment = PA PPreAssignment FAssignment SAssignment++-- | Extend a package preassignment.+--+-- Either returns a witness of the conflict that would arise during the merge,+-- or the successfully extended assignment.+extend :: Var QPN -> PPreAssignment -> [Dep QPN] -> Either (ConflictSet QPN, [Dep QPN]) PPreAssignment+extend var pa qa = foldM (\ a (Dep qpn ci) ->+                     let ci' = M.findWithDefault (Constrained []) qpn a+                     in  case (\ x -> M.insert qpn x a) <$> merge ci' ci of+                           Left (c, (d, d')) -> Left  (c, L.map (Dep qpn) (simplify (P qpn) d d'))+                           Right x           -> Right x)+                    pa qa+  where+    -- We're trying to remove trivial elements of the conflict. If we're just+    -- making a choice pkg == instance, and pkg => pkg == instance is a part+    -- of the conflict, then this info is clear from the context and does not+    -- have to be repeated.+    simplify v (Fixed _ (Goal var' _)) c | v == var && var' == var = [c]+    simplify v c (Fixed _ (Goal var' _)) | v == var && var' == var = [c]+    simplify _ c                       d                           = [c, d]++-- | Delivers an ordered list of fully configured packages.+--+-- TODO: This function is (sort of) ok. However, there's an open bug+-- w.r.t. unqualification. There might be several different instances+-- of one package version chosen by the solver, which will lead to+-- clashes.+toCPs :: Assignment -> RevDepMap -> [CP QPN]+toCPs (A pa fa sa) rdm =+  let+    -- get hold of the graph+    g   :: Graph+    vm  :: Vertex -> ((), QPN, [QPN])+    cvm :: QPN -> Maybe Vertex+    -- Note that the RevDepMap contains duplicate dependencies. Therefore the nub.+    (g, vm, cvm) = graphFromEdges (L.map (\ (x, xs) -> ((), x, nub xs))+                                  (M.toList rdm))+    tg :: Graph+    tg = transposeG g+    -- Topsort the dependency graph, yielding a list of pkgs in the right order.+    -- The graph will still contain all the installed packages, and it might+    -- contain duplicates, because several variables might actually resolve to+    -- the same package in the presence of qualified package names.+    ps :: [PI QPN]+    ps = L.map ((\ (_, x, _) -> PI x (pa M.! x)) . vm) $+         topSort g+    -- Determine the flags per package, by walking over and regrouping the+    -- complete flag assignment by package.+    fapp :: Map QPN FlagAssignment+    fapp = M.fromListWith (++) $+           L.map (\ ((FN (PI qpn _) fn), b) -> (qpn, [(fn, b)])) $+           M.toList $+           fa+    -- Stanzas per package.+    sapp :: Map QPN [OptionalStanza]+    sapp = M.fromListWith (++) $+           L.map (\ ((SN (PI qpn _) sn), b) -> (qpn, if b then [sn] else [])) $+           M.toList $+           sa+    -- Dependencies per package.+    depp :: QPN -> [PI QPN]+    depp qpn = let v :: Vertex+                   v   = fromJust (cvm qpn)+                   dvs :: [Vertex]+                   dvs = tg A.! v+               in L.map (\ dv -> case vm dv of (_, x, _) -> PI x (pa M.! x)) dvs+  in+    L.map (\ pi@(PI qpn _) -> CP pi+                                 (M.findWithDefault [] qpn fapp)+                                 (M.findWithDefault [] qpn sapp)+                                 (depp qpn))+          ps++-- | Finalize an assignment and a reverse dependency map.+--+-- This is preliminary, and geared towards output right now.+finalize :: Index -> Assignment -> RevDepMap -> IO ()+finalize idx (A pa fa _) rdm =+  let+    -- get hold of the graph+    g  :: Graph+    vm :: Vertex -> ((), QPN, [QPN])+    (g, vm) = graphFromEdges' (L.map (\ (x, xs) -> ((), x, xs)) (M.toList rdm))+    -- topsort the dependency graph, yielding a list of pkgs in the right order+    f :: [PI QPN]+    f = L.filter (not . instPI) (L.map ((\ (_, x, _) -> PI x (pa M.! x)) . vm) (topSort g))+    fapp :: Map QPN [(QFN, Bool)] -- flags per package+    fapp = M.fromListWith (++) $+           L.map (\ (qfn@(FN (PI qpn _) _), b) -> (qpn, [(qfn, b)])) $ M.toList $ fa+    -- print one instance+    ppi pi@(PI qpn _) = showPI pi ++ status pi ++ " " ++ pflags (M.findWithDefault [] qpn fapp)+    -- print install status+    status :: PI QPN -> String+    status (PI (Q _ pn) _) =+      case insts of+        [] -> " (new)"+        vs -> " (" ++ intercalate ", " (L.map showVer vs) ++ ")"+      where insts = L.map (\ (I v _) -> v) $ L.filter isInstalled $+                    M.keys (M.findWithDefault M.empty pn idx)+            isInstalled (I _ (Inst _ )) = True+            isInstalled _               = False+    -- print flag assignment+    pflags = unwords . L.map (uncurry showFBool)+  in+    -- show packages with associated flag assignments+    putStr (unlines (L.map ppi f))
+ Distribution/Client/Dependency/Modular/Builder.hs view
@@ -0,0 +1,143 @@+module Distribution.Client.Dependency.Modular.Builder where++-- Building the search tree.+--+-- In this phase, we build a search tree that is too large, i.e, it contains+-- invalid solutions. We keep track of the open goals at each point. We+-- nondeterministically pick an open goal (via a goal choice node), create+-- subtrees according to the index and the available solutions, and extend the+-- set of open goals by superficially looking at the dependencies recorded in+-- the index.+--+-- For each goal, we keep track of all the *reasons* why it is being+-- introduced. These are for debugging and error messages, mainly. A little bit+-- of care has to be taken due to the way we treat flags. If a package has+-- flag-guarded dependencies, we cannot introduce them immediately. Instead, we+-- store the entire dependency.++import Control.Monad.Reader hiding (sequence, mapM)+import Data.List as L+import Data.Map as M+import Prelude hiding (sequence, mapM)++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree++-- | The state needed during the build phase of the search tree.+data BuildState = BS {+  index :: Index,           -- ^ information about packages and their dependencies+  scope :: Scope,           -- ^ information about encapsulations+  rdeps :: RevDepMap,       -- ^ set of all package goals, completed and open, with reverse dependencies+  open  :: PSQ OpenGoal (), -- ^ set of still open goals (flag and package goals)+  next  :: BuildType        -- ^ kind of node to generate next+}++-- | Extend the set of open goals with the new goals listed.+--+-- We also adjust the map of overall goals, and keep track of the+-- reverse dependencies of each of the goals.+extendOpen :: QPN -> [OpenGoal] -> BuildState -> BuildState+extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs+  where+    go g o []                                             = s { rdeps = g, open = o }+    go g o (ng@(OpenGoal (Flagged _ _ _ _)    _gr) : ngs) = go g (cons ng () o) ngs+    go g o (ng@(OpenGoal (Stanza  _   _  )    _gr) : ngs) = go g (cons ng () o) ngs+    go g o (ng@(OpenGoal (Simple (Dep qpn _)) _gr) : ngs)+      | qpn == qpn'                                       = go                       g              o  ngs+                                       -- we ignore self-dependencies at this point; TODO: more care may be needed+      | qpn `M.member` g                                  = go (M.adjust (qpn':) qpn g)             o  ngs+      | otherwise                                         = go (M.insert qpn [qpn']  g) (cons ng () o) ngs+                                       -- code above is correct; insert/adjust have different arg order++-- | Update the current scope by taking into account the encapsulations that+-- are defined for the current package.+establishScope :: QPN -> Encaps -> BuildState -> BuildState+establishScope (Q pp pn) ecs s =+    s { scope = L.foldl (\ m e -> M.insert e pp' m) (scope s) ecs }+  where+    pp' = pn : pp -- new path++-- | Given the current scope, qualify all the package names in the given set of+-- dependencies and then extend the set of open goals accordingly.+scopedExtendOpen :: QPN -> I -> QGoalReasons -> FlaggedDeps PN -> FlagDefaults ->+                    BuildState -> BuildState+scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s+  where+    sc     = scope s+    qfdeps = L.map (fmap (qualify sc)) fdeps -- qualify all the package names+    qfdefs = L.map (\ (fn, b) -> Flagged (FN (PI qpn i) fn) b [] []) $ M.toList fdefs+    gs     = L.map (flip OpenGoal gr) (qfdeps ++ qfdefs)++data BuildType = Goals | OneGoal OpenGoal | Instance QPN I PInfo QGoalReasons++build :: BuildState -> Tree (QGoalReasons, Scope)+build = ana go+  where+    go :: BuildState -> TreeF (QGoalReasons, Scope) BuildState++    -- If we have a choice between many goals, we just record the choice in+    -- the tree. We select each open goal in turn, and before we descend, remove+    -- it from the queue of open goals.+    go bs@(BS { rdeps = rds, open = gs, next = Goals })+      | P.null gs = DoneF rds+      | otherwise = GoalChoiceF (P.mapWithKey (\ g (_sc, gs') -> bs { next = OneGoal g, open = gs' })+                                              (P.splits gs))++    -- If we have already picked a goal, then the choice depends on the kind+    -- of goal.+    --+    -- For a package, we look up the instances available in the global info,+    -- and then handle each instance in turn.+    go bs@(BS { index = idx, scope = sc, next = OneGoal (OpenGoal (Simple (Dep qpn@(Q _ pn) _)) gr) }) =+      case M.lookup pn idx of+        Nothing  -> FailF (toConflictSet (Goal (P qpn) gr)) (BuildFailureNotInIndex pn)+        Just pis -> PChoiceF qpn (gr, sc) (P.fromList (L.map (\ (i, info) ->+                                                           (i, bs { next = Instance qpn i info gr }))+                                                         (M.toList pis)))+          -- TODO: data structure conversion is rather ugly here++    -- For a flag, we create only two subtrees, and we create them in the order+    -- that is indicated by the flag default.+    --+    -- TODO: Should we include the flag default in the tree?+    go bs@(BS { scope = sc, next = OneGoal (OpenGoal (Flagged qfn@(FN (PI qpn _) _) b t f) gr) }) =+      FChoiceF qfn (gr, sc) trivial (P.fromList (reorder b+        [(True,  (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True  : gr)) t) bs) { next = Goals }),+         (False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False : gr)) f) bs) { next = Goals })]))+      where+        reorder True  = id+        reorder False = reverse+        trivial = L.null t && L.null f++    go bs@(BS { scope = sc, next = OneGoal (OpenGoal (Stanza qsn@(SN (PI qpn _) _) t) gr) }) =+      SChoiceF qsn (gr, sc) trivial (P.fromList+        [(False,                                                                        bs  { next = Goals }),+         (True,  (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn : gr)) t) bs) { next = Goals })])+      where+        trivial = L.null t++    -- For a particular instance, we change the state: we update the scope,+    -- and furthermore we update the set of goals.+    --+    -- TODO: We could inline this above.+    go bs@(BS { next = Instance qpn i (PInfo fdeps fdefs ecs _) gr }) =+      go ((establishScope qpn ecs+             (scopedExtendOpen qpn i (PDependency (PI qpn i) : gr) fdeps fdefs bs))+             { next = Goals })++-- | Interface to the tree builder. Just takes an index and a list of package names,+-- and computes the initial state and then the tree from there.+buildTree :: Index -> Bool -> [PN] -> Tree (QGoalReasons, Scope)+buildTree idx ind igs =+    build (BS idx sc+                  (M.fromList (L.map (\ qpn -> (qpn, []))                                                     qpns))+                  (P.fromList (L.map (\ qpn -> (OpenGoal (Simple (Dep qpn (Constrained []))) [UserGoal], ())) qpns))+                  Goals)+  where+    sc | ind       = makeIndependent igs+       | otherwise = emptyScope+    qpns           = L.map (qualify sc) igs
+ Distribution/Client/Dependency/Modular/Configured.hs view
@@ -0,0 +1,10 @@+module Distribution.Client.Dependency.Modular.Configured where++import Distribution.PackageDescription (FlagAssignment) -- from Cabal+import Distribution.Client.Types (OptionalStanza)++import Distribution.Client.Dependency.Modular.Package++-- | A configured package is a package instance together with+-- a flag assignment and complete dependencies.+data CP qpn = CP (PI qpn) FlagAssignment [OptionalStanza] [PI qpn]
+ Distribution/Client/Dependency/Modular/ConfiguredConversion.hs view
@@ -0,0 +1,40 @@+module Distribution.Client.Dependency.Modular.ConfiguredConversion where++import Data.Maybe+import Prelude hiding (pi)++import Distribution.Client.InstallPlan+import Distribution.Client.Types+import Distribution.Compiler+import qualified Distribution.Client.PackageIndex as CI+import qualified Distribution.Simple.PackageIndex as SI+import Distribution.System++import Distribution.Client.Dependency.Modular.Configured+import Distribution.Client.Dependency.Modular.Package++mkPlan :: Platform -> CompilerId ->+          SI.PackageIndex -> CI.PackageIndex SourcePackage ->+          [CP QPN] -> Either [PlanProblem] InstallPlan+mkPlan plat comp iidx sidx cps =+  new plat comp (CI.fromList (map (convCP iidx sidx) cps))++convCP :: SI.PackageIndex -> CI.PackageIndex SourcePackage ->+          CP QPN -> PlanPackage+convCP iidx sidx (CP qpi fa es ds) =+  case convPI qpi of+    Left  pi -> PreExisting $ InstalledPackage+                  (fromJust $ SI.lookupInstalledPackageId iidx pi)+                  (map convPI' ds)+    Right pi -> Configured $ ConfiguredPackage+                  (fromJust $ CI.lookupPackageId sidx pi)+                  fa+                  es+                  (map convPI' ds)++convPI :: PI QPN -> Either InstalledPackageId PackageId+convPI (PI _ (I _ (Inst pi))) = Left pi+convPI qpi                    = Right $ convPI' qpi++convPI' :: PI QPN -> PackageId+convPI' (PI (Q _ pn) (I v _))  = PackageIdentifier pn v
+ Distribution/Client/Dependency/Modular/Dependency.hs view
@@ -0,0 +1,194 @@+module Distribution.Client.Dependency.Modular.Dependency where++import Prelude hiding (pi)++import Data.List as L+import Data.Map as M+import Data.Set as S++import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Version++-- | The type of variables that play a role in the solver.+-- Note that the tree currently does not use this type directly,+-- and rather has separate tree nodes for the different types of+-- variables. This fits better with the fact that in most cases,+-- these have to be treated differently.+--+-- TODO: This isn't the ideal location to declare the type,+-- but we need them for constrained instances.+data Var qpn = P qpn | F (FN qpn) | S (SN qpn)+  deriving (Eq, Ord, Show)++showVar :: Var QPN -> String+showVar (P qpn) = showQPN qpn+showVar (F qfn) = showQFN qfn+showVar (S qsn) = showQSN qsn++instance Functor Var where+  fmap f (P n)  = P (f n)+  fmap f (F fn) = F (fmap f fn)+  fmap f (S sn) = S (fmap f sn)++type ConflictSet qpn = Set (Var qpn)++showCS :: ConflictSet QPN -> String+showCS = intercalate ", " . L.map showVar . S.toList++-- | Constrained instance. If the choice has already been made, this is+-- a fixed instance, and we record the package name for which the choice+-- is for convenience. Otherwise, it is a list of version ranges paired with+-- the goals / variables that introduced them.+data CI qpn = Fixed I (Goal qpn) | Constrained [VROrigin qpn]+  deriving (Eq, Show)++instance Functor CI where+  fmap f (Fixed i g)       = Fixed i (fmap f g)+  fmap f (Constrained vrs) = Constrained (L.map (\ (x, y) -> (x, fmap f y)) vrs)++instance ResetGoal CI where+  resetGoal g (Fixed i _)       = Fixed i g+  resetGoal g (Constrained vrs) = Constrained (L.map (\ (x, y) -> (x, resetGoal g y)) vrs)++type VROrigin qpn = (VR, Goal qpn)++-- | Helper function to collapse a list of version ranges with origins into+-- a single, simplified, version range.+collapse :: [VROrigin qpn] -> VR+collapse = simplifyVR . L.foldr (.&&.) anyVR . L.map fst++showCI :: CI QPN -> String+showCI (Fixed i _)      = "==" ++ showI i+showCI (Constrained vr) = showVR (collapse vr)++-- | Merge constrained instances. We currently adopt a lazy strategy for+-- merging, i.e., we only perform actual checking if one of the two choices+-- is fixed. If the merge fails, we return a conflict set indicating the+-- variables responsible for the failure, as well as the two conflicting+-- fragments.+--+-- Note that while there may be more than one conflicting pair of version+-- ranges, we only return the first we find.+--+-- TODO: Different pairs might have different conflict sets. We're+-- obviously interested to return a conflict that has a "better" conflict+-- set in the sense the it contains variables that allow us to backjump+-- further. We might apply some heuristics here, such as to change the+-- order in which we check the constraints.+merge :: Ord qpn => CI qpn -> CI qpn -> Either (ConflictSet qpn, (CI qpn, CI qpn)) (CI qpn)+merge c@(Fixed i g1)       d@(Fixed j g2)+  | i == j                                    = Right c+  | otherwise                                 = Left (S.union (toConflictSet g1) (toConflictSet g2), (c, d))+merge c@(Fixed (I v _) g1)   (Constrained rs) = go rs -- I tried "reverse rs" here, but it seems to slow things down ...+  where+    go []              = Right c+    go (d@(vr, g2) : vrs)+      | checkVR vr v   = go vrs+      | otherwise      = Left (S.union (toConflictSet g1) (toConflictSet g2), (c, Constrained [d]))+merge c@(Constrained _)    d@(Fixed _ _)      = merge d c+merge   (Constrained rs)     (Constrained ss) = Right (Constrained (rs ++ ss))+++type FlaggedDeps qpn = [FlaggedDep qpn]++-- | Flagged dependencies can either be plain dependency constraints,+-- or flag-dependent dependency trees.+data FlaggedDep qpn =+    Flagged (FN qpn) FDefault (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn)+  | Stanza  (SN qpn)          (TrueFlaggedDeps qpn)+  | Simple (Dep qpn)+  deriving (Eq, Show)++instance Functor FlaggedDep where+  fmap f (Flagged x y tt ff) = Flagged (fmap f x) y+                                       (fmap (fmap f) tt) (fmap (fmap f) ff)+  fmap f (Stanza x tt)       = Stanza (fmap f x) (fmap (fmap f) tt)+  fmap f (Simple d)          = Simple (fmap f d)++type TrueFlaggedDeps  qpn = FlaggedDeps qpn+type FalseFlaggedDeps qpn = FlaggedDeps qpn++-- | A dependency (constraint) associates a package name with a+-- constrained instance.+data Dep qpn = Dep qpn (CI qpn)+  deriving (Eq, Show)++showDep :: Dep QPN -> String+showDep (Dep qpn (Fixed i (Goal v _))          ) =+  (if P qpn /= v then showVar v ++ " => " else "") +++  showQPN qpn ++ "==" ++ showI i+showDep (Dep qpn (Constrained [(vr, Goal v _)])) =+  showVar v ++ " => " ++ showQPN qpn ++ showVR vr+showDep (Dep qpn ci                            ) =+  showQPN qpn ++ showCI ci++instance Functor Dep where+  fmap f (Dep x y) = Dep (f x) (fmap f y)++instance ResetGoal Dep where+  resetGoal g (Dep qpn ci) = Dep qpn (resetGoal g ci)++-- | A map containing reverse dependencies between qualified+-- package names.+type RevDepMap = Map QPN [QPN]++-- | Goals are solver variables paired with information about+-- why they have been introduced.+data Goal qpn = Goal (Var qpn) (GoalReasons qpn)+  deriving (Eq, Show)++instance Functor Goal where+  fmap f (Goal v gr) = Goal (fmap f v) (fmap (fmap f) gr)++class ResetGoal f where+  resetGoal :: Goal qpn -> f qpn -> f qpn++instance ResetGoal Goal where+  resetGoal = const++-- | For open goals as they occur during the build phase, we need to store+-- additional information about flags.+data OpenGoal = OpenGoal (FlaggedDep QPN) QGoalReasons+  deriving (Eq, Show)++-- | Reasons why a goal can be added to a goal set.+data GoalReason qpn =+    UserGoal+  | PDependency (PI qpn)+  | FDependency (FN qpn) Bool+  | SDependency (SN qpn)+  deriving (Eq, Show)++instance Functor GoalReason where+  fmap _ UserGoal           = UserGoal+  fmap f (PDependency pi)   = PDependency (fmap f pi)+  fmap f (FDependency fn b) = FDependency (fmap f fn) b+  fmap f (SDependency sn)   = SDependency (fmap f sn)++-- | The first element is the immediate reason. The rest are the reasons+-- for the reasons ...+type GoalReasons qpn = [GoalReason qpn]++type QGoalReasons = GoalReasons QPN++goalReasonToVars :: GoalReason qpn -> ConflictSet qpn+goalReasonToVars UserGoal                 = S.empty+goalReasonToVars (PDependency (PI qpn _)) = S.singleton (P qpn)+goalReasonToVars (FDependency qfn _)      = S.singleton (F qfn)+goalReasonToVars (SDependency qsn)        = S.singleton (S qsn)++goalReasonsToVars :: Ord qpn => GoalReasons qpn -> ConflictSet qpn+goalReasonsToVars = S.unions . L.map goalReasonToVars++-- | Closes a goal, i.e., removes all the extraneous information that we+-- need only during the build phase.+close :: OpenGoal -> Goal QPN+close (OpenGoal (Simple (Dep qpn _)) gr) = Goal (P qpn) gr+close (OpenGoal (Flagged qfn _ _ _ ) gr) = Goal (F qfn) gr+close (OpenGoal (Stanza  qsn _)      gr) = Goal (S qsn) gr++-- | Compute a conflic set from a goal. The conflict set contains the+-- closure of goal reasons as well as the variable of the goal itself.+toConflictSet :: Ord qpn => Goal qpn -> ConflictSet qpn+toConflictSet (Goal g gr) = S.insert g (goalReasonsToVars gr)
+ Distribution/Client/Dependency/Modular/Explore.hs view
@@ -0,0 +1,148 @@+module Distribution.Client.Dependency.Modular.Explore where++import Control.Applicative as A+import Data.Foldable+import Data.List as L+import Data.Map as M+import Data.Set as S++import Distribution.Client.Dependency.Modular.Assignment+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Log+import Distribution.Client.Dependency.Modular.Message+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree++-- | Backjumping.+--+-- A tree traversal that tries to propagate conflict sets+-- up the tree from the leaves, and thereby cut branches.+-- All the tricky things are done in the function 'combine'.+backjump :: Tree a -> Tree (Maybe (ConflictSet QPN))+backjump = snd . cata go+  where+    go (FailF c fr) = (Just c, Fail c fr)+    go (DoneF rdm ) = (Nothing, Done rdm)+    go (PChoiceF qpn _   ts) = (c, PChoice qpn c   (P.fromList ts'))+      where+        ~(c, ts') = combine (P qpn) (P.toList ts) S.empty+    go (FChoiceF qfn _ b ts) = (c, FChoice qfn c b (P.fromList ts'))+      where+        ~(c, ts') = combine (F qfn) (P.toList ts) S.empty+    go (SChoiceF qsn _ b ts) = (c, SChoice qsn c b (P.fromList ts'))+      where+        ~(c, ts') = combine (S qsn) (P.toList ts) S.empty+    go (GoalChoiceF      ts) = (c, GoalChoice      (P.fromList ts'))+      where+        ~(cs, ts') = unzip $ L.map (\ (k, (x, v)) -> (x, (k, v))) $ P.toList ts+        c          = case cs of []    -> Nothing+                                d : _ -> d++-- | The 'combine' function is at the heart of backjumping. It takes+-- the variable we're currently considering, and a list of children+-- annotated with their respective conflict sets, and an accumulator+-- for the result conflict set. It returns a combined conflict set+-- for the parent node, and a (potentially shortened) list of children+-- with the annotations removed.+--+-- It is *essential* that we produce the results as early as possible.+-- In particular, we have to produce the list of children prior to+-- traversing the entire list -- otherwise we lose the desired behaviour+-- of being able to traverse the tree from left to right incrementally.+--+-- We can shorten the list of children if we find an individual conflict+-- set that does not contain the current variable. In this case, we can+-- just lift the conflict set to the current level, because the current+-- level cannot possibly have contributed to this conflict, so no other+-- choice at the current level would avoid the conflict.+--+-- If any of the children might contain a successful solution+-- (indicated by Nothing), then Nothing will be the combined+-- conflict set. If all children contain conflict sets, we can+-- take the union as the combined conflict set.+combine :: Var QPN -> [(a, (Maybe (ConflictSet QPN), b))] ->+           ConflictSet QPN -> (Maybe (ConflictSet QPN), [(a, b)])+combine _   []                      c = (Just c, [])+combine var ((k, (     d, v)) : xs) c = (\ ~(e, ys) -> (e, (k, v) : ys)) $+                                        case d of+                                          Just e | not (var `S.member` e) -> (Just e, [])+                                                 | otherwise              -> combine var xs (e `S.union` c)+                                          Nothing                         -> (Nothing, snd $ combine var xs S.empty)++-- | Naive backtracking exploration of the search tree. This will yield correct+-- assignments only once the tree itself is validated.+explore :: Alternative m => Tree a -> (Assignment -> m (Assignment, RevDepMap))+explore = cata go+  where+    go (FailF _ _)           _           = A.empty+    go (DoneF rdm)           a           = pure (a, rdm)+    go (PChoiceF qpn _   ts) (A pa fa sa)   =+      asum $                                      -- try children in order,+      P.mapWithKey                                -- when descending ...+        (\ k r -> r (A (M.insert qpn k pa) fa sa)) $ -- record the pkg choice+      ts+    go (FChoiceF qfn _ _ ts) (A pa fa sa)   =+      asum $                                      -- try children in order,+      P.mapWithKey                                -- when descending ...+        (\ k r -> r (A pa (M.insert qfn k fa) sa)) $ -- record the flag choice+      ts+    go (SChoiceF qsn _ _ ts) (A pa fa sa)   =+      asum $                                      -- try children in order,+      P.mapWithKey                                -- when descending ...+        (\ k r -> r (A pa fa (M.insert qsn k sa))) $ -- record the flag choice+      ts+    go (GoalChoiceF      ts) a           =+      casePSQ ts A.empty                      -- empty goal choice is an internal error+        (\ _k v _xs -> v a)                   -- commit to the first goal choice++-- | Version of 'explore' that returns a 'Log'.+exploreLog :: Tree (Maybe (ConflictSet QPN)) -> (Assignment -> Log Message (Assignment, RevDepMap))+exploreLog = cata go+  where+    go (FailF c fr)          _           = failWith (Failure c fr)+    go (DoneF rdm)           a           = succeedWith Success (a, rdm)+    go (PChoiceF qpn c   ts) (A pa fa sa)   =+      backjumpInfo c $+      asum $                                      -- try children in order,+      P.mapWithKey                                -- when descending ...+        (\ k r -> tryWith (TryP (PI qpn k)) $     -- log and ...+                    r (A (M.insert qpn k pa) fa sa)) -- record the pkg choice+      ts+    go (FChoiceF qfn c _ ts) (A pa fa sa)   =+      backjumpInfo c $+      asum $                                      -- try children in order,+      P.mapWithKey                                -- when descending ...+        (\ k r -> tryWith (TryF qfn k) $          -- log and ...+                    r (A pa (M.insert qfn k fa) sa)) -- record the pkg choice+      ts+    go (SChoiceF qsn c _ ts) (A pa fa sa)   =+      backjumpInfo c $+      asum $                                      -- try children in order,+      P.mapWithKey                                -- when descending ...+        (\ k r -> tryWith (TryS qsn k) $          -- log and ...+                    r (A pa fa (M.insert qsn k sa))) -- record the pkg choice+      ts+    go (GoalChoiceF      ts) a           =+      casePSQ ts+        (failWith (Failure S.empty EmptyGoalChoice))   -- empty goal choice is an internal error+        (\ k v _xs -> continueWith (Next (close k)) (v a))     -- commit to the first goal choice++-- | Add in information about pruned trees.+--+-- TODO: This isn't quite optimal, because we do not merely report the shape of the+-- tree, but rather make assumptions about where that shape originated from. It'd be+-- better if the pruning itself would leave information that we could pick up at this+-- point.+backjumpInfo :: Maybe (ConflictSet QPN) -> Log Message a -> Log Message a+backjumpInfo c m = m <|> case c of -- important to produce 'm' before matching on 'c'!+                           Nothing -> A.empty+                           Just cs -> failWith (Failure cs Backjump)++-- | Interface.+exploreTree :: Alternative m => Tree a -> m (Assignment, RevDepMap)+exploreTree t = explore t (A M.empty M.empty M.empty)++-- | Interface.+exploreTreeLog :: Tree (Maybe (ConflictSet QPN)) -> Log Message (Assignment, RevDepMap)+exploreTreeLog t = exploreLog t (A M.empty M.empty M.empty)
+ Distribution/Client/Dependency/Modular/Flag.hs view
@@ -0,0 +1,69 @@+module Distribution.Client.Dependency.Modular.Flag where++import Data.Map as M+import Prelude hiding (pi)++import Distribution.PackageDescription hiding (Flag) -- from Cabal++import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Types (OptionalStanza(..))++-- | Flag name. Consists of a package instance and the flag identifier itself.+data FN qpn = FN (PI qpn) Flag+  deriving (Eq, Ord, Show)++-- | Extract the package name from a flag name.+getPN :: FN qpn -> qpn+getPN (FN (PI qpn _) _) = qpn++instance Functor FN where+  fmap f (FN x y) = FN (fmap f x) y++-- | Flag identifier. Just a string.+type Flag = FlagName++unFlag :: Flag -> String+unFlag (FlagName fn) = fn++-- | Flag default. Just a bool.+type FDefault = Bool++-- | Flag defaults.+type FlagDefaults = Map Flag FDefault++-- | Qualified flag name.+type QFN = FN QPN++-- | Stanza name. Paired with a package name, much like a flag.+data SN qpn = SN (PI qpn) OptionalStanza+  deriving (Eq, Ord, Show)++instance Functor SN where+  fmap f (SN x y) = SN (fmap f x) y++-- | Qualified stanza name.+type QSN = SN QPN++unStanza :: OptionalStanza -> String+unStanza TestStanzas  = "test"+unStanza BenchStanzas = "bench"++showQFNBool :: QFN -> Bool -> String+showQFNBool qfn@(FN pi _f) b = showPI pi ++ ":" ++ showFBool qfn b++showQSNBool :: QSN -> Bool -> String+showQSNBool qsn@(SN pi _f) b = showPI pi ++ ":" ++ showSBool qsn b++showFBool :: FN qpn -> Bool -> String+showFBool (FN _ f) True  = "+" ++ unFlag f+showFBool (FN _ f) False = "-" ++ unFlag f++showSBool :: SN qpn -> Bool -> String+showSBool (SN _ s) True  = "*" ++ unStanza s+showSBool (SN _ s) False = "!" ++ unStanza s++showQFN :: QFN -> String+showQFN (FN pi f) = showPI pi ++ ":" ++ unFlag f++showQSN :: QSN -> String+showQSN (SN pi f) = showPI pi ++ ":" ++ unStanza f
+ Distribution/Client/Dependency/Modular/Index.hs view
@@ -0,0 +1,33 @@+module Distribution.Client.Dependency.Modular.Index where++import Data.List as L+import Data.Map as M+import Prelude hiding (pi)++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree++-- | An index contains information about package instances. This is a nested+-- dictionary. Package names are mapped to instances, which in turn is mapped+-- to info.+type Index = Map PN (Map I PInfo)++-- | Info associated with a package instance.+-- Currently, dependencies, flags, encapsulations and failure reasons.+-- Packages that have a failure reason recorded for them are disabled+-- globally, for reasons external to the solver. We currently use this+-- for shadowing which essentially is a GHC limitation, and for+-- installed packages that are broken.+data PInfo = PInfo (FlaggedDeps PN) FlagDefaults Encaps (Maybe FailReason)+  deriving (Show)++-- | Encapsulations. A list of package names.+type Encaps = [PN]++mkIndex :: [(PN, I, PInfo)] -> Index+mkIndex xs = M.map M.fromList (groupMap (L.map (\ (pn, i, pi) -> (pn, (i, pi))) xs))++groupMap :: Ord a => [(a, b)] -> Map a [b]+groupMap xs = M.fromListWith (flip (++)) (L.map (\ (x, y) -> (x, [y])) xs)
+ Distribution/Client/Dependency/Modular/IndexConversion.hs view
@@ -0,0 +1,185 @@+module Distribution.Client.Dependency.Modular.IndexConversion where++import Data.List as L+import Data.Map as M+import Data.Maybe+import Prelude hiding (pi)++import qualified Distribution.Client.PackageIndex as CI+import Distribution.Client.Types+import Distribution.Compiler+import Distribution.InstalledPackageInfo as IPI+import Distribution.Package                          -- from Cabal+import Distribution.PackageDescription as PD         -- from Cabal+import qualified Distribution.Simple.PackageIndex as SI+import Distribution.Simple.Utils (equating)+import Distribution.System++import Distribution.Client.Dependency.Modular.Dependency as D+import Distribution.Client.Dependency.Modular.Flag as F+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree+import Distribution.Client.Dependency.Modular.Version++-- | Convert both the installed package index and the source package+-- index into one uniform solver index.+--+-- We use 'allPackagesByName' for the installed package index because+-- that returns us several instances of the same package and version+-- in order of preference. This allows us in principle to "shadow"+-- packages if there are several installed packages of the same version.+-- There are currently some shortcomings in both GHC and Cabal in+-- resolving these situations. However, the right thing to do is to+-- fix the problem there, so for now, shadowing is only activated if+-- explicitly requested.+convPIs :: OS -> Arch -> CompilerId -> Bool ->+           SI.PackageIndex -> CI.PackageIndex SourcePackage -> Index+convPIs os arch cid sip iidx sidx =+  mkIndex (convIPI' sip iidx ++ convSPI' os arch cid sidx)++-- | Convert a Cabal installed package index to the simpler,+-- more uniform index format of the solver.+convIPI' :: Bool -> SI.PackageIndex -> [(PN, I, PInfo)]+convIPI' sip idx = combine (convIP idx) . versioned . SI.allPackagesByName $ idx+  where+    -- group installed packages by version+    versioned = L.map (groupBy (equating packageVersion))+    -- apply shadowing whenever there are multple installed packages with+    -- the same version+    combine f pkgs = [ g (f p) | pbn <- pkgs, pbv <- pbn,+                                 (g, p) <- zip (id : repeat shadow) pbv ]+    -- shadowing is recorded in the package info+    shadow (pn, i, PInfo fdeps fds encs _) | sip = (pn, i, PInfo fdeps fds encs (Just Shadowed))+    shadow x                                     = x++convIPI :: Bool -> SI.PackageIndex -> Index+convIPI sip = mkIndex . convIPI' sip++-- | Convert a single installed package into the solver-specific format.+convIP :: SI.PackageIndex -> InstalledPackageInfo -> (PN, I, PInfo)+convIP idx ipi =+  let ipid = installedPackageId ipi+      i = I (pkgVersion (sourcePackageId ipi)) (Inst ipid)+      pn = pkgName (sourcePackageId ipi)+  in  case mapM (convIPId pn idx) (IPI.depends ipi) of+        Nothing  -> (pn, i, PInfo [] M.empty [] (Just Broken))+        Just fds -> (pn, i, PInfo fds M.empty [] Nothing)+-- TODO: Installed packages should also store their encapsulations!++-- | Convert dependencies specified by an installed package id into+-- flagged dependencies of the solver.+--+-- May return Nothing if the package can't be found in the index. That+-- indicates that the original package having this dependency is broken+-- and should be ignored.+convIPId :: PN -> SI.PackageIndex -> InstalledPackageId -> Maybe (FlaggedDep PN)+convIPId pn' idx ipid =+  case SI.lookupInstalledPackageId idx ipid of+    Nothing  -> Nothing+    Just ipi -> let i = I (pkgVersion (sourcePackageId ipi)) (Inst ipid)+                    pn = pkgName (sourcePackageId ipi)+                in  Just (D.Simple (Dep pn (Fixed i (Goal (P pn') []))))++-- | Convert a cabal-install source package index to the simpler,+-- more uniform index format of the solver.+convSPI' :: OS -> Arch -> CompilerId ->+            CI.PackageIndex SourcePackage -> [(PN, I, PInfo)]+convSPI' os arch cid = L.map (convSP os arch cid) . CI.allPackages++convSPI :: OS -> Arch -> CompilerId ->+           CI.PackageIndex SourcePackage -> Index+convSPI os arch cid = mkIndex . convSPI' os arch cid++-- | Convert a single source package into the solver-specific format.+convSP :: OS -> Arch -> CompilerId -> SourcePackage -> (PN, I, PInfo)+convSP os arch cid (SourcePackage (PackageIdentifier pn pv) gpd _pl) =+  let i = I pv InRepo+  in  (pn, i, convGPD os arch cid (PI pn i) gpd)++-- We do not use 'flattenPackageDescription' or 'finalizePackageDescription'+-- from 'Distribution.PackageDescription.Configuration' here, because we+-- want to keep the condition tree, but simplify much of the test.++-- | Convert a generic package description to a solver-specific 'PInfo'.+--+-- TODO: We currently just take all dependencies from all specified library,+-- executable and test components. This does not quite seem fair.+convGPD :: OS -> Arch -> CompilerId ->+           PI PN -> GenericPackageDescription -> PInfo+convGPD os arch cid pi+        (GenericPackageDescription _ flags libs exes tests benchs) =+  let+    fds = flagDefaults flags+  in+    PInfo+      (maybe []    (convCondTree os arch cid pi fds (const True)          ) libs    +++       concatMap   (convCondTree os arch cid pi fds (const True)     . snd) exes    +++      (prefix (Stanza (SN pi TestStanzas))+        (concatMap (convCondTree os arch cid pi fds (const True)     . snd) tests)) +++      (prefix (Stanza (SN pi BenchStanzas))+        (concatMap (convCondTree os arch cid pi fds (const True)     . snd) benchs)))+      fds+      [] -- TODO: add encaps+      Nothing++prefix :: (FlaggedDeps qpn -> FlaggedDep qpn) -> FlaggedDeps qpn -> FlaggedDeps qpn+prefix _ []  = []+prefix f fds = [f fds]++-- | Convert flag information.+flagDefaults :: [PD.Flag] -> FlagDefaults+flagDefaults = M.fromList . L.map (\ (MkFlag fn _ b _) -> (fn, b))++-- | Convert condition trees to flagged dependencies.+convCondTree :: OS -> Arch -> CompilerId -> PI PN -> FlagDefaults ->+                (a -> Bool) -> -- how to detect if a branch is active+                CondTree ConfVar [Dependency] a -> FlaggedDeps PN+convCondTree os arch cid pi@(PI pn _) fds p (CondNode info ds branches)+  | p info    = L.map (D.Simple . convDep pn) ds  -- unconditional dependencies+              ++ concatMap (convBranch os arch cid pi fds p) branches+  | otherwise = []++-- | Branch interpreter.+--+-- Here, we try to simplify one of Cabal's condition tree branches into the+-- solver's flagged dependency format, which is weaker. Condition trees can+-- contain complex logical expression composed from flag choices and special+-- flags (such as architecture, or compiler flavour). We try to evaluate the+-- special flags and subsequently simplify to a tree that only depends on+-- simple flag choices.+convBranch :: OS -> Arch -> CompilerId ->+              PI PN -> FlagDefaults ->+              (a -> Bool) -> -- how to detect if a branch is active+              (Condition ConfVar,+               CondTree ConfVar [Dependency] a,+               Maybe (CondTree ConfVar [Dependency] a)) -> FlaggedDeps PN+convBranch os arch cid@(CompilerId cf cv) pi fds p (c', t', mf') =+  go c' (          convCondTree os arch cid pi fds p   t')+        (maybe [] (convCondTree os arch cid pi fds p) mf')+  where+    go :: Condition ConfVar ->+          FlaggedDeps PN -> FlaggedDeps PN -> FlaggedDeps PN+    go (Lit True)  t _ = t+    go (Lit False) _ f = f+    go (CNot c)    t f = go c f t+    go (CAnd c d)  t f = go c (go d t f) f+    go (COr  c d)  t f = go c t (go d t f)+    go (Var (Flag fn)) t f = [Flagged (FN pi fn) (fds ! fn) t f]+    go (Var (OS os')) t f+      | os == os'      = t+      | otherwise      = f+    go (Var (Arch arch')) t f+      | arch == arch'  = t+      | otherwise      = f+    go (Var (Impl cf' cvr')) t f+      | cf == cf' && checkVR cvr' cv = t+      | otherwise      = f++-- | Convert a Cabal dependency to a solver-specific dependency.+convDep :: PN -> Dependency -> Dep PN+convDep pn' (Dependency pn vr) = Dep pn (Constrained [(vr, Goal (P pn') [])])++-- | Convert a Cabal package identifier to a solver-specific dependency.+convPI :: PN -> PackageIdentifier -> Dep PN+convPI pn' (PackageIdentifier pn v) = Dep pn (Constrained [(eqVR v, Goal (P pn') [])])
+ Distribution/Client/Dependency/Modular/Log.hs view
@@ -0,0 +1,96 @@+module Distribution.Client.Dependency.Modular.Log where++import Control.Applicative+import Data.List as L+import Data.Set as S++import Distribution.Client.Dependency.Types -- from Cabal++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Message+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree (FailReason(..))++-- | The 'Log' datatype.+--+-- Represents the progress of a computation lazily.+--+-- Parameterized over the type of actual messages and the final result.+type Log m a = Progress m () a++runLog :: Log m a -> ([m], Maybe a)+runLog (Done x)       = ([], Just x)+runLog (Fail _)       = ([], Nothing)+runLog (Step m p)     = let+                          (ms, r) = runLog p+                        in+                          (m : ms, r)++-- | Postprocesses a log file. Takes as an argument a limit on allowed backjumps.+-- If the limit is 'Nothing', then infinitely many backjumps are allowed. If the+-- limit is 'Just 0', backtracking is completely disabled.+logToProgress :: Maybe Int -> Log Message a -> Progress String String a+logToProgress mbj l = let+                        (ms, s) = runLog l+                        (es, e) = proc 0 ms -- catch first error (always)+                        (ns, t) = case mbj of+                                    Nothing -> (ms, Nothing)+                                    Just n  -> proc n ms+                        -- prefer first error over later error+                        r       = case t of+                                    Nothing -> case s of+                                                 Nothing -> e+                                                 Just _  -> Nothing+                                    Just _  -> e+                      in go es -- trace for first error+                            (showMessages (const True) True ns) -- shortened run+                            r s+  where+    proc :: Int -> [Message] -> ([Message], Maybe (ConflictSet QPN))+    proc _ []                             = ([], Nothing)+    proc n (   Failure cs Backjump  : xs@(Leave : Failure cs' Backjump : _))+      | cs == cs'                         = proc n xs -- repeated backjumps count as one+    proc 0 (   Failure cs Backjump  : _ ) = ([], Just cs)+    proc n (x@(Failure _  Backjump) : xs) = (\ ~(ys, r) -> (x : ys, r)) (proc (n - 1) xs)+    proc n (x                       : xs) = (\ ~(ys, r) -> (x : ys, r)) (proc  n      xs)++    -- The order of arguments is important! In particular 's' must not be evaluated+    -- unless absolutely necessary. It contains the final result, and if we shortcut+    -- with an error due to backjumping, evaluating 's' would still require traversing+    -- the entire tree.+    go ms (x : xs) r         s        = Step x (go ms xs r s)+    go ms []       (Just cs) _        = Fail ("Could not resolve dependencies:\n" +++                                        unlines (showMessages (L.foldr (\ v _ -> v `S.member` cs) True) False ms))+    go _  []       _         (Just s) = Done s+    go _  []       _         _        = Fail ("Could not resolve dependencies.") -- should not happen++logToProgress' :: Log Message a -> Progress String String a+logToProgress' l = let+                    (ms, r) = runLog l+                    xs      = showMessages (const True) True ms+                  in go xs r+  where+    go [x]    Nothing  = Fail x+    go []     Nothing  = Fail ""+    go []     (Just r) = Done r+    go (x:xs) r        = Step x (go xs r)+++runLogIO :: Log Message a -> IO (Maybe a)+runLogIO x =+  do+    let (ms, r) = runLog x+    putStr (unlines $ showMessages (const True) True ms)+    return r++failWith :: m -> Log m a+failWith m = Step m (Fail ())++succeedWith :: m -> a -> Log m a+succeedWith m x = Step m (Done x)++continueWith :: m -> Log m a -> Log m a+continueWith = Step++tryWith :: Message -> Log Message a -> Log Message a+tryWith m x = Step m (Step Enter x) <|> failWith Leave
+ Distribution/Client/Dependency/Modular/Message.hs view
@@ -0,0 +1,97 @@+module Distribution.Client.Dependency.Modular.Message where++import qualified Data.List as L+import Prelude hiding (pi)++import Distribution.Text -- from Cabal++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree++data Message =+    Enter           -- ^ increase indentation level+  | Leave           -- ^ decrease indentation level+  | TryP (PI QPN)+  | TryF QFN Bool+  | TryS QSN Bool+  | Next (Goal QPN)+  | Success+  | Failure (ConflictSet QPN) FailReason++-- | Transforms the structured message type to actual messages (strings).+--+-- Takes an additional relevance predicate. The predicate gets a stack of goal+-- variables and can decide whether messages regarding these goals are relevant.+-- You can plug in 'const True' if you're interested in a full trace. If you+-- want a slice of the trace concerning a particular conflict set, then plug in+-- a predicate returning 'True' on the empty stack and if the head is in the+-- conflict set.+--+-- The second argument indicates if the level numbers should be shown. This is+-- recommended for any trace that involves backtracking, because only the level+-- numbers will allow to keep track of backjumps.+showMessages :: ([Var QPN] -> Bool) -> Bool -> [Message] -> [String]+showMessages p sl = go [] 0+  where+    go :: [Var QPN] -> Int -> [Message] -> [String]+    go _ _ []                            = []+    -- complex patterns+    go v l (TryP (PI qpn i) : Enter : Failure c fr : Leave : ms) = goPReject v l qpn [i] c fr ms+    go v l (TryF qfn b : Enter : Failure c fr : Leave : ms) = (atLevel (F qfn : v) l $ "rejecting: " ++ showQFNBool qfn b ++ showFR c fr) (go v l ms)+    go v l (TryS qsn b : Enter : Failure c fr : Leave : ms) = (atLevel (S qsn : v) l $ "rejecting: " ++ showQSNBool qsn b ++ showFR c fr) (go v l ms)+    go v l (Next (Goal (P qpn) gr) : TryP pi : ms@(Enter : Next _ : _)) = (atLevel (P qpn : v) l $ "trying: " ++ showPI pi ++ showGRs gr) (go (P qpn : v) l ms)+    go v l (Failure c Backjump : ms@(Leave : Failure c' Backjump : _)) | c == c' = go v l ms+    -- standard display+    go v l (Enter                  : ms) = go v          (l+1) ms+    go v l (Leave                  : ms) = go (drop 1 v) (l-1) ms+    go v l (TryP pi@(PI qpn _)     : ms) = (atLevel (P qpn : v) l $ "trying: " ++ showPI pi) (go (P qpn : v) l ms)+    go v l (TryF qfn b             : ms) = (atLevel (F qfn : v) l $ "trying: " ++ showQFNBool qfn b) (go (F qfn : v) l ms)+    go v l (TryS qsn b             : ms) = (atLevel (S qsn : v) l $ "trying: " ++ showQSNBool qsn b) (go (S qsn : v) l ms)+    go v l (Next (Goal (P qpn) gr) : ms) = (atLevel (P qpn : v) l $ "next goal: " ++ showQPN qpn ++ showGRs gr) (go v l ms)+    go v l (Next _                 : ms) = go v l ms -- ignore flag goals in the log+    go v l (Success                : ms) = (atLevel v l $ "done") (go v l ms)+    go v l (Failure c fr           : ms) = (atLevel v l $ "fail" ++ showFR c fr) (go v l ms)++    -- special handler for many subsequent package rejections+    goPReject :: [Var QPN] -> Int -> QPN -> [I] -> ConflictSet QPN -> FailReason -> [Message] -> [String]+    goPReject v l qpn is c fr (TryP (PI qpn' i) : Enter : Failure _ fr' : Leave : ms) | qpn == qpn' && fr == fr' = goPReject v l qpn (i : is) c fr ms+    goPReject v l qpn is c fr ms = (atLevel (P qpn : v) l $ "rejecting: " ++ showQPN qpn ++ "-" ++ L.intercalate ", " (map showI (reverse is)) ++ showFR c fr) (go v l ms)++    -- write a message, but only if it's relevant; we can also enable or disable the display of the current level+    atLevel v l x xs+      | sl && p v = let s = show l+                    in  ("[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ x) : xs+      | p v       = x : xs+      | otherwise = xs++showGRs :: QGoalReasons -> String+showGRs (gr : _) = showGR gr+showGRs []       = ""++showGR :: GoalReason QPN -> String+showGR UserGoal            = " (user goal)"+showGR (PDependency pi)    = " (dependency of " ++ showPI pi            ++ ")"+showGR (FDependency qfn b) = " (dependency of " ++ showQFNBool qfn b    ++ ")"+showGR (SDependency qsn)   = " (dependency of " ++ showQSNBool qsn True ++ ")"++showFR :: ConflictSet QPN -> FailReason -> String+showFR _ InconsistentInitialConstraints = " (inconsistent initial constraints)"+showFR _ (Conflicting ds)               = " (conflict: " ++ L.intercalate ", " (map showDep ds) ++ ")"+showFR _ CannotInstall                  = " (only already installed instances can be used)"+showFR _ CannotReinstall                = " (avoiding to reinstall a package with same version but new dependencies)"+showFR _ Shadowed                       = " (shadowed by another installed package with same version)"+showFR _ Broken                         = " (package is broken)"+showFR _ (GlobalConstraintVersion vr)   = " (global constraint requires " ++ display vr ++ ")"+showFR _ GlobalConstraintInstalled      = " (global constraint requires installed instance)"+showFR _ GlobalConstraintSource         = " (global constraint requires source instance)"+showFR _ GlobalConstraintFlag           = " (global constraint requires opposite flag selection)"+showFR _ (BuildFailureNotInIndex pn)    = " (unknown package: " ++ display pn ++ ")"+showFR c Backjump                       = " (backjumping, conflict set: " ++ showCS c ++ ")"+-- The following are internal failures. They should not occur. In the+-- interest of not crashing unnecessarily, we still just print an error+-- message though.+showFR _ (MalformedFlagChoice qfn)      = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")"+showFR _ (MalformedStanzaChoice qsn)    = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")"+showFR _ EmptyGoalChoice                = " (INTERNAL ERROR: EMPTY GOAL CHOICE)"
+ Distribution/Client/Dependency/Modular/PSQ.hs view
@@ -0,0 +1,102 @@+module Distribution.Client.Dependency.Modular.PSQ where++-- Priority search queues.+--+-- I am not yet sure what exactly is needed. But we need a datastructure with+-- key-based lookup that can be sorted. We're using a sequence right now with+-- (inefficiently implemented) lookup, because I think that queue-based+-- opertions and sorting turn out to be more efficiency-critical in practice.++import Control.Applicative+import Data.Foldable+import Data.Function+import Data.List as S hiding (foldr)+import Data.Traversable+import Prelude hiding (foldr)++newtype PSQ k v = PSQ [(k, v)]+  deriving (Eq, Show)++instance Functor (PSQ k) where+  fmap f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f v)) xs)++instance Foldable (PSQ k) where+  foldr op e (PSQ xs) = foldr op e (fmap snd xs)++instance Traversable (PSQ k) where+  traverse f (PSQ xs) = PSQ <$> traverse (\ (k, v) -> (\ x -> (k, x)) <$> f v) xs++keys :: PSQ k v -> [k]+keys (PSQ xs) = fmap fst xs++lookup :: Eq k => k -> PSQ k v -> Maybe v+lookup k (PSQ xs) = S.lookup k xs++map :: (v1 -> v2) -> PSQ k v1 -> PSQ k v2+map f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f v)) xs)++mapKeys :: (k1 -> k2) -> PSQ k1 v -> PSQ k2 v+mapKeys f (PSQ xs) = PSQ (fmap (\ (k, v) -> (f k, v)) xs)++mapWithKey :: (k -> a -> b) -> PSQ k a -> PSQ k b+mapWithKey f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f k v)) xs)++mapWithKeyState :: (s -> k -> a -> (b, s)) -> PSQ k a -> s -> PSQ k b+mapWithKeyState p (PSQ xs) s0 =+  PSQ (foldr (\ (k, v) r s -> case p s k v of+                                (w, n) -> (k, w) : (r n))+             (const []) xs s0)++delete :: Eq k => k -> PSQ k a -> PSQ k a+delete k (PSQ xs) = PSQ (snd (partition ((== k) . fst) xs))++fromList :: [(k, a)] -> PSQ k a+fromList = PSQ++cons :: k -> a -> PSQ k a -> PSQ k a+cons k x (PSQ xs) = PSQ ((k, x) : xs)++snoc :: PSQ k a -> k -> a -> PSQ k a+snoc (PSQ xs) k x = PSQ (xs ++ [(k, x)])++casePSQ :: PSQ k a -> r -> (k -> a -> PSQ k a -> r) -> r+casePSQ (PSQ xs) n c =+  case xs of+    []          -> n+    (k, v) : ys -> c k v (PSQ ys)++splits :: PSQ k a -> PSQ k (a, PSQ k a)+splits xs =+  casePSQ xs+    (PSQ [])+    (\ k v ys -> cons k (v, ys) (fmap (\ (w, zs) -> (w, cons k v zs)) (splits ys)))++sortBy :: (a -> a -> Ordering) -> PSQ k a -> PSQ k a+sortBy cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` snd) xs)++sortByKeys :: (k -> k -> Ordering) -> PSQ k a -> PSQ k a+sortByKeys cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` fst) xs)++filterKeys :: (k -> Bool) -> PSQ k a -> PSQ k a+filterKeys p (PSQ xs) = PSQ (S.filter (p . fst) xs)++filter :: (a -> Bool) -> PSQ k a -> PSQ k a+filter p (PSQ xs) = PSQ (S.filter (p . snd) xs)++length :: PSQ k a -> Int+length (PSQ xs) = S.length xs++-- | "Lazy length".+--+-- Only approximates the length, but doesn't force the list.+llength :: PSQ k a -> Int+llength (PSQ [])       = 0+llength (PSQ (_:[]))   = 1+llength (PSQ (_:_:[])) = 2+llength (PSQ _)        = 3++null :: PSQ k a -> Bool+null (PSQ xs) = S.null xs++toList :: PSQ k a -> [(k, a)]+toList (PSQ xs) = xs
+ Distribution/Client/Dependency/Modular/Package.hs view
@@ -0,0 +1,113 @@+module Distribution.Client.Dependency.Modular.Package+  (module Distribution.Client.Dependency.Modular.Package,+   module Distribution.Package) where++import Data.List as L+import Data.Map as M++import Distribution.Package -- from Cabal+import Distribution.Text    -- from Cabal++import Distribution.Client.Dependency.Modular.Version++-- | A package name.+type PN = PackageName++-- | Unpacking a package name.+unPN :: PN -> String+unPN (PackageName pn) = pn++-- | Package version. A package name plus a version number.+type PV = PackageId++-- | Qualified package version.+type QPV = Q PV++-- | Package id. Currently just a black-box string.+type PId = InstalledPackageId++-- | Location. Info about whether a package is installed or not, and where+-- exactly it is located. For installed packages, uniquely identifies the+-- package instance via its 'PId'.+--+-- TODO: More information is needed about the repo.+data Loc = Inst PId | InRepo+  deriving (Eq, Ord, Show)++-- | Instance. A version number and a location.+data I = I Ver Loc+  deriving (Eq, Ord, Show)++-- | String representation of an instance.+showI :: I -> String+showI (I v InRepo)   = showVer v+showI (I v (Inst (InstalledPackageId i))) = showVer v ++ "/installed" ++ shortId i+  where+    -- A hack to extract the beginning of the package ABI hash+    shortId = snip (splitAt 4) (++ "...") .+              snip ((\ (x, y) -> (reverse x, y)) . break (=='-') . reverse) ('-':)+    snip p f xs = case p xs of+                    (ys, zs) -> (if L.null zs then id else f) ys++-- | Package instance. A package name and an instance.+data PI qpn = PI qpn I+  deriving (Eq, Ord, Show)++-- | String representation of a package instance.+showPI :: PI QPN -> String+showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI i++-- | Checks if a package instance corresponds to an installed package.+instPI :: PI qpn -> Bool+instPI (PI _ (I _ (Inst _))) = True+instPI _                     = False++instI :: I -> Bool+instI (I _ (Inst _)) = True+instI _              = False++instance Functor PI where+  fmap f (PI x y) = PI (f x) y++-- | Package path. (Stored in "reverse" order.)+type PP = [PN]++-- | String representation of a package path.+showPP :: PP -> String+showPP = intercalate "." . L.map display . reverse+++-- | A qualified entity. Pairs a package path with the entity.+data Q a = Q PP a+  deriving (Eq, Ord, Show)++-- | Standard string representation of a qualified entity.+showQ :: (a -> String) -> (Q a -> String)+showQ showa (Q [] x) = showa x+showQ showa (Q pp x) = showPP pp ++ "." ++ showa x++-- | Qualified package name.+type QPN = Q PN++-- | String representation of a qualified package path.+showQPN :: QPN -> String+showQPN = showQ display++-- | The scope associates every package with a path. The convention is that packages+-- not in the data structure have an empty path associated with them.+type Scope = Map PN PP++-- | An empty scope structure, for initialization.+emptyScope :: Scope+emptyScope = M.empty++-- | Create artificial parents for each of the package names, making+-- them all independent.+makeIndependent :: [PN] -> Scope+makeIndependent ps = L.foldl (\ sc (n, p) -> M.insert p [PackageName (show n)] sc) emptyScope (zip ([0..] :: [Int]) ps)++qualify :: Scope -> PN -> QPN+qualify sc pn = Q (findWithDefault [] pn sc) pn++unQualify :: Q a -> a+unQualify (Q _ x) = x
+ Distribution/Client/Dependency/Modular/Preference.hs view
@@ -0,0 +1,258 @@+module Distribution.Client.Dependency.Modular.Preference where++-- Reordering or pruning the tree in order to prefer or make certain choices.++import qualified Data.List as L+import qualified Data.Map as M+import Data.Monoid+import Data.Ord++import Distribution.Client.Dependency.Types+  ( PackageConstraint(..), PackagePreferences(..), InstalledPreference(..) )+import Distribution.Client.Types+  ( OptionalStanza(..) )++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree+import Distribution.Client.Dependency.Modular.Version++-- | Generic abstraction for strategies that just rearrange the package order.+-- Only packages that match the given predicate are reordered.+packageOrderFor :: (PN -> Bool) -> (PN -> I -> I -> Ordering) -> Tree a -> Tree a+packageOrderFor p cmp = trav go+  where+    go (PChoiceF v@(Q _ pn) r cs)+      | p pn                        = PChoiceF v r (P.sortByKeys (flip (cmp pn)) cs)+      | otherwise                   = PChoiceF v r                               cs+    go x                            = x++-- | 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)++-- | Traversal that tries to establish package preferences (not constraints).+-- Works by reordering choice nodes.+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+          locationsOrdering ipref i1 i2++    -- Note that we always rank installed before uninstalled, and later+    -- versions before earlier, but we can change the priority of the+    -- two orderings.+    locationsOrdering PreferInstalled v1 v2 =+      preferInstalledOrdering v1 v2 `mappend` preferLatestOrdering v1 v2+    locationsOrdering PreferLatest v1 v2 =+      preferLatestOrdering v1 v2 `mappend` preferInstalledOrdering v1 v2++-- | Ordering that treats installed instances as greater than uninstalled ones.+preferInstalledOrdering :: I -> I -> Ordering+preferInstalledOrdering (I _ (Inst _)) (I _ (Inst _)) = EQ+preferInstalledOrdering (I _ (Inst _)) _              = GT+preferInstalledOrdering _              (I _ (Inst _)) = LT+preferInstalledOrdering _              _              = EQ++-- | Compare instances by their version numbers.+preferLatestOrdering :: I -> I -> Ordering+preferLatestOrdering (I v1 _) (I v2 _) = compare v1 v2++-- | Helper function that tries to enforce a single package constraint on a+-- given instance for a P-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintP :: ConflictSet QPN -> I -> PackageConstraint -> Tree a -> Tree a+processPackageConstraintP c (I v _) (PackageConstraintVersion _ vr) r+  | checkVR vr v  = r+  | otherwise     = Fail c (GlobalConstraintVersion vr)+processPackageConstraintP c i       (PackageConstraintInstalled _)  r+  | instI i       = r+  | otherwise     = Fail c GlobalConstraintInstalled+processPackageConstraintP c i       (PackageConstraintSource    _)  r+  | not (instI i) = r+  | otherwise     = Fail c GlobalConstraintSource+processPackageConstraintP _ _       _                               r = r++-- | Helper function that tries to enforce a single package constraint on a+-- given flag setting for an F-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintF :: Flag -> ConflictSet QPN -> Bool -> PackageConstraint -> Tree a -> Tree a+processPackageConstraintF f c b' (PackageConstraintFlags _ fa) r =+  case L.lookup f fa of+    Nothing            -> r+    Just b | b == b'   -> r+           | otherwise -> Fail c GlobalConstraintFlag+processPackageConstraintF _ _ _  _                             r = r++-- | Helper function that tries to enforce a single package constraint on a+-- given flag setting for an F-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintS :: OptionalStanza -> ConflictSet QPN -> Bool -> PackageConstraint -> Tree a -> Tree a+processPackageConstraintS s c b' (PackageConstraintStanzas _ ss) r =+  if not b' && s `elem` ss then Fail c GlobalConstraintFlag+                           else r+processPackageConstraintS _ _ _  _                             r = r++-- | Traversal that tries to establish various kinds of user constraints. Works+-- by selectively disabling choices that have been ruled out by global user+-- constraints.+enforcePackageConstraints :: M.Map PN [PackageConstraint] -> Tree QGoalReasons -> Tree QGoalReasons+enforcePackageConstraints pcs = trav go+  where+    go (PChoiceF qpn@(Q _ pn)               gr    ts) =+      let c = toConflictSet (Goal (P qpn) gr)+          -- compose the transformation functions for each of the relevant constraint+          g = \ i -> foldl (\ h pc -> h . processPackageConstraintP   c i pc) id+                           (M.findWithDefault [] pn pcs)+      in PChoiceF qpn gr    (P.mapWithKey g ts)+    go (FChoiceF qfn@(FN (PI (Q _ pn) _) f) gr tr ts) =+      let c = toConflictSet (Goal (F qfn) gr)+          -- compose the transformation functions for each of the relevant constraint+          g = \ b -> foldl (\ h pc -> h . processPackageConstraintF f c b pc) id+                           (M.findWithDefault [] pn pcs)+      in FChoiceF qfn gr tr (P.mapWithKey g ts)+    go (SChoiceF qsn@(SN (PI (Q _ pn) _) f) gr tr ts) =+      let c = toConflictSet (Goal (S qsn) gr)+          -- compose the transformation functions for each of the relevant constraint+          g = \ b -> foldl (\ h pc -> h . processPackageConstraintS f c b pc) id+                           (M.findWithDefault [] pn pcs)+      in SChoiceF qsn gr tr (P.mapWithKey g ts)+    go x = x++-- | Prefer installed packages over non-installed packages, generally.+-- All installed packages or non-installed packages are treated as+-- equivalent.+preferInstalled :: Tree a -> Tree a+preferInstalled = packageOrderFor (const True) (const preferInstalledOrdering)++-- | Prefer packages with higher version numbers over packages with+-- lower version numbers, for certain packages.+preferLatestFor :: (PN -> Bool) -> Tree a -> Tree a+preferLatestFor p = packageOrderFor p (const preferLatestOrdering)++-- | Prefer packages with higher version numbers over packages with+-- lower version numbers, for all packages.+preferLatest :: Tree a -> Tree a+preferLatest = preferLatestFor (const True)++-- | Require installed packages.+requireInstalled :: (PN -> Bool) -> Tree (QGoalReasons, a) -> Tree (QGoalReasons, a)+requireInstalled p = trav go+  where+    go (PChoiceF v@(Q _ pn) i@(gr, _) cs)+      | p pn      = PChoiceF v i (P.mapWithKey installed cs)+      | otherwise = PChoiceF v i                         cs+      where+        installed (I _ (Inst _)) x = x+        installed _              _ = Fail (toConflictSet (Goal (P v) gr)) CannotInstall+    go x          = x++-- | Avoid reinstalls.+--+-- This is a tricky strategy. If a package version is installed already and the+-- same version is available from a repo, the repo version will never be chosen.+-- This would result in a reinstall (either destructively, or potentially,+-- shadowing). The old instance won't be visible or even present anymore, but+-- other packages might have depended on it.+--+-- TODO: It would be better to actually check the reverse dependencies of installed+-- packages. If they're not depended on, then reinstalling should be fine. Even if+-- they are, perhaps this should just result in trying to reinstall those other+-- packages as well. However, doing this all neatly in one pass would require to+-- change the builder, or at least to change the goal set after building.+avoidReinstalls :: (PN -> Bool) -> Tree (QGoalReasons, a) -> Tree (QGoalReasons, a)+avoidReinstalls p = trav go+  where+    go (PChoiceF qpn@(Q _ pn) i@(gr, _) cs)+      | p pn      = PChoiceF qpn i disableReinstalls+      | otherwise = PChoiceF qpn i cs+      where+        disableReinstalls =+          let installed = [ v | (I v (Inst _), _) <- toList cs ]+          in  P.mapWithKey (notReinstall installed) cs++        notReinstall vs (I v InRepo) _+          | v `elem` vs                = Fail (toConflictSet (Goal (P qpn) gr)) CannotReinstall+        notReinstall _  _            x = x+    go x          = x++-- | Always choose the first goal in the list next, abandoning all+-- other choices.+--+-- This is unnecessary for the default search strategy, because+-- it descends only into the first goal choice anyway,+-- but may still make sense to just reduce the tree size a bit.+firstGoal :: Tree a -> Tree a+firstGoal = trav go+  where+    go (GoalChoiceF xs) = casePSQ xs (GoalChoiceF xs) (\ _ t _ -> out t)+    go x                = x+    -- Note that we keep empty choice nodes, because they mean success.++-- | Transformation that tries to make a decision on base as early as+-- possible. In nearly all cases, there's a single choice for the base+-- package. Also, fixing base early should lead to better error messages.+preferBaseGoalChoice :: Tree a -> Tree a+preferBaseGoalChoice = trav go+  where+    go (GoalChoiceF xs) = GoalChoiceF (P.sortByKeys preferBase xs)+    go x                = x++    preferBase :: OpenGoal -> OpenGoal -> Ordering+    preferBase (OpenGoal (Simple (Dep (Q [] pn) _)) _) _ | unPN pn == "base" = LT+    preferBase _ (OpenGoal (Simple (Dep (Q [] pn) _)) _) | unPN pn == "base" = GT+    preferBase _ _                                                           = EQ++-- | Transformation that sorts choice nodes so that+-- child nodes with a small branching degree are preferred. As a+-- special case, choices with 0 branches will be preferred (as they+-- are immediately considered inconsistent), and choices with 1+-- branch will also be preferred (as they don't involve choice).+preferEasyGoalChoices :: Tree a -> Tree a+preferEasyGoalChoices = trav go+  where+    go (GoalChoiceF xs) = GoalChoiceF (P.sortBy (comparing choices) xs)+    go x                = x++-- | Transformation that tries to avoid making inconsequential+-- flag choices early.+deferDefaultFlagChoices :: Tree a -> Tree a+deferDefaultFlagChoices = trav go+  where+    go (GoalChoiceF xs) = GoalChoiceF (P.sortBy defer xs)+    go x                = x++    defer :: Tree a -> Tree a -> Ordering+    defer (FChoice _ _ True _) _ = GT+    defer _ (FChoice _ _ True _) = LT+    defer _ _                    = EQ++-- | Variant of 'preferEasyGoalChoices'.+--+-- Only approximates the number of choices in the branches. Less accurate,+-- more efficient.+lpreferEasyGoalChoices :: Tree a -> Tree a+lpreferEasyGoalChoices = trav go+  where+    go (GoalChoiceF xs) = GoalChoiceF (P.sortBy (comparing lchoices) xs)+    go x                = x++-- | Variant of 'preferEasyGoalChoices'.+--+-- I first thought that using a paramorphism might be faster here,+-- but it doesn't seem to make any difference.+preferEasyGoalChoices' :: Tree a -> Tree a+preferEasyGoalChoices' = para (inn . go)+  where+    go (GoalChoiceF xs) = GoalChoiceF (P.map fst (P.sortBy (comparing (choices . snd)) xs))+    go x                = fmap fst x+
+ Distribution/Client/Dependency/Modular/Solver.hs view
@@ -0,0 +1,52 @@+module Distribution.Client.Dependency.Modular.Solver where++import Data.Map as M++import Distribution.Client.Dependency.Types++import Distribution.Client.Dependency.Modular.Assignment+import Distribution.Client.Dependency.Modular.Builder+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Explore+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Log+import Distribution.Client.Dependency.Modular.Message+import Distribution.Client.Dependency.Modular.Package+import qualified Distribution.Client.Dependency.Modular.Preference as P+import Distribution.Client.Dependency.Modular.Validate++-- | Various options for the modular solver.+data SolverConfig = SolverConfig {+  preferEasyGoalChoices :: Bool,+  independentGoals      :: Bool,+  avoidReinstalls       :: Bool,+  shadowPkgs            :: Bool,+  maxBackjumps          :: Maybe Int+}++solve :: SolverConfig ->   -- solver parameters+         Index ->          -- all available packages as an index+         (PN -> PackagePreferences) -> -- preferences+         Map PN [PackageConstraint] -> -- global constraints+         [PN] ->                       -- global goals+         Log Message (Assignment, RevDepMap)+solve sc idx userPrefs userConstraints userGoals =+  explorePhase     $+  heuristicsPhase  $+  preferencesPhase $+  validationPhase  $+  prunePhase       $+  buildPhase+  where+    explorePhase     = exploreTreeLog . backjump+    heuristicsPhase  = if preferEasyGoalChoices sc+                         then P.preferBaseGoalChoice . P.deferDefaultFlagChoices . P.lpreferEasyGoalChoices+                         else P.preferBaseGoalChoice+    preferencesPhase = P.preferPackagePreferences userPrefs+    validationPhase  = P.enforcePackageConstraints userConstraints .+                       validateTree idx+    prunePhase       = (if avoidReinstalls sc then P.avoidReinstalls (const True) else id) .+                       -- packages that can never be "upgraded":+                       P.requireInstalled (`elem` [PackageName "base",+                                                   PackageName "ghc-prim"])+    buildPhase       = buildTree idx (independentGoals sc) userGoals
+ Distribution/Client/Dependency/Modular/Tree.hs view
@@ -0,0 +1,146 @@+module Distribution.Client.Dependency.Modular.Tree where++import Control.Applicative+import Control.Monad hiding (mapM)+import Data.Foldable+import Data.Traversable+import Prelude hiding (foldr, mapM)++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Version++-- | Type of the search tree. Inlining the choice nodes for now.+data Tree a =+    PChoice     QPN a      (PSQ I        (Tree a))+  | FChoice     QFN a Bool (PSQ Bool     (Tree a)) -- Bool indicates whether it's trivial+  | SChoice     QSN a Bool (PSQ Bool     (Tree a)) -- Bool indicates whether it's trivial+  | GoalChoice             (PSQ OpenGoal (Tree a)) -- PSQ should never be empty+  | Done        RevDepMap+  | Fail        (ConflictSet QPN) FailReason+  deriving (Eq, Show)+  -- Above, a choice is called trivial if it clearly does not matter. The+  -- special case of triviality we actually consider is if there are no new+  -- dependencies introduced by this node.++instance Functor Tree where+  fmap  f (PChoice qpn i   xs) = PChoice qpn (f i)   (fmap (fmap f) xs)+  fmap  f (FChoice qfn i b xs) = FChoice qfn (f i) b (fmap (fmap f) xs)+  fmap  f (SChoice qsn i b xs) = SChoice qsn (f i) b (fmap (fmap f) xs)+  fmap  f (GoalChoice      xs) = GoalChoice          (fmap (fmap f) xs)+  fmap _f (Done    rdm       ) = Done    rdm+  fmap _f (Fail    cs fr     ) = Fail    cs fr++data FailReason = InconsistentInitialConstraints+                | Conflicting [Dep QPN]+                | CannotInstall+                | CannotReinstall+                | Shadowed+                | Broken+                | GlobalConstraintVersion VR+                | GlobalConstraintInstalled+                | GlobalConstraintSource+                | GlobalConstraintFlag+                | BuildFailureNotInIndex PN+                | MalformedFlagChoice QFN+                | MalformedStanzaChoice QSN+                | EmptyGoalChoice+                | Backjump+  deriving (Eq, Show)++-- | Functor for the tree type.+data TreeF a b =+    PChoiceF    QPN a      (PSQ I        b)+  | FChoiceF    QFN a Bool (PSQ Bool     b)+  | SChoiceF    QSN a Bool (PSQ Bool     b)+  | GoalChoiceF            (PSQ OpenGoal b)+  | DoneF       RevDepMap+  | FailF       (ConflictSet QPN) FailReason++out :: Tree a -> TreeF a (Tree a)+out (PChoice    p i   ts) = PChoiceF    p i   ts+out (FChoice    p i b ts) = FChoiceF    p i b ts+out (SChoice    p i b ts) = SChoiceF    p i b ts+out (GoalChoice       ts) = GoalChoiceF       ts+out (Done       x       ) = DoneF       x+out (Fail       c x     ) = FailF       c x++inn :: TreeF a (Tree a) -> Tree a+inn (PChoiceF    p i   ts) = PChoice    p i   ts+inn (FChoiceF    p i b ts) = FChoice    p i b ts+inn (SChoiceF    p i b ts) = SChoice    p i b ts+inn (GoalChoiceF       ts) = GoalChoice       ts+inn (DoneF       x       ) = Done       x+inn (FailF       c x     ) = Fail       c x++instance Functor (TreeF a) where+  fmap f (PChoiceF    p i   ts) = PChoiceF    p i   (fmap f ts)+  fmap f (FChoiceF    p i b ts) = FChoiceF    p i b (fmap f ts)+  fmap f (SChoiceF    p i b ts) = SChoiceF    p i b (fmap f ts)+  fmap f (GoalChoiceF       ts) = GoalChoiceF       (fmap f ts)+  fmap _ (DoneF       x       ) = DoneF       x+  fmap _ (FailF       c x     ) = FailF       c x++instance Foldable (TreeF a) where+  foldr op e (PChoiceF    _ _   ts) = foldr op e ts+  foldr op e (FChoiceF    _ _ _ ts) = foldr op e ts+  foldr op e (SChoiceF    _ _ _ ts) = foldr op e ts+  foldr op e (GoalChoiceF       ts) = foldr op e ts+  foldr _  e (DoneF       _       ) = e+  foldr _  e (FailF       _ _     ) = e++instance Traversable (TreeF a) where+  traverse f (PChoiceF    p i   ts) = PChoiceF    <$> pure p <*> pure i <*>            traverse f ts+  traverse f (FChoiceF    p i b ts) = FChoiceF    <$> pure p <*> pure i <*> pure b <*> traverse f ts+  traverse f (SChoiceF    p i b ts) = SChoiceF    <$> pure p <*> pure i <*> pure b <*> traverse f ts+  traverse f (GoalChoiceF       ts) = GoalChoiceF <$>                                  traverse f ts+  traverse _ (DoneF       x       ) = DoneF       <$> pure x+  traverse _ (FailF       c x     ) = FailF       <$> pure c <*> pure x++-- | Determines whether a tree is active, i.e., isn't a failure node.+active :: Tree a -> Bool+active (Fail _ _) = False+active _          = True++-- | Determines how many active choices are available in a node. Note that we+-- count goal choices as having one choice, always.+choices :: Tree a -> Int+choices (PChoice    _ _   ts) = P.length (P.filter active ts)+choices (FChoice    _ _ _ ts) = P.length (P.filter active ts)+choices (SChoice    _ _ _ ts) = P.length (P.filter active ts)+choices (GoalChoice       _ ) = 1+choices (Done       _       ) = 1+choices (Fail       _ _     ) = 0++-- | Variant of 'choices' that only approximates the number of choices,+-- using 'llength'.+lchoices :: Tree a -> Int+lchoices (PChoice    _ _   ts) = P.llength (P.filter active ts)+lchoices (FChoice    _ _ _ ts) = P.llength (P.filter active ts)+lchoices (SChoice    _ _ _ ts) = P.llength (P.filter active ts)+lchoices (GoalChoice       _ ) = 1+lchoices (Done       _       ) = 1+lchoices (Fail       _ _     ) = 0++-- | Catamorphism on trees.+cata :: (TreeF a b -> b) -> Tree a -> b+cata phi x = (phi . fmap (cata phi) . out) x++trav :: (TreeF a (Tree b) -> TreeF b (Tree b)) -> Tree a -> Tree b+trav psi x = cata (inn . psi) x++-- | Paramorphism on trees.+para :: (TreeF a (b, Tree a) -> b) -> Tree a -> b+para phi = phi . fmap (\ x -> (para phi x, x)) . out++cataM :: Monad m => (TreeF a b -> m b) -> Tree a -> m b+cataM phi = phi <=< mapM (cataM phi) <=< return . out++-- | Anamorphism on trees.+ana :: (b -> TreeF a b) -> b -> Tree a+ana psi = inn . fmap (ana psi) . psi++anaM :: Monad m => (b -> m (TreeF a b)) -> b -> m (Tree a)+anaM psi = return . inn <=< mapM (anaM psi) <=< psi
+ Distribution/Client/Dependency/Modular/Validate.hs view
@@ -0,0 +1,232 @@+module Distribution.Client.Dependency.Modular.Validate where++-- Validation of the tree.+--+-- The task here is to make sure all constraints hold. After validation, any+-- assignment returned by exploration of the tree should be a complete valid+-- assignment, i.e., actually constitute a solution.++import Control.Applicative+import Control.Monad.Reader hiding (sequence)+import Data.List as L+import Data.Map as M+import Data.Traversable+import Prelude hiding (sequence)++import Distribution.Client.Dependency.Modular.Assignment+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree++-- In practice, most constraints are implication constraints (IF we have made+-- a number of choices, THEN we also have to ensure that). We call constraints+-- that for which the precondiditions are fulfilled ACTIVE. We maintain a set+-- of currently active constraints that we pass down the node.+--+-- We aim at detecting inconsistent states as early as possible.+--+-- Whenever we make a choice, there are two things that need to happen:+--+--   (1) We must check that the choice is consistent with the currently+--       active constraints.+--+--   (2) The choice increases the set of active constraints. For the new+--       active constraints, we must check that they are consistent with+--       the current state.+--+-- We can actually merge (1) and (2) by saying the the current choice is+-- a new active constraint, fixing the choice.+--+-- If a test fails, we have detected an inconsistent state. We can+-- disable the current subtree and do not have to traverse it any further.+--+-- We need a good way to represent the current state, i.e., the current+-- set of active constraints. Since the main situation where we have to+-- search in it is (1), it seems best to store the state by package: for+-- every package, we store which versions are still allowed. If for any+-- package, we have inconsistent active constraints, we can also stop.+-- This is a particular way to read task (2):+--+--   (2, weak) We only check if the new constraints are consistent with+--       the choices we've already made, and add them to the active set.+--+--   (2, strong) We check if the new constraints are consistent with the+--       choices we've already made, and the constraints we already have.+--+-- It currently seems as if we're implementing the weak variant. However,+-- when used together with 'preferEasyGoalChoices', we will find an+-- inconsistent state in the very next step.+--+-- What do we do about flags?+--+-- Like for packages, we store the flag choices we have already made.+-- Now, regarding (1), we only have to test whether we've decided the+-- current flag before. Regarding (2), the interesting bit is in discovering+-- the new active constraints. To this end, we look up the constraints for+-- the package the flag belongs to, and traverse its flagged dependencies.+-- Wherever we find the flag in question, we start recording dependencies+-- underneath as new active dependencies. If we encounter other flags, we+-- check if we've chosen them already and either proceed or stop.++-- | The state needed during validation.+data ValidateState = VS {+  index :: Index,+  saved :: Map QPN (FlaggedDeps QPN), -- saved, scoped, dependencies+  pa    :: PreAssignment+}++type Validate = Reader ValidateState++validate :: Tree (QGoalReasons, Scope) -> Validate (Tree QGoalReasons)+validate = cata go+  where+    go :: TreeF (QGoalReasons, Scope) (Validate (Tree QGoalReasons)) -> Validate (Tree QGoalReasons)++    go (PChoiceF qpn (gr,  sc)   ts) = PChoice qpn gr <$> sequence (P.mapWithKey (goP qpn gr sc) ts)+    go (FChoiceF qfn (gr, _sc) b ts) =+      do+        -- Flag choices may occur repeatedly (because they can introduce new constraints+        -- in various places). However, subsequent choices must be consistent. We thereby+        -- collapse repeated flag choice nodes.+        PA _ pfa _ <- asks pa -- obtain current flag-preassignment+        case M.lookup qfn pfa of+          Just rb -> -- flag has already been assigned; collapse choice to the correct branch+                     case P.lookup rb ts of+                       Just t  -> goF qfn gr rb t+                       Nothing -> return $ Fail (toConflictSet (Goal (F qfn) gr)) (MalformedFlagChoice qfn)+          Nothing -> -- flag choice is new, follow both branches+                     FChoice qfn gr b <$> sequence (P.mapWithKey (goF qfn gr) ts)+    go (SChoiceF qsn (gr, _sc) b ts) =+      do+        -- Optional stanza choices are very similar to flag choices.+        PA _ _ psa <- asks pa -- obtain current stanza-preassignment+        case M.lookup qsn psa of+          Just rb -> -- stanza choice has already been made; collapse choice to the correct branch+                     case P.lookup rb ts of+                       Just t  -> goS qsn gr rb t+                       Nothing -> return $ Fail (toConflictSet (Goal (S qsn) gr)) (MalformedStanzaChoice qsn)+          Nothing -> -- stanza choice is new, follow both branches+                     SChoice qsn gr b <$> sequence (P.mapWithKey (goS qsn gr) ts)++    -- We don't need to do anything for goal choices or failure nodes.+    go (GoalChoiceF              ts) = GoalChoice <$> sequence ts+    go (DoneF    rdm               ) = pure (Done rdm)+    go (FailF    c fr              ) = pure (Fail c fr)++    -- What to do for package nodes ...+    goP :: QPN -> QGoalReasons -> Scope -> I -> Validate (Tree QGoalReasons) -> Validate (Tree QGoalReasons)+    goP qpn@(Q _pp pn) gr sc i r = do+      PA ppa pfa psa <- asks pa    -- obtain current preassignment+      idx            <- asks index -- obtain the index+      svd            <- asks saved -- obtain saved dependencies+      let (PInfo deps _ _ mfr) = idx ! pn ! i -- obtain dependencies and index-dictated exclusions introduced by the choice+      let qdeps = L.map (fmap (qualify sc)) deps -- qualify the deps in the current scope+      -- the new active constraints are given by the instance we have chosen,+      -- plus the dependency information we have for that instance+      let goal = Goal (P qpn) gr+      let newactives = Dep qpn (Fixed i goal) : L.map (resetGoal goal) (extractDeps pfa psa qdeps)+      -- We now try to extend the partial assignment with the new active constraints.+      let mnppa = extend (P qpn) ppa newactives+      -- In case we continue, we save the scoped dependencies+      let nsvd = M.insert qpn qdeps svd+      case mfr of+        Just fr -> -- The index marks this as an invalid choice. We can stop.+                   return (Fail (toConflictSet goal) fr)+        _       -> case mnppa of+                     Left (c, d) -> -- We have an inconsistency. We can stop.+                                    return (Fail c (Conflicting d))+                     Right nppa  -> -- We have an updated partial assignment for the recursive validation.+                                    local (\ s -> s { pa = PA nppa pfa psa, saved = nsvd }) r++    -- What to do for flag nodes ...+    goF :: QFN -> QGoalReasons -> Bool -> Validate (Tree QGoalReasons) -> Validate (Tree QGoalReasons)+    goF qfn@(FN (PI qpn _i) _f) gr b r = do+      PA ppa pfa psa <- asks pa -- obtain current preassignment+      svd <- asks saved         -- obtain saved dependencies+      -- Note that there should be saved dependencies for the package in question,+      -- because while building, we do not choose flags before we see the packages+      -- that define them.+      let qdeps = svd ! qpn+      -- We take the *saved* dependencies, because these have been qualified in the+      -- correct scope.+      --+      -- Extend the flag assignment+      let npfa = M.insert qfn b pfa+      -- We now try to get the new active dependencies we might learn about because+      -- we have chosen a new flag.+      let newactives = extractNewDeps (F qfn) gr b npfa psa qdeps+      -- As in the package case, we try to extend the partial assignment.+      case extend (F qfn) ppa newactives of+        Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found+        Right nppa  -> local (\ s -> s { pa = PA nppa npfa psa }) r++    -- What to do for stanza nodes (similar to flag nodes) ...+    goS :: QSN -> QGoalReasons -> Bool -> Validate (Tree QGoalReasons) -> Validate (Tree QGoalReasons)+    goS qsn@(SN (PI qpn _i) _f) gr b r = do+      PA ppa pfa psa <- asks pa -- obtain current preassignment+      svd <- asks saved         -- obtain saved dependencies+      -- Note that there should be saved dependencies for the package in question,+      -- because while building, we do not choose flags before we see the packages+      -- that define them.+      let qdeps = svd ! qpn+      -- We take the *saved* dependencies, because these have been qualified in the+      -- correct scope.+      --+      -- Extend the flag assignment+      let npsa = M.insert qsn b psa+      -- We now try to get the new active dependencies we might learn about because+      -- we have chosen a new flag.+      let newactives = extractNewDeps (S qsn) gr b pfa npsa qdeps+      -- As in the package case, we try to extend the partial assignment.+      case extend (S qsn) ppa newactives of+        Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found+        Right nppa  -> local (\ s -> s { pa = PA nppa pfa npsa }) r++-- | We try to extract as many concrete dependencies from the given flagged+-- dependencies as possible. We make use of all the flag knowledge we have+-- already acquired.+extractDeps :: FAssignment -> SAssignment -> FlaggedDeps QPN -> [Dep QPN]+extractDeps fa sa deps = do+  d <- deps+  case d of+    Simple sd           -> return sd+    Flagged qfn _ td fd -> case M.lookup qfn fa of+                             Nothing    -> mzero+                             Just True  -> extractDeps fa sa td+                             Just False -> extractDeps fa sa fd+    Stanza qsn td       -> case M.lookup qsn sa of+                             Nothing    -> mzero+                             Just True  -> extractDeps fa sa td+                             Just False -> []++-- | We try to find new dependencies that become available due to the given+-- flag or stanza choice. We therefore look for the choice in question, and then call+-- 'extractDeps' for everything underneath.+extractNewDeps :: Var QPN -> QGoalReasons -> Bool -> FAssignment -> SAssignment -> FlaggedDeps QPN -> [Dep QPN]+extractNewDeps v gr b fa sa = go+  where+    go deps = do+      d <- deps+      case d of+        Simple _             -> mzero+        Flagged qfn' _ td fd+          | v == F qfn'      -> L.map (resetGoal (Goal v gr)) $+                                if b then extractDeps fa sa td else extractDeps fa sa fd+          | otherwise        -> case M.lookup qfn' fa of+                                  Nothing    -> mzero+                                  Just True  -> go td+                                  Just False -> go fd+        Stanza qsn' td+          | v == S qsn'      -> L.map (resetGoal (Goal v gr)) $+                                if b then extractDeps fa sa td else []+          | otherwise        -> case M.lookup qsn' sa of+                                  Nothing    -> mzero+                                  Just True  -> go td+                                  Just False -> []++-- | Interface.+validateTree :: Index -> Tree (QGoalReasons, Scope) -> Tree QGoalReasons+validateTree idx t = runReader (validate t) (VS idx M.empty (PA M.empty M.empty M.empty))
+ Distribution/Client/Dependency/Modular/Version.hs view
@@ -0,0 +1,43 @@+module Distribution.Client.Dependency.Modular.Version where++import qualified Distribution.Version as CV -- from Cabal+import Distribution.Text -- from Cabal++-- | Preliminary type for versions.+type Ver = CV.Version++-- | String representation of a version.+showVer :: Ver -> String+showVer = display++-- | Version range. Consists of a lower and upper bound.+type VR = CV.VersionRange++-- | String representation of a version range.+showVR :: VR -> String+showVR = display++-- | Unconstrained version range.+anyVR :: VR+anyVR = CV.anyVersion++-- | Version range fixing a single version.+eqVR :: Ver -> VR+eqVR = CV.thisVersion++-- | Intersect two version ranges.+(.&&.) :: VR -> VR -> VR+(.&&.) = CV.intersectVersionRanges++-- | Simplify a version range.+simplifyVR :: VR -> VR+simplifyVR = CV.simplifyVersionRange++-- | Checking a version against a version range.+checkVR :: VR -> Ver -> Bool+checkVR = flip CV.withinRange++-- | Make a version number.+mkV :: [Int] -> Ver+mkV xs = CV.Version xs []+
Distribution/Client/Dependency/TopDown.hs view
@@ -18,11 +18,14 @@ import qualified Distribution.Client.Dependency.TopDown.Constraints as Constraints import Distribution.Client.Dependency.TopDown.Constraints          ( Satisfiable(..) )+import Distribution.Client.IndexUtils+         ( convert ) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.InstallPlan          ( PlanPackage(..) ) import Distribution.Client.Types-         ( AvailablePackage(..), ConfiguredPackage(..), InstalledPackage(..) )+         ( SourcePackage(..), ConfiguredPackage(..), InstalledPackage(..)+         , enableStanzas ) import Distribution.Client.Dependency.Types          ( DependencyResolver, PackageConstraint(..)          , PackagePreferences(..), InstalledPreference(..)@@ -31,9 +34,9 @@ import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Client.PackageIndex (PackageIndex) import Distribution.Package-         ( PackageName(..), PackageIdentifier, Package(packageId), packageVersion, packageName-         , Dependency(Dependency), thisPackageVersion, notThisPackageVersion-         , PackageFixedDeps(depends) )+         ( PackageName(..), PackageId, Package(..), packageVersion, packageName+         , Dependency(Dependency), thisPackageVersion+         , simplifyDependency, PackageFixedDeps(depends) ) import Distribution.PackageDescription          ( PackageDescription(buildDepends) ) import Distribution.Client.PackageUtils@@ -41,7 +44,7 @@ import Distribution.PackageDescription.Configuration          ( finalizePackageDescription, flattenPackageDescription ) import Distribution.Version-         ( VersionRange, anyVersion, withinRange, simplifyVersionRange+         ( VersionRange, withinRange, simplifyVersionRange          , UpperBound(..), asVersionIntervals ) import Distribution.Compiler          ( CompilerId )@@ -53,7 +56,7 @@          ( display )  import Data.List-         ( foldl', maximumBy, minimumBy, nub, sort, groupBy )+         ( foldl', maximumBy, minimumBy, nub, sort, sortBy, groupBy ) import Data.Maybe          ( fromJust, fromMaybe, catMaybes ) import Data.Monoid@@ -105,9 +108,9 @@         (_, node') = maximumBy (bestByPref pkgname) choice   where     topSortNumber choice = case fst (head choice) of-      InstalledOnly           (InstalledPackageEx  _ i _) -> i-      AvailableOnly           (UnconfiguredPackage _ i _) -> i-      InstalledAndAvailable _ (UnconfiguredPackage _ i _) -> i+      InstalledOnly        (InstalledPackageEx  _ i _) -> i+      SourceOnly           (UnconfiguredPackage _ i _ _) -> i+      InstalledAndSource _ (UnconfiguredPackage _ i _ _) -> i      bestByPref pkgname = case packageInstalledPreference of         PreferLatest    ->@@ -115,8 +118,8 @@         PreferInstalled ->           comparing (\(p,_) -> (isInstalled p, isPreferred p, packageId p))       where-        isInstalled (AvailableOnly _) = False-        isInstalled _                 = True+        isInstalled (SourceOnly _) = False+        isInstalled _              = True         isPreferred p = packageVersion p `withinRange` preferredVersions         (PackagePreferences preferredVersions packageInstalledPreference)           = pref pkgname@@ -135,7 +138,7 @@                      -> Either [Dependency] SelectedPackage  -- | (packages selected, packages discarded)-type SelectionChanges = ([SelectedPackage], [PackageIdentifier])+type SelectionChanges = ([SelectedPackage], [PackageId])  searchSpace :: ConfigurePackage             -> Constraints@@ -145,6 +148,10 @@             -> SearchSpace (SelectedPackages, Constraints, SelectionChanges)                            SelectablePackage searchSpace configure constraints selected changes next =+  assert (Set.null (selectedSet `Set.intersection` next)) $+  assert (selectedSet `Set.isSubsetOf` Constraints.packages constraints) $+  assert (next `Set.isSubsetOf` Constraints.packages constraints) $+   ChoiceNode (selected, constraints, changes)     [ [ (pkg, select name pkg)       | pkg <- PackageIndex.lookupPackageName available name ]@@ -152,15 +159,18 @@   where     available = Constraints.choices constraints +    selectedSet = Set.fromList (map packageName (PackageIndex.allPackages selected))+     select name pkg = case configure available pkg of       Left missing -> Failure $ ConfigureFailed pkg                         [ (dep, Constraints.conflicting constraints dep)                         | dep <- missing ]-      Right pkg' -> case constrainDeps pkg' newDeps constraints [] of-        Left failure       -> Failure failure-        Right (constraints', newDiscarded) ->-          searchSpace configure-            constraints' selected' (newSelected, newDiscarded) next'+      Right pkg' ->+        case constrainDeps pkg' newDeps (addDeps constraints newPkgs) [] of+          Left failure       -> Failure failure+          Right (constraints', newDiscarded) ->+            searchSpace configure+              constraints' selected' (newSelected, newDiscarded) next'         where           selected' = foldl' (flip PackageIndex.insert) selected newSelected           newSelected =@@ -172,39 +182,45 @@                     (PackageIndex.lookupPackageId available pkgid')            newPkgs   = [ name'-                      | dep <- newDeps-                      , let (Dependency name' _) = untagDependency dep+                      | (Dependency name' _, _) <- newDeps                       , null (PackageIndex.lookupPackageName selected' name') ]           newDeps   = concatMap packageConstraints newSelected           next'     = Set.delete name                     $ foldl' (flip Set.insert) next newPkgs -packageConstraints :: SelectedPackage -> [TaggedDependency]+packageConstraints :: SelectedPackage -> [(Dependency, Bool)] packageConstraints = either installedConstraints availableConstraints-                   . preferAvailable+                   . preferSource   where-    preferAvailable (InstalledOnly           pkg) = Left pkg-    preferAvailable (AvailableOnly           pkg) = Right pkg-    preferAvailable (InstalledAndAvailable _ pkg) = Right pkg+    preferSource (InstalledOnly        pkg) = Left pkg+    preferSource (SourceOnly           pkg) = Right pkg+    preferSource (InstalledAndSource _ pkg) = Right pkg     installedConstraints (InstalledPackageEx    _ _ deps) =-      [ TaggedDependency InstalledConstraint (thisPackageVersion dep)+      [ (thisPackageVersion dep, True)       | dep <- deps ]-    availableConstraints (SemiConfiguredPackage _ _ deps) =-      [ TaggedDependency NoInstalledConstraint dep | dep <- deps ]+    availableConstraints (SemiConfiguredPackage _ _ _ deps) =+      [ (dep, False) | dep <- deps ] -constrainDeps :: SelectedPackage -> [TaggedDependency] -> Constraints-              -> [PackageIdentifier]-              -> Either Failure (Constraints, [PackageIdentifier])+addDeps :: Constraints -> [PackageName] -> Constraints+addDeps =+  foldr $ \pkgname cs ->+            case Constraints.addTarget pkgname cs of+              Satisfiable cs' () -> cs'+              _                  -> impossible "addDeps unsatisfiable"++constrainDeps :: SelectedPackage -> [(Dependency, Bool)] -> Constraints+              -> [PackageId]+              -> Either Failure (Constraints, [PackageId]) constrainDeps pkg []         cs discard =   case addPackageSelectConstraint (packageId pkg) cs of     Satisfiable cs' discard' -> Right (cs', discard' ++ discard)-    _                        -> impossible-constrainDeps pkg (dep:deps) cs discard =-  case addPackageDependencyConstraint (packageId pkg) dep cs of+    _                        -> impossible "constrainDeps unsatisfiable(1)"+constrainDeps pkg ((dep, installedConstraint):deps) cs discard =+  case addPackageDependencyConstraint (packageId pkg) dep installedConstraint cs of     Satisfiable cs' discard' -> constrainDeps pkg deps cs' (discard' ++ discard)-    Unsatisfiable            -> impossible+    Unsatisfiable            -> impossible "constrainDeps unsatisfiable(2)"     ConflictsWith conflicts  ->-      Left (DependencyConflict pkg dep conflicts)+      Left (DependencyConflict pkg dep installedConstraint conflicts)  -- ------------------------------------------------------------ -- * The main algorithm@@ -226,7 +242,11 @@ -- the standard 'DependencyResolver' interface. -- topDownResolver :: DependencyResolver-topDownResolver = ((((((mapMessages .).).).).).) . topDownResolver'+topDownResolver platform comp installedPkgIndex sourcePkgIndex+                preferences constraints targets =+    mapMessages (topDownResolver' platform comp+                                  (convert installedPkgIndex) sourcePkgIndex+                                  preferences constraints targets)   where     mapMessages :: Progress Log Failure a -> Progress String String a     mapMessages = foldProgress (Step . showLog) (Fail . showFailure) Done@@ -235,45 +255,60 @@ -- topDownResolver' :: Platform -> CompilerId                  -> PackageIndex InstalledPackage-                 -> PackageIndex AvailablePackage+                 -> PackageIndex SourcePackage                  -> (PackageName -> PackagePreferences)                  -> [PackageConstraint]                  -> [PackageName]                  -> Progress Log Failure [PlanPackage]-topDownResolver' platform comp installed available+topDownResolver' platform comp installedPkgIndex sourcePkgIndex                  preferences constraints targets =       fmap (uncurry finalise)     . (\cs -> search configure preferences cs initialPkgNames)-  =<< addTopLevelConstraints constraints constraintSet+  =<< pruneBottomUp platform comp+  =<< addTopLevelConstraints constraints+  =<< addTopLevelTargets targets emptyConstraintSet    where     configure   = configurePackage platform comp-    constraintSet :: Constraints-    constraintSet = Constraints.empty-      (annotateInstalledPackages             topSortNumber installed')-      (annotateAvailablePackages constraints topSortNumber available')-    (installed', available') = selectNeededSubset installed available-                                                  initialPkgNames-    topSortNumber = topologicalSortNumbering installed' available'+    emptyConstraintSet :: Constraints+    emptyConstraintSet = Constraints.empty+      (annotateInstalledPackages          topSortNumber installedPkgIndex')+      (annotateSourcePackages constraints topSortNumber sourcePkgIndex')+    (installedPkgIndex', sourcePkgIndex') =+      selectNeededSubset installedPkgIndex sourcePkgIndex initialPkgNames+    topSortNumber = topologicalSortNumbering installedPkgIndex' sourcePkgIndex'      initialPkgNames = Set.fromList targets      finalise selected' constraints' =         PackageIndex.allPackages-      . fst . improvePlan installed' constraints'+      . fst . improvePlan installedPkgIndex' constraints'       . PackageIndex.fromList       $ finaliseSelectedPackages preferences selected' constraints' ++addTopLevelTargets :: [PackageName]+                   -> Constraints+                   -> Progress a Failure Constraints+addTopLevelTargets []         cs = Done cs+addTopLevelTargets (pkg:pkgs) cs =+  case Constraints.addTarget pkg cs of+    Satisfiable cs' ()       -> addTopLevelTargets pkgs cs'+    Unsatisfiable            -> Fail (NoSuchPackage pkg)+    ConflictsWith _conflicts -> impossible "addTopLevelTargets conflicts"++ addTopLevelConstraints :: [PackageConstraint] -> Constraints-                       -> Progress a Failure Constraints+                       -> Progress Log Failure Constraints addTopLevelConstraints []                                      cs = Done cs-addTopLevelConstraints (PackageFlagsConstraint   _   _  :deps) cs =+addTopLevelConstraints (PackageConstraintFlags   _   _  :deps) cs =   addTopLevelConstraints deps cs -addTopLevelConstraints (PackageVersionConstraint pkg ver:deps) cs =+addTopLevelConstraints (PackageConstraintVersion pkg ver:deps) cs =   case addTopLevelVersionConstraint pkg ver cs of-    Satisfiable cs' _       ->-      addTopLevelConstraints deps cs'+    Satisfiable cs' pkgids  ->+      Step (AppliedVersionConstraint pkg ver pkgids)+           (addTopLevelConstraints deps cs')      Unsatisfiable           ->       Fail (TopLevelVersionConstraintUnsatisfiable pkg ver)@@ -281,29 +316,93 @@     ConflictsWith conflicts ->       Fail (TopLevelVersionConstraintConflict pkg ver conflicts) -addTopLevelConstraints (PackageInstalledConstraint pkg:deps) cs =+addTopLevelConstraints (PackageConstraintInstalled pkg:deps) cs =   case addTopLevelInstalledConstraint pkg cs of-    Satisfiable cs' _       -> addTopLevelConstraints deps cs'+    Satisfiable cs' pkgids  ->+      Step (AppliedInstalledConstraint pkg InstalledConstraint pkgids)+           (addTopLevelConstraints deps cs')      Unsatisfiable           ->-      Fail (TopLevelInstallConstraintUnsatisfiable pkg)+      Fail (TopLevelInstallConstraintUnsatisfiable pkg InstalledConstraint)      ConflictsWith conflicts ->-      Fail (TopLevelInstallConstraintConflict pkg conflicts)+      Fail (TopLevelInstallConstraintConflict pkg InstalledConstraint conflicts) +addTopLevelConstraints (PackageConstraintSource pkg:deps) cs =+  case addTopLevelSourceConstraint pkg cs of+    Satisfiable cs' pkgids  ->+      Step (AppliedInstalledConstraint pkg SourceConstraint pkgids)+            (addTopLevelConstraints deps cs')++    Unsatisfiable           ->+      Fail (TopLevelInstallConstraintUnsatisfiable pkg SourceConstraint)++    ConflictsWith conflicts ->+      Fail (TopLevelInstallConstraintConflict pkg SourceConstraint conflicts)++addTopLevelConstraints (PackageConstraintStanzas _ _ : deps) cs =+    addTopLevelConstraints deps cs++-- | Add exclusion on available packages that cannot be configured.+--+pruneBottomUp :: Platform -> CompilerId+              -> Constraints -> Progress Log Failure Constraints+pruneBottomUp platform comp constraints =+    foldr prune Done (initialPackages constraints) constraints++  where+    prune pkgs rest cs = foldr addExcludeConstraint rest unconfigurable cs+      where+        unconfigurable =+          [ (pkg, missing) -- if necessary we could look up missing reasons+          | (Just pkg', pkg) <- zip (map getSourcePkg pkgs) pkgs+          , Left missing <- [configure cs pkg'] ]++    addExcludeConstraint (pkg, missing) rest cs =+      let reason = ExcludedByConfigureFail missing in+      case addPackageExcludeConstraint (packageId pkg) reason cs of+        Satisfiable cs' [pkgid]| packageId pkg == pkgid+                         -> Step (ExcludeUnconfigurable pkgid) (rest cs')+        Satisfiable _ _  -> impossible "pruneBottomUp satisfiable"+        _                -> Fail $ ConfigureFailed pkg+                              [ (dep, Constraints.conflicting cs dep)+                              | dep <- missing ]++    configure cs (UnconfiguredPackage (SourcePackage _ pkg _) _ flags stanzas) =+      finalizePackageDescription flags (dependencySatisfiable cs)+                                 platform comp [] (enableStanzas stanzas pkg)+    dependencySatisfiable cs =+      not . null . PackageIndex.lookupDependency (Constraints.choices cs)++    -- collect each group of packages (by name) in reverse topsort order+    initialPackages =+        reverse+      . sortBy (comparing (topSortNumber . head))+      . PackageIndex.allPackagesByName+      . Constraints.choices++    topSortNumber (InstalledOnly        (InstalledPackageEx  _ i _)) = i+    topSortNumber (SourceOnly           (UnconfiguredPackage _ i _ _)) = i+    topSortNumber (InstalledAndSource _ (UnconfiguredPackage _ i _ _)) = i++    getSourcePkg (InstalledOnly      _     ) = Nothing+    getSourcePkg (SourceOnly           spkg) = Just spkg+    getSourcePkg (InstalledAndSource _ spkg) = Just spkg++ configurePackage :: Platform -> CompilerId -> ConfigurePackage configurePackage platform comp available spkg = case spkg of-  InstalledOnly         ipkg      -> Right (InstalledOnly ipkg)-  AvailableOnly              apkg -> fmap AvailableOnly (configure apkg)-  InstalledAndAvailable ipkg apkg -> fmap (InstalledAndAvailable ipkg)-                                          (configure apkg)+  InstalledOnly      ipkg      -> Right (InstalledOnly ipkg)+  SourceOnly              apkg -> fmap SourceOnly (configure apkg)+  InstalledAndSource ipkg apkg -> fmap (InstalledAndSource ipkg)+                                       (configure apkg)   where-  configure (UnconfiguredPackage apkg@(AvailablePackage _ p _) _ flags) =+  configure (UnconfiguredPackage apkg@(SourcePackage _ p _) _ flags stanzas) =     case finalizePackageDescription flags dependencySatisfiable-                                    platform comp [] p of+                                    platform comp [] (enableStanzas stanzas p) of       Left missing        -> Left missing       Right (pkg, flags') -> Right $-        SemiConfiguredPackage apkg flags' (externalBuildDepends pkg)+        SemiConfiguredPackage apkg flags' stanzas (externalBuildDepends pkg)    dependencySatisfiable = not . null . PackageIndex.lookupDependency available @@ -317,7 +416,7 @@   [ InstalledPackageEx pkg (dfsNumber (packageName pkg)) (transitiveDepends pkg)   | pkg <- PackageIndex.allPackages installed ]   where-    transitiveDepends :: InstalledPackage -> [PackageIdentifier]+    transitiveDepends :: InstalledPackage -> [PackageId]     transitiveDepends = map (packageId . toPkg) . tail . Graph.reachable graph                       . fromJust . toVertex . packageId     (graph, toPkg, toVertex) = PackageIndex.dependencyGraph installed@@ -326,19 +425,24 @@ -- | Annotate each available packages with its topological sort number and any -- user-supplied partial flag assignment. ---annotateAvailablePackages :: [PackageConstraint]-                          -> (PackageName -> TopologicalSortNumber)-                          -> PackageIndex AvailablePackage-                          -> PackageIndex UnconfiguredPackage-annotateAvailablePackages constraints dfsNumber available = PackageIndex.fromList-  [ UnconfiguredPackage pkg (dfsNumber name) (flagsFor name)-  | pkg <- PackageIndex.allPackages available-  , let name = packageName pkg ]+annotateSourcePackages :: [PackageConstraint]+                       -> (PackageName -> TopologicalSortNumber)+                       -> PackageIndex SourcePackage+                       -> PackageIndex UnconfiguredPackage+annotateSourcePackages constraints dfsNumber sourcePkgIndex =+    PackageIndex.fromList+      [ UnconfiguredPackage pkg (dfsNumber name) (flagsFor name) (stanzasFor name)+      | pkg <- PackageIndex.allPackages sourcePkgIndex+      , let name = packageName pkg ]   where     flagsFor = fromMaybe [] . flip Map.lookup flagsMap     flagsMap = Map.fromList       [ (name, flags)-      | PackageFlagsConstraint name flags <- constraints ]+      | PackageConstraintFlags name flags <- constraints ]+    stanzasFor = fromMaybe [] . flip Map.lookup stanzasMap+    stanzasMap = Map.fromListWith (++)+        [ (name, stanzas)+        | PackageConstraintStanzas name stanzas <- constraints ]  -- | One of the heuristics we use when guessing which path to take in the -- search space is an ordering on the choices we make. It's generally better@@ -352,7 +456,7 @@ -- one possible choice for B in which case we pick that immediately). -- -- To construct these topological sort numbers we combine and flatten the--- installed and available package sets. We consider only dependencies between+-- installed and source package sets. We consider only dependencies between -- named packages, not including versions and for not-yet-configured packages -- we look at all the possible dependencies, not just those under any single -- flag assignment. This means we can actually get impossible combinations of@@ -360,9 +464,9 @@ -- heuristic. -- topologicalSortNumbering :: PackageIndex InstalledPackage-                         -> PackageIndex AvailablePackage+                         -> PackageIndex SourcePackage                          -> (PackageName -> TopologicalSortNumber)-topologicalSortNumbering installed available =+topologicalSortNumbering installedPkgIndex sourcePkgIndex =     \pkgname -> let Just vertex = toVertex pkgname                  in topologicalSortNumbers Array.! vertex   where@@ -370,14 +474,14 @@                                          (zip (Graph.topSort graph) [0..])     (graph, _, toVertex)   = Graph.graphFromEdges $          [ ((), packageName pkg, nub deps)-         | pkgs@(pkg:_) <- PackageIndex.allPackagesByName installed+         | pkgs@(pkg:_) <- PackageIndex.allPackagesByName installedPkgIndex          , let deps = [ packageName dep                       | pkg' <- pkgs                       , dep  <- depends pkg' ] ]       ++ [ ((), packageName pkg, nub deps)-         | pkgs@(pkg:_) <- PackageIndex.allPackagesByName available+         | pkgs@(pkg:_) <- PackageIndex.allPackagesByName sourcePkgIndex          , let deps = [ depName-                      | AvailablePackage _ pkg' _ <- pkgs+                      | SourcePackage _ pkg' _ <- pkgs                       , Dependency depName _ <-                           buildDepends (flattenPackageDescription pkg') ] ] @@ -387,24 +491,24 @@ -- and looking at the names of all possible dependencies. -- selectNeededSubset :: PackageIndex InstalledPackage-                   -> PackageIndex AvailablePackage+                   -> PackageIndex SourcePackage                    -> Set PackageName                    -> (PackageIndex InstalledPackage-                      ,PackageIndex AvailablePackage)-selectNeededSubset installed available = select mempty mempty+                      ,PackageIndex SourcePackage)+selectNeededSubset installedPkgIndex sourcePkgIndex = select mempty mempty   where     select :: PackageIndex InstalledPackage-           -> PackageIndex AvailablePackage+           -> PackageIndex SourcePackage            -> Set PackageName            -> (PackageIndex InstalledPackage-              ,PackageIndex AvailablePackage)-    select installed' available' remaining-      | Set.null remaining = (installed', available')-      | otherwise = select installed'' available'' remaining''+              ,PackageIndex SourcePackage)+    select installedPkgIndex' sourcePkgIndex' remaining+      | Set.null remaining = (installedPkgIndex', sourcePkgIndex')+      | otherwise = select installedPkgIndex'' sourcePkgIndex'' remaining''       where         (next, remaining') = Set.deleteFindMin remaining-        moreInstalled = PackageIndex.lookupPackageName installed next-        moreAvailable = PackageIndex.lookupPackageName available next+        moreInstalled = PackageIndex.lookupPackageName installedPkgIndex next+        moreSource    = PackageIndex.lookupPackageName sourcePkgIndex next         moreRemaining = -- we filter out packages already included in the indexes                         -- this avoids an infinite loop if a package depends on itself                         -- like base-3.0.3.0 with base-4.0.0.0@@ -413,14 +517,18 @@                         | pkg <- moreInstalled                         , dep <- depends pkg ]                      ++ [ name-                        | AvailablePackage _ pkg _ <- moreAvailable+                        | SourcePackage _ pkg _ <- moreSource                         , Dependency name _ <-                             buildDepends (flattenPackageDescription pkg) ]-        installed''   = foldl' (flip PackageIndex.insert) installed' moreInstalled-        available''   = foldl' (flip PackageIndex.insert) available' moreAvailable-        remaining''   = foldl' (flip         Set.insert) remaining' moreRemaining-        notAlreadyIncluded name = null (PackageIndex.lookupPackageName installed' name)-                                  && null (PackageIndex.lookupPackageName available' name)+        installedPkgIndex'' = foldl' (flip PackageIndex.insert)+                                     installedPkgIndex' moreInstalled+        sourcePkgIndex''    = foldl' (flip PackageIndex.insert)+                                     sourcePkgIndex' moreSource+        remaining''         = foldl' (flip          Set.insert)+                                     remaining' moreRemaining+        notAlreadyIncluded name =+            null (PackageIndex.lookupPackageName installedPkgIndex' name)+         && null (PackageIndex.lookupPackageName sourcePkgIndex' name)  -- ------------------------------------------------------------ -- * Post processing the solution@@ -434,24 +542,26 @@   map finaliseSelected (PackageIndex.allPackages selected)   where     remainingChoices = Constraints.choices constraints-    finaliseSelected (InstalledOnly         ipkg     ) = finaliseInstalled ipkg-    finaliseSelected (AvailableOnly              apkg) = finaliseAvailable Nothing apkg-    finaliseSelected (InstalledAndAvailable ipkg apkg) =+    finaliseSelected (InstalledOnly      ipkg     ) = finaliseInstalled ipkg+    finaliseSelected (SourceOnly              apkg) = finaliseSource Nothing apkg+    finaliseSelected (InstalledAndSource ipkg apkg) =       case PackageIndex.lookupPackageId remainingChoices (packageId ipkg) of-        Nothing                          -> impossible --picked package not in constraints-        Just (AvailableOnly _)           -> impossible --to constrain to avail only-        Just (InstalledOnly _)           -> finaliseInstalled ipkg-        Just (InstalledAndAvailable _ _) -> finaliseAvailable (Just ipkg) apkg+                                        --picked package not in constraints+        Nothing                       -> impossible "finaliseSelected no pkg"+                                        -- to constrain to avail only:+        Just (SourceOnly _)           -> impossible "finaliseSelected src only"+        Just (InstalledOnly _)        -> finaliseInstalled ipkg+        Just (InstalledAndSource _ _) -> finaliseSource (Just ipkg) apkg      finaliseInstalled (InstalledPackageEx pkg _ _) = InstallPlan.PreExisting pkg-    finaliseAvailable mipkg (SemiConfiguredPackage pkg flags deps) =-      InstallPlan.Configured (ConfiguredPackage pkg flags deps')+    finaliseSource mipkg (SemiConfiguredPackage pkg flags stanzas deps) =+      InstallPlan.Configured (ConfiguredPackage pkg flags stanzas deps')       where         deps' = map (packageId . pickRemaining mipkg) deps      pickRemaining mipkg dep@(Dependency _name versionRange) =           case PackageIndex.lookupDependency remainingChoices dep of-            []        -> impossible+            []        -> impossible "pickRemaining no pkg"             [pkg']    -> pkg'             remaining -> assert (checkIsPaired remaining)                        $ maximumBy bestByPref remaining@@ -540,14 +650,14 @@      constraintsOk _     []              constraints = Just constraints     constraintsOk pkgid (pkgid':pkgids) constraints =-      case addPackageDependencyConstraint pkgid dep constraints of+      case addPackageDependencyConstraint pkgid dep True constraints of         Satisfiable constraints' _ -> constraintsOk pkgid pkgids constraints'         _                          -> Nothing       where-        dep = TaggedDependency InstalledConstraint (thisPackageVersion pkgid')+        dep = thisPackageVersion pkgid'      reverseTopologicalOrder :: PackageFixedDeps pkg-                            => PackageIndex pkg -> [PackageIdentifier]+                            => PackageIndex pkg -> [PackageId]     reverseTopologicalOrder index = map (packageId . toPkg)                                   . Graph.topSort                                   . Graph.transposeG@@ -558,55 +668,68 @@ -- * Adding and recording constraints -- ------------------------------------------------------------ -addPackageSelectConstraint :: PackageIdentifier -> Constraints+addPackageSelectConstraint :: PackageId -> Constraints                            -> Satisfiable Constraints-                                [PackageIdentifier] ExclusionReason-addPackageSelectConstraint pkgid constraints =-  Constraints.constrain dep reason constraints+                                [PackageId] ExclusionReason+addPackageSelectConstraint pkgid =+    Constraints.constrain pkgname constraint reason   where-    dep    = TaggedDependency NoInstalledConstraint (thisPackageVersion pkgid)-    reason = SelectedOther pkgid+    pkgname          = packageName pkgid+    constraint ver _ = ver == packageVersion pkgid+    reason           = SelectedOther pkgid -addPackageExcludeConstraint :: PackageIdentifier -> Constraints+addPackageExcludeConstraint :: PackageId -> ExclusionReason+                            -> Constraints                             -> Satisfiable Constraints-                                 [PackageIdentifier] ExclusionReason-addPackageExcludeConstraint pkgid constraints =-  Constraints.constrain dep reason constraints+                                           [PackageId] ExclusionReason+addPackageExcludeConstraint pkgid reason =+    Constraints.constrain pkgname constraint reason   where-    dep    = TaggedDependency NoInstalledConstraint-               (notThisPackageVersion pkgid)-    reason = ExcludedByConfigureFail+    pkgname = packageName pkgid+    constraint ver installed+      | ver == packageVersion pkgid = installed+      | otherwise                   = True -addPackageDependencyConstraint :: PackageIdentifier -> TaggedDependency -> Constraints+addPackageDependencyConstraint :: PackageId -> Dependency -> Bool+                               -> Constraints                                -> Satisfiable Constraints-                                    [PackageIdentifier] ExclusionReason-addPackageDependencyConstraint pkgid dep constraints =-  Constraints.constrain dep reason constraints+                                    [PackageId] ExclusionReason+addPackageDependencyConstraint pkgid dep@(Dependency pkgname verrange)+                                     installedConstraint =+    Constraints.constrain pkgname constraint reason   where-    reason = ExcludedByPackageDependency pkgid dep+    constraint ver installed = ver `withinRange` verrange+                            && if installedConstraint then installed else True+    reason = ExcludedByPackageDependency pkgid dep installedConstraint  addTopLevelVersionConstraint :: PackageName -> VersionRange                              -> Constraints                              -> Satisfiable Constraints-                                  [PackageIdentifier] ExclusionReason-addTopLevelVersionConstraint pkg ver constraints =-  Constraints.constrain taggedDep reason constraints+                                  [PackageId] ExclusionReason+addTopLevelVersionConstraint pkgname verrange =+    Constraints.constrain pkgname constraint reason   where-    dep       = Dependency pkg ver-    taggedDep = TaggedDependency NoInstalledConstraint dep-    reason    = ExcludedByTopLevelDependency dep+    constraint ver _installed = ver `withinRange` verrange+    reason = ExcludedByTopLevelConstraintVersion pkgname verrange -addTopLevelInstalledConstraint :: PackageName-                               -> Constraints-                               -> Satisfiable Constraints-                                    [PackageIdentifier] ExclusionReason-addTopLevelInstalledConstraint pkg constraints =-  Constraints.constrain taggedDep reason constraints+addTopLevelInstalledConstraint,+  addTopLevelSourceConstraint :: PackageName+                              -> Constraints+                              -> Satisfiable Constraints+                                   [PackageId] ExclusionReason+addTopLevelInstalledConstraint pkgname =+    Constraints.constrain pkgname constraint reason   where-    dep       = Dependency pkg anyVersion-    taggedDep = TaggedDependency InstalledConstraint dep-    reason    = ExcludedByTopLevelDependency dep+    constraint _ver installed = installed+    reason = ExcludedByTopLevelConstraintInstalled pkgname +addTopLevelSourceConstraint pkgname =+    Constraints.constrain pkgname constraint reason+  where+    constraint _ver installed = not installed+    reason = ExcludedByTopLevelConstraintSource pkgname++ -- ------------------------------------------------------------ -- * Reasons for constraints -- ------------------------------------------------------------@@ -619,60 +742,80 @@       -- | We selected this other version of the package. That means we exclude      -- all the other versions.-     SelectedOther PackageIdentifier+     SelectedOther PackageId       -- | We excluded this version of the package because it failed to      -- configure probably because of unsatisfiable deps.-   | ExcludedByConfigureFail+   | ExcludedByConfigureFail [Dependency]       -- | We excluded this version of the package because another package that      -- we selected imposed a dependency which this package did not satisfy.-   | ExcludedByPackageDependency PackageIdentifier TaggedDependency+   | ExcludedByPackageDependency PackageId Dependency Bool       -- | We excluded this version of the package because it did not satisfy      -- a dependency given as an original top level input.      ---   | ExcludedByTopLevelDependency Dependency+   | ExcludedByTopLevelConstraintVersion   PackageName VersionRange+   | ExcludedByTopLevelConstraintInstalled PackageName+   | ExcludedByTopLevelConstraintSource    PackageName +  deriving Eq+ -- | Given an excluded package and the reason it was excluded, produce a human -- readable explanation. ---showExclusionReason :: PackageIdentifier -> ExclusionReason -> String+showExclusionReason :: PackageId -> ExclusionReason -> String showExclusionReason pkgid (SelectedOther pkgid') =   display pkgid ++ " was excluded because " ++   display pkgid' ++ " was selected instead"-showExclusionReason pkgid ExcludedByConfigureFail =-  display pkgid ++ " was excluded because it could not be configured"-showExclusionReason pkgid (ExcludedByPackageDependency pkgid' dep) =-  display pkgid ++ " was excluded because " ++-  display pkgid' ++ " requires " ++ displayDep (untagDependency dep)-showExclusionReason pkgid (ExcludedByTopLevelDependency dep) =-  display pkgid ++ " was excluded because of the top level dependency " ++-  displayDep dep+showExclusionReason pkgid (ExcludedByConfigureFail missingDeps) =+  display pkgid ++ " was excluded because it could not be configured. "+  ++ "It requires " ++ listOf displayDep missingDeps+showExclusionReason pkgid (ExcludedByPackageDependency pkgid' dep installedConstraint)+  = display pkgid ++ " was excluded because " ++ display pkgid' ++ " requires "+ ++ (if installedConstraint then "an installed instance of " else "")+ ++ displayDep dep+showExclusionReason pkgid (ExcludedByTopLevelConstraintVersion pkgname verRange) =+  display pkgid ++ " was excluded because of the top level constraint " +++  displayDep (Dependency pkgname verRange)+showExclusionReason pkgid (ExcludedByTopLevelConstraintInstalled pkgname)+  = display pkgid ++ " was excluded because of the top level constraint '"+ ++ display pkgname ++ " installed' which means that only installed instances "+ ++ "of the package may be selected."+showExclusionReason pkgid (ExcludedByTopLevelConstraintSource pkgname)+  = display pkgid ++ " was excluded because of the top level constraint '"+ ++ display pkgname ++ " source' which means that only source versions "+ ++ "of the package may be selected."   -- ------------------------------------------------------------ -- * Logging progress and failures -- ------------------------------------------------------------ -data Log = Select [SelectedPackage] [PackageIdentifier]+data Log = Select [SelectedPackage] [PackageId]+         | AppliedVersionConstraint   PackageName VersionRange [PackageId]+         | AppliedInstalledConstraint PackageName InstalledConstraint [PackageId]+         | ExcludeUnconfigurable PackageId+ data Failure-   = ConfigureFailed+   = NoSuchPackage+       PackageName+   | ConfigureFailed        SelectablePackage-       [(Dependency, [(PackageIdentifier, [ExclusionReason])])]+       [(Dependency, [(PackageId, [ExclusionReason])])]    | DependencyConflict-       SelectedPackage TaggedDependency-       [(PackageIdentifier, [ExclusionReason])]+       SelectedPackage Dependency Bool+       [(PackageId, [ExclusionReason])]    | TopLevelVersionConstraintConflict        PackageName VersionRange-       [(PackageIdentifier, [ExclusionReason])]+       [(PackageId, [ExclusionReason])]    | TopLevelVersionConstraintUnsatisfiable        PackageName VersionRange    | TopLevelInstallConstraintConflict-       PackageName-       [(PackageIdentifier, [ExclusionReason])]+       PackageName InstalledConstraint+       [(PackageId, [ExclusionReason])]    | TopLevelInstallConstraintUnsatisfiable-       PackageName+       PackageName InstalledConstraint  showLog :: Log -> String showLog (Select selected discarded) = case (selectedMsg, discardedMsg) of@@ -689,9 +832,9 @@               : [ display (packageVersion s') ++ " " ++ kind s'                 | s' <- ss ] -    kind (InstalledOnly _)           = "(installed)"-    kind (AvailableOnly _)           = "(hackage)"-    kind (InstalledAndAvailable _ _) = "(installed or hackage)"+    kind (InstalledOnly _)        = "(installed)"+    kind (SourceOnly _)           = "(source)"+    kind (InstalledAndSource _ _) = "(installed or source)"      discardedMsg = case discarded of       []  -> ""@@ -699,8 +842,23 @@         [ element         | (pkgid:pkgids) <- groupBy (equating packageName) (sort discarded)         , element <- display pkgid : map (display . packageVersion) pkgids ]+showLog (AppliedVersionConstraint pkgname ver pkgids) =+     "applying constraint " ++ display (Dependency pkgname ver)+  ++ if null pkgids+       then ""+       else "which excludes " ++ listOf display pkgids+showLog (AppliedInstalledConstraint pkgname inst pkgids) =+     "applying constraint " ++ display pkgname ++ " '"+  ++ (case inst of InstalledConstraint -> "installed"; _ -> "source") ++ "' "+  ++ if null pkgids+       then ""+       else "which excludes " ++ listOf display pkgids+showLog (ExcludeUnconfigurable pkgid) =+     "excluding " ++ display pkgid ++ " (it cannot be configured)"  showFailure :: Failure -> String+showFailure (NoSuchPackage pkgname) =+     "The package " ++ display pkgname ++ " is unknown." showFailure (ConfigureFailed pkg missingDeps) =      "cannot configure " ++ displayPkg pkg ++ ". It requires "   ++ listOf (displayDep . fst) missingDeps@@ -720,15 +878,17 @@        where pkgs = map fst conflicts -showFailure (DependencyConflict pkg (TaggedDependency _ dep) conflicts) =+showFailure (DependencyConflict pkg dep installedConstraint conflicts) =      "dependencies conflict: "-  ++ displayPkg pkg ++ " requires " ++ displayDep dep ++ " however\n"+  ++ displayPkg pkg ++ " requires "+  ++ (if installedConstraint then "an installed instance of " else "")+  ++ displayDep dep ++ " however:\n"   ++ unlines [ showExclusionReason (packageId pkg') reason              | (pkg', reasons) <- conflicts, reason <- reasons ]  showFailure (TopLevelVersionConstraintConflict name ver conflicts) =-     "constraints conflict: "-  ++ "top level constraint " ++ displayDep (Dependency name ver) ++ " however\n"+     "constraints conflict: we have the top level constraint "+  ++ displayDep (Dependency name ver) ++ ", but\n"   ++ unlines [ showExclusionReason (packageId pkg') reason              | (pkg', reasons) <- conflicts, reason <- reasons ] @@ -736,31 +896,37 @@      "There is no available version of " ++ display name       ++ " that satisfies " ++ displayVer ver -showFailure (TopLevelInstallConstraintConflict name conflicts) =+showFailure (TopLevelInstallConstraintConflict name InstalledConstraint conflicts) =      "constraints conflict: "-  ++ "top level constraint " ++ display name ++ "-installed however\n"+  ++ "top level constraint '" ++ display name ++ " installed' however\n"   ++ unlines [ showExclusionReason (packageId pkg') reason              | (pkg', reasons) <- conflicts, reason <- reasons ] -showFailure (TopLevelInstallConstraintUnsatisfiable name) =+showFailure (TopLevelInstallConstraintUnsatisfiable name InstalledConstraint) =      "There is no installed version of " ++ display name +showFailure (TopLevelInstallConstraintConflict name SourceConstraint conflicts) =+     "constraints conflict: "+  ++ "top level constraint '" ++ display name ++ " source' however\n"+  ++ unlines [ showExclusionReason (packageId pkg') reason+             | (pkg', reasons) <- conflicts, reason <- reasons ]++showFailure (TopLevelInstallConstraintUnsatisfiable name SourceConstraint) =+     "There is no available source version of " ++ display name+ displayVer :: VersionRange -> String displayVer = display . simplifyVersionRange  displayDep :: Dependency -> String displayDep = display . simplifyDependency -simplifyDependency :: Dependency -> Dependency-simplifyDependency (Dependency name range) =-  Dependency name (simplifyVersionRange range)  -- ------------------------------------------------------------ -- * Utils -- ------------------------------------------------------------ -impossible :: a-impossible = internalError "impossible"+impossible :: String -> a+impossible msg = internalError $ "assertion failure: " ++ msg  internalError :: String -> a internalError msg = error $ "internal error: " ++ msg
Distribution/Client/Dependency/TopDown/Constraints.hs view
@@ -8,14 +8,16 @@ -- Stability   :  provisional -- Portability :  portable ----- A set of satisfiable dependencies (package version constraints).+-- A set of satisfiable constraints on a set of packages. ----------------------------------------------------------------------------- module Distribution.Client.Dependency.TopDown.Constraints (   Constraints,   empty,+  packages,   choices,   isPaired, +  addTarget,   constrain,   Satisfiable(..),   conflicting,@@ -25,61 +27,103 @@ import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Client.PackageIndex (PackageIndex) import Distribution.Package-         ( PackageName, PackageIdentifier(..)+         ( PackageName, PackageId, PackageIdentifier(..)          , Package(packageId), packageName, packageVersion-         , PackageFixedDeps(depends)-         , Dependency(Dependency) )+         , Dependency, PackageFixedDeps(depends) ) import Distribution.Version-         ( Version, withinRange )+         ( Version ) import Distribution.Client.Utils          ( mergeBy, MergeResult(..) ) -import Data.List-         ( foldl' ) import Data.Monoid          ( Monoid(mempty) )-import Data.Maybe-         ( catMaybes )+import Data.Either+         ( partitionEithers ) import qualified Data.Map as Map import Data.Map (Map)+import qualified Data.Set as Set+import Data.Set (Set) import Control.Exception          ( assert ) --- | A set of constraints on package versions. For each package name we record--- what other packages depends on it and what constraints they impose on the--- version of the package.++-- | A set of satisfiable constraints on a set of packages. ---data (Package installed, Package available)-  => Constraints installed available reason+-- The 'Constraints' type keeps track of a set of targets (identified by+-- package name) that we know that we need. It also keeps track of a set of+-- constraints over all packages in the environment.+--+-- It maintains the guarantee that, for the target set, the constraints are+-- satisfiable, meaning that there is at least one instance available for each+-- package name that satisfies the constraints on that package name.+--+-- Note that it is possible to over-constrain a package in the environment that+-- is not in the target set -- the satisfiability guarantee is only maintained+-- for the target set. This is useful because it allows us to exclude packages+-- without needing to know if it would ever be needed or not (e.g. allows+-- excluding broken installed packages).+--+-- Adding a constraint for a target package can fail if it would mean that+-- there are no remaining choices.+--+-- Adding a constraint for package that is not a target never fails.+--+-- Adding a new target package can fail if that package already has conflicting+-- constraints.+--+data Constraints installed source reason    = Constraints -       -- Remaining available choices-       (PackageIndex (InstalledOrAvailable installed available))+       -- | Targets that we know we need. This is the set for which we+       -- guarantee the constraints are satisfiable.+       !(Set PackageName) -       -- Paired choices-       (Map PackageName (Version, Version))+       -- | The available/remaining set. These are packages that have available+       -- choices remaining. This is guaranteed to cover the target packages,+       -- but can also cover other packages in the environment. New targets can+       -- only be added if there are available choices remaining for them.+       !(PackageIndex (InstalledOrSource installed source)) -       -- Choices that we have excluded for some reason-       -- usually by applying constraints-       (PackageIndex (ExcludedPackage PackageIdentifier reason))+       -- | The excluded set. Choices that we have excluded by applying+       -- constraints. Excluded choices are tagged with the reason.+       !(PackageIndex (ExcludedPkg (InstalledOrSource installed source) reason)) -       -- Purely for the invariant, we keep a copy of the original index-       (PackageIndex (InstalledOrAvailable installed available))+       -- | Paired choices, this is an ugly hack.+       !(Map PackageName (Version, Version)) +       -- | Purely for the invariant, we keep a copy of the original index+       !(PackageIndex (InstalledOrSource installed source)) -data ExcludedPackage pkg reason-   = ExcludedPackage pkg [reason] -- reasons for excluding just the available-                         [reason] -- reasons for excluding installed and avail -instance Package pkg => Package (ExcludedPackage pkg reason) where-  packageId (ExcludedPackage p _ _) = packageId p+-- | Reasons for excluding all, or some choices for a package version.+--+-- Each package version can have a source instance, an installed instance or+-- both. We distinguish reasons for constraints that excluded both instances,+-- from reasons for constraints that excluded just one instance.+--+data ExcludedPkg pkg reason+   = ExcludedPkg pkg+       [reason] -- ^ reasons for excluding both source and installed instances+       [reason] -- ^ reasons for excluding the installed instance+       [reason] -- ^ reasons for excluding the source instance +instance Package pkg => Package (ExcludedPkg pkg reason) where+  packageId (ExcludedPkg p _ _ _) = packageId p++ -- | There is a conservation of packages property. Packages are never gained or--- lost, they just transfer from the remaining pot to the excluded pot.+-- lost, they just transfer from the remaining set to the excluded set. ---invariant :: (Package installed, Package available)-          => Constraints installed available a -> Bool-invariant (Constraints available _ excluded original) = all check merged+invariant :: (Package installed, Package source)+          => Constraints installed source a -> Bool+invariant (Constraints targets available excluded _ original) =++    -- Relationship between available, excluded and original+    all check merged++    -- targets is a subset of available+ && all (PackageIndex.elemByPackageName available) (Set.elems targets)+   where     merged = mergeBy (\a b -> packageId a `compare` mergedPackageId b)                      (PackageIndex.allPackages original)@@ -91,86 +135,113 @@         mergedPackageId (OnlyInRight   p) = packageId p         mergedPackageId (InBoth      p _) = packageId p +    -- If the package was originally installed only, then     check (InBoth (InstalledOnly _) cur) = case cur of-      -- If the package was originally installed only then       -- now it's either still remaining as installed only-      -- or it has been excluded in which case we excluded both-      -- installed and available since it was only installed-      OnlyInLeft  (InstalledOnly _)            -> True-      OnlyInRight (ExcludedPackage _ [] (_:_)) -> True-      _                                        -> False+      OnlyInLeft               (InstalledOnly _)              -> True+      -- or it has been excluded+      OnlyInRight (ExcludedPkg (InstalledOnly _) [] (_:_) []) -> True+      _                                                       -> False -    check (InBoth (AvailableOnly _) cur) = case cur of-      -- If the package was originally available only then-      -- now it's either still remaining as available only-      -- or it has been excluded in which case we excluded both-      -- installed and available since it was only available-      OnlyInLeft  (AvailableOnly   _)          -> True-      OnlyInRight (ExcludedPackage _ [] (_:_)) -> True-      _                                        -> True+    -- If the package was originally available only, then+    check (InBoth (SourceOnly _) cur) = case cur of+      -- now it's either still remaining as source only+      OnlyInLeft               (SourceOnly _)              -> True+      -- or it has been excluded+      OnlyInRight (ExcludedPkg (SourceOnly _) [] [] (_:_)) -> True+      _                                                    -> False -    -- If the package was originally installed and available-    -- then there are three cases.-    check (InBoth (InstalledAndAvailable _ _) cur) = case cur of+    -- If the package was originally installed and source, then+    check (InBoth (InstalledAndSource _ _) cur) = case cur of       -- We can have both remaining:-      OnlyInLeft                    (InstalledAndAvailable _ _)  -> True-      -- both excluded, in particular it can have had the available excluded-      -- and later had both excluded so we do not mind if the available excluded-      -- is empty or non-empty.-      OnlyInRight                   (ExcludedPackage _ _  (_:_)) -> True-      -- the installed remaining and the available excluded:-      InBoth      (InstalledOnly _) (ExcludedPackage _ (_:_) []) -> True-      _                                                          -> False+      OnlyInLeft               (InstalledAndSource _ _)        -> True +      -- both excluded, in particular it can have had the just source or+      -- installed excluded and later had both excluded so we do not mind if+      -- the source or installed excluded is empty or non-empty.+      OnlyInRight (ExcludedPkg (InstalledAndSource _ _) _ _ _) -> True++      -- the installed remaining and the source excluded:+      InBoth                   (InstalledOnly _)+                  (ExcludedPkg (SourceOnly _) [] [] (_:_))     -> True++      -- the source remaining and the installed excluded:+      InBoth                   (SourceOnly _)+                  (ExcludedPkg (InstalledOnly _) [] (_:_) [])  -> True+      _                                                        -> False+     check _ = False + -- | An update to the constraints can move packages between the two piles -- but not gain or loose packages.-transitionsTo :: (Package installed, Package available)-              => Constraints installed available a-              -> Constraints installed available a -> Bool-transitionsTo constraints @(Constraints available  _ excluded  _)-              constraints'@(Constraints available' _ excluded' _) =+transitionsTo :: (Package installed, Package source)+              => Constraints installed source a+              -> Constraints installed source a -> Bool+transitionsTo constraints @(Constraints _ available  excluded  _ _)+              constraints'@(Constraints _ available' excluded' _ _) =+      invariant constraints && invariant constraints'   && null availableGained  && null excludedLost-  && map packageId availableLost == map packageId excludedGained+  &&    map (mapInstalledOrSource packageId packageId) availableLost+     == map (mapInstalledOrSource packageId packageId) excludedGained    where-    availableLost   = foldr lost [] availableChange where-      lost (OnlyInLeft  pkg)          rest = pkg : rest-      lost (InBoth (InstalledAndAvailable _ pkg)-                   (InstalledOnly _)) rest = AvailableOnly pkg : rest-      lost _                          rest = rest-    availableGained = [ pkg | OnlyInRight pkg <- availableChange ]-    excludedLost    = [ pkg | OnlyInLeft  pkg <- excludedChange  ]-    excludedGained  = [ pkg | OnlyInRight pkg <- excludedChange  ]-                   ++ [ pkg | InBoth (ExcludedPackage _ (_:_) [])-                                 pkg@(ExcludedPackage _ (_:_) (_:_))-                                              <- excludedChange  ]-    availableChange = mergeBy (\a b -> packageId a `compare` packageId b)-                              (PackageIndex.allPackages available)-                              (PackageIndex.allPackages available')-    excludedChange  = mergeBy (\a b -> packageId a `compare` packageId b)-                              (PackageIndex.allPackages excluded)-                              (PackageIndex.allPackages excluded')+    (availableLost, availableGained)+      = partitionEithers (foldr lostAndGained [] availableChange) +    (excludedLost, excludedGained)+      = partitionEithers (foldr lostAndGained [] excludedChange)++    availableChange =+      mergeBy (\a b -> packageId a `compare` packageId b)+        (PackageIndex.allPackages available)+        (PackageIndex.allPackages available')++    excludedChange =+      mergeBy (\a b -> packageId a `compare` packageId b)+        [ pkg | ExcludedPkg pkg _ _ _ <- PackageIndex.allPackages excluded  ]+        [ pkg | ExcludedPkg pkg _ _ _ <- PackageIndex.allPackages excluded' ]++    lostAndGained mr rest = case mr of+      OnlyInLeft pkg                    -> Left pkg : rest+      InBoth (InstalledAndSource pkg _)+             (SourceOnly _)             -> Left (InstalledOnly pkg) : rest+      InBoth (InstalledAndSource _ pkg)+             (InstalledOnly _)          -> Left (SourceOnly pkg) : rest+      InBoth (SourceOnly _)+             (InstalledAndSource pkg _) -> Right (InstalledOnly pkg) : rest+      InBoth (InstalledOnly _)+             (InstalledAndSource _ pkg) -> Right (SourceOnly pkg) : rest+      OnlyInRight pkg                   -> Right pkg : rest+      _                                 -> rest++    mapInstalledOrSource f g pkg = case pkg of+      InstalledOnly      a   -> InstalledOnly (f a)+      SourceOnly           b -> SourceOnly    (g b)+      InstalledAndSource a b -> InstalledAndSource (f a) (g b)++ -- | We construct 'Constraints' with an initial 'PackageIndex' of all the -- packages available. ---empty :: (PackageFixedDeps installed, Package available)+empty :: (PackageFixedDeps installed, Package source)       => PackageIndex installed-      -> PackageIndex available-      -> Constraints installed available reason-empty installed available = Constraints pkgs pairs mempty pkgs+      -> PackageIndex source+      -> Constraints installed source reason+empty installed source =+    Constraints targets pkgs excluded pairs pkgs   where+    targets  = mempty+    excluded = mempty     pkgs = PackageIndex.fromList-         . map toInstalledOrAvailable+         . map toInstalledOrSource          $ mergeBy (\a b -> packageId a `compare` packageId b)                    (PackageIndex.allPackages installed)-                   (PackageIndex.allPackages available)-    toInstalledOrAvailable (OnlyInLeft  i  ) = InstalledOnly         i-    toInstalledOrAvailable (OnlyInRight   a) = AvailableOnly           a-    toInstalledOrAvailable (InBoth      i a) = InstalledAndAvailable i a+                   (PackageIndex.allPackages source)+    toInstalledOrSource (OnlyInLeft  i  ) = InstalledOnly      i+    toInstalledOrSource (OnlyInRight   a) = SourceOnly           a+    toInstalledOrSource (InBoth      i a) = InstalledAndSource i a      -- pick up cases like base-3 and 4 where one version depends on the other:     pairs = Map.fromList@@ -182,135 +253,348 @@       ,    any ((pkgid1==) . packageId) (depends pkg2)         || any ((pkgid2==) . packageId) (depends pkg1) ] ++-- | The package targets.+--+packages :: (Package installed, Package source)+         => Constraints installed source reason+         -> Set PackageName+packages (Constraints ts _ _ _ _) = ts++ -- | The package choices that are still available. ---choices :: (Package installed, Package available)-        => Constraints installed available reason-        -> PackageIndex (InstalledOrAvailable installed available)-choices (Constraints available _ _ _) = available+choices :: (Package installed, Package source)+        => Constraints installed source reason+        -> PackageIndex (InstalledOrSource installed source)+choices (Constraints _ available _ _ _) = available -isPaired :: (Package installed, Package available)-         => Constraints installed available reason-         -> PackageIdentifier -> Maybe PackageIdentifier-isPaired (Constraints _ pairs _ _) (PackageIdentifier name version) =+isPaired :: (Package installed, Package source)+         => Constraints installed source reason+         -> PackageId -> Maybe PackageId+isPaired (Constraints _ _ _ pairs _) (PackageIdentifier name version) =   case Map.lookup name pairs of     Just (v1, v2)       | version == v1 -> Just (PackageIdentifier name v2)       | version == v2 -> Just (PackageIdentifier name v1)     _                 -> Nothing + data Satisfiable constraints discarded reason        = Satisfiable constraints discarded        | Unsatisfiable-       | ConflictsWith [(PackageIdentifier, [reason])]+       | ConflictsWith [(PackageId, [reason])] -constrain :: (Package installed, Package available)-          => TaggedDependency-          -> reason-          -> Constraints installed available reason-          -> Satisfiable (Constraints installed available reason)-                         [PackageIdentifier] reason-constrain (TaggedDependency installedConstraint (Dependency name versionRange))-          reason constraints@(Constraints available paired excluded original) -  | not anyRemaining+addTarget :: (Package installed, Package source)+          => PackageName+          -> Constraints installed source reason+          -> Satisfiable (Constraints installed source reason)+                         () reason+addTarget pkgname+          constraints@(Constraints targets available excluded paired original)++    -- If it's already a target then there's no change+  | pkgname `Set.member` targets+  = Satisfiable constraints ()++    -- If there is some possible choice available for this target then we're ok+  | PackageIndex.elemByPackageName available pkgname+  = let targets'     = Set.insert pkgname targets+        constraints' = Constraints targets' available excluded paired original+     in assert (constraints `transitionsTo` constraints') $+        Satisfiable constraints' ()++    -- If it's not available and it is excluded then we return the conflicts+  | PackageIndex.elemByPackageName excluded pkgname+  = ConflictsWith conflicts++    -- Otherwise, it's not available and it has not been excluded so the+    -- package is simply completely unknown.+  | otherwise+  = Unsatisfiable++  where+    conflicts =+      [ (packageId pkg, reasons)+      | let excludedChoices = PackageIndex.lookupPackageName excluded pkgname+      , ExcludedPkg pkg isReasons iReasons sReasons <- excludedChoices+      , let reasons = isReasons ++ iReasons ++ sReasons ]+++constrain :: (Package installed, Package source)+          => PackageName                -- ^ which package to constrain+          -> (Version -> Bool -> Bool)  -- ^ the constraint test+          -> reason                     -- ^ the reason for the constraint+          -> Constraints installed source reason+          -> Satisfiable (Constraints installed source reason)+                         [PackageId] reason+constrain pkgname constraint reason+          constraints@(Constraints targets available excluded paired original)++  | pkgname `Set.member` targets  &&  not anyRemaining   = if null conflicts then Unsatisfiable                       else ConflictsWith conflicts    | otherwise-  = let constraints' = Constraints available' paired excluded' original+  = let constraints' = Constraints targets available' excluded' paired original      in assert (constraints `transitionsTo` constraints') $         Satisfiable constraints' (map packageId newExcluded)    where-  -- This tells us if any packages would remain at all for this package name if-  -- we applied this constraint. This amounts to checking if any package-  -- satisfies the given constraint, including version range and installation-  -- status.-  ---  anyRemaining = any satisfiesConstraint availableChoices+    -- This tells us if any packages would remain at all for this package name if+    -- we applied this constraint. This amounts to checking if any package+    -- satisfies the given constraint, including version range and installation+    -- status.+    --+    (available', excluded', newExcluded, anyRemaining, conflicts) =+      updatePkgsStatus+        available excluded+        [] False []+        (mergeBy (\pkg pkg' -> packageVersion pkg `compare` packageVersion pkg')+                 (PackageIndex.lookupPackageName available pkgname)+                 (PackageIndex.lookupPackageName excluded  pkgname)) -  conflicts = [ (packageId pkg, reasonsAvail ++ reasonsAll)-              | ExcludedPackage pkg reasonsAvail reasonsAll <- excludedChoices-              , satisfiesVersionConstraint pkg ]+    testConstraint pkg =+      let ver = packageVersion pkg in+      case Map.lookup (packageName pkg) paired of -  -- Applying this constraint may involve deleting some choices for this-  -- package name, or restricting which install states are available.-  available' = updateAvailable available-  updateAvailable = flip (foldl' (flip update)) availableChoices where-    update pkg | not (satisfiesVersionConstraint pkg)-               = PackageIndex.deletePackageId (packageId pkg)-    update _   | installedConstraint == NoInstalledConstraint-               = id-    update pkg = case pkg of-      InstalledOnly         _   -> id-      AvailableOnly           _ -> PackageIndex.deletePackageId (packageId pkg)-      InstalledAndAvailable i _ -> PackageIndex.insert (InstalledOnly i)+        Just (v1, v2)+          | ver == v1 || ver == v2+          -> case pkg of+               InstalledOnly ipkg -> InstalledOnly (ipkg, iOk)+               SourceOnly    spkg -> SourceOnly    (spkg, sOk)+               InstalledAndSource ipkg spkg ->+                 InstalledAndSource (ipkg, iOk) (spkg, sOk)+          where+            iOk = constraint v1 True  || constraint v2 True+            sOk = constraint v1 False || constraint v2 False -  -- Applying the constraint means adding exclusions for the packages that-  -- we're just freshly excluding, ie the ones we're removing from available.-  excluded' = foldl' (flip PackageIndex.insert) excluded-                (newExcluded ++ oldExcluded)+        _ -> case pkg of+               InstalledOnly ipkg -> InstalledOnly (ipkg, iOk)+               SourceOnly    spkg -> SourceOnly    (spkg, sOk)+               InstalledAndSource ipkg spkg ->+                 InstalledAndSource (ipkg, iOk) (spkg, sOk)+          where+            iOk = constraint ver True+            sOk = constraint ver False -  newExcluded = catMaybes (map exclude availableChoices) where-    exclude pkg-      | not (satisfiesVersionConstraint pkg)-      = Just (ExcludedPackage pkgid [] [reason])-      | installedConstraint == NoInstalledConstraint-      = Nothing-      | otherwise = case pkg of-      InstalledOnly         _   -> Nothing-      AvailableOnly           _ -> Just (ExcludedPackage pkgid [] [reason])-      InstalledAndAvailable _ _ ->-        case PackageIndex.lookupPackageId excluded pkgid of-          Just (ExcludedPackage _ avail both)-                  -> Just (ExcludedPackage pkgid (reason:avail) both)-          Nothing -> Just (ExcludedPackage pkgid [reason] [])-      where pkgid = packageId pkg+    -- For the info about available and excluded versions of the package in+    -- question, update the info given the current constraint+    --+    -- We update the available package map and the excluded package map+    -- we also collect:+    --   * the change in available packages (for logging)+    --   * whether there are any remaining choices+    --   * any constraints that conflict with the current constraint -  -- Additionally we have to add extra exclusions for any already-excluded-  -- packages that happen to be covered by the (inverse of the) constraint.-  oldExcluded = catMaybes (map exclude excludedChoices) where-    exclude (ExcludedPackage pkgid avail both)-      -- if it doesn't satisfy the version constraint then we exclude the-      -- package as a whole, the available or the installed instances or both.-      | not (satisfiesVersionConstraint pkgid)-      = Just (ExcludedPackage pkgid avail (reason:both))-      -- if on the other hand it does satisfy the constraint and we were also-      -- constraining to just the installed version then we exclude just the-      -- available instance.-      | installedConstraint == InstalledConstraint-      = Just (ExcludedPackage pkgid (reason:avail) both)-      | otherwise = Nothing+    updatePkgsStatus _ _ nePkgs ok cs _+      | seq nePkgs $ seq ok $ seq cs False = undefined -  -- util definitions-  availableChoices = PackageIndex.lookupPackageName available name-  excludedChoices  = PackageIndex.lookupPackageName excluded  name+    updatePkgsStatus aPkgs ePkgs nePkgs ok cs []+      = (aPkgs, ePkgs, reverse nePkgs, ok, reverse cs) -  satisfiesConstraint pkg = satisfiesVersionConstraint pkg-                         && satisfiesInstallStateConstraint pkg+    updatePkgsStatus aPkgs ePkgs nePkgs ok cs (pkg:pkgs) =+        let (aPkgs', ePkgs', mnePkg, ok', mc) = updatePkgStatus aPkgs ePkgs pkg+            nePkgs' = maybeCons mnePkg nePkgs+            cs'     = maybeCons mc cs+         in updatePkgsStatus aPkgs' ePkgs' nePkgs' (ok' || ok) cs' pkgs -  satisfiesVersionConstraint :: Package pkg => pkg -> Bool-  satisfiesVersionConstraint = case Map.lookup name paired of-    Nothing       -> \pkg ->-      packageVersion pkg `withinRange` versionRange-    Just (v1, v2) -> \pkg -> case packageVersion pkg of-      v | v == v1-       || v == v2   -> v1 `withinRange` versionRange-                    || v2 `withinRange` versionRange-        | otherwise -> v `withinRange` versionRange+    maybeCons Nothing  xs = xs+    maybeCons (Just x) xs = x:xs -  satisfiesInstallStateConstraint = case installedConstraint of-    NoInstalledConstraint -> \_   -> True-    InstalledConstraint   -> \pkg -> case pkg of-      AvailableOnly _             -> False-      _                           -> True -conflicting :: (Package installed, Package available)-            => Constraints installed available reason+    -- For the info about an available or excluded version of the package in+    -- question, update the info given the current constraint.+    --+    updatePkgStatus aPkgs ePkgs pkg =+      case viewPackageStatus pkg of+        AllAvailable (InstalledOnly (aiPkg, False)) ->+          removeAvailable False+            (InstalledOnly aiPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (InstalledOnly aiPkg) [] [reason] [])+            Nothing++        AllAvailable (SourceOnly (asPkg, False)) ->+          removeAvailable False+            (SourceOnly asPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (SourceOnly asPkg) [] [] [reason])+            Nothing++        AllAvailable (InstalledAndSource (aiPkg, False) (asPkg, False)) ->+          removeAvailable False+            (InstalledAndSource aiPkg asPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (InstalledAndSource aiPkg asPkg) [reason] [] [])+            Nothing++        AllAvailable (InstalledAndSource (aiPkg, True) (asPkg, False)) ->+          removeAvailable True+            (SourceOnly asPkg)+            (PackageIndex.insert (InstalledOnly aiPkg))+            (ExcludedPkg (SourceOnly asPkg) [] [] [reason])+            Nothing++        AllAvailable (InstalledAndSource (aiPkg, False) (asPkg, True)) ->+          removeAvailable True+            (InstalledOnly aiPkg)+            (PackageIndex.insert (SourceOnly asPkg))+            (ExcludedPkg (InstalledOnly aiPkg) [] [reason] [])+            Nothing++        AllAvailable _ -> noChange True Nothing++        AvailableExcluded (aiPkg, False) (ExcludedPkg (esPkg, False) _ _ srs) ->+          removeAvailable False+            (InstalledOnly aiPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (InstalledAndSource aiPkg esPkg) [reason] [] srs)+            Nothing++        AvailableExcluded (_aiPkg, True) (ExcludedPkg (esPkg, False) _ _ srs) ->+          addExtraExclusion True+            (ExcludedPkg (SourceOnly esPkg) [] [] (reason:srs))+            Nothing++        AvailableExcluded (aiPkg, False) (ExcludedPkg (esPkg, True) _ _ srs) ->+          removeAvailable  True+            (InstalledOnly aiPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (InstalledAndSource aiPkg esPkg) [] [reason] srs)+            (Just (pkgid, srs))++        AvailableExcluded (_aiPkg, True) (ExcludedPkg (_esPkg, True) _ _ srs) ->+          noChange True+            (Just (pkgid, srs))++        ExcludedAvailable (ExcludedPkg (eiPkg, False) _ irs _) (asPkg, False) ->+          removeAvailable  False+            (SourceOnly asPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (InstalledAndSource eiPkg asPkg) [reason] irs [])+            Nothing++        ExcludedAvailable (ExcludedPkg (eiPkg, True) _ irs _) (asPkg, False) ->+          removeAvailable False+            (SourceOnly asPkg)+            (PackageIndex.deletePackageId pkgid)+            (ExcludedPkg (InstalledAndSource eiPkg asPkg) [] irs [reason])+            (Just (pkgid, irs))++        ExcludedAvailable (ExcludedPkg (eiPkg, False) _ irs _) (_asPkg, True) ->+          addExtraExclusion True+            (ExcludedPkg (InstalledOnly eiPkg) [] (reason:irs) [])+            Nothing++        ExcludedAvailable (ExcludedPkg (_eiPkg, True) _ irs _) (_asPkg, True) ->+          noChange True+            (Just (pkgid, irs))++        AllExcluded (ExcludedPkg (InstalledOnly (eiPkg, False)) _ irs _) ->+          addExtraExclusion False+            (ExcludedPkg (InstalledOnly eiPkg) [] (reason:irs) [])+            Nothing++        AllExcluded (ExcludedPkg (InstalledOnly (_eiPkg, True)) _ irs _) ->+          noChange False+            (Just (pkgid, irs))++        AllExcluded (ExcludedPkg (SourceOnly (esPkg, False)) _ _ srs) ->+          addExtraExclusion False+            (ExcludedPkg (SourceOnly esPkg) [] [] (reason:srs))+            Nothing++        AllExcluded (ExcludedPkg (SourceOnly (_esPkg, True)) _ _ srs) ->+          noChange False+            (Just (pkgid, srs))++        AllExcluded (ExcludedPkg (InstalledAndSource (eiPkg, False) (esPkg, False)) isrs irs srs) ->+          addExtraExclusion False+            (ExcludedPkg (InstalledAndSource eiPkg esPkg) (reason:isrs) irs srs)+            Nothing++        AllExcluded (ExcludedPkg (InstalledAndSource (eiPkg, True) (esPkg, False)) isrs irs srs) ->+          addExtraExclusion False+            (ExcludedPkg (InstalledAndSource eiPkg esPkg) isrs irs (reason:srs))+            (Just (pkgid, irs))++        AllExcluded (ExcludedPkg (InstalledAndSource (eiPkg, False) (esPkg, True)) isrs irs srs) ->+          addExtraExclusion False+            (ExcludedPkg (InstalledAndSource eiPkg esPkg) isrs (reason:irs) srs)+            (Just (pkgid, srs))++        AllExcluded (ExcludedPkg (InstalledAndSource (_eiPkg, True) (_esPkg, True)) isrs irs srs) ->+          noChange False+            (Just (pkgid, isrs ++ irs ++ srs))++      where+        removeAvailable ok nePkg adjustAvailable ePkg c =+          let aPkgs' = adjustAvailable aPkgs+              ePkgs' = PackageIndex.insert ePkg ePkgs+           in aPkgs' `seq` ePkgs' `seq`+              (aPkgs', ePkgs', Just nePkg, ok, c)++        addExtraExclusion ok ePkg c =+          let ePkgs' = PackageIndex.insert ePkg ePkgs+           in ePkgs' `seq`+              (aPkgs, ePkgs', Nothing, ok, c)++        noChange ok c =+          (aPkgs, ePkgs, Nothing, ok, c)++        pkgid = case pkg of OnlyInLeft  p   -> packageId p+                            OnlyInRight p   -> packageId p+                            InBoth      p _ -> packageId p+++    viewPackageStatus+      :: (Package installed, Package source)+      => MergeResult (InstalledOrSource installed source)+                     (ExcludedPkg (InstalledOrSource installed source) reason)+      -> PackageStatus (installed, Bool) (source, Bool) reason+    viewPackageStatus merged =+        case merged of+          OnlyInLeft aPkg ->+            AllAvailable (testConstraint aPkg)++          OnlyInRight (ExcludedPkg ePkg isrs irs srs) ->+            AllExcluded (ExcludedPkg (testConstraint ePkg) isrs irs srs)++          InBoth (InstalledOnly aiPkg)+                 (ExcludedPkg (SourceOnly esPkg) [] [] srs) ->+            case testConstraint (InstalledAndSource aiPkg esPkg) of+              InstalledAndSource (aiPkg', iOk) (esPkg', sOk) ->+                AvailableExcluded (aiPkg', iOk) (ExcludedPkg (esPkg', sOk) [] [] srs)+              _ -> impossible++          InBoth (SourceOnly asPkg)+                 (ExcludedPkg (InstalledOnly eiPkg) [] irs []) ->+            case testConstraint (InstalledAndSource eiPkg asPkg) of+              InstalledAndSource (eiPkg', iOk) (asPkg', sOk) ->+                ExcludedAvailable (ExcludedPkg (eiPkg', iOk) [] irs []) (asPkg', sOk)+              _ -> impossible+          _ -> impossible+      where+        impossible = error "impossible: viewPackageStatus invariant violation"++-- A intermediate structure that enumerates all the possible cases given the+-- invariant. This helps us to get simpler and complete pattern matching in+-- updatePkg above+--+data PackageStatus installed source reason+   = AllAvailable (InstalledOrSource installed source)+   | AllExcluded  (ExcludedPkg (InstalledOrSource installed source) reason)+   | AvailableExcluded installed (ExcludedPkg source reason)+   | ExcludedAvailable (ExcludedPkg installed reason) source+++conflicting :: (Package installed, Package source)+            => Constraints installed source reason             -> Dependency-            -> [(PackageIdentifier, [reason])]-conflicting (Constraints _ _ excluded _) dep =-  [ (pkgid, reasonsAvail ++ reasonsAll) --TODO-  | ExcludedPackage pkgid reasonsAvail reasonsAll <-+            -> [(PackageId, [reason])]+conflicting (Constraints _ _ excluded _ _) dep =+  [ (packageId pkg, reasonsAll ++ reasonsAvail ++ reasonsInstalled) --TODO+  | ExcludedPkg pkg reasonsAll reasonsAvail reasonsInstalled <-       PackageIndex.lookupDependency excluded dep ]
Distribution/Client/Dependency/TopDown/Types.hs view
@@ -13,7 +13,7 @@ module Distribution.Client.Dependency.TopDown.Types where  import Distribution.Client.Types-         ( AvailablePackage(..), InstalledPackage )+         ( SourcePackage(..), InstalledPackage, OptionalStanza )  import Distribution.Package          ( PackageIdentifier, Dependency@@ -26,15 +26,16 @@ -- ------------------------------------------------------------  type SelectablePackage-   = InstalledOrAvailable InstalledPackageEx UnconfiguredPackage+   = InstalledOrSource InstalledPackageEx UnconfiguredPackage  type SelectedPackage-   = InstalledOrAvailable InstalledPackageEx SemiConfiguredPackage+   = InstalledOrSource InstalledPackageEx SemiConfiguredPackage -data InstalledOrAvailable installed available-   = InstalledOnly         installed-   | AvailableOnly                   available-   | InstalledAndAvailable installed available+data InstalledOrSource installed source+   = InstalledOnly      installed+   | SourceOnly                   source+   | InstalledAndSource installed source+  deriving Eq  type TopologicalSortNumber = Int @@ -46,14 +47,16 @@  data UnconfiguredPackage    = UnconfiguredPackage-       AvailablePackage+       SourcePackage        !TopologicalSortNumber        FlagAssignment+       [OptionalStanza]  data SemiConfiguredPackage    = SemiConfiguredPackage-       AvailablePackage  -- package info+       SourcePackage     -- package info        FlagAssignment    -- total flag assignment for the package+       [OptionalStanza]  -- enabled optional stanzas        [Dependency]      -- dependencies we end up with when we apply                          -- the flag assignment @@ -64,30 +67,25 @@   depends (InstalledPackageEx _ _ deps) = deps  instance Package UnconfiguredPackage where-  packageId (UnconfiguredPackage p _ _) = packageId p+  packageId (UnconfiguredPackage p _ _ _) = packageId p  instance Package SemiConfiguredPackage where-  packageId (SemiConfiguredPackage p _ _) = packageId p+  packageId (SemiConfiguredPackage p _ _ _) = packageId p -instance (Package installed, Package available)-      => Package (InstalledOrAvailable installed available) where-  packageId (InstalledOnly         p  ) = packageId p-  packageId (AvailableOnly         p  ) = packageId p-  packageId (InstalledAndAvailable p _) = packageId p+instance (Package installed, Package source)+      => Package (InstalledOrSource installed source) where+  packageId (InstalledOnly      p  ) = packageId p+  packageId (SourceOnly         p  ) = packageId p+  packageId (InstalledAndSource p _) = packageId p --- --------------------------------------------------------------- * Tagged Dependency type--- ------------------------------------------------------------ --- | Installed packages can only depend on other installed packages while--- packages that are not yet installed but which we plan to install can depend--- on installed or other not-yet-installed packages.+-- | We can have constraints on selecting just installed or just source+-- packages. ----- This makes life more complex as we have to remember these constraints.+-- In particular, installed packages can only depend on other installed+-- packages while packages that are not yet installed but which we plan to+-- install can depend on installed or other not-yet-installed packages. ---data TaggedDependency = TaggedDependency InstalledConstraint Dependency-data InstalledConstraint = InstalledConstraint | NoInstalledConstraint-  deriving Eq--untagDependency :: TaggedDependency -> Dependency-untagDependency (TaggedDependency _ dep) = dep+data InstalledConstraint = InstalledConstraint+                         | SourceConstraint+  deriving (Eq, Show)
Distribution/Client/Dependency/Types.hs view
@@ -11,35 +11,91 @@ -- Common types for dependency resolution. ----------------------------------------------------------------------------- module Distribution.Client.Dependency.Types (+    ExtDependency(..),++    PreSolver(..),+    Solver(..),     DependencyResolver,      PackageConstraint(..),     PackagePreferences(..),     InstalledPreference(..),+    PackagesPreferenceDefault(..),      Progress(..),     foldProgress,   ) where +import Control.Applicative+         ( Applicative(..), Alternative(..) )++import Data.Char+         ( isAlpha, toLower )+import Data.Monoid+         ( Monoid(..) )+ import Distribution.Client.Types-         ( AvailablePackage(..), InstalledPackage )+         ( OptionalStanza, SourcePackage(..) ) import qualified Distribution.Client.InstallPlan as InstallPlan +import Distribution.Compat.ReadP+         ( (<++) )++import qualified Distribution.Compat.ReadP as Parse+         ( pfail, munch1 ) import Distribution.PackageDescription          ( FlagAssignment )-import Distribution.Client.PackageIndex+import qualified Distribution.Client.PackageIndex as PackageIndex          ( PackageIndex )+import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex+         ( PackageIndex ) import Distribution.Package-         ( PackageName )+         ( Dependency, PackageName, InstalledPackageId ) import Distribution.Version          ( VersionRange ) import Distribution.Compiler          ( CompilerId ) import Distribution.System          ( Platform )+import Distribution.Text+         ( Text(..) ) +import Text.PrettyPrint+         ( text )+ import Prelude hiding (fail) +-- | Covers source dependencies and installed dependencies in+-- one type.+data ExtDependency = SourceDependency Dependency+                   | InstalledDependency InstalledPackageId++instance Text ExtDependency where+  disp (SourceDependency    dep) = disp dep+  disp (InstalledDependency dep) = disp dep++  parse = (SourceDependency `fmap` parse) <++ (InstalledDependency `fmap` parse)++-- | All the solvers that can be selected.+data PreSolver = AlwaysTopDown | AlwaysModular | Choose+  deriving (Eq, Ord, Show, Bounded, Enum)++-- | All the solvers that can be used.+data Solver = TopDown | Modular+  deriving (Eq, Ord, Show, Bounded, Enum)++instance Text PreSolver where+  disp AlwaysTopDown = text "topdown"+  disp AlwaysModular = text "modular"+  disp Choose        = text "choose"+  parse = do+    name <- Parse.munch1 isAlpha+    case map toLower name of+      "topdown" -> return AlwaysTopDown+      "modular" -> return AlwaysModular+      "choose"  -> return Choose+      _         -> Parse.pfail+ -- | A dependency resolver is a function that works out an installation plan -- given the set of installed and available packages and a set of deps to -- solve for.@@ -50,8 +106,8 @@ -- type DependencyResolver = Platform                        -> CompilerId-                       -> PackageIndex InstalledPackage-                       -> PackageIndex AvailablePackage+                       -> InstalledPackageIndex.PackageIndex+                       ->          PackageIndex.PackageIndex SourcePackage                        -> (PackageName -> PackagePreferences)                        -> [PackageConstraint]                        -> [PackageName]@@ -63,9 +119,11 @@ -- range or inconsistent flag assignment). -- data PackageConstraint-   = PackageVersionConstraint   PackageName VersionRange-   | PackageInstalledConstraint PackageName-   | PackageFlagsConstraint     PackageName FlagAssignment+   = PackageConstraintVersion   PackageName VersionRange+   | PackageConstraintInstalled PackageName+   | PackageConstraintSource    PackageName+   | PackageConstraintFlags     PackageName FlagAssignment+   | PackageConstraintStanzas   PackageName [OptionalStanza]   deriving (Show,Eq)  -- | A per-package preference on the version. It is a soft constraint that the@@ -85,6 +143,30 @@ -- data InstalledPreference = PreferInstalled | PreferLatest +-- | Global policy for all packages to say if we prefer package versions that+-- are already installed locally or if we just prefer the latest available.+--+data PackagesPreferenceDefault =++     -- | Always prefer the latest version irrespective of any existing+     -- installed version.+     --+     -- * This is the standard policy for upgrade.+     --+     PreferAllLatest++     -- | Always prefer the installed versions over ones that would need to be+     -- installed. Secondarily, prefer latest versions (eg the latest installed+     -- version or if there are none then the latest source version).+   | PreferAllInstalled++     -- | Prefer the latest version for packages that are explicitly requested+     -- but prefers the installed version for any other packages.+     --+     -- * This is the standard policy for install.+     --+   | PreferLatestForSelected+ -- | A type to represent the unfolding of an expensive long running -- calculation that may fail. We may get intermediate steps before the final -- retult which may be used to indicate progress and\/or logging messages.@@ -113,3 +195,11 @@ instance Monad (Progress step fail) where   return a = Done a   p >>= f  = foldProgress Step Fail f p++instance Applicative (Progress step fail) where+  pure a  = Done a+  p <*> x = foldProgress Step Fail (flip fmap x) p++instance Monoid fail => Alternative (Progress step fail) where+  empty   = Fail mempty+  p <|> q = foldProgress Step (const q) Done p
Distribution/Client/Fetch.hs view
@@ -19,9 +19,8 @@ import Distribution.Client.Targets import Distribution.Client.FetchUtils hiding (fetchPackage) import Distribution.Client.Dependency-import Distribution.Client.PackageIndex (PackageIndex) import Distribution.Client.IndexUtils as IndexUtils-         ( getAvailablePackages, getInstalledPackages )+         ( getSourcePackages, getInstalledPackages ) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.Setup          ( GlobalFlags(..), FetchFlags(..) )@@ -30,6 +29,7 @@          ( packageId ) import Distribution.Simple.Compiler          ( Compiler(compilerId), PackageDBStack )+import Distribution.Simple.PackageIndex (PackageIndex) import Distribution.Simple.Program          ( ProgramConfiguration ) import Distribution.Simple.Setup@@ -79,15 +79,17 @@      mapM_ checkTarget userTargets -    installed     <- getInstalledPackages verbosity comp packageDBs conf-    availableDb   <- getAvailablePackages verbosity repos+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    sourcePkgDb       <- getSourcePackages    verbosity repos      pkgSpecifiers <- resolveUserTargets verbosity-                       globalFlags (packageIndex availableDb) userTargets+                       (fromFlag $ globalWorldFile globalFlags)+                       (packageIndex sourcePkgDb)+                       userTargets      pkgs  <- planPackages                verbosity comp fetchFlags-               installed availableDb pkgSpecifiers+               installedPkgIndex sourcePkgDb pkgSpecifiers      pkgs' <- filterM (fmap not . isFetched . packageSource) pkgs     if null pkgs'@@ -110,25 +112,27 @@ planPackages :: Verbosity              -> Compiler              -> FetchFlags-             -> PackageIndex InstalledPackage-             -> AvailablePackageDb-             -> [PackageSpecifier AvailablePackage]-             -> IO [AvailablePackage]+             -> PackageIndex+             -> SourcePackageDb+             -> [PackageSpecifier SourcePackage]+             -> IO [SourcePackage] planPackages verbosity comp fetchFlags-             installed availableDb pkgSpecifiers+             installedPkgIndex sourcePkgDb pkgSpecifiers    | includeDependencies = do+      solver <- chooseSolver verbosity (fromFlag (fetchSolver fetchFlags)) (compilerId comp)       notice verbosity "Resolving dependencies..."       installPlan <- foldProgress logMsg die return $                        resolveDependencies                          buildPlatform (compilerId comp)+                         solver                          resolverParams        -- The packages we want to fetch are those packages the 'InstallPlan'       -- that are in the 'InstallPlan.Configured' state.       return         [ pkg-        | (InstallPlan.Configured (InstallPlan.ConfiguredPackage pkg _ _))+        | (InstallPlan.Configured (InstallPlan.ConfiguredPackage pkg _ _ _))             <- InstallPlan.toList installPlan ]    | otherwise =@@ -138,16 +142,30 @@   where     resolverParams = +        setMaxBackjumps (if maxBackjumps < 0 then Nothing+                                             else Just maxBackjumps)++      . setIndependentGoals independentGoals++      . setReorderGoals reorderGoals++      . setShadowPkgs shadowPkgs+         -- Reinstall the targets given on the command line so that the dep         -- resolver will decide that they need fetching, even if they're-        -- already installed. Sicne we want to get the source packages of+        -- already installed. Since we want to get the source packages of         -- things we might have installed (but not have the sources for).-        reinstallTargets+      . reinstallTargets -      $ standardInstallPolicy installed availableDb pkgSpecifiers+      $ standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers      includeDependencies = fromFlag (fetchDeps fetchFlags)     logMsg message rest = debug verbosity message >> rest++    reorderGoals     = fromFlag (fetchReorderGoals     fetchFlags)+    independentGoals = fromFlag (fetchIndependentGoals fetchFlags)+    shadowPkgs       = fromFlag (fetchShadowPkgs       fetchFlags)+    maxBackjumps     = fromFlag (fetchMaxBackjumps     fetchFlags)   checkTarget :: UserTarget -> IO ()
Distribution/Client/Haddock.hs view
@@ -10,7 +10,7 @@ -- Interfacing with Haddock -- ------------------------------------------------------------------------------module Distribution.Client.Haddock +module Distribution.Client.Haddock     (      regenerateHaddockIndex     )@@ -28,24 +28,22 @@ import Distribution.Version (Version(Version), orLaterVersion) import Distribution.Verbosity (Verbosity) import Distribution.Text (display)-import Distribution.Client.PackageIndex(PackageIndex, allPackages,-                                        allPackagesByName, fromList)+import Distribution.Simple.PackageIndex (PackageIndex, allPackages,+                                         allPackagesByName, fromList) import Distribution.Simple.Utils          ( comparing, intercalate, debug          , installDirectoryContents, withTempDirectory )-import Distribution.InstalledPackageInfo as InstalledPackageInfo +import Distribution.InstalledPackageInfo as InstalledPackageInfo          ( InstalledPackageInfo          , InstalledPackageInfo_(haddockHTMLs, haddockInterfaces, exposed) )-import Distribution.Client.Types-         ( InstalledPackage(..) ) -regenerateHaddockIndex :: Verbosity -> PackageIndex InstalledPackage -> ProgramConfiguration -> FilePath -> IO ()+regenerateHaddockIndex :: Verbosity -> PackageIndex -> ProgramConfiguration -> FilePath -> IO () regenerateHaddockIndex verbosity pkgs conf index = do       (paths,warns) <- haddockPackagePaths pkgs'       case warns of         Nothing -> return ()         Just m  -> debug verbosity m-      +       (confHaddock, _, _) <-           requireProgramVersion verbosity haddockProgram                                     (orLaterVersion (Version [0,6] [])) conf@@ -63,14 +61,13 @@         rawSystemProgram verbosity confHaddock flags         renameFile (tempDir </> "index.html") (tempDir </> destFile)         installDirectoryContents verbosity tempDir destDir-      -  where ++  where     (destDir,destFile) = splitFileName index-    pkgs' = map (maximumBy $ comparing packageId) -            . allPackagesByName +    pkgs' = map (maximumBy $ comparing packageId)+            . allPackagesByName             . fromList             . filter exposed-            . map (\(InstalledPackage pkg _) -> pkg)             . allPackages             $ pkgs 
Distribution/Client/HttpUtils.hs view
@@ -5,6 +5,7 @@ module Distribution.Client.HttpUtils (     downloadURI,     getHTTP,+    cabalBrowse,     proxy,     isOldHackageURI   ) where@@ -17,10 +18,10 @@ import Network.Stream          ( Result, ConnError(..) ) import Network.Browser-         ( Proxy (..), Authority (..), browse-         , setOutHandler, setErrHandler, setProxy, request)+         ( Proxy (..), Authority (..), BrowserAction, browse+         , setOutHandler, setErrHandler, setProxy, setAuthorityGen, request) import Control.Monad-         ( mplus, join, liftM2 )+         ( mplus, join, liftM, liftM2 ) import qualified Data.ByteString.Lazy.Char8 as ByteString import Data.ByteString.Lazy (ByteString) #ifdef WIN32@@ -151,15 +152,22 @@  -- |Carry out a GET request, using the local proxy settings getHTTP :: Verbosity -> URI -> IO (Result (Response ByteString))-getHTTP verbosity uri = do-                 p   <- proxy verbosity-                 let req = mkRequest uri-                 (_, resp) <- browse $ do-                                setErrHandler (warn verbosity . ("http error: "++))-                                setOutHandler (debug verbosity)-                                setProxy p-                                request req-                 return (Right resp)+getHTTP verbosity uri = liftM (\(_, resp) -> Right resp) $+                              cabalBrowse verbosity (return ()) (request (mkRequest uri))++cabalBrowse :: Verbosity+            -> BrowserAction s ()+            -> BrowserAction s a+            -> IO a+cabalBrowse verbosity auth act = do+    p   <- proxy verbosity+    browse $ do+        setProxy p+        setErrHandler (warn verbosity . ("http error: "++))+        setOutHandler (debug verbosity)+        auth+        setAuthorityGen (\_ _ -> return Nothing)+        act  downloadURI :: Verbosity             -> URI      -- ^ What to download
Distribution/Client/IndexUtils.hs view
@@ -12,10 +12,13 @@ ----------------------------------------------------------------------------- module Distribution.Client.IndexUtils (   getInstalledPackages,-  getAvailablePackages,+  getSourcePackages,+  convert,    readPackageIndexFile,-  parseRepoIndex,+  parsePackageIndex,+  readRepoIndex,+  updateRepoIndexCache,   ) where  import qualified Distribution.Client.Tar as Tar@@ -23,7 +26,7 @@  import Distribution.Package          ( PackageId, PackageIdentifier(..), PackageName(..)-         , Package(..), packageVersion+         , Package(..), packageVersion, packageName          , Dependency(Dependency), InstalledPackageId(..) ) import Distribution.Client.PackageIndex (PackageIndex) import qualified Distribution.Client.PackageIndex as PackageIndex@@ -44,53 +47,57 @@ import Distribution.Version          ( Version(Version), intersectVersionRanges ) import Distribution.Text-         ( simpleParse )+         ( display, simpleParse ) import Distribution.Verbosity          ( Verbosity, lessVerbose ) import Distribution.Simple.Utils-         ( warn, info, fromUTF8, equating )+         ( die, warn, info, fromUTF8, equating ) +import Data.Char   (isAlphaNum) import Data.Maybe  (catMaybes, fromMaybe) import Data.List   (isPrefixOf, groupBy) import Data.Monoid (Monoid(..)) import qualified Data.Map as Map-import Control.Monad (MonadPlus(mplus), when)+import Control.Monad (MonadPlus(mplus), when, unless, liftM) import Control.Exception (evaluate) import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy.Char8 as BS.Char8+import qualified Data.ByteString.Char8 as BSS import Data.ByteString.Lazy (ByteString) import Distribution.Client.GZipUtils (maybeDecompress) import System.FilePath ((</>), takeExtension, splitDirectories, normalise) import System.FilePath.Posix as FilePath.Posix          ( takeFileName )+import System.IO+import System.IO.Unsafe (unsafeInterleaveIO) import System.IO.Error (isDoesNotExistError) import System.Directory-         ( getModificationTime )+         ( getModificationTime, doesFileExist ) import System.Time          ( getClockTime, diffClockTimes, normalizeTimeDiff, TimeDiff(tdDay) ) + getInstalledPackages :: Verbosity -> Compiler                      -> PackageDBStack -> ProgramConfiguration-                     -> IO (PackageIndex InstalledPackage)+                     -> IO InstalledPackageIndex.PackageIndex getInstalledPackages verbosity comp packageDbs conf =-    fmap convert (Configure.getInstalledPackages verbosity'-                                                 comp packageDbs conf)+    Configure.getInstalledPackages verbosity' comp packageDbs conf   where     --FIXME: make getInstalledPackages use sensible verbosity in the first place     verbosity'  = lessVerbose verbosity -    convert :: InstalledPackageIndex.PackageIndex -> PackageIndex InstalledPackage-    convert index = PackageIndex.fromList-      -- There can be multiple installed instances of each package version,-      -- like when the same package is installed in the global & user dbs.-      -- InstalledPackageIndex.allPackagesByName gives us the installed-      -- packages with the most preferred instances first, so by picking the-      -- first we should get the user one. This is almost but not quite the-      -- same as what ghc does.-      [ InstalledPackage ipkg (sourceDeps index ipkg)-      | ipkgs <- InstalledPackageIndex.allPackagesByName index-      , (ipkg:_) <- groupBy (equating packageVersion) ipkgs ]-+convert :: InstalledPackageIndex.PackageIndex -> PackageIndex InstalledPackage+convert index' = PackageIndex.fromList+  -- There can be multiple installed instances of each package version,+  -- like when the same package is installed in the global & user dbs.+  -- InstalledPackageIndex.allPackagesByName gives us the installed+  -- packages with the most preferred instances first, so by picking the+  -- first we should get the user one. This is almost but not quite the+  -- same as what ghc does.+  [ InstalledPackage ipkg (sourceDeps index' ipkg)+  | ipkgs <- InstalledPackageIndex.allPackagesByName index'+  , (ipkg:_) <- groupBy (equating packageVersion) ipkgs ]+  where     -- The InstalledPackageInfo only lists dependencies by the     -- InstalledPackageId, which means we do not directly know the corresponding     -- source dependency. The only way to find out is to lookup the@@ -109,23 +116,27 @@     brokenPackageId (InstalledPackageId str) =       PackageIdentifier (PackageName (str ++ "-broken")) (Version [] []) +------------------------------------------------------------------------+-- Reading the source package index+--+ -- | Read a repository index from disk, from the local files specified by -- a list of 'Repo's. ----- All the 'AvailablePackage's are marked as having come from the appropriate+-- All the 'SourcePackage's are marked as having come from the appropriate -- 'Repo'. -- -- This is a higher level wrapper used internally in cabal-install. ---getAvailablePackages :: Verbosity -> [Repo] -> IO AvailablePackageDb-getAvailablePackages verbosity [] = do+getSourcePackages :: Verbosity -> [Repo] -> IO SourcePackageDb+getSourcePackages verbosity [] = do   warn verbosity $ "No remote package servers have been specified. Usually "                 ++ "you would have one specified in the config file."-  return AvailablePackageDb {+  return SourcePackageDb {     packageIndex       = mempty,     packagePreferences = mempty   }-getAvailablePackages verbosity repos = do+getSourcePackages verbosity repos = do   info verbosity "Reading available packages..."   pkgss <- mapM (readRepoIndex verbosity) repos   let (pkgs, prefs) = mconcat pkgss@@ -133,50 +144,39 @@                  [ (name, range) | Dependency name range <- prefs ]   _ <- evaluate pkgs   _ <- evaluate prefs'-  return AvailablePackageDb {+  return SourcePackageDb {     packageIndex       = pkgs,     packagePreferences = prefs'   } + -- | Read a repository index from disk, from the local file specified by -- the 'Repo'. ----- All the 'AvailablePackage's are marked as having come from the given 'Repo'.+-- All the 'SourcePackage's are marked as having come from the given 'Repo'. -- -- This is a higher level wrapper used internally in cabal-install. -- readRepoIndex :: Verbosity -> Repo-              -> IO (PackageIndex AvailablePackage, [Dependency])-readRepoIndex verbosity repo = handleNotFound $ do+              -> IO (PackageIndex SourcePackage, [Dependency])+readRepoIndex verbosity repo =   let indexFile = repoLocalDir repo </> "00-index.tar"-  (pkgs, prefs) <- either fail return-                 . foldlTarball extract ([], [])-               =<< BS.readFile indexFile+      cacheFile = repoLocalDir repo </> "00-index.cache"+  in handleNotFound $ do+    warnIfIndexIsOld indexFile+    whenCacheOutOfDate indexFile cacheFile $ do+      info verbosity $ "Updating the index cache file..."+      updatePackageIndexCacheFile indexFile cacheFile+    readPackageIndexCacheFile mkAvailablePackage indexFile cacheFile -  pkgIndex <- evaluate $ PackageIndex.fromList-    [ AvailablePackage {+  where+    mkAvailablePackage pkgid pkg =+      SourcePackage {         packageInfoId      = pkgid,         packageDescription = pkg,         packageSource      = RepoTarballPackage repo pkgid Nothing       }-    | (pkgid, pkg) <- pkgs] -  warnIfIndexIsOld indexFile-  return (pkgIndex, prefs)--  where-    extract (pkgs, prefs) entry = fromMaybe (pkgs, prefs) $-              (do pkg <- extractPkg entry; return (pkg:pkgs, prefs))-      `mplus` (do prefs' <- extractPrefs entry; return (pkgs, prefs'++prefs))--    extractPrefs :: Tar.Entry -> Maybe [Dependency]-    extractPrefs entry = case Tar.entryContent entry of-      Tar.NormalFile content _-         | takeFileName (Tar.entryPath entry) == "preferred-versions"-        -> Just . parsePreferredVersions-         . BS.Char8.unpack $ content-      _ -> Nothing-     handleNotFound action = catch action $ \e -> if isDoesNotExistError e       then do         case repoKind repo of@@ -201,12 +201,33 @@           ++ "'cabal update' to get the latest list of available packages."         Right _localRepo -> return () -parsePreferredVersions :: String -> [Dependency]-parsePreferredVersions = catMaybes-                       . map simpleParse-                       . filter (not . isPrefixOf "--")-                       . lines+-- | It is not necessary to call this, as the cache will be updated when the+-- index is read normally. However you can do the work earlier if you like.+--+updateRepoIndexCache :: Verbosity -> Repo -> IO ()+updateRepoIndexCache verbosity repo =+    whenCacheOutOfDate indexFile cacheFile $ do+      info verbosity $ "Updating the index cache file..."+      updatePackageIndexCacheFile indexFile cacheFile+  where+    indexFile = repoLocalDir repo </> "00-index.tar"+    cacheFile = repoLocalDir repo </> "00-index.cache" +whenCacheOutOfDate :: FilePath-> FilePath -> IO () -> IO ()+whenCacheOutOfDate origFile cacheFile action = do+  exists <- doesFileExist cacheFile+  if not exists+    then action+    else do+      origTime  <- getModificationTime origFile+      cacheTime <- getModificationTime cacheFile+      unless (cacheTime >= origTime) action+++------------------------------------------------------------------------+-- Reading the index file+--+ -- | Read a compressed \"00-index.tar.gz\" file into a 'PackageIndex'. -- -- This is supposed to be an \"all in one\" way to easily get at the info in@@ -218,23 +239,56 @@ -- readPackageIndexFile :: Package pkg                      => (PackageId -> GenericPackageDescription -> pkg)-                     -> FilePath -> IO (PackageIndex pkg)+                     -> FilePath+                     -> IO (PackageIndex pkg, [Dependency]) readPackageIndexFile mkPkg indexFile = do-  pkgs <- either fail return-        . parseRepoIndex-        . maybeDecompress-      =<< BS.readFile indexFile-  -  evaluate $ PackageIndex.fromList-   [ mkPkg pkgid pkg | (pkgid, pkg) <- pkgs]+  (pkgs, prefs) <- either fail return+                 . parsePackageIndex+                 . maybeDecompress+               =<< BS.readFile indexFile +  pkgs' <- evaluate $ PackageIndex.fromList+            [ mkPkg pkgid pkg | (pkgid, pkg, _) <- pkgs]+  return (pkgs', prefs)+ -- | Parse an uncompressed \"00-index.tar\" repository index file represented -- as a 'ByteString'. ---parseRepoIndex :: ByteString-               -> Either String [(PackageId, GenericPackageDescription)]-parseRepoIndex = foldlTarball (\pkgs -> maybe pkgs (:pkgs) . extractPkg) []+parsePackageIndex :: ByteString+                  -> Either String+                            ( [(PackageId, GenericPackageDescription, BlockNo)]+                            , [Dependency] )+parsePackageIndex = accum 0 [] [] . Tar.read+  where+    accum blockNo pkgs prefs es = case es of+      Tar.Fail err   -> Left  err+      Tar.Done       -> Right (reverse pkgs, reverse prefs)+      Tar.Next e es' -> accum blockNo' pkgs' prefs' es'+        where+          (pkgs', prefs') = extract blockNo pkgs prefs e+          blockNo'        = blockNo + sizeInBlocks e +    extract blockNo pkgs prefs entry =+       fromMaybe (pkgs, prefs) $+                 tryExtractPkg+         `mplus` tryExtractPrefs+      where+        tryExtractPkg = do+          (pkgid, pkg) <- extractPkg entry+          return ((pkgid, pkg, blockNo):pkgs, prefs)++        tryExtractPrefs = do+          prefs' <- extractPrefs entry+          return (pkgs, prefs'++prefs)++    sizeInBlocks entry =+        1 + case Tar.entryContent entry of+              Tar.NormalFile     _   size -> bytesToBlocks size+              Tar.OtherEntryType _ _ size -> bytesToBlocks size+              _                           -> 0+      where+        bytesToBlocks s = 1 + ((fromIntegral s - 1) `div` 512)+ extractPkg :: Tar.Entry -> Maybe (PackageId, GenericPackageDescription) extractPkg entry = case Tar.entryContent entry of   Tar.NormalFile content _@@ -256,10 +310,156 @@   where     fileName = Tar.entryPath entry -foldlTarball :: (a -> Tar.Entry -> a) -> a-             -> ByteString -> Either String a-foldlTarball f z = either Left (Right . foldl f z) . check [] . Tar.read+extractPrefs :: Tar.Entry -> Maybe [Dependency]+extractPrefs entry = case Tar.entryContent entry of+  Tar.NormalFile content _+     | takeFileName (Tar.entryPath entry) == "preferred-versions"+    -> Just . parsePreferredVersions+     . BS.Char8.unpack $ content+  _ -> Nothing++parsePreferredVersions :: String -> [Dependency]+parsePreferredVersions = catMaybes+                       . map simpleParse+                       . filter (not . isPrefixOf "--")+                       . lines++------------------------------------------------------------------------+-- Reading and updating the index cache+--++updatePackageIndexCacheFile :: FilePath -> FilePath -> IO ()+updatePackageIndexCacheFile indexFile cacheFile = do+    (pkgs, prefs) <- either fail return+                   . parsePackageIndex+                   . maybeDecompress+                 =<< BS.readFile indexFile+    let cache = mkCache pkgs prefs+    writeFile cacheFile (showIndexCache cache)   where-    check _  (Tar.Fail err)  = Left  err-    check ok Tar.Done        = Right ok-    check ok (Tar.Next e es) = check (e:ok) es+    mkCache pkgs prefs =+        [ CachePrefrence pref          | pref <- prefs ]+     ++ [ CachePackageId pkgid blockNo | (pkgid, _, blockNo) <- pkgs ]++readPackageIndexCacheFile :: Package pkg+                          => (PackageId -> GenericPackageDescription -> pkg)+                          -> FilePath+                          -> FilePath+                          -> IO (PackageIndex pkg, [Dependency])+readPackageIndexCacheFile mkPkg indexFile cacheFile = do+  indexHnd <- openFile indexFile ReadMode+  cache    <- liftM readIndexCache (BSS.readFile cacheFile)+  packageIndexFromCache mkPkg indexHnd cache+++packageIndexFromCache :: Package pkg+                      => (PackageId -> GenericPackageDescription -> pkg)+                      -> Handle+                      -> [IndexCacheEntry]+                      -> IO (PackageIndex pkg, [Dependency])+packageIndexFromCache mkPkg hnd = accum mempty []+  where+    accum srcpkgs prefs [] = do+      -- Have to reverse entries, since in a tar file, later entries mask+      -- earlier ones, and PackageIndex.fromList does the same, but we+      -- accumulate the list of entries in reverse order, so need to reverse.+      pkgIndex <- evaluate $ PackageIndex.fromList (reverse srcpkgs)+      return (pkgIndex, prefs)++    accum srcpkgs prefs (CachePackageId pkgid blockno : entries) = do+      -- Given the cache entry, make a package index entry.+      -- The magic here is that we use lazy IO to read the .cabal file+      -- from the index tarball if it turns out that we need it.+      -- Most of the time we only need the package id.+      pkg <- unsafeInterleaveIO $ do+               getPackageDescription blockno+      let srcpkg = mkPkg pkgid pkg+      accum (srcpkg:srcpkgs) prefs entries++    accum srcpkgs prefs (CachePrefrence pref : entries) =+      accum srcpkgs (pref:prefs) entries++    getPackageDescription blockno = do+      hSeek hnd AbsoluteSeek (fromIntegral (blockno * 512))+      header  <- BS.hGet hnd 512+      size    <- getEntrySize header+      content <- BS.hGet hnd (fromIntegral size)+      readPackageDescription content++    getEntrySize header =+      case Tar.read header of+        Tar.Next e _ ->+          case Tar.entryContent e of+            Tar.NormalFile _ size -> return size+            _                     -> interror "unexpected tar entry type"+        _ -> interror "could not read tar file entry"++    readPackageDescription content =+      case parsePackageDescription . fromUTF8 . BS.Char8.unpack $ content of+        ParseOk _ d -> return d+        _           -> interror "failed to parse .cabal file"++    interror msg = die $ "internal error when reading package index: " ++ msg+                      ++ "The package index or index cache is probably "+                      ++ "corrupt. Running cabal update might fix it."++------------------------------------------------------------------------+-- Index cache data structure+--++-- | Tar files are block structured with 512 byte blocks. Every header and file+-- content starts on a block boundary.+--+type BlockNo = Int++data IndexCacheEntry = CachePackageId PackageId BlockNo+                     | CachePrefrence Dependency+  deriving (Eq, Show)++readIndexCacheEntry :: BSS.ByteString -> Maybe IndexCacheEntry+readIndexCacheEntry = \line ->+  case BSS.words line of+    [key, pkgnamestr, pkgverstr, sep, blocknostr]+      | key == packageKey && sep == blocknoKey ->+      case (parseName pkgnamestr, parseVer pkgverstr [], parseBlockNo blocknostr) of+        (Just pkgname, Just pkgver, Just blockno)+          -> Just (CachePackageId (PackageIdentifier pkgname pkgver) blockno)+        _ -> Nothing+    (key: remainder) | key == preferredVersionKey ->+      fmap CachePrefrence (simpleParse (BSS.unpack (BSS.unwords remainder)))+    _  -> Nothing+  where+    packageKey = BSS.pack "pkg:"+    blocknoKey = BSS.pack "b#"+    preferredVersionKey = BSS.pack "pref-ver:"++    parseName str+      | BSS.all (\c -> isAlphaNum c || c == '-') str+                  = Just (PackageName (BSS.unpack str))+      | otherwise = Nothing++    parseVer str vs =+      case BSS.readInt str of+        Nothing        -> Nothing+        Just (v, str') -> case BSS.uncons str' of+          Just ('.', str'') -> parseVer str'' (v:vs)+          Just _            -> Nothing+          Nothing           -> Just (Version (reverse (v:vs)) [])++    parseBlockNo str =+      case BSS.readInt str of+        Just (blockno, remainder) | BSS.null remainder -> Just blockno+        _                                              -> Nothing++showIndexCacheEntry :: IndexCacheEntry -> String+showIndexCacheEntry entry = case entry of+   CachePackageId pkgid b -> "pkg: " ++ display (packageName pkgid)+                                  ++ " " ++ display (packageVersion pkgid)+                          ++ " b# " ++ show b+   CachePrefrence dep     -> "pref-ver: " ++ display dep++readIndexCache :: BSS.ByteString -> [IndexCacheEntry]+readIndexCache = catMaybes . map readIndexCacheEntry . BSS.lines++showIndexCache :: [IndexCacheEntry] -> String+showIndexCache = unlines . map showIndexCacheEntry
Distribution/Client/Init.hs view
@@ -24,29 +24,45 @@ import System.IO   ( hSetBuffering, stdout, BufferMode(..) ) import System.Directory-  ( getCurrentDirectory )+  ( getCurrentDirectory, doesDirectoryExist )+import System.FilePath+  ( (</>) ) import Data.Time   ( getCurrentTime, utcToLocalTime, toGregorian, localDay, getCurrentTimeZone )  import Data.List-  ( intersperse )+  ( intersperse, intercalate, nub, groupBy, (\\) ) import Data.Maybe-  ( fromMaybe, isJust )+  ( fromMaybe, isJust, catMaybes )+import Data.Function+  ( on )+import qualified Data.Map as M import Data.Traversable   ( traverse )+import Control.Applicative+  ( (<$>) ) import Control.Monad   ( when ) #if MIN_VERSION_base(3,0,0) import Control.Monad-  ( (>=>) )+  ( (>=>), join ) #endif+import Control.Arrow+  ( (&&&) ) -import Text.PrettyPrint.HughesPJ hiding (mode, cat)+import Text.PrettyPrint hiding (mode, cat)  import Data.Version   ( Version(..) ) import Distribution.Version-  ( orLaterVersion )+  ( orLaterVersion, withinVersion, VersionRange )+import Distribution.Verbosity+  ( Verbosity )+import Distribution.ModuleName+  ( ModuleName, fromString )+import Distribution.InstalledPackageInfo+  ( InstalledPackageInfo, sourcePackageId, exposed )+import qualified Distribution.Package as P  import Distribution.Client.Init.Types   ( InitFlags(..), PackageType(..), Category(..) )@@ -64,14 +80,30 @@   ( runReadE, readP_to_E ) import Distribution.Simple.Setup   ( Flag(..), flagToMaybe )+import Distribution.Simple.Configure+  ( getInstalledPackages )+import Distribution.Simple.Compiler+  ( PackageDBStack, Compiler )+import Distribution.Simple.Program+  ( ProgramConfiguration )+import Distribution.Simple.PackageIndex+  ( PackageIndex, moduleNameIndex ) import Distribution.Text   ( display, Text(..) ) -initCabal :: InitFlags -> IO ()-initCabal initFlags = do+initCabal :: Verbosity+          -> PackageDBStack+          -> Compiler+          -> ProgramConfiguration+          -> InitFlags+          -> IO ()+initCabal verbosity packageDBs comp conf initFlags = do++  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+   hSetBuffering stdout NoBuffering -  initFlags' <- extendFlags initFlags+  initFlags' <- extendFlags installedPkgIndex initFlags    writeLicense initFlags'   writeSetupFile initFlags'@@ -85,17 +117,19 @@  -- | Fill in more details by guessing, discovering, or prompting the --   user.-extendFlags :: InitFlags -> IO InitFlags-extendFlags =  getPackageName-           >=> getVersion-           >=> getLicense-           >=> getAuthorInfo-           >=> getHomepage-           >=> getSynopsis-           >=> getCategory-           >=> getLibOrExec-           >=> getSrcDir-           >=> getModulesAndBuildTools+extendFlags :: PackageIndex -> InitFlags -> IO InitFlags+extendFlags pkgIx =+      getPackageName+  >=> getVersion+  >=> getLicense+  >=> getAuthorInfo+  >=> getHomepage+  >=> getSynopsis+  >=> getCategory+  >=> getLibOrExec+  >=> getGenComments+  >=> getSrcDir+  >=> getModulesBuildToolsAndDeps pkgIx  -- | Combine two actions which may return a value, preferring the first. That --   is, run the second action only if the first doesn't return a value.@@ -124,11 +158,11 @@    return $ flags { packageName = maybeToFlag pkgName' } --- | Package version: use 0.1 as a last resort, but try prompting the user if---   possible.+-- | Package version: use 0.1.0.0 as a last resort, but try prompting the user+--  if possible. getVersion :: InitFlags -> IO InitFlags getVersion flags = do-  let v = Just $ Version { versionBranch = [0,1], versionTags = [] }+  let v = Just $ Version { versionBranch = [0,1,0,0], versionTags = [] }   v' <-     return (flagToMaybe $ version flags)         ?>> maybePrompt flags (prompt "Package version" v)         ?>> return v@@ -138,11 +172,13 @@ getLicense :: InitFlags -> IO InitFlags getLicense flags = do   lic <-     return (flagToMaybe $ license flags)-         ?>> fmap (fmap (either UnknownLicense id))+         ?>> fmap (fmap (either UnknownLicense id) . join)                   (maybePrompt flags-                    (promptList "Please choose a license"-                                knownLicenses (Just BSD3) True))+                    (promptListOptional "Please choose a license" listedLicenses))   return $ flags { license = maybeToFlag lic }+  where+    listedLicenses =+      knownLicenses \\ [GPL Nothing, LGPL Nothing, OtherLicense]  -- | The author's name and email. Prompt, or try to guess from an existing --   darcs repo.@@ -166,7 +202,7 @@ getHomepage flags = do   hp  <- queryHomepage   hp' <-     return (flagToMaybe $ homepage flags)-         ?>> maybePrompt flags (promptStr "Project homepage/repo URL" hp)+         ?>> maybePrompt flags (promptStr "Project homepage URL" hp)          ?>> return hp    return $ flags { homepage = maybeToFlag hp' }@@ -190,8 +226,8 @@ getCategory :: InitFlags -> IO InitFlags getCategory flags = do   cat <-     return (flagToMaybe $ category flags)-         ?>> maybePrompt flags (promptList "Project category" [Codec ..]-                                                              Nothing True)+         ?>> fmap join (maybePrompt flags+                         (promptListOptional "Project category" [Codec ..]))   return $ flags { category = maybeToFlag cat }  -- | Ask whether the project builds a library or executable.@@ -201,42 +237,122 @@            ?>> maybePrompt flags (either (const Library) id `fmap`                                    (promptList "What does the package build"                                                [Library, Executable]-                                               Nothing False))+                                               Nothing display False))            ?>> return (Just Library)    return $ flags { packageType = maybeToFlag isLib } +-- | Ask whether to generate explanitory comments.+getGenComments :: InitFlags -> IO InitFlags+getGenComments flags = do+  genComments <-     return (flagToMaybe $ noComments flags)+                 ?>> maybePrompt flags (promptYesNo promptMsg (Just False))+                 ?>> return (Just False)+  return $ flags { noComments = maybeToFlag (fmap not genComments) }+  where+    promptMsg = "Include documentation on what each field means (y/n)"+ -- | Try to guess the source root directory (don't prompt the user). getSrcDir :: InitFlags -> IO InitFlags getSrcDir flags = do   srcDirs <-     return (sourceDirs flags)-             ?>> guessSourceDirs+             ?>> Just `fmap` (guessSourceDirs flags)    return $ flags { sourceDirs = srcDirs } --- XXX--- | Try to guess source directories.-guessSourceDirs :: IO (Maybe [String])-guessSourceDirs = return Nothing+-- | Try to guess source directories.  Could try harder; for the+--   moment just looks to see whether there is a directory called 'src'.+guessSourceDirs :: InitFlags -> IO [String]+guessSourceDirs flags = do+  dir      <- fromMaybe getCurrentDirectory+                (fmap return . flagToMaybe $ packageDir flags)+  srcIsDir <- doesDirectoryExist (dir </> "src")+  if srcIsDir+    then return ["src"]+    else return []  -- | Get the list of exposed modules and extra tools needed to build them.-getModulesAndBuildTools :: InitFlags -> IO InitFlags-getModulesAndBuildTools flags = do+getModulesBuildToolsAndDeps :: PackageIndex -> InitFlags -> IO InitFlags+getModulesBuildToolsAndDeps pkgIx flags = do   dir <- fromMaybe getCurrentDirectory                    (fmap return . flagToMaybe $ packageDir flags)    -- XXX really should use guessed source roots.   sourceFiles <- scanForModules dir -  mods <-      return (exposedModules flags)+  Just mods <-      return (exposedModules flags)            ?>> (return . Just . map moduleName $ sourceFiles)    tools <-     return (buildTools flags)            ?>> (return . Just . neededBuildPrograms $ sourceFiles) -  return $ flags { exposedModules = mods-                 , buildTools     = tools }+  deps <-      return (dependencies flags)+           ?>> Just <$> importsToDeps flags+                        (fromString "Prelude" : concatMap imports sourceFiles)+                        pkgIx +  return $ flags { exposedModules = Just mods+                 , buildTools     = tools+                 , dependencies   = deps+                 }++importsToDeps :: InitFlags -> [ModuleName] -> PackageIndex -> IO [P.Dependency]+importsToDeps flags mods pkgIx = do++  let modMap :: M.Map ModuleName [InstalledPackageInfo]+      modMap  = M.map (filter exposed) $ moduleNameIndex pkgIx++      modDeps :: [(ModuleName, Maybe [InstalledPackageInfo])]+      modDeps = map (id &&& flip M.lookup modMap) mods++  message flags "\nGuessing dependencies..."+  nub . catMaybes <$> mapM (chooseDep flags) modDeps++-- Given a module and a list of installed packages providing it,+-- choose a dependency (i.e. package + version range) to use for that+-- module.+chooseDep :: InitFlags -> (ModuleName, Maybe [InstalledPackageInfo])+          -> IO (Maybe P.Dependency)++chooseDep flags (m, Nothing)+  = message flags ("\nWarning: no package found providing " ++ display m ++ ".")+    >> return Nothing++chooseDep flags (m, Just [])+  = message flags ("\nWarning: no package found providing " ++ display m ++ ".")+    >> return Nothing++    -- We found some packages: group them by name.+chooseDep flags (m, Just ps)+  = case pkgGroups of+      -- if there's only one group, i.e. multiple versions of a single package,+      -- we make it into a dependency, choosing the latest-ish version (see toDep).+      [grp] -> Just <$> toDep grp+      -- otherwise, we refuse to choose between different packages and make the user+      -- do it.+      grps  -> do message flags ("\nWarning: multiple packages found providing "+                                 ++ display m+                                 ++ ": " ++ intercalate ", " (map (display . P.pkgName . head) grps))+                  message flags ("You will need to pick one and manually add it to the Build-depends: field.")+                  return Nothing+  where+    pkgGroups = groupBy ((==) `on` P.pkgName) (map sourcePackageId ps)++    -- Given a list of available versions of the same package, pick a dependency.+    toDep :: [P.PackageIdentifier] -> IO P.Dependency++    -- If only one version, easy.  We change e.g. 0.4.2  into  0.4.*+    toDep [pid] = return $ P.Dependency (P.pkgName pid) (pvpize . P.pkgVersion $ pid)++    -- Otherwise, choose the latest version and issue a warning.+    toDep pids  = do+      message flags ("\nWarning: multiple versions of " ++ display (P.pkgName . head $ pids) ++ " provide " ++ display m ++ ", choosing the latest.")+      return $ P.Dependency (P.pkgName . head $ pids)+                            (pvpize . maximum . map P.pkgVersion $ pids)++    pvpize :: Version -> VersionRange+    pvpize v = withinVersion $ v { versionBranch = take 2 (versionBranch v) }+ --------------------------------------------------------------------------- --  Prompting/user interaction  ------------------------------------------- ---------------------------------------------------------------------------@@ -254,6 +370,18 @@ promptStr :: String -> Maybe String -> IO String promptStr = promptDefault' Just id +-- | Create a yes/no prompt with optional default value.+--+promptYesNo :: String -> Maybe Bool -> IO Bool+promptYesNo =+    promptDefault' recogniseYesNo showYesNo+  where+    recogniseYesNo s | s == "y" || s == "Y" = Just True+                     | s == "n" || s == "N" = Just False+                     | otherwise            = Nothing+    showYesNo True  = "y"+    showYesNo False = "n"+ -- | Create a prompt with optional default value that returns a value --   of some Text instance. prompt :: Text t => String -> Maybe t -> IO t@@ -280,34 +408,46 @@ -- | Create a prompt from a prompt string and a String representation --   of an optional default value. mkDefPrompt :: String -> Maybe String -> String-mkDefPrompt pr def = pr ++ defStr def ++ "? "-  where defStr Nothing  = ""-        defStr (Just s) = " [default \"" ++ s ++ "\"]"+mkDefPrompt pr def = pr ++ "?" ++ defStr def+  where defStr Nothing  = " "+        defStr (Just s) = " [default: " ++ s ++ "] " +promptListOptional :: (Text t, Eq t)+                   => String            -- ^ prompt+                   -> [t]               -- ^ choices+                   -> IO (Maybe (Either String t))+promptListOptional pr choices =+    fmap rearrange+  $ promptList pr (Nothing : map Just choices) (Just Nothing)+               (maybe "(none)" display) True+  where+    rearrange = either (Just . Left) (maybe Nothing (Just . Right))+ -- | Create a prompt from a list of items.-promptList :: (Text t, Eq t)+promptList :: Eq t            => String            -- ^ prompt            -> [t]               -- ^ choices            -> Maybe t           -- ^ optional default value+           -> (t -> String)     -- ^ show an item            -> Bool              -- ^ whether to allow an 'other' option            -> IO (Either String t)-promptList pr choices def other = do+promptList pr choices def displayItem other = do   putStrLn $ pr ++ ":"-  let options1 = map (\c -> (Just c == def, display c)) choices+  let options1 = map (\c -> (Just c == def, displayItem c)) choices       options2 = zip ([1..]::[Int])                      (options1 ++ if other then [(False, "Other (specify)")]                                            else [])   mapM_ (putStrLn . \(n,(i,s)) -> showOption n i ++ s) options2-  promptList' (length options2) choices def other+  promptList' displayItem (length options2) choices def other  where showOption n i | n < 10 = " " ++ star i ++ " " ++ rest                       | otherwise = " " ++ star i ++ rest                   where rest = show n ++ ") "                         star True = "*"                         star False = " " -promptList' :: Text t => Int -> [t] -> Maybe t -> Bool -> IO (Either String t)-promptList' numChoices choices def other = do-  putStr $ mkDefPrompt "Your choice" (display `fmap` def)+promptList' :: (t -> String) -> Int -> [t] -> Maybe t -> Bool -> IO (Either String t)+promptList' displayItem numChoices choices def other = do+  putStr $ mkDefPrompt "Your choice" (displayItem `fmap` def)   inp <- getLine   case (inp, def) of     ("", Just d) -> return $ Right d@@ -315,7 +455,7 @@             Nothing -> invalidChoice inp             Just n  -> getChoice n  where invalidChoice inp = do putStrLn $ inp ++ " is not a valid choice."-                              promptList' numChoices choices def other+                              promptList' displayItem numChoices choices def other        getChoice n | n < 1 || n > numChoices = invalidChoice (show n)                    | n < numChoices ||                      (n == numChoices && not other)@@ -333,7 +473,7 @@  writeLicense :: InitFlags -> IO () writeLicense flags = do-  message flags "Generating LICENSE..."+  message flags "\nGenerating LICENSE..."   year <- getYear   let licenseFile =         case license flags of@@ -379,6 +519,8 @@     , "main = defaultMain"     ] +-- XXX ought to do something sensible if a .cabal file already exists,+-- instead of overwriting. writeCabalFile :: InitFlags -> IO Bool writeCabalFile flags@(InitFlags{packageName = NoFlag}) = do   message flags "Error: no package name provided."@@ -398,105 +540,114 @@ --   structure onto a low-level AST structure and use the existing --   pretty-printing code to generate the file. generateCabalFile :: String -> InitFlags -> String-generateCabalFile fileName c = render $+generateCabalFile fileName c =+  renderStyle style { lineLength = 79, ribbonsPerLine = 1.1 } $   (if (minimal c /= Flag True)-    then showComment (Just $ fileName ++ " auto-generated by cabal init.  For additional options, see http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.")+    then showComment (Just $ "Initial " ++ fileName ++ " generated by cabal "+                          ++ "init.  For further documentation, see "+                          ++ "http://haskell.org/cabal/users-guide/")+         $$ text ""     else empty)   $$-  vcat [ fieldS "Name"          (packageName   c)+  vcat [ fieldS "name"          (packageName   c)                 (Just "The name of the package.")                 True -       , field  "Version"       (version       c)-                (Just "The package version.  See the Haskell package versioning policy (http://www.haskell.org/haskellwiki/Package_versioning_policy) for standards guiding when and how versions should be incremented.")+       , field  "version"       (version       c)+                (Just $ "The package version.  See the Haskell package versioning policy (PVP) for standards guiding when and how versions should be incremented.\nhttp://www.haskell.org/haskellwiki/Package_versioning_policy\n"+                ++ "PVP summary:      +-+------- breaking API changes\n"+                ++ "                  | | +----- non-breaking API additions\n"+                ++ "                  | | | +--- code changes with no API change")                 True -       , fieldS "Synopsis"      (synopsis      c)+       , fieldS "synopsis"      (synopsis      c)                 (Just "A short (one-line) description of the package.")                 True -       , fieldS "Description"   NoFlag+       , fieldS "description"   NoFlag                 (Just "A longer description of the package.")                 True -       , fieldS "Homepage"      (homepage     c)+       , fieldS "homepage"      (homepage     c)                 (Just "URL for the project homepage or repository.")                 False -       , fieldS "Bug-reports"   NoFlag+       , fieldS "bug-reports"   NoFlag                 (Just "A URL where users can report bugs.")                 False -       , field  "License"       (license      c)+       , field  "license"       (license      c)                 (Just "The license under which the package is released.")                 True -       , fieldS "License-file" (Flag "LICENSE")+       , fieldS "license-file" (Flag "LICENSE")                 (Just "The file containing the license text.")                 True -       , fieldS "Author"        (author       c)+       , fieldS "author"        (author       c)                 (Just "The package author(s).")                 True -       , fieldS "Maintainer"    (email        c)+       , fieldS "maintainer"    (email        c)                 (Just "An email address to which users can send suggestions, bug reports, and patches.")                 True -       , fieldS "Copyright"     NoFlag+       , fieldS "copyright"     NoFlag                 (Just "A copyright notice.")                 True -       , fieldS "Category"      (either id display `fmap` category c)+       , fieldS "category"      (either id display `fmap` category c)                 Nothing                 True -       , fieldS "Build-type"    (Flag "Simple")+       , fieldS "build-type"    (Flag "Simple")                 Nothing                 True -       , fieldS "Extra-source-files" NoFlag+       , fieldS "extra-source-files" NoFlag                 (Just "Extra files to be distributed with the package, such as examples or a README.")-                True+                False -       , field  "Cabal-version" (Flag $ orLaterVersion (Version [1,2] []))+       , field  "cabal-version" (Flag $ orLaterVersion (Version [1,8] []))                 (Just "Constraint on the version of Cabal needed to build this package.")                 False         , case packageType c of            Flag Executable ->-             text "\nExecutable" <+> text (fromMaybe "" . flagToMaybe $ packageName c) $$ (nest 2 $ vcat-             [ fieldS "Main-is" NoFlag (Just ".hs or .lhs file containing the Main module.") True+             text "\nexecutable" <+> text (fromMaybe "" . flagToMaybe $ packageName c) $$ (nest 2 $ vcat+             [ fieldS "main-is" NoFlag (Just ".hs or .lhs file containing the Main module.") True -             , generateBuildInfo c+             , generateBuildInfo Executable c              ])-           Flag Library    -> text "\nLibrary" $$ (nest 2 $ vcat-             [ fieldS "Exposed-modules" (listField (exposedModules c))+           Flag Library    -> text "\nlibrary" $$ (nest 2 $ vcat+             [ fieldS "exposed-modules" (listField (exposedModules c))                       (Just "Modules exported by the library.")                       True -             , generateBuildInfo c+             , generateBuildInfo Library c              ])            _               -> empty        ]  where-   generateBuildInfo :: InitFlags -> Doc-   generateBuildInfo c' = vcat-     [ fieldS "Build-depends" (listField (dependencies c'))-              (Just "Packages needed in order to build this package.")+   generateBuildInfo :: PackageType -> InitFlags -> Doc+   generateBuildInfo pkgtype c' = vcat+     [ fieldS "other-modules" (listField (otherModules c'))+              (Just $ case pkgtype of+                 Library    -> "Modules included in this library but not exported."+                 Executable -> "Modules included in this executable, other than Main.")               True -     , fieldS "Other-modules" (listField (otherModules c'))-              (Just "Modules not exported by this package.")+     , fieldS "build-depends" (listField (dependencies c'))+              (Just "Other library packages from which modules are imported.")               True       , fieldS "hs-source-dirs" (listFieldS (sourceDirs c'))-              (Just "Directories other than the root containing source files.")+              (Just "Directories containing source files.")               False -     , fieldS "Build-tools" (listFieldS (buildTools c'))+     , fieldS "build-tools" (listFieldS (buildTools c'))               (Just "Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.")-              True+              False      ]     listField :: Text s => Maybe [s] -> Flag String@@ -531,8 +682,20 @@    showComment :: Maybe String -> Doc    showComment (Just t) = vcat . map text                         . map ("-- "++) . lines-                        . render . fsep . map text . words $ t+                        . renderStyle style {+                            lineLength = 76,+                            ribbonsPerLine = 1.05+                          }+                        . vcat+                        . map (fcat . map text . breakLine)+                        . lines+                        $ t    showComment Nothing  = text ""++   breakLine  [] = []+   breakLine  cs = case break (==' ') cs of (w,cs') -> w : breakLine' cs'+   breakLine' [] = []+   breakLine' cs = case span (==' ') cs of (w,cs') -> w : breakLine cs'  -- | Generate warnings for missing fields etc. generateWarnings :: InitFlags -> IO ()
Distribution/Client/Init/Heuristics.hs view
@@ -19,8 +19,10 @@     guessAuthorNameMail,     knownCategories, ) where-import Distribution.Simple.Setup(Flag(..))-import Distribution.ModuleName ( ModuleName, fromString )+import Distribution.Text         (simpleParse)+import Distribution.Simple.Setup (Flag(..))+import Distribution.ModuleName+    ( ModuleName, fromString, toFilePath ) import Distribution.Client.PackageIndex     ( allPackagesByName ) import qualified Distribution.PackageDescription as PD@@ -28,12 +30,13 @@ import Distribution.Simple.Utils          ( intercalate ) -import Distribution.Client.Types ( packageDescription, AvailablePackageDb(..) )+import Distribution.Client.Types ( packageDescription, SourcePackageDb(..) ) import Control.Monad (liftM ) import Data.Char   ( isUpper, isLower, isSpace ) #if MIN_VERSION_base(3,0,3) import Data.Either ( partitionEithers ) #endif+import Data.List   ( isPrefixOf ) import Data.Maybe  ( catMaybes ) import Data.Monoid ( mempty, mappend ) import qualified Data.Set as Set ( fromList, toList )@@ -41,7 +44,7 @@                           getHomeDirectory, canonicalizePath ) import System.Environment ( getEnvironment ) import System.FilePath ( takeExtension, takeBaseName, dropExtension,-                         (</>), splitDirectories, makeRelative )+                         (</>), (<.>), splitDirectories, makeRelative )  -- |Guess the package name based on the given root directory guessPackageName :: FilePath -> IO String@@ -50,10 +53,15 @@ -- |Data type of source files found in the working directory data SourceFileEntry = SourceFileEntry     { relativeSourcePath :: FilePath-    , moduleName :: ModuleName-    , fileExtension :: String+    , moduleName         :: ModuleName+    , fileExtension      :: String+    , imports            :: [ModuleName]     } deriving Show +sfToFileName :: FilePath -> SourceFileEntry -> FilePath+sfToFileName projectRoot (SourceFileEntry relPath m ext _)+  = projectRoot </> relPath </> toFilePath m <.> ext+ -- |Search for source files in the given directory -- and return pairs of guessed haskell source path and -- module names.@@ -69,14 +77,15 @@         let modules = catMaybes [ guessModuleName hierarchy file                                 | file <- files                                 , isUpper (head file) ]+        modules' <- mapM (findImports projectRoot) modules         recMods <- mapM (scanRecursive dir hierarchy) dirs-        return $ concat (modules : recMods)+        return $ concat (modules' : recMods)     tagIsDir parent entry = do         isDir <- doesDirectoryExist (parent </> entry)         return $ (if isDir then Right else Left) entry     guessModuleName hierarchy entry         | takeBaseName entry == "Setup" = Nothing-        | ext `elem` sourceExtensions = Just $ SourceFileEntry relRoot modName ext+        | ext `elem` sourceExtensions = Just $ SourceFileEntry relRoot modName ext []         | otherwise = Nothing       where         relRoot = makeRelative projectRoot srcRoot@@ -91,6 +100,35 @@     ignoreDir ('.':_)  = True     ignoreDir dir      = dir `elem` ["dist", "_darcs"] +findImports :: FilePath -> SourceFileEntry -> IO SourceFileEntry+findImports projectRoot sf = do+  s <- readFile (sfToFileName projectRoot sf)++  let modules = catMaybes+              . map ( getModName+                    . drop 1+                    . filter (not . null)+                    . dropWhile (/= "import")+                    . words+                    )+              . filter (not . ("--" `isPrefixOf`)) -- poor man's comment filtering+              . lines+              $ s++      -- XXX we should probably make a better attempt at parsing+      -- comments above.  Unfortunately we can't use a full-fledged+      -- Haskell parser since cabal's dependencies must be kept at a+      -- minimum.++  return sf { imports = modules }++ where getModName :: [String] -> Maybe ModuleName+       getModName []               = Nothing+       getModName ("qualified":ws) = getModName ws+       getModName (ms:_)           = simpleParse ms+++ -- Unfortunately we cannot use the version exported by Distribution.Simple.Program knownSuffixHandlers :: [(String,String)] knownSuffixHandlers =@@ -134,9 +172,9 @@     authorRepoFile  = "_darcs" </> "prefs" </> "author"  -- |Get list of categories used in hackage. NOTE: Very slow, needs to be cached-knownCategories :: AvailablePackageDb -> [String]-knownCategories (AvailablePackageDb available _) = nubSet $-    [ cat | pkg <- map head (allPackagesByName available)+knownCategories :: SourcePackageDb -> [String]+knownCategories (SourcePackageDb sourcePkgIndex _) = nubSet $+    [ cat | pkg <- map head (allPackagesByName sourcePkgIndex)           , let catList = (PD.category . PD.packageDescription . packageDescription) pkg           , cat <- splitString ',' catList     ]
Distribution/Client/Init/Licenses.hs view
@@ -12,7 +12,7 @@  bsd3 :: String -> String -> License bsd3 authors year = unlines-    [ "Copyright (c)" ++ year ++ ", " ++ authors+    [ "Copyright (c) " ++ year ++ ", " ++ authors     , ""     , "All rights reserved."     , ""
Distribution/Client/Init/Types.hs view
@@ -18,6 +18,7 @@   ( Flag(..) )  import Distribution.Version+import Distribution.Verbosity import qualified Distribution.Package as P import Distribution.License import Distribution.ModuleName@@ -59,6 +60,8 @@               , dependencies :: Maybe [P.Dependency]               , sourceDirs   :: Maybe [String]               , buildTools   :: Maybe [String]++              , initVerbosity :: Flag Verbosity               }   deriving (Show) @@ -91,6 +94,7 @@     , dependencies   = mempty     , sourceDirs     = mempty     , buildTools     = mempty+    , initVerbosity  = mempty     }   mappend  a b = InitFlags     { nonInteractive = combine nonInteractive@@ -113,6 +117,7 @@     , dependencies   = combine dependencies     , sourceDirs     = combine sourceDirs     , buildTools     = combine buildTools+    , initVerbosity  = combine initVerbosity     }     where combine field = field a `mappend` field b 
Distribution/Client/Install.hs view
@@ -19,9 +19,9 @@   ) where  import Data.List-         ( unfoldr, find, nub, sort )+         ( unfoldr, nub, sort, (\\) ) import Data.Maybe-         ( isJust, fromMaybe )+         ( isJust, fromMaybe, maybeToList ) import Control.Exception as Exception          ( handleJust ) #if MIN_VERSION_base(4,0,0)@@ -42,17 +42,19 @@ import System.FilePath          ( (</>), (<.>), takeDirectory ) import System.IO-         ( openFile, IOMode(AppendMode) )+         ( openFile, IOMode(AppendMode), stdout, hFlush ) import System.IO.Error          ( isDoesNotExistError, ioeGetFileName )  import Distribution.Client.Targets import Distribution.Client.Dependency+import Distribution.Client.Dependency.Types+         ( Solver(..) ) import Distribution.Client.FetchUtils import qualified Distribution.Client.Haddock as Haddock (regenerateHaddockIndex) -- import qualified Distribution.Client.Info as Info import Distribution.Client.IndexUtils as IndexUtils-         ( getAvailablePackages, getInstalledPackages )+         ( getSourcePackages, getInstalledPackages ) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.InstallPlan (InstallPlan) import Distribution.Client.Setup@@ -62,7 +64,7 @@ import Distribution.Client.Config          ( defaultCabalDir ) import Distribution.Client.Tar (extractTarGzFile)-import Distribution.Client.Types as Available+import Distribution.Client.Types as Source import Distribution.Client.BuildReports.Types          ( ReportLevel(..) ) import Distribution.Client.SetupWrapper@@ -74,6 +76,7 @@          ( symlinkBinaries ) import qualified Distribution.Client.Win32SelfUpgrade as Win32SelfUpgrade import qualified Distribution.Client.World as World+import qualified Distribution.InstalledPackageInfo as Installed import Paths_cabal_install (getBinDir)  import Distribution.Simple.Compiler@@ -81,14 +84,15 @@          , PackageDB(..), PackageDBStack ) import Distribution.Simple.Program (ProgramConfiguration, defaultProgramConfiguration) import qualified Distribution.Simple.InstallDirs as InstallDirs-import qualified Distribution.Client.PackageIndex as PackageIndex-import Distribution.Client.PackageIndex (PackageIndex)+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PackageIndex (PackageIndex) import Distribution.Simple.Setup-         ( haddockCommand, HaddockFlags(..), emptyHaddockFlags+         ( haddockCommand, HaddockFlags(..)          , buildCommand, BuildFlags(..), emptyBuildFlags          , toFlag, fromFlag, fromFlagOrDefault, flagToMaybe ) import qualified Distribution.Simple.Setup as Cabal-         ( installCommand, InstallFlags(..), emptyInstallFlags )+         ( installCommand, InstallFlags(..), emptyInstallFlags+         , emptyTestFlags, testCommand ) import Distribution.Simple.Utils          ( rawSystemExit, comparing ) import Distribution.Simple.InstallDirs as InstallDirs@@ -97,16 +101,17 @@ import Distribution.Package          ( PackageIdentifier, packageName, packageVersion          , Package(..), PackageFixedDeps(..)-         , Dependency(..), thisPackageVersion )+         , Dependency(..), thisPackageVersion, InstalledPackageId ) import qualified Distribution.PackageDescription as PackageDescription import Distribution.PackageDescription-         ( PackageDescription )+         ( PackageDescription, GenericPackageDescription(..), Flag(..)+         , FlagName(..), FlagAssignment ) import Distribution.PackageDescription.Configuration          ( finalizePackageDescription ) import Distribution.Version          ( Version, anyVersion, thisVersion ) import Distribution.Simple.Utils as Utils-         ( notice, info, debug, warn, die, intercalate, withTempDirectory )+         ( notice, info, warn, die, intercalate, withTempDirectory ) import Distribution.Client.Utils          ( inDir, mergeBy, MergeResult(..) ) import Distribution.System@@ -114,7 +119,7 @@ import Distribution.Text          ( display ) import Distribution.Verbosity as Verbosity-         ( Verbosity, showForCabal, verbose )+         ( Verbosity, showForCabal, verbose, deafening ) import Distribution.Simple.BuildPaths ( exeExtension )  --TODO:@@ -145,45 +150,53 @@   -> ConfigFlags   -> ConfigExFlags   -> InstallFlags+  -> HaddockFlags   -> [UserTarget]   -> IO () install verbosity packageDBs repos comp conf-  globalFlags configFlags configExFlags installFlags userTargets0 = do+  globalFlags configFlags configExFlags installFlags haddockFlags userTargets0 = do -    installed     <- getInstalledPackages verbosity comp packageDBs conf-    availableDb   <- getAvailablePackages verbosity repos+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    sourcePkgDb       <- getSourcePackages    verbosity repos +    solver <- chooseSolver verbosity (fromFlag (configSolver  configExFlags)) (compilerId comp)+     let -- For install, if no target is given it means we use the         -- current directory as the single target         userTargets | null userTargets0 = [UserTargetLocalDir "."]                     | otherwise         = userTargets0      pkgSpecifiers <- resolveUserTargets verbosity-                       globalFlags (packageIndex availableDb) userTargets+                       (fromFlag $ globalWorldFile globalFlags)+                       (packageIndex sourcePkgDb)+                       userTargets      notice verbosity "Resolving dependencies..."     installPlan   <- foldProgress logMsg die return $                        planPackages-                         comp configFlags configExFlags installFlags-                         installed availableDb pkgSpecifiers+                         comp solver configFlags configExFlags installFlags+                         installedPkgIndex sourcePkgDb pkgSpecifiers -    printPlanMessages verbosity installed installPlan dryRun+    checkPrintPlan verbosity installedPkgIndex installPlan installFlags pkgSpecifiers      unless dryRun $ do       installPlan' <- performInstallations verbosity-                        context installed installPlan+                        context installedPkgIndex installPlan       postInstallActions verbosity context userTargets installPlan'    where     context :: InstallContext     context = (packageDBs, repos, comp, conf,-               globalFlags, configFlags, configExFlags, installFlags)+               globalFlags, configFlags, configExFlags, installFlags, haddockFlags)      dryRun      = fromFlag (installDryRun installFlags)-    logMsg message rest = debug verbosity message >> rest-+    logMsg message rest = debugNoWrap message >> rest+    -- Solver debug output really looks better without automatic+    -- line wrapping. TODO: This should probably be moved into+    -- the utilities module.+    debugNoWrap xs = when (verbosity >= deafening) (putStrLn xs >> hFlush stdout) -upgrade _ _ _ _ _ _ _ _ _ _ = die $+upgrade _ _ _ _ _ _ _ _ _ _ _ = die $     "Use the 'cabal install' command instead of 'cabal upgrade'.\n"  ++ "You can install the latest version of a package using 'cabal install'. "  ++ "The 'cabal upgrade' command has been removed because people found it "@@ -203,25 +216,28 @@                       , GlobalFlags                       , ConfigFlags                       , ConfigExFlags-                      , InstallFlags )+                      , InstallFlags+                      , HaddockFlags )  -- ------------------------------------------------------------ -- * Installation planning -- ------------------------------------------------------------  planPackages :: Compiler+             -> Solver              -> ConfigFlags              -> ConfigExFlags              -> InstallFlags-             -> PackageIndex InstalledPackage-             -> AvailablePackageDb-             -> [PackageSpecifier AvailablePackage]+             -> PackageIndex+             -> SourcePackageDb+             -> [PackageSpecifier SourcePackage]              -> Progress String String InstallPlan-planPackages comp configFlags configExFlags installFlags-             installed availableDb pkgSpecifiers =+planPackages comp solver configFlags configExFlags installFlags+             installedPkgIndex sourcePkgDb pkgSpecifiers =          resolveDependencies           buildPlatform (compilerId comp)+          solver           resolverParams      >>= if onlyDeps then adjustPlanOnlyDeps else return@@ -229,7 +245,18 @@   where     resolverParams = -        setPreferenceDefault (if upgradeDeps then PreferAllLatest+        setMaxBackjumps (if maxBackjumps < 0 then Nothing+                                             else Just maxBackjumps)++      . setIndependentGoals independentGoals++      . setReorderGoals reorderGoals++      . setAvoidReinstalls avoidReinstalls++      . setShadowPkgs shadowPkgs++      . setPreferenceDefault (if upgradeDeps then PreferAllLatest                                              else PreferLatestForSelected)        . addPreferences@@ -239,32 +266,39 @@        . addConstraints           -- version constraints from the config file or command line-          [ PackageVersionConstraint name ver-          | Dependency name ver <- configConstraints configFlags ]+            (map userToPackageConstraint (configExConstraints configExFlags))        . addConstraints           --FIXME: this just applies all flags to all targets which           -- is silly. We should check if the flags are appropriate-          [ PackageFlagsConstraint (pkgSpecifierTarget pkgSpecifier) flags+          [ PackageConstraintFlags (pkgSpecifierTarget pkgSpecifier) flags           | let flags = configConfigurationsFlags configFlags           , not (null flags)           , pkgSpecifier <- pkgSpecifiers ] +      . addConstraints+          [ PackageConstraintStanzas (pkgSpecifierTarget pkgSpecifier) stanzas+          | pkgSpecifier <- pkgSpecifiers ]+       . (if reinstall then reinstallTargets else id) -      $ standardInstallPolicy installed availableDb pkgSpecifiers+      $ standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers +    stanzas = concat+        [ if testsEnabled then [TestStanzas] else []+        , if benchmarksEnabled then [BenchStanzas] else []+        ]+    testsEnabled = fromFlagOrDefault False $ configTests configFlags+    benchmarksEnabled = fromFlagOrDefault False $ configBenchmarks configFlags+     --TODO: this is a general feature and should be moved to D.C.Dependency     -- Also, the InstallPlan.remove should return info more precise to the     -- problem, rather than the very general PlanProblem type.     adjustPlanOnlyDeps :: InstallPlan -> Progress String String InstallPlan     adjustPlanOnlyDeps =         either (Fail . explain) Done-      . InstallPlan.remove isTarget+      . InstallPlan.remove (isTarget pkgSpecifiers)       where-        isTarget pkg = packageName pkg `elem` targetnames-        targetnames  = map pkgSpecifierTarget pkgSpecifiers-                 explain :: [InstallPlan.PlanProblem] -> String         explain problems =             "Cannot select only the dependencies (as requested by the "@@ -281,74 +315,210 @@                   , depid <- depids                   , packageName depid `elem` targetnames ] -    reinstall   = fromFlag (installReinstall installFlags)-    upgradeDeps = fromFlag (installUpgradeDeps installFlags)-    onlyDeps    = fromFlag (installOnlyDeps installFlags)+        targetnames  = map pkgSpecifierTarget pkgSpecifiers ++    reinstall        = fromFlag (installReinstall        installFlags)+    reorderGoals     = fromFlag (installReorderGoals     installFlags)+    independentGoals = fromFlag (installIndependentGoals installFlags)+    avoidReinstalls  = fromFlag (installAvoidReinstalls  installFlags)+    shadowPkgs       = fromFlag (installShadowPkgs       installFlags)+    maxBackjumps     = fromFlag (installMaxBackjumps     installFlags)+    upgradeDeps      = fromFlag (installUpgradeDeps      installFlags)+    onlyDeps         = fromFlag (installOnlyDeps         installFlags)+ -- ------------------------------------------------------------ -- * Informational messages -- ------------------------------------------------------------ -printPlanMessages :: Verbosity-                  -> PackageIndex InstalledPackage-                  -> InstallPlan-                  -> Bool-                  -> IO ()-printPlanMessages verbosity installed installPlan dryRun = do+-- | Perform post-solver checks of the install plan and print it if+-- either requested or needed.+checkPrintPlan :: Verbosity+               -> PackageIndex+               -> InstallPlan+               -> InstallFlags+               -> [PackageSpecifier SourcePackage]+               -> IO ()+checkPrintPlan verbosity installed installPlan installFlags pkgSpecifiers = do +  -- User targets that are already installed.+  let preExistingTargets =+        [ p | let tgts = map pkgSpecifierTarget pkgSpecifiers,+              InstallPlan.PreExisting p <- InstallPlan.toList installPlan,+              packageName p `elem` tgts ]++  -- If there's nothing to install, we print the already existing+  -- target packages as an explanation.   when nothingToInstall $-    notice verbosity $-         "No packages to be installed. All the requested packages are "-      ++ "already installed.\n If you want to reinstall anyway then use "-      ++ "the --reinstall flag."+    notice verbosity $ unlines $+         "All the requested packages are already installed:"+       : map (display . packageId) preExistingTargets+      ++ ["Use --reinstall if you want to reinstall anyway."] -  when (dryRun || verbosity >= verbose) $-    printDryRun verbosity installed installPlan+  let lPlan = linearizeInstallPlan installed installPlan+  -- Are any packages classified as reinstalls?+  let reinstalledPkgs = concatMap (extractReinstalls . snd) lPlan+  -- Packages that are already broken.+  let oldBrokenPkgs =+          map Installed.installedPackageId+        . PackageIndex.reverseDependencyClosure installed+        . map (Installed.installedPackageId . fst)+        . PackageIndex.brokenPackages+        $ installed+  let excluded = reinstalledPkgs ++ oldBrokenPkgs+  -- Packages that are reverse dependencies of replaced packages are very+  -- likely to be broken. We exclude packages that are already broken.+  let newBrokenPkgs =+        filter (\ p -> not (Installed.installedPackageId p `elem` excluded))+               (PackageIndex.reverseDependencyClosure installed reinstalledPkgs)+  let containsReinstalls = not (null reinstalledPkgs)+  let breaksPkgs         = not (null newBrokenPkgs) +  let adaptedVerbosity+        | containsReinstalls && not overrideReinstall = verbosity `max` verbose+        | otherwise                                   = verbosity++  -- We print the install plan if we are in a dry-run or if we are confronted+  -- with a dangerous install plan.+  when (dryRun || containsReinstalls && not overrideReinstall) $+    printPlan (dryRun || breaksPkgs && not overrideReinstall) adaptedVerbosity lPlan++  -- If the install plan is dangerous, we print various warning messages. In+  -- particular, if we can see that packages are likely to be broken, we even+  -- bail out (unless installation has been forced with --force-reinstalls).+  when containsReinstalls $ do+    if breaksPkgs+      then do+        (if dryRun || overrideReinstall then warn verbosity else die) $ unlines $+            "The following packages are likely to be broken by the reinstalls:"+          : map (display . Installed.sourcePackageId) newBrokenPkgs+          ++ if overrideReinstall+               then if dryRun then [] else+                 ["Continuing even though the plan contains dangerous reinstalls."]+               else+                 ["Use --force-reinstalls if you want to install anyway."]+      else unless dryRun $ warn verbosity+             "Note that reinstalls are always dangerous. Continuing anyway..."+   where     nothingToInstall = null (InstallPlan.ready installPlan) +    dryRun            = fromFlag (installDryRun            installFlags)+    overrideReinstall = fromFlag (installOverrideReinstall installFlags) -printDryRun :: Verbosity-            -> PackageIndex InstalledPackage-            -> InstallPlan-            -> IO ()-printDryRun verbosity installed plan = case unfoldr next plan of-  []   -> return ()-  pkgs-    | verbosity >= Verbosity.verbose -> notice verbosity $ unlines $-        "In order, the following would be installed:"-      : map showPkgAndReason pkgs-    | otherwise -> notice verbosity $ unlines $-        "In order, the following would be installed (use -v for more details):"-      : map (display . packageId) pkgs+linearizeInstallPlan :: PackageIndex+                     -> InstallPlan+                     -> [(ConfiguredPackage, PackageStatus)]+linearizeInstallPlan installedPkgIndex plan = unfoldr next plan   where     next plan' = case InstallPlan.ready plan' of       []      -> Nothing-      (pkg:_) -> Just (pkg, InstallPlan.completed pkgid result plan')-        where pkgid = packageId pkg+      (pkg:_) -> Just ((pkg, status), InstallPlan.completed pkgid result plan')+        where pkgid  = packageId pkg+              status = packageStatus installedPkgIndex pkg               result = BuildOk DocsNotTried TestsNotTried               --FIXME: This is a bit of a hack,               -- pretending that each package is installed -    showPkgAndReason pkg' = display (packageId pkg') ++ " " ++-          case PackageIndex.lookupPackageName installed (packageName pkg') of-            [] -> "(new package)"-            ps ->  case find ((==packageId pkg') . packageId) ps of-              Nothing  -> "(new version)"-              Just pkg -> "(reinstall)" ++ case changes pkg pkg' of-                []   -> ""-                diff -> " changes: "  ++ intercalate ", " diff-    changes pkg pkg' = map change . filter changed+data PackageStatus = NewPackage+                   | NewVersion [Version]+                   | Reinstall  [InstalledPackageId] [PackageChange]++type PackageChange = MergeResult PackageIdentifier PackageIdentifier++isTarget :: Package pkg => [PackageSpecifier SourcePackage] -> pkg -> Bool+isTarget pkgSpecifiers pkg = packageName pkg `elem` targetnames+  where+    targetnames  = map pkgSpecifierTarget pkgSpecifiers++extractReinstalls :: PackageStatus -> [InstalledPackageId]+extractReinstalls (Reinstall ipids _) = ipids+extractReinstalls _                   = []++packageStatus :: PackageIndex -> ConfiguredPackage -> PackageStatus+packageStatus installedPkgIndex cpkg =+  case PackageIndex.lookupPackageName installedPkgIndex+                                      (packageName cpkg) of+    [] -> NewPackage+    ps ->  case filter ((==packageId cpkg) . Installed.sourcePackageId) (concatMap snd ps) of+      []           -> NewVersion (map fst ps)+      pkgs@(pkg:_) -> Reinstall (map Installed.installedPackageId pkgs)+                                (changes pkg cpkg)++  where++    changes :: Installed.InstalledPackageInfo+            -> ConfiguredPackage+            -> [MergeResult PackageIdentifier PackageIdentifier]+    changes pkg pkg' = filter changed                      $ mergeBy (comparing packageName)-                         (nub . sort . depends $ pkg)+                         -- get dependencies of installed package (convert to source pkg ids via index)+                         (nub . sort . concatMap (maybeToList .+                                                  fmap Installed.sourcePackageId .+                                                  PackageIndex.lookupInstalledPackageId installedPkgIndex) .+                                                  Installed.depends $ pkg)+                         -- get dependencies of configured package                          (nub . sort . depends $ pkg')++    changed (InBoth    pkgid pkgid') = pkgid /= pkgid'+    changed _                        = True++printPlan :: Bool -- is dry run+          -> Verbosity+          -> [(ConfiguredPackage, PackageStatus)]+          -> IO ()+printPlan dryRun verbosity plan = case plan of+  []   -> return ()+  pkgs+    | verbosity >= Verbosity.verbose -> notice verbosity $ unlines $+        ("In order, the following " ++ wouldWill ++ " be installed:")+      : map showPkgAndReason pkgs+    | otherwise -> notice verbosity $ unlines $+        ("In order, the following " ++ wouldWill ++ " be installed (use -v for more details):")+      : map (display . packageId) (map fst pkgs)+  where+    wouldWill | dryRun    = "would"+              | otherwise = "will"++    showPkgAndReason (pkg', pr) = display (packageId pkg') +++          showFlagAssignment (nonDefaultFlags pkg') +++          showStanzas (stanzas pkg') ++ " " +++          case pr of+            NewPackage     -> "(new package)"+            NewVersion _   -> "(new version)"+            Reinstall _ cs -> "(reinstall)" ++ case cs of+                []   -> ""+                diff -> " changes: "  ++ intercalate ", " (map change diff)++    toFlagAssignment :: [Flag] -> FlagAssignment+    toFlagAssignment = map (\ f -> (flagName f, flagDefault f))++    nonDefaultFlags :: ConfiguredPackage -> FlagAssignment+    nonDefaultFlags (ConfiguredPackage spkg fa _ _) =+      let defaultAssignment =+            toFlagAssignment+             (genPackageFlags (Source.packageDescription spkg))+      in  fa \\ defaultAssignment++    stanzas :: ConfiguredPackage -> [OptionalStanza]+    stanzas (ConfiguredPackage _ _ sts _) = sts++    showStanzas :: [OptionalStanza] -> String+    showStanzas = concatMap ((' ' :) . showStanza)+    showStanza TestStanzas  = "*test"+    showStanza BenchStanzas = "*bench"++    -- FIXME: this should be a proper function in a proper place+    showFlagAssignment :: FlagAssignment -> String+    showFlagAssignment = concatMap ((' ' :) . showFlagValue)+    showFlagValue (f, True)   = '+' : showFlagName f+    showFlagValue (f, False)  = '-' : showFlagName f+    showFlagName (FlagName f) = f+     change (OnlyInLeft pkgid)        = display pkgid ++ " removed"     change (InBoth     pkgid pkgid') = display pkgid ++ " -> "                                     ++ display (packageVersion pkgid')     change (OnlyInRight      pkgid') = display pkgid' ++ " added"-    changed (InBoth    pkgid pkgid') = pkgid /= pkgid'-    changed _                        = True  -- ------------------------------------------------------------ -- * Post installation stuff@@ -369,7 +539,7 @@                    -> InstallPlan                    -> IO () postInstallActions verbosity-  (packageDBs, _, comp, conf, globalFlags, configFlags, _, installFlags)+  (packageDBs, _, comp, conf, globalFlags, configFlags, _, installFlags, _)   targets installPlan = do    unless oneShot $@@ -458,8 +628,8 @@      "Updating documentation index " ++ indexFile    --TODO: might be nice if the install plan gave us the new InstalledPackageInfo-  installed <- getInstalledPackages verbosity comp packageDBs conf-  Haddock.regenerateHaddockIndex verbosity installed conf indexFile+  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+  Haddock.regenerateHaddockIndex verbosity installedPkgIndex conf indexFile    | otherwise = return ()   where@@ -542,6 +712,8 @@                         ++ " The exception was:\n  " ++ show e       BuildFailed     e -> " failed during the building phase."                         ++ " The exception was:\n  " ++ show e+      TestsFailed     e -> " failed during the tests phase."+                        ++ " The exception was:\n  " ++ show e       InstallFailed   e -> " failed during the final install step."                         ++ " The exception was:\n  " ++ show e @@ -557,21 +729,22 @@  performInstallations :: Verbosity                      -> InstallContext-                     -> PackageIndex InstalledPackage+                     -> PackageIndex                      -> InstallPlan                      -> IO InstallPlan performInstallations verbosity   (packageDBs, _, comp, conf,-   globalFlags, configFlags, configExFlags, installFlags)-  installed installPlan = do+   globalFlags, configFlags, configExFlags, installFlags, haddockFlags)+  installedPkgIndex installPlan = do    executeInstallPlan installPlan $ \cpkg ->     installConfiguredPackage platform compid configFlags                              cpkg $ \configFlags' src pkg ->-      fetchAvailablePackage verbosity src $ \src' ->+      fetchSourcePackage verbosity src $ \src' ->         installLocalPackage verbosity (packageId pkg) src' $ \mpath ->-          installUnpackedPackage verbosity (setupScriptOptions installed)-                                 miscOptions configFlags' installFlags+          installUnpackedPackage verbosity+                                 (setupScriptOptions installedPkgIndex)+                                 miscOptions configFlags' installFlags haddockFlags                                  compid pkg mpath useLogFile    where@@ -646,7 +819,7 @@         -- which kind of means it was not their fault.  --- | Call an installer for an 'AvailablePackage' but override the configure+-- | Call an installer for an 'SourcePackage' but override the configure -- flags with the ones given by the 'ConfiguredPackage'. In particular the -- 'ConfiguredPackage' specifies an exact 'FlagAssignment' and exactly -- versioned package dependencies. So we ignore any previous partial flag@@ -658,24 +831,26 @@                                          -> PackageDescription -> a)                          -> a installConfiguredPackage platform comp configFlags-  (ConfiguredPackage (AvailablePackage _ gpkg source) flags deps)+  (ConfiguredPackage (SourcePackage _ gpkg source) flags stanzas deps)   installPkg = installPkg configFlags {     configConfigurationsFlags = flags,-    configConstraints = map thisPackageVersion deps+    configConstraints = map thisPackageVersion deps,+    configBenchmarks = toFlag False,+    configTests = toFlag (TestStanzas `elem` stanzas)   } source pkg   where     pkg = case finalizePackageDescription flags            (const True)-           platform comp [] gpkg of+           platform comp [] (enableStanzas stanzas gpkg) of       Left _ -> error "finalizePackageDescription ConfiguredPackage failed"       Right (desc, _) -> desc -fetchAvailablePackage+fetchSourcePackage   :: Verbosity   -> PackageLocation (Maybe FilePath)   -> (PackageLocation FilePath -> IO BuildResult)   -> IO BuildResult-fetchAvailablePackage verbosity src installPkg = do+fetchSourcePackage verbosity src installPkg = do   fetched <- checkFetched src   case fetched of     Just src' -> installPkg src'@@ -728,13 +903,14 @@                    -> InstallMisc                    -> ConfigFlags                    -> InstallFlags+                   -> HaddockFlags                    -> CompilerId                    -> PackageDescription                    -> Maybe FilePath -- ^ Directory to change to before starting the installation.                    -> Maybe (PackageIdentifier -> FilePath) -- ^ File to log output to (if any)                    -> IO BuildResult installUnpackedPackage verbosity scriptOptions miscOptions-                       configFlags installConfigFlags+                       configFlags installConfigFlags haddockFlags                        compid pkg workingDir useLogFile =    -- Configure phase@@ -747,23 +923,28 @@    -- Doc generation phase       docsResult <- if shouldHaddock-        then (do setup haddockCommand haddockFlags+        then (do setup haddockCommand haddockFlags'                  return DocsOk)                `catchIO`   (\_ -> return DocsFailed)                `catchExit` (\_ -> return DocsFailed)         else return DocsNotTried    -- Tests phase-      testsResult <- return TestsNotTried  --TODO: add optional tests+      onFailure TestsFailed $ do+        when (testsEnabled && PackageDescription.hasTests pkg) $+            setup Cabal.testCommand testFlags -  -- Install phase-      onFailure InstallFailed $-        withWin32SelfUpgrade verbosity configFlags compid pkg $ do-          case rootCmd miscOptions of-            (Just cmd) -> reexec cmd-            Nothing    -> setup Cabal.installCommand installFlags-          return (Right (BuildOk docsResult testsResult))+        let testsResult | testsEnabled = TestsOk+                        | otherwise = TestsNotTried +      -- Install phase+        onFailure InstallFailed $+          withWin32SelfUpgrade verbosity configFlags compid pkg $ do+            case rootCmd miscOptions of+              (Just cmd) -> reexec cmd+              Nothing    -> setup Cabal.installCommand installFlags+            return (Right (BuildOk docsResult testsResult))+   where     configureFlags   = filterConfigureFlags configFlags {       configVerbosity = toFlag verbosity'@@ -774,10 +955,11 @@       buildVerbosity = toFlag verbosity'     }     shouldHaddock    = fromFlag (installDocumentation installConfigFlags)-    haddockFlags _   = emptyHaddockFlags {-      haddockDistPref  = configDistPref configFlags,+    haddockFlags' _   = haddockFlags {       haddockVerbosity = toFlag verbosity'     }+    testsEnabled = fromFlag (configTests configFlags)+    testFlags _ = Cabal.emptyTestFlags     installFlags _   = Cabal.emptyInstallFlags {       Cabal.installDistPref  = configDistPref configFlags,       Cabal.installVerbosity = toFlag verbosity'
Distribution/Client/InstallPlan.hs view
@@ -45,9 +45,8 @@   ) where  import Distribution.Client.Types-         ( AvailablePackage(packageDescription), ConfiguredPackage(..)-         , InstalledPackage-         , BuildFailure, BuildSuccess )+         ( SourcePackage(packageDescription), ConfiguredPackage(..)+         , InstalledPackage, BuildFailure, BuildSuccess, enableStanzas ) import Distribution.Package          ( PackageIdentifier(..), PackageName(..), Package(..), packageName          , PackageFixedDeps(..), Dependency(..) )@@ -88,32 +87,32 @@ -- -- The Problem: ----- In general we start with a set of installed packages and a set of available+-- In general we start with a set of installed packages and a set of source -- packages. -- -- Installed packages have fixed dependencies. They have already been built and -- we know exactly what packages they were built against, including their exact--- versions. +-- versions. ----- Available package have somewhat flexible dependencies. They are specified as+-- Source package have somewhat flexible dependencies. They are specified as -- version ranges, though really they're predicates. To make matters worse they -- have conditional flexible dependencies. Configuration flags can affect which -- packages are required and can place additional constraints on their -- versions. -- -- These two sets of package can and usually do overlap. There can be installed--- packages that are also available which means they could be re-installed if--- required, though there will also be packages which are not available and--- cannot be re-installed. Very often there will be extra versions available--- than are installed. Sometimes we may like to prefer installed packages over--- available ones or perhaps always prefer the latest available version whether--- installed or not.+-- packages that are also available as source packages which means they could+-- be re-installed if required, though there will also be packages which are+-- not available as source and cannot be re-installed. Very often there will be+-- extra versions available than are installed. Sometimes we may like to prefer+-- installed packages over source ones or perhaps always prefer the latest+-- available version whether installed or not. -- -- The goal is to calculate an installation plan that is closed, acyclic and -- consistent and where every configured package is valid. -- -- An installation plan is a set of packages that are going to be used--- together. It will consist of a mixture of installed packages and available+-- together. It will consist of a mixture of installed packages and source -- packages along with their exact version dependencies. An installation plan -- is closed if for every package in the set, all of its dependencies are -- also in the set. It is consistent if for every package in the set, all@@ -472,7 +471,7 @@ configuredPackageProblems :: Platform -> CompilerId                           -> ConfiguredPackage -> [PackageProblem] configuredPackageProblems platform comp-  (ConfiguredPackage pkg specifiedFlags specifiedDeps) =+  (ConfiguredPackage pkg specifiedFlags stanzas specifiedDeps) =      [ DuplicateFlag flag | ((flag,_):_) <- duplicates specifiedFlags ]   ++ [ MissingFlag flag | OnlyInLeft  flag <- mergedFlags ]   ++ [ ExtraFlag   flag | OnlyInRight flag <- mergedFlags ]@@ -506,6 +505,6 @@          (const True)          platform comp          []-         (packageDescription pkg) of+         (enableStanzas stanzas $ packageDescription pkg) of         Right (resolvedPkg, _) -> externalBuildDepends resolvedPkg         Left  _ -> error "configuredPackageInvalidDeps internal error"
Distribution/Client/InstallSymlink.hs view
@@ -40,7 +40,7 @@ #else  import Distribution.Client.Types-         ( AvailablePackage(..), ConfiguredPackage(..) )+         ( SourcePackage(..), ConfiguredPackage(..), enableStanzas ) import Distribution.Client.Setup          ( InstallFlags(installSymlinkBinDir) ) import qualified Distribution.Client.InstallPlan as InstallPlan@@ -132,10 +132,10 @@       , PackageDescription.buildable (PackageDescription.buildInfo exe) ]      pkgDescription :: ConfiguredPackage -> PackageDescription-    pkgDescription (ConfiguredPackage (AvailablePackage _ pkg _) flags _) =+    pkgDescription (ConfiguredPackage (SourcePackage _ pkg _) flags stanzas _) =       case finalizePackageDescription flags              (const True)-             platform compilerId [] pkg of+             platform compilerId [] (enableStanzas stanzas pkg) of         Left _ -> error "finalizePackageDescription ConfiguredPackage failed"         Right (desc, _) -> desc 
Distribution/Client/List.hs view
@@ -15,11 +15,11 @@  import Distribution.Package          ( PackageName(..), Package(..), packageName, packageVersion-         , Dependency(..), thisPackageVersion, depends, simplifyDependency )+         , Dependency(..), simplifyDependency ) import Distribution.ModuleName (ModuleName) import Distribution.License (License) import qualified Distribution.InstalledPackageInfo as Installed-import qualified Distribution.PackageDescription   as Available+import qualified Distribution.PackageDescription   as Source import Distribution.PackageDescription          ( Flag(..), FlagName(..) ) import Distribution.PackageDescription.Configuration@@ -31,6 +31,7 @@ import Distribution.Simple.Utils         ( equating, comparing, die, notice ) import Distribution.Simple.Setup (fromFlag)+import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Version          ( Version(..), VersionRange, withinRange, anyVersion@@ -40,10 +41,9 @@          ( Text(disp), display )  import Distribution.Client.Types-         ( AvailablePackage(..), Repo, AvailablePackageDb(..)-         , InstalledPackage(..) )+         ( SourcePackage(..), Repo, SourcePackageDb(..) ) import Distribution.Client.Dependency.Types-         ( PackageConstraint(..) )+         ( PackageConstraint(..), ExtDependency(..) ) import Distribution.Client.Targets          ( UserTarget, resolveUserTargets, PackageSpecifier(..) ) import Distribution.Client.Setup@@ -51,7 +51,7 @@ import Distribution.Client.Utils          ( mergeBy, MergeResult(..) ) import Distribution.Client.IndexUtils as IndexUtils-         ( getAvailablePackages, getInstalledPackages )+         ( getSourcePackages, getInstalledPackages ) import Distribution.Client.FetchUtils          ( isFetched ) @@ -65,7 +65,7 @@          ( MonadPlus(mplus), join ) import Control.Exception          ( assert )-import Text.PrettyPrint.HughesPJ as Disp+import Text.PrettyPrint as Disp import System.Directory          ( doesDirectoryExist ) @@ -81,29 +81,29 @@      -> IO () list verbosity packageDBs repos comp conf listFlags pats = do -    installed   <- getInstalledPackages verbosity comp packageDBs conf-    availableDb <- getAvailablePackages verbosity repos-    let available  = packageIndex availableDb+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    sourcePkgDb       <- getSourcePackages    verbosity repos+    let sourcePkgIndex = packageIndex sourcePkgDb         prefs name = fromMaybe anyVersion-                       (Map.lookup name (packagePreferences availableDb))+                       (Map.lookup name (packagePreferences sourcePkgDb)) -        pkgsInfo :: [(PackageName, [InstalledPackage], [AvailablePackage])]+        pkgsInfo :: [(PackageName, [Installed.InstalledPackageInfo], [SourcePackage])]         pkgsInfo             -- gather info for all packages-          | null pats = mergePackages (PackageIndex.allPackages installed)-                                      (PackageIndex.allPackages available)+          | null pats = mergePackages (InstalledPackageIndex.allPackages installedPkgIndex)+                                      (         PackageIndex.allPackages sourcePkgIndex)              -- gather info for packages matching search term-          | otherwise = mergePackages (matchingPackages installed)-                                      (matchingPackages available)+          | otherwise = mergePackages (matchingPackages InstalledPackageIndex.searchByNameSubstring installedPkgIndex)+                                      (matchingPackages (\ idx n -> concatMap snd (PackageIndex.searchByNameSubstring idx n)) sourcePkgIndex)          matches :: [PackageDisplayInfo]         matches = [ mergePackageInfo pref-                      installedPkgs availablePkgs selectedPkg False-                  | (pkgname, installedPkgs, availablePkgs) <- pkgsInfo+                      installedPkgs sourcePkgs selectedPkg False+                  | (pkgname, installedPkgs, sourcePkgs) <- pkgsInfo                   , not onlyInstalled || not (null installedPkgs)                   , let pref        = prefs pkgname-                        selectedPkg = latestWithPref pref availablePkgs ]+                        selectedPkg = latestWithPref pref sourcePkgs ]      if simpleOutput       then putStr $ unlines@@ -112,10 +112,10 @@              , version <- if onlyInstalled                             then              installedVersions pkg                             else nub . sort $ installedVersions pkg-                                           ++ availableVersions pkg ]+                                           ++ sourceVersions    pkg ]              -- Note: this only works because for 'list', one cannot currently              -- specify any version constraints, so listing all installed-             -- and available ones works.+             -- and source ones works.       else         if null matches             then notice verbosity "No matches found."@@ -124,11 +124,10 @@     onlyInstalled = fromFlag (listInstalled listFlags)     simpleOutput  = fromFlag (listSimpleOutput listFlags) -    matchingPackages index =+    matchingPackages search index =       [ pkg       | pat <- pats-      , (_, pkgs) <- PackageIndex.searchByNameSubstring index pat-      , pkg <- pkgs ]+      , pkg <- search index pat ]  info :: Verbosity      -> PackageDBStack@@ -142,66 +141,73 @@ info verbosity packageDBs repos comp conf      globalFlags _listFlags userTargets = do -    installed     <- getInstalledPackages verbosity comp packageDBs conf-    availableDb   <- getAvailablePackages verbosity repos-    let available  = packageIndex availableDb+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    sourcePkgDb   <- getSourcePackages    verbosity repos+    let sourcePkgIndex = packageIndex sourcePkgDb         prefs name = fromMaybe anyVersion-                       (Map.lookup name (packagePreferences availableDb))+                       (Map.lookup name (packagePreferences sourcePkgDb))          -- Users may specify names of packages that are only installed, not         -- just available source packages, so we must resolve targets using-        -- the combination of installed and available packages.-    let available' = PackageIndex.fromList-                   $ map packageId (PackageIndex.allPackages installed)-                  ++ map packageId (PackageIndex.allPackages available)+        -- the combination of installed and source packages.+    let sourcePkgs' = PackageIndex.fromList+                    $ map packageId (InstalledPackageIndex.allPackages installedPkgIndex)+                   ++ map packageId (         PackageIndex.allPackages sourcePkgIndex)     pkgSpecifiers <- resolveUserTargets verbosity-                       globalFlags available' userTargets+                       (fromFlag $ globalWorldFile globalFlags)+                       sourcePkgs' userTargets      pkgsinfo      <- sequence                        [ do pkginfo <- either die return $                                          gatherPkgInfo prefs-                                           installed available pkgSpecifier+                                           installedPkgIndex sourcePkgIndex+                                           pkgSpecifier                             updateFileSystemPackageDetails pkginfo                        | pkgSpecifier <- pkgSpecifiers ]      putStr $ unlines (map showPackageDetailedInfo pkgsinfo)    where-    gatherPkgInfo prefs installed available (NamedPackage name constraints)-      | null (selectedInstalledPkgs) && null (selectedAvailablePkgs)+    gatherPkgInfo :: (PackageName -> VersionRange) ->+                     InstalledPackageIndex.PackageIndex ->+                     PackageIndex.PackageIndex SourcePackage ->+                     PackageSpecifier SourcePackage ->+                     Either String PackageDisplayInfo+    gatherPkgInfo prefs installedPkgIndex sourcePkgIndex (NamedPackage name constraints)+      | null (selectedInstalledPkgs) && null (selectedSourcePkgs)       = Left $ "There is no available version of " ++ display name             ++ " that satisfies "             ++ display (simplifyVersionRange verConstraint)        | otherwise       = Right $ mergePackageInfo pref installedPkgs-                                 availablePkgs  selectedAvailablePkg+                                 sourcePkgs  selectedSourcePkg'                                  showPkgVersion       where         pref           = prefs name-        installedPkgs  = PackageIndex.lookupPackageName installed name-        availablePkgs  = PackageIndex.lookupPackageName available name+        installedPkgs  = concatMap snd (InstalledPackageIndex.lookupPackageName installedPkgIndex name)+        sourcePkgs     =                         PackageIndex.lookupPackageName sourcePkgIndex name -        selectedInstalledPkgs = PackageIndex.lookupDependency installed+        selectedInstalledPkgs = InstalledPackageIndex.lookupDependency installedPkgIndex                                     (Dependency name verConstraint)-        selectedAvailablePkgs = PackageIndex.lookupDependency available+        selectedSourcePkgs    =          PackageIndex.lookupDependency sourcePkgIndex                                     (Dependency name verConstraint)-        selectedAvailablePkg  = latestWithPref pref selectedAvailablePkgs+        selectedSourcePkg'    = latestWithPref pref selectedSourcePkgs                           -- display a specific package version if the user                          -- supplied a non-trivial version constraint         showPkgVersion = not (null verConstraints)         verConstraint  = foldr intersectVersionRanges anyVersion verConstraints-        verConstraints = [ vr | PackageVersionConstraint _ vr <- constraints ]+        verConstraints = [ vr | PackageConstraintVersion _ vr <- constraints ] -    gatherPkgInfo prefs installed available (SpecificSourcePackage pkg) =-        Right $ mergePackageInfo pref installedPkgs availablePkgs+    gatherPkgInfo prefs installedPkgIndex sourcePkgIndex (SpecificSourcePackage pkg) =+        Right $ mergePackageInfo pref installedPkgs sourcePkgs                                  selectedPkg True       where         name          = packageName pkg         pref          = prefs name-        installedPkgs = PackageIndex.lookupPackageName installed name-        availablePkgs = PackageIndex.lookupPackageName available name+        installedPkgs = concatMap snd (InstalledPackageIndex.lookupPackageName installedPkgIndex name)+        sourcePkgs    =                         PackageIndex.lookupPackageName sourcePkgIndex name         selectedPkg   = Just pkg  @@ -211,9 +217,9 @@ data PackageDisplayInfo = PackageDisplayInfo {     pkgName           :: PackageName,     selectedVersion   :: Maybe Version,-    selectedAvailable :: Maybe AvailablePackage,+    selectedSourcePkg :: Maybe SourcePackage,     installedVersions :: [Version],-    availableVersions :: [Version],+    sourceVersions    :: [Version],     preferredVersions :: VersionRange,     homepage          :: String,     bugReports        :: String,@@ -224,7 +230,7 @@     license           :: License,     author            :: String,     maintainer        :: String,-    dependencies      :: [Dependency],+    dependencies      :: [ExtDependency],     flags             :: [Flag],     hasLib            :: Bool,     hasExe            :: Bool,@@ -242,7 +248,7 @@      (nest 4 $ vcat [        maybeShow (synopsis pkginfo) "Synopsis:" reflowParagraphs      , text "Default available version:" <+>-       case selectedAvailable pkginfo of+       case selectedSourcePkg pkginfo of          Nothing  -> text "[ Not available from any configured repository ]"          Just pkg -> disp (packageVersion pkg)      , text "Installed versions:" <+>@@ -269,7 +275,7 @@    $+$    (nest 4 $ vcat [      entry "Synopsis"      synopsis     hideIfNull     reflowParagraphs-   , entry "Versions available" availableVersions+   , entry "Versions available" sourceVersions            (altText null "[ Not available from server ]")            (dispTopVersions 9 (preferredVersions pkginfo))    , entry "Versions installed" installedVersions@@ -351,56 +357,55 @@ -- package name. -- mergePackageInfo :: VersionRange-                 -> [InstalledPackage]-                 -> [AvailablePackage]-                 -> Maybe AvailablePackage+                 -> [Installed.InstalledPackageInfo]+                 -> [SourcePackage]+                 -> Maybe SourcePackage                  -> Bool                  -> PackageDisplayInfo-mergePackageInfo versionPref installedPkgs availablePkgs selectedPkg showVer =-  assert (length installedPkgs + length availablePkgs > 0) $+mergePackageInfo versionPref installedPkgs sourcePkgs selectedPkg showVer =+  assert (length installedPkgs + length sourcePkgs > 0) $   PackageDisplayInfo {-    pkgName           = combine packageName available+    pkgName           = combine packageName source                                 packageName installed,     selectedVersion   = if showVer then fmap packageVersion selectedPkg                                    else Nothing,-    selectedAvailable = availableSelected,+    selectedSourcePkg = sourceSelected,     installedVersions = map packageVersion installedPkgs,-    availableVersions = map packageVersion availablePkgs,+    sourceVersions    = map packageVersion sourcePkgs,     preferredVersions = versionPref, -    license      = combine Available.license    available+    license      = combine Source.license       source                            Installed.license    installed,-    maintainer   = combine Available.maintainer available+    maintainer   = combine Source.maintainer    source                            Installed.maintainer installed,-    author       = combine Available.author     available+    author       = combine Source.author        source                            Installed.author     installed,-    homepage     = combine Available.homepage   available+    homepage     = combine Source.homepage      source                            Installed.homepage   installed,-    bugReports   = maybe "" Available.bugReports available,+    bugReports   = maybe "" Source.bugReports source,     sourceRepo   = fromMaybe "" . join-                 . fmap (uncons Nothing Available.repoLocation-                       . sortBy (comparing Available.repoKind)-                       . Available.sourceRepos)-                 $ available,+                 . fmap (uncons Nothing Source.repoLocation+                       . sortBy (comparing Source.repoKind)+                       . Source.sourceRepos)+                 $ source,                     --TODO: installed package info is missing synopsis-    synopsis     = maybe "" Available.synopsis   available,-    description  = combine Available.description available+    synopsis     = maybe "" Source.synopsis      source,+    description  = combine Source.description    source                            Installed.description installed,-    category     = combine Available.category    available+    category     = combine Source.category       source                            Installed.category    installed,-    flags        = maybe [] Available.genPackageFlags availableGeneric,+    flags        = maybe [] Source.genPackageFlags sourceGeneric,     hasLib       = isJust installed                 || fromMaybe False-                   (fmap (isJust . Available.condLibrary) availableGeneric),+                   (fmap (isJust . Source.condLibrary) sourceGeneric),     hasExe       = fromMaybe False-                   (fmap (not . null . Available.condExecutables) availableGeneric),-    executables  = map fst (maybe [] Available.condExecutables availableGeneric),+                   (fmap (not . null . Source.condExecutables) sourceGeneric),+    executables  = map fst (maybe [] Source.condExecutables sourceGeneric),     modules      = combine Installed.exposedModules installed-                           (maybe [] Available.exposedModules-                                   . Available.library) available,-    dependencies = map simplifyDependency-                 $ combine Available.buildDepends available-                           (map thisPackageVersion . depends) installed',+                           (maybe [] Source.exposedModules+                                   . Source.library) source,+    dependencies = combine (map (SourceDependency . simplifyDependency) . Source.buildDepends) source+                           (map InstalledDependency . Installed.depends) installed,     haddockHtml  = fromMaybe "" . join                  . fmap (listToMaybe . Installed.haddockHTMLs)                  $ installed,@@ -408,14 +413,14 @@   }   where     combine f x g y  = fromJust (fmap f x `mplus` fmap g y)-    installed'       = latestWithPref versionPref installedPkgs-    installed        = fmap (\(InstalledPackage p _) -> p) installed'+    installed :: Maybe Installed.InstalledPackageInfo+    installed = latestWithPref versionPref installedPkgs -    availableSelected+    sourceSelected       | isJust selectedPkg = selectedPkg-      | otherwise          = latestWithPref versionPref availablePkgs-    availableGeneric = fmap packageDescription availableSelected-    available        = fmap flattenPackageDescription availableGeneric+      | otherwise          = latestWithPref versionPref sourcePkgs+    sourceGeneric = fmap packageDescription sourceSelected+    source        = fmap flattenPackageDescription sourceGeneric      uncons :: b -> (a -> b) -> [a] -> b     uncons z _ []    = z@@ -429,7 +434,7 @@ updateFileSystemPackageDetails :: PackageDisplayInfo -> IO PackageDisplayInfo updateFileSystemPackageDetails pkginfo = do   fetched   <- maybe (return False) (isFetched . packageSource)-                     (selectedAvailable pkginfo)+                     (selectedSourcePkg pkginfo)   docsExist <- doesDirectoryExist (haddockHtml pkginfo)   return pkginfo {     haveTarball = fetched,@@ -444,20 +449,20 @@                            in (withinRange ver pref, ver)  --- | Rearrange installed and available packages into groups referring to the+-- | Rearrange installed and source packages into groups referring to the -- same package by name. In the result pairs, the lists are guaranteed to not -- both be empty. ---mergePackages :: [InstalledPackage]-              -> [AvailablePackage]+mergePackages :: [Installed.InstalledPackageInfo]+              -> [SourcePackage]               -> [( PackageName-                  , [InstalledPackage]-                  , [AvailablePackage] )]-mergePackages installed available =+                  , [Installed.InstalledPackageInfo]+                  , [SourcePackage] )]+mergePackages installedPkgs sourcePkgs =     map collect   $ mergeBy (\i a -> fst i `compare` fst a)-            (groupOn packageName installed)-            (groupOn packageName available)+            (groupOn packageName installedPkgs)+            (groupOn packageName sourcePkgs)   where     collect (OnlyInLeft  (name,is)         ) = (name, is, [])     collect (    InBoth  (_,is)   (name,as)) = (name, is, as)
Distribution/Client/PackageIndex.hs view
@@ -27,6 +27,8 @@   -- * Queries    -- ** Precise lookups+  elemByPackageId,+  elemByPackageName,   lookupPackageName,   lookupPackageId,   lookupDependency,@@ -61,7 +63,7 @@ import Data.Array ((!)) import Data.List (groupBy, sortBy, nub, isInfixOf) import Data.Monoid (Monoid(..))-import Data.Maybe (isNothing, fromMaybe)+import Data.Maybe (isJust, isNothing, fromMaybe)  import Distribution.Package          ( PackageName(..), PackageIdentifier(..)@@ -76,7 +78,7 @@ -- -- It can be searched effeciently by package name and version. ---newtype Package pkg => PackageIndex pkg = PackageIndex+newtype PackageIndex pkg = PackageIndex   -- This index package names to all the package records matching that package   -- name case-sensitively. It includes all versions.   --@@ -235,6 +237,13 @@ -- -- * Lookups --++elemByPackageId :: Package pkg => PackageIndex pkg -> PackageIdentifier -> Bool+elemByPackageId index = isJust . lookupPackageId index++elemByPackageName :: Package pkg => PackageIndex pkg -> PackageName -> Bool+elemByPackageName index = not . null . lookupPackageName index+  -- | Does a lookup by package id (name & version). --
Distribution/Client/Setup.hs view
@@ -26,6 +26,7 @@     , reportCommand, ReportFlags(..)     , unpackCommand, UnpackFlags(..)     , initCommand, IT.InitFlags(..)+    , sdistCommand, SDistFlags(..), SDistExFlags(..), ArchiveFormat(..)      , parsePackageArgs     --TODO: stop exporting these:@@ -37,20 +38,23 @@          ( Username(..), Password(..), Repo(..), RemoteRepo(..), LocalRepo(..) ) import Distribution.Client.BuildReports.Types          ( ReportLevel(..) )+import Distribution.Client.Dependency.Types+         ( PreSolver(..) ) import qualified Distribution.Client.Init.Types as IT          ( InitFlags(..), PackageType(..) )+import Distribution.Client.Targets+         ( UserConstraint, readUserConstraint )  import Distribution.Simple.Program          ( defaultProgramConfiguration ) import Distribution.Simple.Command hiding (boolOpt)-import qualified Distribution.Simple.Command as Command import qualified Distribution.Simple.Setup as Cabal-         ( configureCommand )+         ( configureCommand, sdistCommand, haddockCommand ) import Distribution.Simple.Setup-         ( ConfigFlags(..) )+         ( ConfigFlags(..), SDistFlags(..), HaddockFlags(..) ) import Distribution.Simple.Setup-         ( Flag(..), toFlag, fromFlag, flagToList, flagToMaybe-         , optionVerbosity, trueArg, falseArg )+         ( Flag(..), toFlag, fromFlag, flagToMaybe, flagToList+         , optionVerbosity, boolOpt, trueArg, falseArg ) import Distribution.Simple.InstallDirs          ( PathTemplate, toPathTemplate, fromPathTemplate ) import Distribution.Version@@ -60,9 +64,9 @@ import Distribution.Text          ( Text(parse), display ) import Distribution.ReadE-         ( readP_to_E, succeedReadE )+         ( ReadE(..), readP_to_E, succeedReadE ) import qualified Distribution.Compat.ReadP as Parse-         ( ReadP, readP_to_S, char, munch1, pfail, (+++) )+         ( ReadP, readP_to_S, readS_to_P, char, munch1, pfail, (+++) ) import Distribution.Verbosity          ( Verbosity, normal ) import Distribution.Simple.Utils@@ -70,6 +74,8 @@  import Data.Char          ( isSpace, isAlphaNum )+import Data.List+         ( intercalate ) import Data.Maybe          ( listToMaybe, maybeToList, fromMaybe ) import Data.Monoid@@ -90,7 +96,7 @@     globalVersion        :: Flag Bool,     globalNumericVersion :: Flag Bool,     globalConfigFile     :: Flag FilePath,-    globalRemoteRepos    :: [RemoteRepo],     -- ^Available Hackage servers.+    globalRemoteRepos    :: [RemoteRepo],     -- ^ Available Hackage servers.     globalCacheDir       :: Flag FilePath,     globalLocalRepos     :: [FilePath],     globalLogsDir        :: Flag FilePath,@@ -222,7 +228,6 @@     -- older Cabal does not grok the constraints flag:   | otherwise = flags { configConstraints = [] } - -- ------------------------------------------------------------ -- * Config extra flags -- ------------------------------------------------------------@@ -231,17 +236,20 @@ -- data ConfigExFlags = ConfigExFlags {     configCabalVersion :: Flag Version,-    configPreferences  :: [Dependency]+    configExConstraints:: [UserConstraint],+    configPreferences  :: [Dependency],+    configSolver       :: Flag PreSolver   }  defaultConfigExFlags :: ConfigExFlags-defaultConfigExFlags = mempty+defaultConfigExFlags = mempty { configSolver = Flag defaultSolver }  configureExCommand :: CommandUI (ConfigFlags, ConfigExFlags) configureExCommand = configureCommand {     commandDefaultFlags = (mempty, defaultConfigExFlags),     commandOptions      = \showOrParseArgs ->-         liftOptions fst setFst (configureOptions   showOrParseArgs)+         liftOptions fst setFst (filter ((/="constraint") . optionName) $+                                 configureOptions   showOrParseArgs)       ++ liftOptions snd setSnd (configureExOptions showOrParseArgs)   }   where@@ -257,23 +265,36 @@       (reqArg "VERSION" (readP_to_E ("Cannot parse cabal lib version: "++)                                     (fmap toFlag parse))                         (map display . flagToList))+  , option [] ["constraint"]+      "Specify constraints on a package (version, installed/source, flags)"+      configExConstraints (\v flags -> flags { configExConstraints = v })+      (reqArg "CONSTRAINT"+              (fmap (\x -> [x]) (ReadE readUserConstraint))+              (map display))    , option [] ["preference"]       "Specify preferences (soft constraints) on the version of a package"       configPreferences (\v flags -> flags { configPreferences = v })-      (reqArg "DEPENDENCY"-        (readP_to_E (const "dependency expected") ((\x -> [x]) `fmap` parse))-                                        (map (\x -> display x)))+      (reqArg "CONSTRAINT"+              (readP_to_E (const "dependency expected")+                          (fmap (\x -> [x]) parse))+              (map display))++  , optionSolver configSolver (\v flags -> flags { configSolver = v })   ]  instance Monoid ConfigExFlags where   mempty = ConfigExFlags {     configCabalVersion = mempty,-    configPreferences  = mempty+    configExConstraints= mempty,+    configPreferences  = mempty,+    configSolver       = mempty   }   mappend a b = ConfigExFlags {     configCabalVersion = combine configCabalVersion,-    configPreferences  = combine configPreferences+    configExConstraints= combine configExConstraints,+    configPreferences  = combine configPreferences,+    configSolver       = combine configSolver   }     where combine field = field a `mappend` field b @@ -285,6 +306,11 @@ --    fetchOutput    :: Flag FilePath,       fetchDeps      :: Flag Bool,       fetchDryRun    :: Flag Bool,+      fetchSolver           :: Flag PreSolver,+      fetchMaxBackjumps     :: Flag Int,+      fetchReorderGoals     :: Flag Bool,+      fetchIndependentGoals :: Flag Bool,+      fetchShadowPkgs       :: Flag Bool,       fetchVerbosity :: Flag Verbosity     } @@ -293,6 +319,11 @@ --  fetchOutput    = mempty,     fetchDeps      = toFlag True,     fetchDryRun    = toFlag False,+    fetchSolver           = Flag defaultSolver,+    fetchMaxBackjumps     = Flag defaultMaxBackjumps,+    fetchReorderGoals     = Flag False,+    fetchIndependentGoals = Flag False,+    fetchShadowPkgs       = Flag False,     fetchVerbosity = toFlag normal    } @@ -303,7 +334,7 @@     commandDescription  = Nothing,     commandUsage        = usagePackages "fetch",     commandDefaultFlags = defaultFetchFlags,-    commandOptions      = \_ -> [+    commandOptions      = \ showOrParseArgs -> [          optionVerbosity fetchVerbosity (\v flags -> flags { fetchVerbosity = v })  --     , option "o" ["output"]@@ -325,7 +356,16 @@            "Do not install anything, only print what would be installed."            fetchDryRun (\v flags -> flags { fetchDryRun = v })            trueArg-       ]++       ] ++++       optionSolver      fetchSolver           (\v flags -> flags { fetchSolver           = v }) :+       optionSolverFlags showOrParseArgs+                         fetchMaxBackjumps     (\v flags -> flags { fetchMaxBackjumps     = v })+                         fetchReorderGoals     (\v flags -> flags { fetchReorderGoals     = v })+                         fetchIndependentGoals (\v flags -> flags { fetchIndependentGoals = v })+                         fetchShadowPkgs       (\v flags -> flags { fetchShadowPkgs       = v })+   }  -- ------------------------------------------------------------@@ -342,13 +382,13 @@     commandOptions      = \_ -> [optionVerbosity id const]   } -upgradeCommand  :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags)+upgradeCommand  :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags) upgradeCommand = configureCommand {     commandName         = "upgrade",     commandSynopsis     = "(command disabled, use install instead)",     commandDescription  = Nothing,     commandUsage        = usagePackages "upgrade",-    commandDefaultFlags = (mempty, mempty, mempty),+    commandDefaultFlags = (mempty, mempty, mempty, mempty),     commandOptions      = commandOptions installCommand   } @@ -557,41 +597,62 @@ -- | Install takes the same flags as configure along with a few extras. -- data InstallFlags = InstallFlags {-    installDocumentation:: Flag Bool,-    installHaddockIndex :: Flag PathTemplate,-    installDryRun       :: Flag Bool,-    installReinstall    :: Flag Bool,-    installUpgradeDeps  :: Flag Bool,-    installOnly         :: Flag Bool,-    installOnlyDeps     :: Flag Bool,-    installRootCmd      :: Flag String,-    installSummaryFile  :: [PathTemplate],-    installLogFile      :: Flag PathTemplate,-    installBuildReports :: Flag ReportLevel,-    installSymlinkBinDir:: Flag FilePath,-    installOneShot      :: Flag Bool+    installDocumentation    :: Flag Bool,+    installHaddockIndex     :: Flag PathTemplate,+    installDryRun           :: Flag Bool,+    installMaxBackjumps     :: Flag Int,+    installReorderGoals     :: Flag Bool,+    installIndependentGoals :: Flag Bool,+    installShadowPkgs       :: Flag Bool,+    installReinstall        :: Flag Bool,+    installAvoidReinstalls  :: Flag Bool,+    installOverrideReinstall :: Flag Bool,+    installUpgradeDeps      :: Flag Bool,+    installOnly             :: Flag Bool,+    installOnlyDeps         :: Flag Bool,+    installRootCmd          :: Flag String,+    installSummaryFile      :: [PathTemplate],+    installLogFile          :: Flag PathTemplate,+    installBuildReports     :: Flag ReportLevel,+    installSymlinkBinDir    :: Flag FilePath,+    installOneShot          :: Flag Bool   }  defaultInstallFlags :: InstallFlags defaultInstallFlags = InstallFlags {-    installDocumentation= Flag False,-    installHaddockIndex = Flag docIndexFile,-    installDryRun       = Flag False,-    installReinstall    = Flag False,-    installUpgradeDeps  = Flag False,-    installOnly         = Flag False,-    installOnlyDeps     = Flag False,-    installRootCmd      = mempty,-    installSummaryFile  = mempty,-    installLogFile      = mempty,-    installBuildReports = Flag NoReports,-    installSymlinkBinDir= mempty,-    installOneShot      = Flag False+    installDocumentation   = Flag False,+    installHaddockIndex    = Flag docIndexFile,+    installDryRun          = Flag False,+    installMaxBackjumps    = Flag defaultMaxBackjumps,+    installReorderGoals    = Flag False,+    installIndependentGoals= Flag False,+    installShadowPkgs      = Flag False,+    installReinstall       = Flag False,+    installAvoidReinstalls = Flag False,+    installOverrideReinstall = Flag False,+    installUpgradeDeps     = Flag False,+    installOnly            = Flag False,+    installOnlyDeps        = Flag False,+    installRootCmd         = mempty,+    installSummaryFile     = mempty,+    installLogFile         = mempty,+    installBuildReports    = Flag NoReports,+    installSymlinkBinDir   = mempty,+    installOneShot         = Flag False   }   where     docIndexFile = toPathTemplate ("$datadir" </> "doc" </> "index.html") -installCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags)+defaultMaxBackjumps :: Int+defaultMaxBackjumps = 200++defaultSolver :: PreSolver+defaultSolver = Choose++allSolvers :: String+allSolvers = intercalate ", " (map display ([minBound .. maxBound] :: [PreSolver]))++installCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags) installCommand = CommandUI {   commandName         = "install",   commandSynopsis     = "Installs a list of packages.",@@ -610,17 +671,38 @@      ++ "    Specific version of a package\n"      ++ "  " ++ pname ++ " install 'foo < 2'       "      ++ "    Constrained package version\n",-  commandDefaultFlags = (mempty, mempty, mempty),+  commandDefaultFlags = (mempty, mempty, mempty, mempty),   commandOptions      = \showOrParseArgs ->-       liftOptions get1 set1 (configureOptions   showOrParseArgs)+       liftOptions get1 set1 (filter ((/="constraint") . optionName) $+                              configureOptions   showOrParseArgs)     ++ liftOptions get2 set2 (configureExOptions showOrParseArgs)     ++ liftOptions get3 set3 (installOptions     showOrParseArgs)+    ++ liftOptions get4 set4 (haddockOptions     showOrParseArgs)   }   where-    get1 (a,_,_) = a; set1 a (_,b,c) = (a,b,c)-    get2 (_,b,_) = b; set2 b (a,_,c) = (a,b,c)-    get3 (_,_,c) = c; set3 c (a,b,_) = (a,b,c)+    get1 (a,_,_,_) = a; set1 a (_,b,c,d) = (a,b,c,d)+    get2 (_,b,_,_) = b; set2 b (a,_,c,d) = (a,b,c,d)+    get3 (_,_,c,_) = c; set3 c (a,b,_,d) = (a,b,c,d)+    get4 (_,_,_,d) = d; set4 d (a,b,c,_) = (a,b,c,d) +    haddockOptions showOrParseArgs+      = [ opt { optionName = "haddock-" ++ name,+                optionDescr = [ fmapOptFlags (\(_, lflags) -> ([], map ("haddock-" ++) lflags)) descr+                              | descr <- optionDescr opt] }+        | opt <- commandOptions Cabal.haddockCommand showOrParseArgs+        , let name = optionName opt+        , name `elem` ["hoogle", "html", "html-location",+                       "executables", "internal", "css",+                       "hyperlink-source", "hscolour-css",+                       "contents-location"]+        ]++    fmapOptFlags :: (OptFlags -> OptFlags) -> OptDescr a -> OptDescr a+    fmapOptFlags modify (ReqArg d f p r w)    = ReqArg d (modify f) p r w+    fmapOptFlags modify (OptArg d f p r i w)  = OptArg d (modify f) p r i w+    fmapOptFlags modify (ChoiceOpt xs)        = ChoiceOpt [(d, modify f, i, w) | (d, f, i, w) <- xs]+    fmapOptFlags modify (BoolOpt d f1 f2 r w) = BoolOpt d (modify f1) (modify f2) r w+ installOptions ::  ShowOrParseArgs -> [OptionField InstallFlags] installOptions showOrParseArgs =       [ option "" ["documentation"]@@ -638,23 +720,38 @@           "Do not install anything, only print what would be installed."           installDryRun (\v flags -> flags { installDryRun = v })           trueArg+      ] ++ -      , option [] ["reinstall"]+      optionSolverFlags showOrParseArgs+                        installMaxBackjumps     (\v flags -> flags { installMaxBackjumps     = v })+                        installReorderGoals     (\v flags -> flags { installReorderGoals     = v })+                        installIndependentGoals (\v flags -> flags { installIndependentGoals = v })+                        installShadowPkgs       (\v flags -> flags { installShadowPkgs       = v }) ++++      [ option [] ["reinstall"]           "Install even if it means installing the same version again."           installReinstall (\v flags -> flags { installReinstall = v })-          trueArg+          (yesNoOpt showOrParseArgs) +      , option [] ["avoid-reinstalls"]+          "Do not select versions that would destructively overwrite installed packages."+          installAvoidReinstalls (\v flags -> flags { installAvoidReinstalls = v })+          (yesNoOpt showOrParseArgs)++      , option [] ["force-reinstalls"]+          "Reinstall packages even if they will most likely break other installed packages."+          installOverrideReinstall (\v flags -> flags { installOverrideReinstall = v })+          (yesNoOpt showOrParseArgs)+       , option [] ["upgrade-dependencies"]           "Pick the latest version for all dependencies, rather than trying to pick an installed version."           installUpgradeDeps (\v flags -> flags { installUpgradeDeps = v })-          trueArg-+          (yesNoOpt showOrParseArgs)        , option [] ["only-dependencies"]           "Install only the dependencies necessary to build the given packages"           installOnlyDeps (\v flags -> flags { installOnlyDeps = v })-          trueArg-+          (yesNoOpt showOrParseArgs)        , option [] ["root-cmd"]           "Command used to gain root privileges, when installing with --global."@@ -688,7 +785,7 @@       , option [] ["one-shot"]           "Do not record the packages in the world file."           installOneShot (\v flags -> flags { installOneShot = v })-          trueArg+          (yesNoOpt showOrParseArgs)       ] ++ case showOrParseArgs of      -- TODO: remove when "cabal install" avoids           ParseArgs ->             option [] ["only"]@@ -700,34 +797,46 @@  instance Monoid InstallFlags where   mempty = InstallFlags {-    installDocumentation= mempty,-    installHaddockIndex = mempty,-    installDryRun       = mempty,-    installReinstall    = mempty,-    installUpgradeDeps  = mempty,-    installOnly         = mempty,-    installOnlyDeps     = mempty,-    installRootCmd      = mempty,-    installSummaryFile  = mempty,-    installLogFile      = mempty,-    installBuildReports = mempty,-    installSymlinkBinDir= mempty,-    installOneShot      = mempty+    installDocumentation   = mempty,+    installHaddockIndex    = mempty,+    installDryRun          = mempty,+    installReinstall       = mempty,+    installAvoidReinstalls = mempty,+    installOverrideReinstall = mempty,+    installMaxBackjumps    = mempty,+    installUpgradeDeps     = mempty,+    installReorderGoals    = mempty,+    installIndependentGoals= mempty,+    installShadowPkgs      = mempty,+    installOnly            = mempty,+    installOnlyDeps        = mempty,+    installRootCmd         = mempty,+    installSummaryFile     = mempty,+    installLogFile         = mempty,+    installBuildReports    = mempty,+    installSymlinkBinDir   = mempty,+    installOneShot         = mempty   }   mappend a b = InstallFlags {-    installDocumentation= combine installDocumentation,-    installHaddockIndex = combine installHaddockIndex,-    installDryRun       = combine installDryRun,-    installReinstall    = combine installReinstall,-    installUpgradeDeps  = combine installUpgradeDeps,-    installOnly         = combine installOnly,-    installOnlyDeps     = combine installOnlyDeps,-    installRootCmd      = combine installRootCmd,-    installSummaryFile  = combine installSummaryFile,-    installLogFile      = combine installLogFile,-    installBuildReports = combine installBuildReports,-    installSymlinkBinDir= combine installSymlinkBinDir,-    installOneShot      = combine installOneShot+    installDocumentation   = combine installDocumentation,+    installHaddockIndex    = combine installHaddockIndex,+    installDryRun          = combine installDryRun,+    installReinstall       = combine installReinstall,+    installAvoidReinstalls = combine installAvoidReinstalls,+    installOverrideReinstall = combine installOverrideReinstall,+    installMaxBackjumps    = combine installMaxBackjumps,+    installUpgradeDeps     = combine installUpgradeDeps,+    installReorderGoals    = combine installReorderGoals,+    installIndependentGoals= combine installIndependentGoals,+    installShadowPkgs      = combine installShadowPkgs,+    installOnly            = combine installOnly,+    installOnlyDeps        = combine installOnlyDeps,+    installRootCmd         = combine installRootCmd,+    installSummaryFile     = combine installSummaryFile,+    installLogFile         = combine installLogFile,+    installBuildReports    = combine installBuildReports,+    installSymlinkBinDir   = combine installSymlinkBinDir,+    installOneShot         = combine installOneShot   }     where combine field = field a `mappend` field b @@ -805,7 +914,7 @@ emptyInitFlags  = mempty  defaultInitFlags :: IT.InitFlags-defaultInitFlags  = emptyInitFlags+defaultInitFlags  = emptyInitFlags { IT.initVerbosity = toFlag normal }  initCommand :: CommandUI IT.InitFlags initCommand = CommandUI {@@ -939,6 +1048,8 @@         IT.buildTools (\v flags -> flags { IT.buildTools = v })         (reqArg' "TOOL" (Just . (:[]))                         (fromMaybe []))++      , optionVerbosity IT.initVerbosity (\v flags -> flags { IT.initVerbosity = v })       ]   }   where readMaybe s = case reads s of@@ -946,19 +1057,113 @@                         _         -> Nothing  -- --------------------------------------------------------------- * GetOpt Utils+-- * SDist flags -- ------------------------------------------------------------ -boolOpt :: SFlags -> SFlags -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a-boolOpt  = Command.boolOpt  flagToMaybe Flag+-- | Extra flags to @sdist@ beyond runghc Setup sdist+--+data SDistExFlags = SDistExFlags {+    sDistFormat    :: Flag ArchiveFormat+  }+  deriving Show -reqArgFlag :: ArgPlaceHolder -> SFlags -> LFlags -> Description ->-              (b -> Flag String) -> (Flag String -> b -> b) -> OptDescr b+data ArchiveFormat = TargzFormat | ZipFormat -- | ...+  deriving (Show, Eq)++defaultSDistExFlags :: SDistExFlags+defaultSDistExFlags = SDistExFlags {+    sDistFormat  = Flag TargzFormat+  }++sdistCommand :: CommandUI (SDistFlags, SDistExFlags)+sdistCommand = Cabal.sdistCommand {+    commandDefaultFlags = (commandDefaultFlags Cabal.sdistCommand, defaultSDistExFlags),+    commandOptions      = \showOrParseArgs ->+         liftOptions fst setFst (commandOptions Cabal.sdistCommand showOrParseArgs)+      ++ liftOptions snd setSnd sdistExOptions+  }+  where+    setFst a (_,b) = (a,b)+    setSnd b (a,_) = (a,b)++    sdistExOptions =+      [option [] ["archive-format"] "archive-format"+         sDistFormat (\v flags -> flags { sDistFormat = v })+         (choiceOpt+            [ (Flag TargzFormat, ([], ["targz"]),+                 "Produce a '.tar.gz' format archive (default and required for uploading to hackage)")+            , (Flag ZipFormat,   ([], ["zip"]),+                 "Produce a '.zip' format archive")+            ])+      ]++instance Monoid SDistExFlags where+  mempty = SDistExFlags {+    sDistFormat  = mempty+  }+  mappend a b = SDistExFlags {+    sDistFormat  = combine sDistFormat+  }+    where+      combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * GetOpt Utils+-- ------------------------------------------------------------++reqArgFlag :: ArgPlaceHolder ->+              MkOptDescr (b -> Flag String) (Flag String -> b -> b) b reqArgFlag ad = reqArg ad (succeedReadE Flag) flagToList  liftOptions :: (b -> a) -> (a -> b -> b)             -> [OptionField a] -> [OptionField b] liftOptions get set = map (liftOption get set)++yesNoOpt :: ShowOrParseArgs -> MkOptDescr (b -> Flag Bool) (Flag Bool -> (b -> b)) b+yesNoOpt ShowArgs sf lf = trueArg sf lf+yesNoOpt _        sf lf = boolOpt' flagToMaybe Flag (sf, lf) ([], map ("no-" ++) lf) sf lf++optionSolver :: (flags -> Flag PreSolver)+             -> (Flag PreSolver -> flags -> flags)+             -> OptionField flags+optionSolver get set =+  option [] ["solver"]+    ("Select dependency solver to use (default: " ++ display defaultSolver ++ "). Choices: " ++ allSolvers ++ ", where 'choose' chooses between 'topdown' and 'modular' based on compiler version.")+    get set+    (reqArg "SOLVER" (readP_to_E (const $ "solver must be one of: " ++ allSolvers)+                                 (toFlag `fmap` parse))+                     (flagToList . fmap display))++optionSolverFlags :: ShowOrParseArgs+                  -> (flags -> Flag Int   ) -> (Flag Int    -> flags -> flags)+                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)+                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)+                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)+                  -> [OptionField flags]+optionSolverFlags showOrParseArgs getmbj setmbj getrg setrg _getig _setig getsip setsip =+  [ option [] ["max-backjumps"]+      ("Maximum number of backjumps allowed while solving (default: " ++ show defaultMaxBackjumps ++ "). Use a negative number to enable unlimited backtracking. Use 0 to disable backtracking completely.")+      getmbj setmbj+      (reqArg "NUM" (readP_to_E ("Cannot parse number: "++)+                                (fmap toFlag (Parse.readS_to_P reads)))+                    (map show . flagToList))+  , option [] ["reorder-goals"]+      "Try to reorder goals according to certain heuristics. Slows things down on average, but may make backtracking faster for some packages."+      getrg setrg+      (yesNoOpt showOrParseArgs)+  -- TODO: Disabled for now because it does not work as advertised (yet).+{-+  , option [] ["independent-goals"]+      "Treat several goals on the command line as independent. If several goals depend on the same package, different versions can be chosen."+      getig setig+      (yesNoOpt showOrParseArgs)+-}+  , option [] ["shadow-installed-packages"]+      "If multiple package instances of the same version are installed, treat all but one as shadowed."+      getsip setsip+      trueArg+  ]+  usagePackages :: String -> String -> String usagePackages name pname =
Distribution/Client/SetupWrapper.hs view
@@ -34,7 +34,8 @@          , packageVersion, Dependency(..) ) import Distribution.PackageDescription          ( GenericPackageDescription(packageDescription)-         , PackageDescription(..), specVersion, BuildType(..) )+         , PackageDescription(..), specVersion+         , BuildType(..), knownBuildTypes ) import Distribution.PackageDescription.Parse          ( readPackageDescription ) import Distribution.Simple.Configure@@ -50,13 +51,13 @@          ( CommandUI(..), commandShowOptions ) import Distribution.Simple.GHC          ( ghcVerbosityOptions )-import qualified Distribution.Client.PackageIndex as PackageIndex-import Distribution.Client.PackageIndex (PackageIndex)+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PackageIndex (PackageIndex) import Distribution.Client.IndexUtils          ( getInstalledPackages ) import Distribution.Simple.Utils          ( die, debug, info, cabalVersion, findPackageDesc, comparing-         , createDirectoryIfMissingVerbose, rewriteFile )+         , createDirectoryIfMissingVerbose, rewriteFile, intercalate ) import Distribution.Client.Utils          ( moreRecentFile, inDir ) import Distribution.Text@@ -78,7 +79,7 @@     useCabalVersion  :: VersionRange,     useCompiler      :: Maybe Compiler,     usePackageDB     :: PackageDBStack,-    usePackageIndex  :: Maybe (PackageIndex InstalledPackage),+    usePackageIndex  :: Maybe PackageIndex,     useProgramConfig :: ProgramConfiguration,     useDistPref      :: FilePath,     useLoggingHandle :: Maybe Handle,@@ -116,12 +117,18 @@       mkArgs cabalLibVersion = commandName cmd                              : commandShowOptions cmd (flags cabalLibVersion)                             ++ extraArgs+  checkBuildType buildType'   setupMethod verbosity options' (packageId pkg) buildType' mkArgs   where     getPkg = findPackageDesc (fromMaybe "." (useWorkingDir options))          >>= readPackageDescription verbosity          >>= return . packageDescription +    checkBuildType (UnknownBuildType name) =+      die $ "The build-type '" ++ name ++ "' is not known. Use one of: "+         ++ intercalate ", " (map display knownBuildTypes) ++ "."+    checkBuildType _ = return ()+ -- | Decide if we're going to be able to do a direct internal call to the -- entry point in the Cabal library or if we're going to have to compile -- and execute an external Setup.hs script.@@ -215,7 +222,7 @@       []   -> die $ "The package requires Cabal library version "                  ++ display (useCabalVersion options)                  ++ " but no suitable version is installed."-      pkgs -> return $ bestVersion (map packageVersion pkgs)+      pkgs -> return $ bestVersion (map fst pkgs)     where       bestVersion          = maximumBy (comparing preference)       preference version   = (sameVersion, sameMajorVersion
Distribution/Client/SrcDist.hs view
@@ -4,42 +4,55 @@ module Distribution.Client.SrcDist (          sdist   )  where++ import Distribution.Simple.SrcDist-         ( printPackageProblems, prepareTree-         , prepareSnapshotTree, snapshotPackage )+         ( printPackageProblems, prepareTree, snapshotPackage ) import Distribution.Client.Tar (createTarGzFile)  import Distribution.Package-         ( Package(..) )+         ( Package(..), packageVersion ) import Distribution.PackageDescription          ( PackageDescription ) import Distribution.PackageDescription.Parse          ( readPackageDescription ) import Distribution.Simple.Utils-         ( defaultPackageDesc, warn, notice, setupMessage-         , createDirectoryIfMissingVerbose, withTempDirectory )-import Distribution.Simple.Setup (SDistFlags(..), fromFlag)+         ( defaultPackageDesc, die, warn, notice, setupMessage+         , createDirectoryIfMissingVerbose, withTempDirectory+         , withUTF8FileContents, writeUTF8File )+import Distribution.Client.Setup+         ( SDistFlags(..), SDistExFlags(..), ArchiveFormat(..) )+import Distribution.Simple.Setup+         ( fromFlag, flagToMaybe ) import Distribution.Verbosity (Verbosity) import Distribution.Simple.PreProcess (knownSuffixHandlers) import Distribution.Simple.BuildPaths ( srcPref) import Distribution.Simple.Configure(maybeGetPersistBuildConfig) import Distribution.PackageDescription.Configuration ( flattenPackageDescription )+import Distribution.Simple.Program (requireProgram, simpleProgram, programPath)+import Distribution.Simple.Program.Db (emptyProgramDb) import Distribution.Text          ( display )+import Distribution.Version+         ( Version )  import System.Time (getClockTime, toCalendarTime) import System.FilePath ((</>), (<.>))-import Control.Monad (when)+import Control.Monad (when, unless) import Data.Maybe (isNothing)+import Data.Char (toLower)+import Data.List (isPrefixOf)+import System.Directory (doesFileExist, removeFile, canonicalizePath)+import System.Process (runProcess, waitForProcess)+import System.Exit    (ExitCode(..))  -- |Create a source distribution.-sdist :: SDistFlags -> IO ()-sdist flags = do+sdist :: SDistFlags -> SDistExFlags -> IO ()+sdist flags exflags = do   pkg <- return . flattenPackageDescription      =<< readPackageDescription verbosity      =<< defaultPackageDesc verbosity   mb_lbi <- maybeGetPersistBuildConfig distPref-  let tmpTargetDir = srcPref distPref    -- do some QA   printPackageProblems verbosity pkg@@ -47,34 +60,101 @@   when (isNothing mb_lbi) $     warn verbosity "Cannot run preprocessors. Run 'configure' command first." -  createDirectoryIfMissingVerbose verbosity True tmpTargetDir-  withTempDirectory verbosity tmpTargetDir "sdist." $ \tmpDir -> do+  date <- toCalendarTime =<< getClockTime+  let pkg' | snapshot  = snapshotPackage date pkg+           | otherwise = pkg -    date <- toCalendarTime =<< getClockTime-    let pkg' | snapshot  = snapshotPackage date pkg-             | otherwise = pkg-    setupMessage verbosity "Building source dist for" (packageId pkg')+  case flagToMaybe (sDistDirectory flags) of+    Just targetDir -> do+      generateSourceDir targetDir pkg' mb_lbi+      notice verbosity $ "Source directory created: " ++ targetDir -    _ <- if snapshot-      then prepareSnapshotTree verbosity pkg' mb_lbi distPref tmpDir pps-      else prepareTree         verbosity pkg' mb_lbi distPref tmpDir pps-    targzFile <- createArchive verbosity pkg' tmpDir distPref-    notice verbosity $ "Source tarball created: " ++ targzFile+    Nothing -> do+      createDirectoryIfMissingVerbose verbosity True tmpTargetDir+      withTempDirectory verbosity tmpTargetDir "sdist." $ \tmpDir -> do+        let targetDir = tmpDir </> tarBallName pkg'+        generateSourceDir targetDir pkg' mb_lbi+        targzFile <- createArchive verbosity format pkg' tmpDir targetPref+        notice verbosity $ "Source tarball created: " ++ targzFile    where+    generateSourceDir targetDir pkg' mb_lbi = do++      setupMessage verbosity "Building source dist for" (packageId pkg')+      prepareTree verbosity pkg' mb_lbi distPref targetDir pps+      when snapshot $+        overwriteSnapshotPackageDesc verbosity pkg' targetDir+     verbosity = fromFlag (sDistVerbosity flags)     snapshot  = fromFlag (sDistSnapshot flags)-    distPref  = fromFlag (sDistDistPref flags)+    format    = fromFlag (sDistFormat exflags)     pps       = knownSuffixHandlers+    distPref     = fromFlag $ sDistDistPref flags+    targetPref   = distPref+    tmpTargetDir = srcPref distPref --- |Create an archive from a tree of source files, and clean up the tree.+tarBallName :: PackageDescription -> String+tarBallName = display . packageId++overwriteSnapshotPackageDesc :: Verbosity          -- ^verbosity+                             -> PackageDescription -- ^info from the cabal file+                             -> FilePath           -- ^source tree+                             -> IO ()+overwriteSnapshotPackageDesc verbosity pkg targetDir = do+    -- We could just writePackageDescription targetDescFile pkg_descr,+    -- but that would lose comments and formatting.+    descFile <- defaultPackageDesc verbosity+    withUTF8FileContents descFile $+      writeUTF8File (targetDir </> descFile)+        . unlines . map (replaceVersion (packageVersion pkg)) . lines++  where+    replaceVersion :: Version -> String -> String+    replaceVersion version line+      | "version:" `isPrefixOf` map toLower line+                  = "version: " ++ display version+      | otherwise = line++-- | Create an archive from a tree of source files.+-- createArchive :: Verbosity+              -> ArchiveFormat               -> PackageDescription               -> FilePath               -> FilePath               -> IO FilePath-createArchive _verbosity pkg tmpDir targetPref = do-  let tarBallName     = display (packageId pkg)-      tarBallFilePath = targetPref </> tarBallName <.> "tar.gz"-  createTarGzFile tarBallFilePath tmpDir tarBallName-  return tarBallFilePath+createArchive _verbosity TargzFormat pkg tmpDir targetPref = do+    createTarGzFile tarBallFilePath tmpDir (tarBallName pkg)+    return tarBallFilePath+  where+    tarBallFilePath = targetPref </> tarBallName pkg <.> "tar.gz"++createArchive verbosity ZipFormat pkg tmpDir targetPref = do+    createZipFile verbosity zipFilePath tmpDir (tarBallName pkg)+    return zipFilePath+  where+    zipFilePath = targetPref </> tarBallName pkg <.> "zip"++createZipFile :: Verbosity -> FilePath -> FilePath -> FilePath -> IO ()+createZipFile verbosity zipfile base dir = do+    (zipProg, _) <- requireProgram verbosity zipProgram emptyProgramDb++    -- zip has an annoying habbit of updating the target rather than creating+    -- it from scratch. While that might sound like an optimisation, it doesn't+    -- remove files already in the archive that are no longer present in the+    -- uncompressed tree.+    alreadyExists <- doesFileExist zipfile+    when alreadyExists $ removeFile zipfile++    -- we call zip with a different CWD, so have to make the path absolute+    zipfileAbs <- canonicalizePath zipfile++    --TODO: use runProgramInvocation, but has to be able to set CWD+    hnd <- runProcess (programPath zipProg) ["-q", "-r", zipfileAbs, dir] (Just base)+                      Nothing Nothing Nothing Nothing+    exitCode <- waitForProcess hnd+    unless (exitCode == ExitSuccess) $+      die $ "Generating the zip file failed "+         ++ "(zip returned exit code " ++ show exitCode ++ ")"+  where+    zipProgram = simpleProgram "zip"
Distribution/Client/Tar.hs view
@@ -743,7 +743,7 @@ putString n s = take n s ++ fill (n - length s) '\NUL'  --TODO: check integer widths, eg for large file sizes-putOct :: Integral a => FieldWidth -> a -> String+putOct :: (Show a, Integral a) => FieldWidth -> a -> String putOct n x =   let octStr = take (n-1) $ showOct x ""    in fill (n - length octStr - 1) '0'
Distribution/Client/Targets.hs view
@@ -37,6 +37,11 @@   disambiguatePackageTargets,   disambiguatePackageName, +  -- * User constraints+  UserConstraint(..),+  readUserConstraint,+  userToPackageConstraint+   ) where  import Distribution.Package@@ -44,7 +49,7 @@          , PackageIdentifier(..), packageName, packageVersion          , Dependency(Dependency) ) import Distribution.Client.Types-         ( AvailablePackage(..), PackageLocation(..) )+         ( SourcePackage(..), PackageLocation(..), OptionalStanza(..) ) import Distribution.Client.Dependency.Types          ( PackageConstraint(..) ) @@ -55,17 +60,14 @@ import Distribution.Client.FetchUtils  import Distribution.PackageDescription-         ( GenericPackageDescription )+         ( GenericPackageDescription, FlagName(..), FlagAssignment ) import Distribution.PackageDescription.Parse          ( readPackageDescription, parsePackageDescription, ParseResult(..) )-import Distribution.Simple.Setup-         ( fromFlag )-import Distribution.Client.Setup-         ( GlobalFlags(..) ) import Distribution.Version-         ( Version(Version), thisVersion, anyVersion, isAnyVersion )+         ( Version(Version), thisVersion, anyVersion, isAnyVersion+         , VersionRange ) import Distribution.Text-         ( Text(parse), display )+         ( Text(..), display ) import Distribution.Verbosity (Verbosity) import Distribution.Simple.Utils          ( die, warn, intercalate, findPackageDesc, fromUTF8, lowercase )@@ -84,9 +86,13 @@ import qualified Distribution.Client.GZipUtils as GZipUtils import Control.Monad (liftM) import qualified Distribution.Compat.ReadP as Parse-         ( ReadP, readP_to_S, (+++) )+import Distribution.Compat.ReadP+         ( (+++), (<++) )+import qualified Text.PrettyPrint as Disp+import Text.PrettyPrint+         ( (<>), (<+>) ) import Data.Char-         ( isSpace )+         ( isSpace, isAlphaNum ) import System.FilePath          ( takeExtension, dropExtension, takeDirectory, splitPath ) import System.Directory@@ -180,7 +186,7 @@                         => PackageSpecifier pkg -> [PackageConstraint] pkgSpecifierConstraints (NamedPackage _ constraints) = constraints pkgSpecifierConstraints (SpecificSourcePackage pkg)  =-  [PackageVersionConstraint (packageName pkg)+  [PackageConstraintVersion (packageName pkg)                             (thisVersion (packageVersion pkg))]  @@ -269,16 +275,17 @@                       && takeExtension (dropExtension f) == ".tar"      parseDependencyOrPackageId :: Parse.ReadP r Dependency-    parseDependencyOrPackageId = parse Parse.+++ liftM pkgidToDependency parse+    parseDependencyOrPackageId = parse+                             +++ liftM pkgidToDependency parse       where         pkgidToDependency :: PackageIdentifier -> Dependency         pkgidToDependency p = case packageVersion p of           Version [] _ -> Dependency (packageName p) anyVersion           version      -> Dependency (packageName p) (thisVersion version) -    readPToMaybe :: Parse.ReadP a a -> String -> Maybe a-    readPToMaybe p str = listToMaybe [ r | (r,s) <- Parse.readP_to_S p str-                                         , all isSpace s ]+readPToMaybe :: Parse.ReadP a a -> String -> Maybe a+readPToMaybe p str = listToMaybe [ r | (r,s) <- Parse.readP_to_S p str+                                     , all isSpace s ]   reportUserTargetProblems :: [UserTargetProblem] -> IO ()@@ -341,17 +348,17 @@ -- resolveUserTargets :: Package pkg                    => Verbosity-                   -> GlobalFlags+                   -> FilePath                    -> PackageIndex pkg                    -> [UserTarget]-                   -> IO [PackageSpecifier AvailablePackage]-resolveUserTargets verbosity globalFlags available userTargets = do+                   -> IO [PackageSpecifier SourcePackage]+resolveUserTargets verbosity worldFile available userTargets = do      -- given the user targets, get a list of fully or partially resolved     -- package references     packageTargets <- mapM (readPackageTarget verbosity)                   =<< mapM (fetchPackageTarget verbosity) . concat-                  =<< mapM (expandUserTarget globalFlags) userTargets+                  =<< mapM (expandUserTarget worldFile) userTargets      -- users are allowed to give package names case-insensitively, so we must     -- disambiguate named package references@@ -391,13 +398,13 @@ -- | Given a user-specified target, expand it to a bunch of package targets -- (each of which refers to only one package). ---expandUserTarget :: GlobalFlags+expandUserTarget :: FilePath                  -> UserTarget                  -> IO [PackageTarget (PackageLocation ())]-expandUserTarget globalFlags userTarget = case userTarget of+expandUserTarget worldFile userTarget = case userTarget of      UserTargetNamed (Dependency name vrange) ->-      let constraints = [ PackageVersionConstraint name vrange+      let constraints = [ PackageConstraintVersion name vrange                         | not (isAnyVersion vrange) ]       in  return [PackageTargetNamedFuzzy name constraints userTarget] @@ -406,9 +413,9 @@       --TODO: should we warn if there are no world targets?       return [ PackageTargetNamed name constraints userTarget              | World.WorldPkgInfo (Dependency name vrange) flags <- worldPkgs-             , let constraints = [ PackageVersionConstraint name vrange+             , let constraints = [ PackageConstraintVersion name vrange                                  | not (isAnyVersion vrange) ]-                              ++ [ PackageFlagsConstraint name flags+                              ++ [ PackageConstraintFlags name flags                                  | not (null flags) ] ]      UserTargetLocalDir dir ->@@ -424,8 +431,6 @@      UserTargetRemoteTarball tarballURL ->       return [ PackageTargetLocation (RemoteTarballPackage tarballURL ()) ]-  where-    worldFile = fromFlag $ globalWorldFile globalFlags   -- ------------------------------------------------------------@@ -452,7 +457,7 @@ -- readPackageTarget :: Verbosity                   -> PackageTarget (PackageLocation FilePath)-                  -> IO (PackageTarget AvailablePackage)+                  -> IO (PackageTarget SourcePackage) readPackageTarget verbosity target = case target of      PackageTargetNamed pkgname constraints userTarget ->@@ -466,7 +471,7 @@       LocalUnpackedPackage dir -> do         pkg <- readPackageDescription verbosity =<< findPackageDesc dir         return $ PackageTargetLocation $-                   AvailablePackage {+                   SourcePackage {                      packageInfoId      = packageId pkg,                      packageDescription = pkg,                      packageSource      = fmap Just location@@ -491,7 +496,7 @@                        ++ filename ++ " in " ++ tarballFile         Just pkg ->           return $ PackageTargetLocation $-                     AvailablePackage {+                     SourcePackage {                        packageInfoId      = packageId pkg,                        packageDescription = pkg,                        packageSource      = fmap Just location@@ -552,14 +557,14 @@                            -> [PackageTarget pkg]                            -> ( [PackageTargetProblem]                               , [PackageSpecifier pkg] )-disambiguatePackageTargets available availableExtra targets =+disambiguatePackageTargets availablePkgIndex availableExtra targets =     partitionEithers (map disambiguatePackageTarget targets)   where     disambiguatePackageTarget packageTarget = case packageTarget of       PackageTargetLocation pkg -> Right (SpecificSourcePackage pkg)        PackageTargetNamed pkgname constraints userTarget-        | null (PackageIndex.lookupPackageName available pkgname)+        | null (PackageIndex.lookupPackageName availablePkgIndex pkgname)                     -> Left (PackageNameUnknown pkgname userTarget)         | otherwise -> Right (NamedPackage pkgname constraints) @@ -569,11 +574,13 @@                                           pkgname userTarget)           Ambiguous   pkgnames -> Left  (PackageNameAmbigious                                           pkgname pkgnames userTarget)-          Unambiguous pkgname' -> Right (NamedPackage pkgname' constraints)+          Unambiguous pkgname' -> Right (NamedPackage pkgname' constraints')+            where+              constraints' = map (renamePackageConstraint pkgname') constraints      -- use any extra specific available packages to help us disambiguate     packageNameEnv :: PackageNameEnv-    packageNameEnv = mappend (indexPackageNameEnv available)+    packageNameEnv = mappend (indexPackageNameEnv availablePkgIndex)                              (extraPackageNameEnv availableExtra)  @@ -605,7 +612,7 @@                  "The following 'world' packages will be ignored because "               ++ "they refer to packages that cannot be found: "               ++ intercalate ", " (map display pkgs) ++ "\n"-              ++ "You can surpress this warning by correcting the world file."+              ++ "You can suppress this warning by correcting the world file."   where     isUserTagetWorld UserTargetWorld = True; isUserTagetWorld _ = False @@ -642,10 +649,10 @@     PackageNameEnv (\name -> lookupA name ++ lookupB name)  indexPackageNameEnv :: Package pkg => PackageIndex pkg -> PackageNameEnv-indexPackageNameEnv index = PackageNameEnv pkgNameLookup+indexPackageNameEnv pkgIndex = PackageNameEnv pkgNameLookup   where     pkgNameLookup (PackageName name) =-      map fst (PackageIndex.searchByName index name)+      map fst (PackageIndex.searchByName pkgIndex name)  extraPackageNameEnv :: [PackageName] -> PackageNameEnv extraPackageNameEnv names = PackageNameEnv pkgNameLookup@@ -655,3 +662,99 @@       | let lname = lowercase name       , PackageName name' <- names       , lowercase name' == lname ]+++-- ------------------------------------------------------------+-- * Package constraints+-- ------------------------------------------------------------++data UserConstraint =+     UserConstraintVersion   PackageName VersionRange+   | UserConstraintInstalled PackageName+   | UserConstraintSource    PackageName+   | UserConstraintFlags     PackageName FlagAssignment+   | UserConstraintStanzas   PackageName [OptionalStanza]+  deriving (Show,Eq)+++userToPackageConstraint :: UserConstraint -> PackageConstraint+-- At the moment, the types happen to be directly equivalent+userToPackageConstraint uc = case uc of+  UserConstraintVersion   name ver   -> PackageConstraintVersion    name ver+  UserConstraintInstalled name       -> PackageConstraintInstalled  name+  UserConstraintSource    name       -> PackageConstraintSource     name+  UserConstraintFlags     name flags -> PackageConstraintFlags      name flags+  UserConstraintStanzas   name stanzas -> PackageConstraintStanzas  name stanzas++renamePackageConstraint :: PackageName -> PackageConstraint -> PackageConstraint+renamePackageConstraint name pc = case pc of+  PackageConstraintVersion   _ ver   -> PackageConstraintVersion    name ver+  PackageConstraintInstalled _       -> PackageConstraintInstalled  name+  PackageConstraintSource    _       -> PackageConstraintSource     name+  PackageConstraintFlags     _ flags -> PackageConstraintFlags      name flags+  PackageConstraintStanzas   _ stanzas -> PackageConstraintStanzas   name stanzas++readUserConstraint :: String -> Either String UserConstraint+readUserConstraint str =+    case readPToMaybe parse str of+      Nothing -> Left msgCannotParse+      Just c  -> Right c+  where+    msgCannotParse =+         "expected a package name followed by a constraint, which is "+      ++ "either a version range, 'installed', 'source' or flags"++--FIXME: use Text instance for FlagName and FlagAssignment+instance Text UserConstraint where+  disp (UserConstraintVersion   pkgname verrange) = disp pkgname <+> disp verrange+  disp (UserConstraintInstalled pkgname)          = disp pkgname <+> Disp.text "installed"+  disp (UserConstraintSource    pkgname)          = disp pkgname <+> Disp.text "source"+  disp (UserConstraintFlags     pkgname flags)    = disp pkgname <+> dispFlagAssignment flags+    where+      dispFlagAssignment = Disp.hsep . map dispFlagValue+      dispFlagValue (f, True)   = Disp.char '+' <> dispFlagName f+      dispFlagValue (f, False)  = Disp.char '-' <> dispFlagName f+      dispFlagName (FlagName f) = Disp.text f++  disp (UserConstraintStanzas   pkgname stanzas)  = disp pkgname <+> dispStanzas stanzas+    where+      dispStanzas = Disp.hsep . map dispStanza+      dispStanza TestStanzas  = Disp.text "test"+      dispStanza BenchStanzas = Disp.text "bench"++  parse = parse >>= parseConstraint+    where+      spaces = Parse.satisfy isSpace >> Parse.skipSpaces++      parseConstraint pkgname =+            (parse >>= return . UserConstraintVersion pkgname)+        +++ (do spaces+                _ <- Parse.string "installed"+                return (UserConstraintInstalled pkgname))+        +++ (do spaces+                _ <- Parse.string "source"+                return (UserConstraintSource pkgname))+        +++ (do spaces+                _ <- Parse.string "test"+                return (UserConstraintStanzas pkgname [TestStanzas]))+        +++ (do spaces+                _ <- Parse.string "bench"+                return (UserConstraintStanzas pkgname [BenchStanzas]))+        <++ (parseFlagAssignment >>= (return . UserConstraintFlags pkgname))++      parseFlagAssignment = Parse.many1 (spaces >> parseFlagValue)+      parseFlagValue =+            (do Parse.optional (Parse.char '+')+                f <- parseFlagName+                return (f, True))+        +++ (do _ <- Parse.char '-'+                f <- parseFlagName+                return (f, False))+      parseFlagName = liftM FlagName ident++      ident :: Parse.ReadP r String+      ident = Parse.munch1 identChar >>= \s -> check s >> return s+        where+          identChar c   = isAlphaNum c || c == '_' || c == '-'+          check ('-':_) = Parse.pfail+          check _       = return ()
Distribution/Client/Types.hs view
@@ -18,7 +18,10 @@ import Distribution.InstalledPackageInfo          ( InstalledPackageInfo ) import Distribution.PackageDescription-         ( GenericPackageDescription, FlagAssignment )+         ( Benchmark(..), GenericPackageDescription(..), FlagAssignment+         , TestSuite(..) )+import Distribution.PackageDescription.Configuration+         ( mapTreeData ) import Distribution.Client.PackageIndex          ( PackageIndex ) import Distribution.Version@@ -34,8 +37,8 @@  -- | This is the information we get from a @00-index.tar.gz@ hackage index. ---data AvailablePackageDb = AvailablePackageDb {-  packageIndex       :: PackageIndex AvailablePackage,+data SourcePackageDb = SourcePackageDb {+  packageIndex       :: PackageIndex SourcePackage,   packagePreferences :: Map PackageName VersionRange } @@ -72,32 +75,52 @@ -- final configure process will be independent of the environment. -- data ConfiguredPackage = ConfiguredPackage-       AvailablePackage    -- package info, including repo+       SourcePackage       -- package info, including repo        FlagAssignment      -- complete flag assignment for the package+       [OptionalStanza]    -- list of enabled optional stanzas for the package        [PackageId]         -- set of exact dependencies. These must be                            -- consistent with the 'buildDepends' in the-                           -- 'PackageDescrption' that you'd get by applying-                           -- the flag assignment.+                           -- 'PackageDescription' that you'd get by applying+                           -- the flag assignment and optional stanzas.   deriving Show  instance Package ConfiguredPackage where-  packageId (ConfiguredPackage pkg _ _) = packageId pkg+  packageId (ConfiguredPackage pkg _ _ _) = packageId pkg  instance PackageFixedDeps ConfiguredPackage where-  depends (ConfiguredPackage _ _ deps) = deps+  depends (ConfiguredPackage _ _ _ deps) = deps  --- | We re-use @GenericPackageDescription@ and use the @package-url@--- field to store the tarball URI.-data AvailablePackage = AvailablePackage {+-- | A package description along with the location of the package sources.+--+data SourcePackage = SourcePackage {     packageInfoId      :: PackageId,     packageDescription :: GenericPackageDescription,     packageSource      :: PackageLocation (Maybe FilePath)   }   deriving Show -instance Package AvailablePackage where packageId = packageInfoId+instance Package SourcePackage where packageId = packageInfoId +data OptionalStanza+    = TestStanzas+    | BenchStanzas+  deriving (Eq, Ord, Show)++enableStanzas+    :: [OptionalStanza]+    -> GenericPackageDescription+    -> GenericPackageDescription+enableStanzas stanzas gpkg = gpkg+    { condBenchmarks = flagBenchmarks $ condBenchmarks gpkg+    , condTestSuites = flagTests $ condTestSuites gpkg+    }+  where+    enableTest t = t { testEnabled = TestStanzas `elem` stanzas }+    enableBenchmark bm = bm { benchmarkEnabled = BenchStanzas `elem` stanzas }+    flagBenchmarks = map (\(n, bm) -> (n, mapTreeData enableBenchmark bm))+    flagTests = map (\(n, t) -> (n, mapTreeData enableTest t))+ -- ------------------------------------------------------------ -- * Package locations and repositories -- ------------------------------------------------------------@@ -156,8 +179,9 @@                   | UnpackFailed    SomeException                   | ConfigureFailed SomeException                   | BuildFailed     SomeException+                  | TestsFailed     SomeException                   | InstallFailed   SomeException data BuildSuccess = BuildOk         DocsResult TestsResult  data DocsResult  = DocsNotTried  | DocsFailed  | DocsOk-data TestsResult = TestsNotTried | TestsFailed | TestsOk+data TestsResult = TestsNotTried | TestsOk
Distribution/Client/Unpack.hs view
@@ -21,7 +21,7 @@ import Distribution.Package          ( PackageId, packageId ) import Distribution.Simple.Setup-         ( fromFlagOrDefault )+         ( fromFlag, fromFlagOrDefault ) import Distribution.Simple.Utils          ( notice, die ) import Distribution.Verbosity@@ -36,7 +36,7 @@ import Distribution.Client.FetchUtils import qualified Distribution.Client.Tar as Tar (extractTarGzFile) import Distribution.Client.IndexUtils as IndexUtils-        ( getAvailablePackages )+        ( getSourcePackages )  import System.Directory          ( createDirectoryIfMissing, doesDirectoryExist, doesFileExist )@@ -60,14 +60,16 @@ unpack verbosity repos globalFlags unpackFlags userTargets = do   mapM_ checkTarget userTargets -  availableDb   <- getAvailablePackages verbosity repos+  sourcePkgDb   <- getSourcePackages verbosity repos    pkgSpecifiers <- resolveUserTargets verbosity-                     globalFlags (packageIndex availableDb) userTargets+                     (fromFlag $ globalWorldFile globalFlags)+                     (packageIndex sourcePkgDb)+                     userTargets    pkgs <- either (die . unlines . map show) return $             resolveWithoutDependencies-              (resolverParams availableDb pkgSpecifiers)+              (resolverParams sourcePkgDb pkgSpecifiers)    unless (null prefix) $          createDirectoryIfMissing True prefix@@ -89,10 +91,10 @@         error "Distribution.Client.Unpack.unpack: the impossible happened."    where-    resolverParams availableDb pkgSpecifiers =+    resolverParams sourcePkgDb pkgSpecifiers =         --TODO: add commandline constraint and preference args for unpack -        standardInstallPolicy mempty availableDb pkgSpecifiers+        standardInstallPolicy mempty sourcePkgDb pkgSpecifiers      prefix = fromFlagOrDefault "" (unpackDestDir unpackFlags) 
Distribution/Client/Update.hs view
@@ -15,12 +15,12 @@     ) where  import Distribution.Client.Types-         ( Repo(..), RemoteRepo(..), LocalRepo(..), AvailablePackageDb(..) )+         ( Repo(..), RemoteRepo(..), LocalRepo(..), SourcePackageDb(..) ) import Distribution.Client.FetchUtils          ( downloadIndex ) import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Client.IndexUtils-         ( getAvailablePackages )+         ( getSourcePackages, updateRepoIndexCache ) import qualified Paths_cabal_install          ( version ) @@ -60,17 +60,18 @@     writeFileAtomic (dropExtension indexPath) . BS.Char8.unpack                                               . maybeDecompress                                             =<< BS.readFile indexPath+    updateRepoIndexCache verbosity repo  checkForSelfUpgrade :: Verbosity -> [Repo] -> IO () checkForSelfUpgrade verbosity repos = do-  AvailablePackageDb available prefs <- getAvailablePackages verbosity repos+  SourcePackageDb sourcePkgIndex prefs <- getSourcePackages verbosity repos    let self = PackageName "cabal-install"       preferredVersionRange  = fromMaybe anyVersion (Map.lookup self prefs)       currentVersion         = Paths_cabal_install.version       laterPreferredVersions =         [ packageVersion pkg-        | pkg <- PackageIndex.lookupPackageName available self+        | pkg <- PackageIndex.lookupPackageName sourcePkgIndex self         , let version = packageVersion pkg         , version > currentVersion         , version `withinRange` preferredVersionRange ]
Distribution/Client/Upload.hs view
@@ -4,7 +4,7 @@ module Distribution.Client.Upload (check, upload, report) where  import Distribution.Client.Types (Username(..), Password(..),Repo(..),RemoteRepo(..))-import Distribution.Client.HttpUtils (proxy, isOldHackageURI)+import Distribution.Client.HttpUtils (isOldHackageURI, cabalBrowse)  import Distribution.Simple.Utils (debug, notice, warn, info) import Distribution.Verbosity (Verbosity)@@ -15,9 +15,8 @@ import qualified Distribution.Client.BuildReports.Upload as BuildReport  import Network.Browser-         ( BrowserAction, browse, request-         , Authority(..), addAuthority, setAuthorityGen-         , setOutHandler, setErrHandler, setProxy )+         ( BrowserAction, request+         , Authority(..), addAuthority ) import Network.HTTP          ( Header(..), HeaderName(..), findHeader          , Request(..), RequestMethod(..), Response(..) )@@ -33,7 +32,7 @@ import System.FilePath  ((</>), takeExtension, takeFileName) import qualified System.FilePath.Posix as FilePath.Posix (combine) import System.Directory-import Control.Monad (forM_)+import Control.Monad (forM_, when)   --FIXME: how do we find this path for an arbitrary hackage server?@@ -98,16 +97,19 @@         Left remoteRepo             -> do dotCabal <- defaultCabalDir                   let srcDir = dotCabal </> "reports" </> remoteRepoName remoteRepo-                  contents <- getDirectoryContents srcDir-                  forM_ (filter (\c -> takeExtension c == ".log") contents) $ \logFile ->-                      do inp <- readFile (srcDir </> logFile)-                         let (reportStr, buildLog) = read inp :: (String,String)-                         case BuildReport.parse reportStr of-                           Left errs -> do warn verbosity $ "Errors: " ++ errs -- FIXME-                           Right report' ->-                               do info verbosity $ "Uploading report for " ++ display (BuildReport.package report')-                                  browse $ BuildReport.uploadReports (remoteRepoURI remoteRepo) [(report', Just buildLog)] auth-                                  return ()+                  -- We don't want to bomb out just because we haven't built any packages from this repo yet+                  srcExists <- doesDirectoryExist srcDir+                  when srcExists $ do+                    contents <- getDirectoryContents srcDir+                    forM_ (filter (\c -> takeExtension c == ".log") contents) $ \logFile ->+                        do inp <- readFile (srcDir </> logFile)+                           let (reportStr, buildLog) = read inp :: (String,String)+                           case BuildReport.parse reportStr of+                             Left errs -> do warn verbosity $ "Errors: " ++ errs -- FIXME+                             Right report' ->+                                 do info verbosity $ "Uploading report for " ++ display (BuildReport.package report')+                                    cabalBrowse verbosity auth $ BuildReport.uploadReports (remoteRepoURI remoteRepo) [(report', Just buildLog)]+                                    return ()         Right{} -> return ()   where     targetRepoURI = remoteRepoURI $ last [ remoteRepo | Left remoteRepo <- map repoKind repos ] --FIXME: better error message when no repos are given@@ -122,15 +124,8 @@               -> FilePath -> IO () handlePackage verbosity uri auth path =   do req <- mkRequest uri path-     p   <- proxy verbosity      debug verbosity $ "\n" ++ show req-     (_,resp) <- browse $ do-                   setProxy p-                   setErrHandler (warn verbosity . ("http error: "++))-                   setOutHandler (debug verbosity)-                   auth-                   setAuthorityGen (\_ _ -> return Nothing)-                   request req+     (_,resp) <- cabalBrowse verbosity auth $ request req      debug verbosity $ show resp      case rspCode resp of        (2,0,0) -> do notice verbosity "Ok"
Main.hs view
@@ -16,7 +16,7 @@ import Distribution.Client.Setup          ( GlobalFlags(..), globalCommand, globalRepos          , ConfigFlags(..)-         , ConfigExFlags(..), configureExCommand+         , ConfigExFlags(..), defaultConfigExFlags, configureExCommand          , InstallFlags(..), defaultInstallFlags          , installCommand, upgradeCommand          , FetchFlags(..), fetchCommand@@ -26,7 +26,8 @@          , InfoFlags(..), infoCommand          , UploadFlags(..), uploadCommand          , ReportFlags(..), reportCommand-         , InitFlags, initCommand+         , InitFlags(initVerbosity), initCommand+         , SDistFlags(..), SDistExFlags(..), sdistCommand          , reportCommand          , unpackCommand, UnpackFlags(..) ) import Distribution.Simple.Setup@@ -36,8 +37,8 @@          , CopyFlags(..), copyCommand          , RegisterFlags(..), registerCommand          , CleanFlags(..), cleanCommand-         , SDistFlags(..), sdistCommand          , TestFlags(..), testCommand+         , BenchmarkFlags(..), benchmarkCommand          , Flag(..), fromFlag, fromFlagOrDefault, flagToMaybe )  import Distribution.Client.SetupWrapper@@ -150,6 +151,8 @@                      regVerbosity      regDistPref       ,wrapperAction testCommand                      testVerbosity     testDistPref+      ,wrapperAction benchmarkCommand+                     benchmarkVerbosity     benchmarkDistPref       ,upgradeCommand         `commandAddAction` upgradeAction       ] @@ -184,29 +187,30 @@             (configPackageDB' configFlags') (globalRepos globalFlags')             comp conf configFlags' configExFlags' extraArgs -installAction :: (ConfigFlags, ConfigExFlags, InstallFlags)+installAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)               -> [String] -> GlobalFlags -> IO ()-installAction (configFlags, _, installFlags) _ _globalFlags+installAction (configFlags, _, installFlags, _) _ _globalFlags   | fromFlagOrDefault False (installOnly installFlags)   = let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)     in setupWrapper verbosity defaultSetupScriptOptions Nothing          installCommand (const mempty) [] -installAction (configFlags, configExFlags, installFlags)+installAction (configFlags, configExFlags, installFlags, haddockFlags)               extraArgs globalFlags = do   let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)   targets <- readUserTargets verbosity extraArgs   config <- loadConfig verbosity (globalConfigFile globalFlags)                                  (configUserInstall configFlags)   let configFlags'   = savedConfigureFlags   config `mappend` configFlags-      configExFlags' = savedConfigureExFlags config `mappend` configExFlags+      configExFlags' = defaultConfigExFlags         `mappend`+                       savedConfigureExFlags config `mappend` configExFlags       installFlags'  = defaultInstallFlags          `mappend`                        savedInstallFlags     config `mappend` installFlags       globalFlags'   = savedGlobalFlags      config `mappend` globalFlags   (comp, conf) <- configCompilerAux' configFlags'   install verbosity           (configPackageDB' configFlags') (globalRepos globalFlags')-          comp conf globalFlags' configFlags' configExFlags' installFlags'+          comp conf globalFlags' configFlags' configExFlags' installFlags' haddockFlags           targets  listAction :: ListFlags -> [String] -> GlobalFlags -> IO ()@@ -250,9 +254,9 @@   let globalFlags' = savedGlobalFlags config `mappend` globalFlags   update verbosity (globalRepos globalFlags') -upgradeAction :: (ConfigFlags, ConfigExFlags, InstallFlags)+upgradeAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)               -> [String] -> GlobalFlags -> IO ()-upgradeAction (configFlags, configExFlags, installFlags)+upgradeAction (configFlags, configExFlags, installFlags, haddockFlags)               extraArgs globalFlags = do   let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)   targets <- readUserTargets verbosity extraArgs@@ -266,7 +270,7 @@   (comp, conf) <- configCompilerAux' configFlags'   upgrade verbosity           (configPackageDB' configFlags') (globalRepos globalFlags')-          comp conf globalFlags' configFlags' configExFlags' installFlags'+          comp conf globalFlags' configFlags' configExFlags' installFlags' haddockFlags           targets  fetchAction :: FetchFlags -> [String] -> GlobalFlags -> IO ()@@ -322,11 +326,11 @@   unless allOk exitFailure  -sdistAction :: SDistFlags -> [String] -> GlobalFlags -> IO ()-sdistAction sflags extraArgs _globalFlags = do+sdistAction :: (SDistFlags, SDistExFlags) -> [String] -> GlobalFlags -> IO ()+sdistAction (sdistFlags, sdistExFlags) extraArgs _globalFlags = do   unless (null extraArgs) $ do     die $ "'sdist' doesn't take any extra arguments: " ++ unwords extraArgs-  sdist sflags+  sdist sdistFlags sdistExFlags  reportAction :: ReportFlags -> [String] -> GlobalFlags -> IO () reportAction reportFlags extraArgs globalFlags = do@@ -355,8 +359,16 @@          targets  initAction :: InitFlags -> [String] -> GlobalFlags -> IO ()-initAction flags _extraArgs _globalFlags = do-  initCabal flags+initAction initFlags _extraArgs globalFlags = do+  let verbosity = fromFlag (initVerbosity initFlags)+  config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+  let configFlags  = savedConfigureFlags config+  (comp, conf) <- configCompilerAux' configFlags+  initCabal verbosity+            (configPackageDB' configFlags)+            comp+            conf+            initFlags  -- | See 'Distribution.Client.Install.withWin32SelfUpgrade' for details. --
README view
@@ -17,13 +17,10 @@ is sometimes packaged separately by Linux distributions, for example on debian or ubuntu it is in "libghc6-network-dev". -It requires a few other Haskell packages that are not always installed:-- * Cabal  (version 1.10 or later)- * HTTP   (version 4000 or later)- * zlib   (version 0.4  or later)--All of these are available from [Hackage](http://hackage.haskell.org).+It requires a few other Haskell packages that are not always installed.+The exact list is specified in the `.cabal` file or in the `bootstrap.sh`+file. All these packages are available from+[Hackage](http://hackage.haskell.org).  Note that on some Unix systems you may need to install an additional zlib development package using your system package manager, for example on@@ -45,7 +42,7 @@      $ ./bootstrap.sh -It will download and install the above three dependencies. The script will+It will download and install the dependencies. The script will install the library packages into `$HOME/.cabal/` and the `cabal` program will be installed into `$HOME/.cabal/bin/`. @@ -139,13 +136,6 @@ By default it installs the latest available version however you can optionally specify exact versions or version ranges. For example `cabal install alex-2.2` or `cabal install parsec < 3`.--    $ cabal upgrade xmonad--This is a variation on the `install` command. Both mean to install the latest-version, the only difference is in the treatment of dependencies. The `install`-command tries to use existing installed versions of dependent packages while-the `upgrade` command tries to upgrade all the dependencies too.      $ cabal list xml 
bootstrap.sh view
@@ -7,7 +7,6 @@ # It expects to be run inside the cabal-install directory.  # install settings, you can override these by setting environment vars-PREFIX=${PREFIX:-${HOME}/.cabal} #VERBOSE #EXTRA_CONFIGURE_OPTS @@ -20,6 +19,7 @@ TAR=${TAR:-tar} GUNZIP=${GUNZIP:-gunzip} SCOPE_OF_INSTALLATION="--user"+DEFAULT_PREFIX="${HOME}/.cabal"   for arg in $*@@ -30,7 +30,7 @@       shift;;     "--global")       SCOPE_OF_INSTALLATION=${arg}-      PREFIX="/usr/local"+      DEFAULT_PREFIX="/usr/local"       shift;;     *)       echo "Unknown argument or option, quitting: ${arg}"@@ -43,17 +43,21 @@   esac done +PREFIX=${PREFIX:-${DEFAULT_PREFIX}}  # Versions of the packages to install. # The version regex says what existing installed versions are ok.-PARSEC_VER="3.1.1";    PARSEC_VER_REGEXP="[23]\."  # == 2.* || == 3.*-NETWORK_VER="2.3.0.2"; NETWORK_VER_REGEXP="2\."    # == 2.*-CABAL_VER="1.10.1.0";  CABAL_VER_REGEXP="1\.10\.[^0]"  # == 1.10.* && >= 1.10.1-TRANS_VER="0.2.2.0";   TRANS_VER_REGEXP="0\.2\."   # == 0.2.*-MTL_VER="2.0.1.0";     MTL_VER_REGEXP="[12]\."     # == 1.* || == 2.*-HTTP_VER="4000.1.1";   HTTP_VER_REGEXP="4000\.[01]\." # == 4000.0.* || 4000.1.*-ZLIB_VER="0.5.3.1";    ZLIB_VER_REGEXP="0\.[45]\." # == 0.4.* || ==0.5.*-TIME_VER="1.2.0.4"     TIME_VER_REGEXP="1\.[12]\." # == 0.1.* || ==0.2.*+PARSEC_VER="3.1.2";    PARSEC_VER_REGEXP="[23]\."  # == 2.* || == 3.*+DEEPSEQ_VER="1.3.0.0"; DEEPSEQ_VER_REGEXP="1\.[1-9]\." # >= 1.1 && < 2+TEXT_VER="0.11.2.0";  TEXT_VER_REGEXP="0\.([2-9]|(1[0-1]))\." # >= 0.2 && < 0.12+NETWORK_VER="2.3.0.11"; NETWORK_VER_REGEXP="2\."    # == 2.*+CABAL_VER="1.14.0";    CABAL_VER_REGEXP="1\.(13\.3|14\.)"  # >= 1.13.3 && < 1.15+TRANS_VER="0.3.0.0";   TRANS_VER_REGEXP="0\.[23]\."   # >= 0.2.* && < 0.4.*+MTL_VER="2.1";     MTL_VER_REGEXP="[12]\."     # == 1.* || == 2.*+HTTP_VER="4000.2.3";   HTTP_VER_REGEXP="4000\.[012]\." # == 4000.0.* || 4000.1.* || 4000.2.*+ZLIB_VER="0.5.3.3";    ZLIB_VER_REGEXP="0\.[45]\." # == 0.4.* || == 0.5.*+TIME_VER="1.4"         TIME_VER_REGEXP="1\.[1234]\.?" # >= 1.1 && < 1.5+RANDOM_VER="1.0.1.1"   RANDOM_VER_REGEXP="1\.0\." # >= 1 && < 1.1  HACKAGE_URL="http://hackage.haskell.org/packages/archive" @@ -86,7 +90,7 @@ need_pkg () {   PKG=$1   VER_MATCH=$2-  if grep " ${PKG}-${VER_MATCH}" ghc-pkg.list > /dev/null 2>&1+  if egrep " ${PKG}-${VER_MATCH}" ghc-pkg.list > /dev/null 2>&1   then     return 1;   else@@ -186,20 +190,26 @@ info_pkg "Cabal"        ${CABAL_VER}   ${CABAL_VER_REGEXP} info_pkg "transformers" ${TRANS_VER}   ${TRANS_VER_REGEXP} info_pkg "mtl"          ${MTL_VER}     ${MTL_VER_REGEXP}+info_pkg "deepseq"      ${DEEPSEQ_VER} ${DEEPSEQ_VER_REGEXP}+info_pkg "text"         ${TEXT_VER}    ${TEXT_VER_REGEXP} info_pkg "parsec"       ${PARSEC_VER}  ${PARSEC_VER_REGEXP} info_pkg "network"      ${NETWORK_VER} ${NETWORK_VER_REGEXP} info_pkg "time"         ${TIME_VER}    ${TIME_VER_REGEXP} info_pkg "HTTP"         ${HTTP_VER}    ${HTTP_VER_REGEXP} info_pkg "zlib"         ${ZLIB_VER}    ${ZLIB_VER_REGEXP}+info_pkg "random"       ${RANDOM_VER}  ${RANDOM_VER_REGEXP}  do_pkg   "Cabal"        ${CABAL_VER}   ${CABAL_VER_REGEXP} do_pkg   "transformers" ${TRANS_VER}   ${TRANS_VER_REGEXP} do_pkg   "mtl"          ${MTL_VER}     ${MTL_VER_REGEXP}+do_pkg   "deepseq"      ${DEEPSEQ_VER} ${DEEPSEQ_VER_REGEXP}+do_pkg   "text"         ${TEXT_VER}    ${TEXT_VER_REGEXP} do_pkg   "parsec"       ${PARSEC_VER}  ${PARSEC_VER_REGEXP} do_pkg   "network"      ${NETWORK_VER} ${NETWORK_VER_REGEXP} do_pkg   "time"         ${TIME_VER}    ${TIME_VER_REGEXP} do_pkg   "HTTP"         ${HTTP_VER}    ${HTTP_VER_REGEXP} do_pkg   "zlib"         ${ZLIB_VER}    ${ZLIB_VER_REGEXP}+do_pkg   "random"       ${RANDOM_VER}  ${RANDOM_VER_REGEXP}  install_pkg "cabal-install" 
cabal-install.cabal view
@@ -1,5 +1,5 @@ Name:               cabal-install-Version:            0.10.2+Version:            0.14.0 Synopsis:           The command-line interface for Cabal and Hackage. Description:     The \'cabal\' command-line program simplifies the process of managing@@ -19,7 +19,7 @@                     2006 Paolo Martini <paolo@nemail.it>                     2007 Bjorn Bringert <bjorn@bringert.net>                     2007 Isaac Potoczny-Jones <ijones@syntaxpolice.org>-                    2007-2011 Duncan Coutts <duncan@community.haskell.org>+                    2007-2012 Duncan Coutts <duncan@community.haskell.org> Category:           Distribution Build-type:         Simple Extra-Source-Files: README bash-completion/cabal bootstrap.sh@@ -27,12 +27,8 @@  source-repository head   type:     darcs-  location: http://darcs.haskell.org/cabal-install/--source-repository this-  type:     darcs-  location: http://darcs.haskell.org/cabal-branches/cabal-install-0.10/-  tag:      0.10.0+  location: http://darcs.haskell.org/cabal/+  subdir:   cabal-install  flag old-base   description: Old, monolithic base@@ -42,9 +38,7 @@  Executable cabal     Main-Is:            Main.hs-    -- We want assertion checking on even if people build with -O-    -- although it is expensive, we want to catch problems early:-    ghc-options:        -Wall -fno-ignore-asserts+    ghc-options:        -Wall     if impl(ghc >= 6.8)       ghc-options: -fwarn-tabs     Other-Modules:@@ -60,6 +54,25 @@         Distribution.Client.Dependency.TopDown.Constraints         Distribution.Client.Dependency.TopDown.Types         Distribution.Client.Dependency.Types+        Distribution.Client.Dependency.Modular+        Distribution.Client.Dependency.Modular.Assignment+        Distribution.Client.Dependency.Modular.Builder+        Distribution.Client.Dependency.Modular.Configured+        Distribution.Client.Dependency.Modular.ConfiguredConversion+        Distribution.Client.Dependency.Modular.Dependency+        Distribution.Client.Dependency.Modular.Explore+        Distribution.Client.Dependency.Modular.Flag+        Distribution.Client.Dependency.Modular.Index+        Distribution.Client.Dependency.Modular.IndexConversion+        Distribution.Client.Dependency.Modular.Log+        Distribution.Client.Dependency.Modular.Message+        Distribution.Client.Dependency.Modular.Package+        Distribution.Client.Dependency.Modular.Preference+        Distribution.Client.Dependency.Modular.PSQ+        Distribution.Client.Dependency.Modular.Solver+        Distribution.Client.Dependency.Modular.Tree+        Distribution.Client.Dependency.Modular.Validate+        Distribution.Client.Dependency.Modular.Version         Distribution.Client.Fetch         Distribution.Client.FetchUtils         Distribution.Client.GZipUtils@@ -93,24 +106,25 @@         Paths_cabal_install      build-depends: base     >= 2        && < 5,-                   Cabal    >= 1.10.1   && < 1.11,-                   filepath >= 1.0      && < 1.3,+                   Cabal    >= 1.14.0   && < 1.15,+                   filepath >= 1.0      && < 1.4,                    network  >= 1        && < 3,                    HTTP     >= 4000.0.2 && < 4001,                    zlib     >= 0.4      && < 0.6,-                   time     >= 1.1      && < 1.3+                   time     >= 1.1      && < 1.5,+                   mtl      >= 2.0      && < 3      if flag(old-base)       build-depends: base < 3     else       build-depends: base       >= 3,-                     process    >= 1   && < 1.1,+                     process    >= 1   && < 1.2,                      directory  >= 1   && < 1.2,-                     pretty     >= 1   && < 1.1,+                     pretty     >= 1   && < 1.2,                      random     >= 1   && < 1.1,                      containers >= 0.1 && < 0.5,-                     array      >= 0.1 && < 0.4,-                     old-time   >= 1   && < 1.1+                     array      >= 0.1 && < 0.5,+                     old-time   >= 1   && < 1.2      if flag(bytestring-in-base)       build-depends: base >= 2.0 && < 2.2@@ -121,5 +135,5 @@       build-depends: Win32 >= 2 && < 3       cpp-options: -DWIN32     else-      build-depends: unix >= 1.0 && < 2.5+      build-depends: unix >= 1.0 && < 2.6     extensions: CPP