diff --git a/cabal-install/Distribution/Client/BuildReports/Storage.hs b/cabal-install/Distribution/Client/BuildReports/Storage.hs index a4f20de32d50b494953b0a75bff2da5ad749049b..a4e17af306559b287fe5797cd7f5bde2d2871fa3 100644 --- a/cabal-install/Distribution/Client/BuildReports/Storage.hs +++ b/cabal-install/Distribution/Client/BuildReports/Storage.hs @@ -75,7 +75,8 @@ storeAnonymous reports = sequence_ . onlyRemote repoName (_,_,rrepo) = remoteRepoName rrepo - onlyRemote :: [(BuildReport, Maybe Repo)] -> [(BuildReport, Repo, RemoteRepo)] + onlyRemote :: [(BuildReport, Maybe Repo)] + -> [(BuildReport, Repo, RemoteRepo)] onlyRemote rs = [ (report, repo, remoteRepo) | (report, Just repo@Repo { repoKind = Left remoteRepo }) <- rs ] @@ -116,34 +117,39 @@ storeLocal cinfo templates reports platform = sequence_ -- * InstallPlan support -- ------------------------------------------------------------ -fromInstallPlan :: InstallPlan -> [(BuildReport, Maybe Repo)] -fromInstallPlan plan = catMaybes - . map (fromPlanPackage platform comp) - . InstallPlan.toList - $ plan - where platform = InstallPlan.planPlatform plan - comp = compilerInfoId (InstallPlan.planCompiler plan) +fromInstallPlan :: Platform -> CompilerId + -> InstallPlan + -> [(BuildReport, Maybe Repo)] +fromInstallPlan platform comp plan = + catMaybes + . map (fromPlanPackage platform comp) + . InstallPlan.toList + $ plan fromPlanPackage :: Platform -> CompilerId -> InstallPlan.PlanPackage -> Maybe (BuildReport, Maybe Repo) fromPlanPackage (Platform arch os) comp planPackage = case planPackage of - InstallPlan.Installed (ReadyPackage srcPkg flags _ deps) result + InstallPlan.Installed (ReadyPackage (ConfiguredPackage srcPkg flags _ _) deps) + _ result -> Just $ ( BuildReport.new os arch comp - (packageId srcPkg) flags (map packageId (CD.nonSetupDeps deps)) + (packageId srcPkg) flags + (map packageId (CD.nonSetupDeps deps)) (Right result) , extractRepo srcPkg) InstallPlan.Failed (ConfiguredPackage srcPkg flags _ deps) result -> Just $ ( BuildReport.new os arch comp - (packageId srcPkg) flags (map confSrcId (CD.nonSetupDeps deps)) + (packageId srcPkg) flags + (map confSrcId (CD.nonSetupDeps deps)) (Left result) , extractRepo srcPkg ) _ -> Nothing where - extractRepo (SourcePackage { packageSource = RepoTarballPackage repo _ _ }) = Just repo + extractRepo (SourcePackage { packageSource = RepoTarballPackage repo _ _ }) + = Just repo extractRepo _ = Nothing fromPlanningFailure :: Platform -> CompilerId diff --git a/cabal-install/Distribution/Client/Configure.hs b/cabal-install/Distribution/Client/Configure.hs index b106cb5ee817683834aaf67f3f5cd7137b56a1b8..8c098220d63a45fe684b7cefa26930e0c0291be5 100644 --- a/cabal-install/Distribution/Client/Configure.hs +++ b/cabal-install/Distribution/Client/Configure.hs @@ -112,14 +112,16 @@ configure verbosity packageDBs repos comp platform conf "Warning: solver failed to find a solution:\n" ++ message ++ "Trying configure anyway." - setupWrapper verbosity (setupScriptOptions installedPkgIndex Nothing) Nothing - configureCommand (const configFlags) extraArgs + setupWrapper verbosity (setupScriptOptions installedPkgIndex Nothing) + Nothing configureCommand (const configFlags) extraArgs Right installPlan -> case InstallPlan.ready installPlan of - [pkg@(ReadyPackage (SourcePackage _ _ (LocalUnpackedPackage _) _) _ _ _)] -> do + [pkg@(ReadyPackage + (ConfiguredPackage (SourcePackage _ _ (LocalUnpackedPackage _) _) + _ _ _) + _)] -> do configurePackage verbosity - (InstallPlan.planPlatform installPlan) - (InstallPlan.planCompiler installPlan) + platform (compilerInfo comp) (setupScriptOptions installedPkgIndex (Just pkg)) configFlags pkg extraArgs @@ -127,7 +129,9 @@ configure verbosity packageDBs repos comp platform conf ++ "one local ready package." where - setupScriptOptions :: InstalledPackageIndex -> Maybe ReadyPackage -> SetupScriptOptions + setupScriptOptions :: InstalledPackageIndex + -> Maybe ReadyPackage + -> SetupScriptOptions setupScriptOptions = configureSetupScript packageDBs @@ -206,7 +210,8 @@ configureSetupScript packageDBs explicitSetupDeps :: Maybe [(InstalledPackageId, PackageId)] explicitSetupDeps = do - ReadyPackage (SourcePackage _ gpkg _ _) _ _ deps <- mpkg + ReadyPackage (ConfiguredPackage (SourcePackage _ gpkg _ _) _ _ _) deps + <- mpkg -- Check if there is an explicit setup stanza _buildInfo <- PkgDesc.setupBuildInfo (PkgDesc.packageDescription gpkg) -- Return the setup dependencies computed by the solver @@ -225,10 +230,12 @@ planLocalPackage :: Verbosity -> Compiler -> InstalledPackageIndex -> SourcePackageDb -> IO (Progress String String InstallPlan) -planLocalPackage verbosity comp platform configFlags configExFlags installedPkgIndex +planLocalPackage verbosity comp platform configFlags configExFlags + installedPkgIndex (SourcePackageDb _ packagePrefs) = do pkg <- readPackageDescription verbosity =<< defaultPackageDesc verbosity - solver <- chooseSolver verbosity (fromFlag $ configSolver configExFlags) (compilerInfo comp) + solver <- chooseSolver verbosity (fromFlag $ configSolver configExFlags) + (compilerInfo comp) let -- We create a local package and ask to resolve a dependency on it localPkg = SourcePackage { @@ -294,7 +301,10 @@ configurePackage :: Verbosity -> [String] -> IO () configurePackage verbosity platform comp scriptOptions configFlags - (ReadyPackage (SourcePackage _ gpkg _ _) flags stanzas deps) extraArgs = + (ReadyPackage (ConfiguredPackage (SourcePackage _ gpkg _ _) + flags stanzas _) + deps) + extraArgs = setupWrapper verbosity scriptOptions (Just pkg) configureCommand configureFlags extraArgs diff --git a/cabal-install/Distribution/Client/Dependency.hs b/cabal-install/Distribution/Client/Dependency.hs index da528194c8217d542ce70905cea62fa3df726a3a..e34be5e16f0e2ca63688dbea47c47232984a0614 100644 --- a/cabal-install/Distribution/Client/Dependency.hs +++ b/cabal-install/Distribution/Client/Dependency.hs @@ -69,26 +69,34 @@ import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.InstallPlan (InstallPlan) import Distribution.Client.Types - ( SourcePackageDb(SourcePackageDb) - , SourcePackage(..) ) + ( SourcePackageDb(SourcePackageDb), SourcePackage(..) + , ConfiguredPackage(..), ConfiguredId(..), enableStanzas ) import Distribution.Client.Dependency.Types - ( PreSolver(..), Solver(..), DependencyResolver, PackageConstraint(..) - , debugPackageConstraint + ( PreSolver(..), Solver(..), DependencyResolver, ResolverPackage(..) + , PackageConstraint(..), debugPackageConstraint , AllowNewer(..), PackagePreferences(..), InstalledPreference(..) , PackagesPreferenceDefault(..) , Progress(..), foldProgress ) import Distribution.Client.Sandbox.Types ( SandboxPackageInfo(..) ) import Distribution.Client.Targets +import Distribution.Client.ComponentDeps (ComponentDeps) +import qualified Distribution.Client.ComponentDeps as CD import qualified Distribution.InstalledPackageInfo as Installed import Distribution.Package - ( PackageName(..), PackageId, Package(..), packageName, packageVersion + ( PackageName(..), PackageIdentifier(PackageIdentifier), PackageId + , Package(..), packageName, packageVersion , InstalledPackageId, Dependency(Dependency)) import qualified Distribution.PackageDescription as PD - ( PackageDescription(..), GenericPackageDescription(..) - , Library(..), Executable(..), TestSuite(..), Benchmark(..), CondTree) + ( PackageDescription(..), Library(..), Executable(..) + , TestSuite(..), Benchmark(..), SetupBuildInfo(..) + , GenericPackageDescription(..), CondTree + , Flag(flagName), FlagName(..) ) import Distribution.PackageDescription (BuildInfo(targetBuildDepends)) -import Distribution.PackageDescription.Configuration (mapCondTree) +import Distribution.PackageDescription.Configuration + ( mapCondTree, finalizePackageDescription ) +import Distribution.Client.PackageUtils + ( externalBuildDepends ) import Distribution.Version ( VersionRange, anyVersion, thisVersion, withinRange , removeUpperBound, simplifyVersionRange ) @@ -96,6 +104,8 @@ import Distribution.Compiler ( CompilerInfo(..) ) import Distribution.System ( Platform ) +import Distribution.Client.Utils + ( duplicates, duplicatesBy, mergeBy, MergeResult(..) ) import Distribution.Simple.Utils ( comparing, warn, info ) import Distribution.Text @@ -103,11 +113,16 @@ import Distribution.Text import Distribution.Verbosity ( Verbosity ) -import Data.List (maximumBy, foldl', intercalate) +import Data.List + ( foldl', sort, sortBy, nubBy, maximumBy, intercalate ) +import Data.Function (on) import Data.Maybe (fromMaybe) import qualified Data.Map as Map import qualified Data.Set as Set import Data.Set (Set) +import Control.Exception + ( assert ) + -- ------------------------------------------------------------ -- * High level planner policy @@ -136,9 +151,11 @@ debugDepResolverParams :: DepResolverParams -> String debugDepResolverParams p = "targets: " ++ intercalate ", " (map display (depResolverTargets p)) ++ "\nconstraints: " - ++ concatMap (("\n " ++) . debugPackageConstraint) (depResolverConstraints p) + ++ concatMap (("\n " ++) . debugPackageConstraint) + (depResolverConstraints p) ++ "\npreferences: " - ++ concatMap (("\n " ++) . debugPackagePreference) (depResolverPreferences p) + ++ concatMap (("\n " ++) . debugPackagePreference) + (depResolverPreferences p) ++ "\nstrategy: " ++ show (depResolverPreferenceDefault p) -- | A package selection preference for a particular package. @@ -513,12 +530,14 @@ resolveDependencies :: Platform --TODO: is this needed here? see dontUpgradeNonUpgradeablePackages resolveDependencies platform comp _solver params | null (depResolverTargets params) - = return (mkInstallPlan platform comp (depResolverIndependentGoals params) []) + = return (validateSolverResult platform comp indGoals []) + where + indGoals = depResolverIndependentGoals params resolveDependencies platform comp solver params = Step (debugDepResolverParams finalparams) - $ fmap (mkInstallPlan platform comp indGoals) + $ fmap (validateSolverResult platform comp indGoals) $ runSolver solver (SolverConfig reorderGoals indGoals noReinstalls shadowing strFlags maxBkjumps) platform comp installedPkgIndex sourcePkgIndex @@ -548,24 +567,6 @@ resolveDependencies platform comp solver params = preferences = interpretPackagesPreference (Set.fromList targets) defpref prefs --- | Make an install plan from the output of the dep resolver. --- It checks that the plan is valid, or it's an error in the dep resolver. --- -mkInstallPlan :: Platform - -> CompilerInfo - -> Bool - -> [InstallPlan.PlanPackage] -> InstallPlan -mkInstallPlan platform comp indepGoals pkgIndex = - let index = InstalledPackageIndex.fromList pkgIndex in - case InstallPlan.new platform comp indepGoals index of - Right plan -> plan - Left problems -> error $ unlines $ - "internal error: could not construct a valid install plan." - : "The proposed (invalid) plan contained the following problems:" - : map InstallPlan.showPlanProblem problems - ++ "Proposed plan:" - : [InstallPlan.showPlanIndex index] - -- | Give an interpretation to the global 'PackagesPreference' as -- specific per-package 'PackageVersionPreference'. @@ -598,6 +599,165 @@ interpretPackagesPreference selected defaultPref prefs = if pkgname `Set.member` selected then PreferLatest else PreferInstalled +-- ------------------------------------------------------------ +-- * Checking the result of the solver +-- ------------------------------------------------------------ + +-- | Make an install plan from the output of the dep resolver. +-- It checks that the plan is valid, or it's an error in the dep resolver. +-- +validateSolverResult :: Platform + -> CompilerInfo + -> Bool + -> [ResolverPackage] + -> InstallPlan +validateSolverResult platform comp indepGoals pkgs = + case planPackagesProblems platform comp pkgs of + [] -> case InstallPlan.new indepGoals index of + Right plan -> plan + Left problems -> error (formatPlanProblems problems) + problems -> error (formatPkgProblems problems) + + where + index = InstalledPackageIndex.fromList (map toPlanPackage pkgs) + + toPlanPackage (PreExisting pkg) = InstallPlan.PreExisting pkg + toPlanPackage (Configured pkg) = InstallPlan.Configured pkg + + formatPkgProblems = formatProblemMessage . map showPlanPackageProblem + formatPlanProblems = formatProblemMessage . map InstallPlan.showPlanProblem + + formatProblemMessage problems = + unlines $ + "internal error: could not construct a valid install plan." + : "The proposed (invalid) plan contained the following problems:" + : problems + ++ "Proposed plan:" + : [InstallPlan.showPlanIndex index] + + +data PlanPackageProblem = + InvalidConfiguredPackage ConfiguredPackage [PackageProblem] + +showPlanPackageProblem :: PlanPackageProblem -> String +showPlanPackageProblem (InvalidConfiguredPackage pkg packageProblems) = + "Package " ++ display (packageId pkg) + ++ " has an invalid configuration, in particular:\n" + ++ unlines [ " " ++ showPackageProblem problem + | problem <- packageProblems ] + +planPackagesProblems :: Platform -> CompilerInfo + -> [ResolverPackage] + -> [PlanPackageProblem] +planPackagesProblems platform cinfo pkgs = + [ InvalidConfiguredPackage pkg packageProblems + | Configured pkg <- pkgs + , let packageProblems = configuredPackageProblems platform cinfo pkg + , not (null packageProblems) ] + +data PackageProblem = DuplicateFlag PD.FlagName + | MissingFlag PD.FlagName + | ExtraFlag PD.FlagName + | DuplicateDeps [PackageId] + | MissingDep Dependency + | ExtraDep PackageId + | InvalidDep Dependency PackageId + +showPackageProblem :: PackageProblem -> String +showPackageProblem (DuplicateFlag (PD.FlagName flag)) = + "duplicate flag in the flag assignment: " ++ flag + +showPackageProblem (MissingFlag (PD.FlagName flag)) = + "missing an assignment for the flag: " ++ flag + +showPackageProblem (ExtraFlag (PD.FlagName flag)) = + "extra flag given that is not used by the package: " ++ flag + +showPackageProblem (DuplicateDeps pkgids) = + "duplicate packages specified as selected dependencies: " + ++ intercalate ", " (map display pkgids) + +showPackageProblem (MissingDep dep) = + "the package has a dependency " ++ display dep + ++ " but no package has been selected to satisfy it." + +showPackageProblem (ExtraDep pkgid) = + "the package configuration specifies " ++ display pkgid + ++ " but (with the given flag assignment) the package does not actually" + ++ " depend on any version of that package." + +showPackageProblem (InvalidDep dep pkgid) = + "the package depends on " ++ display dep + ++ " but the configuration specifies " ++ display pkgid + ++ " which does not satisfy the dependency." + +-- | A 'ConfiguredPackage' is valid if the flag assignment is total and if +-- in the configuration given by the flag assignment, all the package +-- dependencies are satisfied by the specified packages. +-- +configuredPackageProblems :: Platform -> CompilerInfo + -> ConfiguredPackage -> [PackageProblem] +configuredPackageProblems platform cinfo + (ConfiguredPackage pkg specifiedFlags stanzas specifiedDeps') = + [ DuplicateFlag flag | ((flag,_):_) <- duplicates specifiedFlags ] + ++ [ MissingFlag flag | OnlyInLeft flag <- mergedFlags ] + ++ [ ExtraFlag flag | OnlyInRight flag <- mergedFlags ] + ++ [ DuplicateDeps pkgs + | pkgs <- CD.nonSetupDeps (fmap (duplicatesBy (comparing packageName)) + specifiedDeps) ] + ++ [ MissingDep dep | OnlyInLeft dep <- mergedDeps ] + ++ [ ExtraDep pkgid | OnlyInRight pkgid <- mergedDeps ] + ++ [ InvalidDep dep pkgid | InBoth dep pkgid <- mergedDeps + , not (packageSatisfiesDependency pkgid dep) ] + where + specifiedDeps :: ComponentDeps [PackageId] + specifiedDeps = fmap (map confSrcId) specifiedDeps' + + mergedFlags = mergeBy compare + (sort $ map PD.flagName (PD.genPackageFlags (packageDescription pkg))) + (sort $ map fst specifiedFlags) + + packageSatisfiesDependency + (PackageIdentifier name version) + (Dependency name' versionRange) = assert (name == name') $ + version `withinRange` versionRange + + dependencyName (Dependency name _) = name + + mergedDeps :: [MergeResult Dependency PackageId] + mergedDeps = mergeDeps requiredDeps (CD.flatDeps specifiedDeps) + + mergeDeps :: [Dependency] -> [PackageId] + -> [MergeResult Dependency PackageId] + mergeDeps required specified = + let sortNubOn f = nubBy ((==) `on` f) . sortBy (compare `on` f) in + mergeBy + (\dep pkgid -> dependencyName dep `compare` packageName pkgid) + (sortNubOn dependencyName required) + (sortNubOn packageName specified) + + -- TODO: It would be nicer to use ComponentDeps here so we can be more + -- precise in our checks. That's a bit tricky though, as this currently + -- relies on the 'buildDepends' field of 'PackageDescription'. (OTOH, that + -- field is deprecated and should be removed anyway.) As long as we _do_ + -- use a flat list here, we have to allow for duplicates when we fold + -- specifiedDeps; once we have proper ComponentDeps here we should get rid + -- of the `nubOn` in `mergeDeps`. + requiredDeps :: [Dependency] + requiredDeps = + --TODO: use something lower level than finalizePackageDescription + case finalizePackageDescription specifiedFlags + (const True) + platform cinfo + [] + (enableStanzas stanzas $ packageDescription pkg) of + Right (resolvedPkg, _) -> + externalBuildDepends resolvedPkg + ++ maybe [] PD.setupDepends (PD.setupBuildInfo resolvedPkg) + Left _ -> + error "configuredPackageInvalidDeps internal error" + + -- ------------------------------------------------------------ -- * Simple resolver that ignores dependencies -- ------------------------------------------------------------ diff --git a/cabal-install/Distribution/Client/Dependency/Modular.hs b/cabal-install/Distribution/Client/Dependency/Modular.hs index 11745fb5dfaaf5da9b2b8d64c3f1260857b5e60b..b2be4fc2fe1429574ce07e7610a3b9c69726ac5b 100644 --- a/cabal-install/Distribution/Client/Dependency/Modular.hs +++ b/cabal-install/Distribution/Client/Dependency/Modular.hs @@ -26,9 +26,7 @@ import Distribution.Client.Dependency.Modular.Package import Distribution.Client.Dependency.Modular.Solver ( SolverConfig(..), solve ) import Distribution.Client.Dependency.Types - ( DependencyResolver, PackageConstraint(..) ) -import Distribution.Client.InstallPlan - ( PlanPackage ) + ( DependencyResolver, ResolverPackage, PackageConstraint(..) ) import Distribution.System ( Platform(..) ) @@ -46,7 +44,7 @@ modularResolver sc (Platform arch os) cinfo iidx sidx pprefs pcs pns = gcs = M.fromListWith (++) (map (\ pc -> (pcName pc, [pc])) pcs) -- Results have to be converted into an install plan. - postprocess :: Assignment -> RevDepMap -> [PlanPackage] + postprocess :: Assignment -> RevDepMap -> [ResolverPackage] postprocess a rdm = map (convCP iidx sidx) (toCPs a rdm) -- Helper function to extract the PN from a constraint. diff --git a/cabal-install/Distribution/Client/Dependency/Modular/ConfiguredConversion.hs b/cabal-install/Distribution/Client/Dependency/Modular/ConfiguredConversion.hs index 47968d19ec2ba06cca2c760df581b3ff3666e98b..714eb845391afb2ec2d5ea3f77aa096ef4cdd13f 100644 --- a/cabal-install/Distribution/Client/Dependency/Modular/ConfiguredConversion.hs +++ b/cabal-install/Distribution/Client/Dependency/Modular/ConfiguredConversion.hs @@ -3,32 +3,23 @@ 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 Distribution.Client.Dependency.Types (ResolverPackage(..)) 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 import Distribution.Client.ComponentDeps (ComponentDeps) -import qualified Distribution.Client.ComponentDeps as CD -mkPlan :: Platform -> CompilerInfo -> Bool -> - SI.InstalledPackageIndex -> CI.PackageIndex SourcePackage -> - [CP QPN] -> Either [PlanProblem] InstallPlan -mkPlan plat comp indepGoals iidx sidx cps = - new plat comp indepGoals (SI.fromList (map (convCP iidx sidx) cps)) convCP :: SI.InstalledPackageIndex -> CI.PackageIndex SourcePackage -> - CP QPN -> PlanPackage + CP QPN -> ResolverPackage convCP iidx sidx (CP qpi fa es ds) = case convPI qpi of - Left pi -> PreExisting $ InstalledPackage + Left pi -> PreExisting (fromJust $ SI.lookupInstalledPackageId iidx pi) - (map confSrcId $ CD.nonSetupDeps ds') Right pi -> Configured $ ConfiguredPackage (fromJust $ CI.lookupPackageId sidx pi) fa diff --git a/cabal-install/Distribution/Client/Dependency/TopDown.hs b/cabal-install/Distribution/Client/Dependency/TopDown.hs index 74f5d24961ff2c871b0a90078abc12003617e285..32a53b130ab1b15792aae496533a12c442c57f52 100644 --- a/cabal-install/Distribution/Client/Dependency/TopDown.hs +++ b/cabal-install/Distribution/Client/Dependency/TopDown.hs @@ -19,29 +19,28 @@ import Distribution.Client.Dependency.TopDown.Types 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 - ( SourcePackage(..), ConfiguredPackage(..), InstalledPackage(..) + ( SourcePackage(..), ConfiguredPackage(..) , enableStanzas, ConfiguredId(..), fakeInstalledPackageId ) import Distribution.Client.Dependency.Types - ( DependencyResolver, PackageConstraint(..) + ( DependencyResolver, ResolverPackage(..), PackageConstraint(..) , PackagePreferences(..), InstalledPreference(..) , Progress(..), foldProgress ) import qualified Distribution.Client.PackageIndex as PackageIndex +import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex +import Distribution.Simple.PackageIndex (InstalledPackageIndex) +import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo import Distribution.Client.ComponentDeps ( ComponentDeps ) import qualified Distribution.Client.ComponentDeps as CD import Distribution.Client.PackageIndex ( PackageIndex ) import Distribution.Package - ( PackageName(..), PackageId, Package(..), packageVersion, packageName - , Dependency(Dependency), thisPackageVersion - , simplifyDependency ) + ( PackageName(..), PackageId, PackageIdentifier(..) + , InstalledPackageId(..) + , Package(..), packageVersion, packageName + , Dependency(Dependency), thisPackageVersion, simplifyDependency ) import Distribution.PackageDescription ( PackageDescription(buildDepends) ) import Distribution.Client.PackageUtils @@ -49,7 +48,7 @@ import Distribution.Client.PackageUtils import Distribution.PackageDescription.Configuration ( finalizePackageDescription, flattenPackageDescription ) import Distribution.Version - ( VersionRange, withinRange, simplifyVersionRange + ( Version(..), VersionRange, withinRange, simplifyVersionRange , UpperBound(..), asVersionIntervals ) import Distribution.Compiler ( CompilerInfo ) @@ -251,9 +250,12 @@ search configure pref constraints = topDownResolver :: DependencyResolver topDownResolver platform cinfo installedPkgIndex sourcePkgIndex preferences constraints targets = - mapMessages (topDownResolver' platform cinfo - (convert installedPkgIndex) sourcePkgIndex - preferences constraints targets) + mapMessages $ topDownResolver' + platform cinfo + (convertInstalledPackageIndex installedPkgIndex) + sourcePkgIndex + preferences constraints + targets where mapMessages :: Progress Log Failure a -> Progress String String a mapMessages = foldProgress (Step . showLog) (Fail . showFailure) Done @@ -266,7 +268,7 @@ topDownResolver' :: Platform -> CompilerInfo -> (PackageName -> PackagePreferences) -> [PackageConstraint] -> [PackageName] - -> Progress Log Failure [PlanPackage] + -> Progress Log Failure [ResolverPackage] topDownResolver' platform cinfo installedPkgIndex sourcePkgIndex preferences constraints targets = fmap (uncurry finalise) @@ -288,11 +290,16 @@ topDownResolver' platform cinfo installedPkgIndex sourcePkgIndex initialPkgNames = Set.fromList targets finalise selected' constraints' = - PackageIndex.allPackages + map toResolverPackage + . PackageIndex.allPackages . fst . improvePlan installedPkgIndex' constraints' . PackageIndex.fromList $ finaliseSelectedPackages preferences selected' constraints' + toResolverPackage :: FinalSelectedPackage -> ResolverPackage + toResolverPackage (SelectedInstalled (InstalledPackage pkg _)) + = PreExisting pkg + toResolverPackage (SelectedSource pkg) = Configured pkg addTopLevelTargets :: [PackageName] -> Constraints @@ -538,6 +545,43 @@ selectNeededSubset installedPkgIndex sourcePkgIndex = select mempty mempty null (PackageIndex.lookupPackageName installedPkgIndex' name) && null (PackageIndex.lookupPackageName sourcePkgIndex' name) + +-- | The old top down solver assumes that installed packages are indexed by +-- their source package id. But these days they're actually indexed by an +-- installed package id and there can be many installed packages with the same +-- source package id. This function tries to do a convertion, but it can only +-- be partial. +-- +convertInstalledPackageIndex :: InstalledPackageIndex + -> PackageIndex InstalledPackage +convertInstalledPackageIndex 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.allPackagesBySourcePackageId 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 (sourceDepsOf index' ipkg) + | (_,ipkg:_) <- InstalledPackageIndex.allPackagesBySourcePackageId index' ] + 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 + -- InstalledPackageId to get the InstalledPackageInfo and look at its + -- source PackageId. But if the package is broken because it depends on + -- other packages that do not exist then we have a problem we cannot find + -- the original source package id. Instead we make up a bogus package id. + -- This should have the same effect since it should be a dependency on a + -- nonexistent package. + sourceDepsOf index ipkg = + [ maybe (brokenPackageId depid) packageId mdep + | let depids = InstalledPackageInfo.depends ipkg + getpkg = InstalledPackageIndex.lookupInstalledPackageId index + , (depid, mdep) <- zip depids (map getpkg depids) ] + + brokenPackageId (InstalledPackageId str) = + PackageIdentifier (PackageName (str ++ "-broken")) (Version [] []) + -- ------------------------------------------------------------ -- * Post processing the solution -- ------------------------------------------------------------ @@ -545,7 +589,7 @@ selectNeededSubset installedPkgIndex sourcePkgIndex = select mempty mempty finaliseSelectedPackages :: (PackageName -> PackagePreferences) -> SelectedPackages -> Constraints - -> [PlanPackage] + -> [FinalSelectedPackage] finaliseSelectedPackages pref selected constraints = map finaliseSelected (PackageIndex.allPackages selected) where @@ -561,9 +605,9 @@ finaliseSelectedPackages pref selected constraints = Just (InstalledOnly _) -> finaliseInstalled ipkg Just (InstalledAndSource _ _) -> finaliseSource (Just ipkg) apkg - finaliseInstalled (InstalledPackageEx pkg _ _) = InstallPlan.PreExisting pkg + finaliseInstalled (InstalledPackageEx pkg _ _) = SelectedInstalled pkg finaliseSource mipkg (SemiConfiguredPackage pkg flags stanzas deps) = - InstallPlan.Configured (ConfiguredPackage pkg flags stanzas deps') + SelectedSource (ConfiguredPackage pkg flags stanzas deps') where -- We cheat in the cabal solver, and classify all dependencies as -- library dependencies. @@ -649,8 +693,8 @@ finaliseSelectedPackages pref selected constraints = -- improvePlan :: PackageIndex InstalledPackage -> Constraints - -> PackageIndex PlanPackage - -> (PackageIndex PlanPackage, Constraints) + -> PackageIndex FinalSelectedPackage + -> (PackageIndex FinalSelectedPackage, Constraints) improvePlan installed constraints0 selected0 = foldl' improve (selected0, constraints0) (reverseTopologicalOrder selected0) where @@ -663,26 +707,26 @@ improvePlan installed constraints0 selected0 = -- already installed with the exact same dependencies and all the packages -- in the plan that it depends on are in the installed state improvePkg selected constraints pkgid = do - Configured pkg <- PackageIndex.lookupPackageId selected pkgid - ipkg <- PackageIndex.lookupPackageId installed pkgid + SelectedSource pkg <- PackageIndex.lookupPackageId selected pkgid + ipkg <- PackageIndex.lookupPackageId installed pkgid guard $ all (isInstalled selected) (sourceDeps pkg) tryInstalled selected constraints [ipkg] isInstalled selected pkgid = case PackageIndex.lookupPackageId selected pkgid of - Just (PreExisting _) -> True - _ -> False + Just (SelectedInstalled _) -> True + _ -> False - tryInstalled :: PackageIndex PlanPackage -> Constraints + tryInstalled :: PackageIndex FinalSelectedPackage -> Constraints -> [InstalledPackage] - -> Maybe (PackageIndex PlanPackage, Constraints) + -> Maybe (PackageIndex FinalSelectedPackage, Constraints) tryInstalled selected constraints [] = Just (selected, constraints) tryInstalled selected constraints (pkg:pkgs) = case constraintsOk (packageId pkg) (sourceDeps pkg) constraints of Nothing -> Nothing Just constraints' -> tryInstalled selected' constraints' pkgs' where - selected' = PackageIndex.insert (PreExisting pkg) selected + selected' = PackageIndex.insert (SelectedInstalled pkg) selected pkgs' = catMaybes (map notSelected (sourceDeps pkg)) ++ pkgs notSelected pkgid = case (PackageIndex.lookupPackageId installed pkgid @@ -698,7 +742,7 @@ improvePlan installed constraints0 selected0 = where dep = thisPackageVersion pkgid' - reverseTopologicalOrder :: PackageIndex PlanPackage -> [PackageId] + reverseTopologicalOrder :: PackageIndex FinalSelectedPackage -> [PackageId] reverseTopologicalOrder index = map (packageId . toPkg) . Graph.topSort . Graph.transposeG @@ -1001,7 +1045,7 @@ listOf disp (x0:x1:xs) = disp x0 ++ go x1 xs -- this duplication could be avoided, but that's a bit of work and the top-down -- solver is legacy code anyway. -- --- (NOTE: This is called at two types: InstalledPackage and PlanPackage.) +-- (NOTE: This is called at two types: InstalledPackage and FinalSelectedPackage.) dependencyGraph :: PackageSourceDeps pkg => PackageIndex pkg -> (Graph.Graph, diff --git a/cabal-install/Distribution/Client/Dependency/TopDown/Types.hs b/cabal-install/Distribution/Client/Dependency/TopDown/Types.hs index 73c1a27327326279290b9c705dd32842f0f23ecd..d0988c8e37badc89b5b19776e0c4c52ef99794a1 100644 --- a/cabal-install/Distribution/Client/Dependency/TopDown/Types.hs +++ b/cabal-install/Distribution/Client/Dependency/TopDown/Types.hs @@ -14,14 +14,14 @@ module Distribution.Client.Dependency.TopDown.Types where import Distribution.Client.Types - ( SourcePackage(..), ReadyPackage(..), InstalledPackage(..) + ( SourcePackage(..), ConfiguredPackage(..) , OptionalStanza, ConfiguredId(..) ) -import Distribution.Client.InstallPlan - ( ConfiguredPackage(..), PlanPackage(..) ) +import Distribution.InstalledPackageInfo + ( InstalledPackageInfo ) import qualified Distribution.Client.ComponentDeps as CD import Distribution.Package - ( PackageIdentifier, Dependency + ( PackageId, PackageIdentifier, Dependency , Package(packageId) ) import Distribution.PackageDescription ( FlagAssignment ) @@ -42,8 +42,18 @@ data InstalledOrSource installed source | InstalledAndSource installed source deriving Eq +data FinalSelectedPackage + = SelectedInstalled InstalledPackage + | SelectedSource ConfiguredPackage + type TopologicalSortNumber = Int +-- | InstalledPackage caches its dependencies as source package IDs. +data InstalledPackage + = InstalledPackage + InstalledPackageInfo + [PackageId] + data InstalledPackageEx = InstalledPackageEx InstalledPackage @@ -65,6 +75,9 @@ data SemiConfiguredPackage [Dependency] -- dependencies we end up with when we apply -- the flag assignment +instance Package InstalledPackage where + packageId (InstalledPackage pkg _) = packageId pkg + instance Package InstalledPackageEx where packageId (InstalledPackageEx p _ _) = packageId p @@ -80,6 +93,10 @@ instance (Package installed, Package source) packageId (SourceOnly p ) = packageId p packageId (InstalledAndSource p _) = packageId p +instance Package FinalSelectedPackage where + packageId (SelectedInstalled pkg) = packageId pkg + packageId (SelectedSource pkg) = packageId pkg + -- | We can have constraints on selecting just installed or just source -- packages. @@ -117,15 +134,10 @@ instance PackageSourceDeps InstalledPackageEx where instance PackageSourceDeps ConfiguredPackage where sourceDeps (ConfiguredPackage _ _ _ deps) = map confSrcId $ CD.nonSetupDeps deps -instance PackageSourceDeps ReadyPackage where - sourceDeps (ReadyPackage _ _ _ deps) = map packageId $ CD.nonSetupDeps deps - instance PackageSourceDeps InstalledPackage where sourceDeps (InstalledPackage _ deps) = deps -instance PackageSourceDeps PlanPackage where - sourceDeps (PreExisting pkg) = sourceDeps pkg - sourceDeps (Configured pkg) = sourceDeps pkg - sourceDeps (Processing pkg) = sourceDeps pkg - sourceDeps (Installed pkg _) = sourceDeps pkg - sourceDeps (Failed pkg _) = sourceDeps pkg +instance PackageSourceDeps FinalSelectedPackage where + sourceDeps (SelectedInstalled pkg) = sourceDeps pkg + sourceDeps (SelectedSource pkg) = sourceDeps pkg + diff --git a/cabal-install/Distribution/Client/Dependency/Types.hs b/cabal-install/Distribution/Client/Dependency/Types.hs index da10fb92ef87e2725e1bee79f41e8f4fd24760cc..87dde8c903d13f6208c9d0d7477efddb78c9c0b3 100644 --- a/cabal-install/Distribution/Client/Dependency/Types.hs +++ b/cabal-install/Distribution/Client/Dependency/Types.hs @@ -13,11 +13,10 @@ -- Common types for dependency resolution. ----------------------------------------------------------------------------- module Distribution.Client.Dependency.Types ( - ExtDependency(..), - PreSolver(..), Solver(..), DependencyResolver, + ResolverPackage(..), AllowNewer(..), isAllowNewer, PackageConstraint(..), @@ -45,21 +44,19 @@ import Data.Monoid #endif import Distribution.Client.Types - ( OptionalStanza(..), SourcePackage(..) ) -import qualified Distribution.Client.InstallPlan as InstallPlan - -import Distribution.Compat.ReadP - ( (<++) ) + ( OptionalStanza(..), SourcePackage(..), ConfiguredPackage ) import qualified Distribution.Compat.ReadP as Parse ( pfail, munch1 ) import Distribution.PackageDescription ( FlagAssignment, FlagName(..) ) +import Distribution.InstalledPackageInfo + ( InstalledPackageInfo ) import qualified Distribution.Client.PackageIndex as PackageIndex ( PackageIndex ) import Distribution.Simple.PackageIndex ( InstalledPackageIndex ) import Distribution.Package - ( Dependency, PackageName, InstalledPackageId ) + ( PackageName ) import Distribution.Version ( VersionRange, simplifyVersionRange ) import Distribution.Compiler @@ -74,16 +71,6 @@ import Text.PrettyPrint 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 @@ -120,7 +107,15 @@ type DependencyResolver = Platform -> (PackageName -> PackagePreferences) -> [PackageConstraint] -> [PackageName] - -> Progress String String [InstallPlan.PlanPackage] + -> Progress String String [ResolverPackage] + +-- | The dependency resolver picks either pre-existing installed packages +-- or it picks source packages along with package configuration. +-- +-- This is like the 'InstallPlan.PlanPackage' but with fewer cases. +-- +data ResolverPackage = PreExisting InstalledPackageInfo + | Configured ConfiguredPackage -- | Per-package constraints. Package constraints must be respected by the -- solver. Multiple constraints for each package can be given, though obviously diff --git a/cabal-install/Distribution/Client/Fetch.hs b/cabal-install/Distribution/Client/Fetch.hs index cb863525c589cf1ed45464b7501fb03c12b68c05..541e33d60baa082050705eb5ea27e56225888e9a 100644 --- a/cabal-install/Distribution/Client/Fetch.hs +++ b/cabal-install/Distribution/Client/Fetch.hs @@ -87,7 +87,7 @@ fetch verbosity packageDBs repos comp platform conf transport <- configureTransport verbosity (flagToMaybe (globalHttpTransport globalFlags)) - pkgSpecifiers <- resolveUserTargets transport verbosity + pkgSpecifiers <- resolveUserTargets verbosity transport (fromFlag $ globalWorldFile globalFlags) (packageIndex sourcePkgDb) userTargets @@ -139,7 +139,7 @@ planPackages verbosity comp platform fetchFlags -- that are in the 'InstallPlan.Configured' state. return [ pkg - | (InstallPlan.Configured (InstallPlan.ConfiguredPackage pkg _ _ _)) + | (InstallPlan.Configured (ConfiguredPackage pkg _ _ _)) <- InstallPlan.toList installPlan ] | otherwise = diff --git a/cabal-install/Distribution/Client/Freeze.hs b/cabal-install/Distribution/Client/Freeze.hs index 98ec58cc73aa06cc8e46454b0f89ea9babfeca6a..197490a9823793adc283076d145fe760b08162ca 100644 --- a/cabal-install/Distribution/Client/Freeze.hs +++ b/cabal-install/Distribution/Client/Freeze.hs @@ -23,7 +23,7 @@ import Distribution.Client.Dependency import Distribution.Client.IndexUtils as IndexUtils ( getSourcePackages, getInstalledPackages ) import Distribution.Client.InstallPlan - ( PlanPackage ) + ( InstallPlan, PlanPackage ) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.Setup ( GlobalFlags(..), FreezeFlags(..), ConfigExFlags(..) ) @@ -89,9 +89,10 @@ freeze verbosity packageDBs repos comp platform conf mSandboxPkgInfo installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf sourcePkgDb <- getSourcePackages verbosity repos - transport <- configureTransport verbosity (flagToMaybe (globalHttpTransport globalFlags)) + transport <- configureTransport verbosity + (flagToMaybe (globalHttpTransport globalFlags)) - pkgSpecifiers <- resolveUserTargets transport verbosity + pkgSpecifiers <- resolveUserTargets verbosity transport (fromFlag $ globalWorldFile globalFlags) (packageIndex sourcePkgDb) [UserTargetLocalDir "."] @@ -193,20 +194,19 @@ planPackages verbosity comp platform mSandboxPkgInfo freezeFlags -- 2) not a dependency (directly or transitively) of the package we are -- freezing. This is useful for removing previously installed packages -- which are no longer required from the install plan. -pruneInstallPlan :: InstallPlan.InstallPlan +pruneInstallPlan :: InstallPlan -> [PackageSpecifier SourcePackage] -> [PlanPackage] pruneInstallPlan installPlan pkgSpecifiers = - mapLeft (removeSelf pkgIds . PackageIndex.allPackages) $ + either (const brokenPkgsErr) + (removeSelf pkgIds . PackageIndex.allPackages) $ InstallPlan.dependencyClosure installPlan pkgIds where pkgIds = [ packageId pkg | SpecificSourcePackage pkg <- pkgSpecifiers ] - mapLeft f (Left v) = f v - mapLeft _ (Right _) = error "planPackages: installPlan contains broken packages" removeSelf [thisPkg] = filter (\pp -> packageId pp /= thisPkg) - removeSelf _ = - error $ "internal error: 'pruneInstallPlan' given " - ++ "unexpected package specifiers!" + removeSelf _ = error $ "internal error: 'pruneInstallPlan' given " + ++ "unexpected package specifiers!" + brokenPkgsErr = error "planPackages: installPlan contains broken packages" freezePackages :: Package pkg => Verbosity -> [pkg] -> IO () diff --git a/cabal-install/Distribution/Client/Get.hs b/cabal-install/Distribution/Client/Get.hs index 8fc36ea3cb1af11a97553afc27f2fb8394c7aed0..a2b882f28843f4b8d70e6495e519b1be789d2789 100644 --- a/cabal-install/Distribution/Client/Get.hs +++ b/cabal-install/Distribution/Client/Get.hs @@ -94,7 +94,7 @@ get verbosity repos globalFlags getFlags userTargets = do transport <- configureTransport verbosity (flagToMaybe (globalHttpTransport globalFlags)) - pkgSpecifiers <- resolveUserTargets transport verbosity + pkgSpecifiers <- resolveUserTargets verbosity transport (fromFlag $ globalWorldFile globalFlags) (packageIndex sourcePkgDb) userTargets diff --git a/cabal-install/Distribution/Client/IndexUtils.hs b/cabal-install/Distribution/Client/IndexUtils.hs index 7859b547fc695325e5866c8bfb5c9d02891cbe79..d962bcaada64d5cfc2f35f8ccca73720ad3907e7 100644 --- a/cabal-install/Distribution/Client/IndexUtils.hs +++ b/cabal-install/Distribution/Client/IndexUtils.hs @@ -16,7 +16,6 @@ module Distribution.Client.IndexUtils ( getInstalledPackages, getSourcePackages, getSourcePackagesStrict, - convert, readPackageIndexFile, parsePackageIndex, @@ -33,12 +32,10 @@ import Distribution.Client.Types import Distribution.Package ( PackageId, PackageIdentifier(..), PackageName(..) , Package(..), packageVersion, packageName - , Dependency(Dependency), InstalledPackageId(..) ) + , Dependency(Dependency) ) import Distribution.Client.PackageIndex (PackageIndex) import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Simple.PackageIndex (InstalledPackageIndex) -import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex -import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo import qualified Distribution.PackageDescription.Parse as PackageDesc.Parse import Distribution.PackageDescription ( GenericPackageDescription ) @@ -97,35 +94,6 @@ getInstalledPackages verbosity comp packageDbs conf = --FIXME: make getInstalledPackages use sensible verbosity in the first place verbosity' = lessVerbose verbosity -convert :: InstalledPackageIndex -> 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.allPackagesBySourcePackageId 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) - | (_,ipkg:_) <- InstalledPackageIndex.allPackagesBySourcePackageId index' ] - 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 - -- InstalledPackageId to get the InstalledPackageInfo and look at its - -- source PackageId. But if the package is broken because it depends on - -- other packages that do not exist then we have a problem we cannot find - -- the original source package id. Instead we make up a bogus package id. - -- This should have the same effect since it should be a dependency on a - -- nonexistent package. - sourceDeps index ipkg = - [ maybe (brokenPackageId depid) packageId mdep - | let depids = InstalledPackageInfo.depends ipkg - getpkg = InstalledPackageIndex.lookupInstalledPackageId index - , (depid, mdep) <- zip depids (map getpkg depids) ] - - brokenPackageId (InstalledPackageId str) = - PackageIdentifier (PackageName (str ++ "-broken")) (Version [] []) - ------------------------------------------------------------------------ -- Reading the source package index -- diff --git a/cabal-install/Distribution/Client/Install.hs b/cabal-install/Distribution/Client/Install.hs index 90f057b30ad5ea4b152a3f5b947a1c39fbb77a59..839b7f4656ee1b93ca12ac4b3e0125ce35883b34 100644 --- a/cabal-install/Distribution/Client/Install.hs +++ b/cabal-install/Distribution/Client/Install.hs @@ -96,8 +96,6 @@ import Distribution.Client.BuildReports.Types ( ReportLevel(..) ) import Distribution.Client.SetupWrapper ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions ) -import Distribution.Client.PackageIndex - ( PackageFixedDeps(..) ) import qualified Distribution.Client.BuildReports.Anonymous as BuildReports import qualified Distribution.Client.BuildReports.Storage as BuildReports ( storeAnonymous, storeLocal, fromInstallPlan, fromPlanningFailure ) @@ -138,7 +136,9 @@ import Distribution.Simple.InstallDirs as InstallDirs import Distribution.Package ( PackageIdentifier(..), PackageId, packageName, packageVersion , Package(..), LibraryName - , Dependency(..), thisPackageVersion, InstalledPackageId, installedPackageId ) + , Dependency(..), thisPackageVersion + , InstalledPackageId, installedPackageId + , HasInstalledPackageId(..) ) import qualified Distribution.PackageDescription as PackageDescription import Distribution.PackageDescription ( PackageDescription, GenericPackageDescription(..), Flag(..) @@ -230,7 +230,8 @@ install verbosity packageDBs repos comp platform conf useSandbox mSandboxPkgInfo -- TODO: Make InstallContext a proper data type with documented fields. -- | Common context for makeInstallPlan and processInstallPlan. type InstallContext = ( InstalledPackageIndex, SourcePackageDb - , [UserTarget], [PackageSpecifier SourcePackage], HttpTransport ) + , [UserTarget], [PackageSpecifier SourcePackage] + , HttpTransport ) -- TODO: Make InstallArgs a proper data type with documented fields or just get -- rid of it completely. @@ -257,7 +258,8 @@ makeInstallContext verbosity installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf sourcePkgDb <- getSourcePackages verbosity repos - transport <- configureTransport verbosity (flagToMaybe (globalHttpTransport globalFlags)) + transport <- configureTransport verbosity + (flagToMaybe (globalHttpTransport globalFlags)) (userTargets, pkgSpecifiers) <- case mUserTargets of Nothing -> @@ -271,13 +273,14 @@ makeInstallContext verbosity let userTargets | null userTargets0 = [UserTargetLocalDir "."] | otherwise = userTargets0 - pkgSpecifiers <- resolveUserTargets transport verbosity + pkgSpecifiers <- resolveUserTargets verbosity transport (fromFlag $ globalWorldFile globalFlags) (packageIndex sourcePkgDb) userTargets return (userTargets, pkgSpecifiers) - return (installedPkgIndex, sourcePkgDb, userTargets, pkgSpecifiers, transport) + return (installedPkgIndex, sourcePkgDb, userTargets + ,pkgSpecifiers, transport) -- | Make an install plan given install context and install arguments. makeInstallPlan :: Verbosity -> InstallArgs -> InstallContext @@ -301,10 +304,10 @@ processInstallPlan :: Verbosity -> InstallArgs -> InstallContext -> InstallPlan -> IO () processInstallPlan verbosity - args@(_,_, comp, _, _, _, _, _, _, _, installFlags, _) + args@(_,_, _, _, _, _, _, _, _, _, installFlags, _) (installedPkgIndex, sourcePkgDb, userTargets, pkgSpecifiers, _) installPlan = do - checkPrintPlan verbosity comp installedPkgIndex installPlan sourcePkgDb + checkPrintPlan verbosity installedPkgIndex installPlan sourcePkgDb installFlags pkgSpecifiers unless (dryRun || nothingToInstall) $ do @@ -409,8 +412,10 @@ planPackages comp platform mSandboxPkgInfo solver allowNewer = fromFlag (configAllowNewer configExFlags) -- | Remove the provided targets from the install plan. -pruneInstallPlan :: Package pkg => [PackageSpecifier pkg] -> InstallPlan - -> Progress String String InstallPlan +pruneInstallPlan :: Package targetpkg + => [PackageSpecifier targetpkg] + -> InstallPlan + -> Progress String String InstallPlan pruneInstallPlan pkgSpecifiers = -- 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 @@ -418,7 +423,7 @@ pruneInstallPlan pkgSpecifiers = either (Fail . explain) Done . InstallPlan.remove (\pkg -> packageName pkg `elem` targetnames) where - explain :: [InstallPlan.PlanProblem] -> String + explain :: [InstallPlan.PlanProblem ipkg srcpkg iresult ifailure] -> String explain problems = "Cannot select only the dependencies (as requested by the " ++ "'--only-dependencies' flag), " @@ -443,14 +448,13 @@ pruneInstallPlan pkgSpecifiers = -- | Perform post-solver checks of the install plan and print it if -- either requested or needed. checkPrintPlan :: Verbosity - -> Compiler -> InstalledPackageIndex -> InstallPlan -> SourcePackageDb -> InstallFlags -> [PackageSpecifier SourcePackage] -> IO () -checkPrintPlan verbosity comp installed installPlan sourcePkgDb +checkPrintPlan verbosity installed installPlan sourcePkgDb installFlags pkgSpecifiers = do -- User targets that are already installed. @@ -467,7 +471,7 @@ checkPrintPlan verbosity comp installed installPlan sourcePkgDb : map (display . packageId) preExistingTargets ++ ["Use --reinstall if you want to reinstall anyway."] - let lPlan = linearizeInstallPlan comp installed installPlan + let lPlan = linearizeInstallPlan installed installPlan -- Are any packages classified as reinstalls? let reinstalledPkgs = concatMap (extractReinstalls . snd) lPlan -- Packages that are already broken. @@ -507,7 +511,8 @@ checkPrintPlan verbosity comp installed installPlan sourcePkgDb : map (display . Installed.sourcePackageId) newBrokenPkgs ++ if overrideReinstall then if dryRun then [] else - ["Continuing even though the plan contains dangerous reinstalls."] + ["Continuing even though " ++ + "the plan contains dangerous reinstalls."] else ["Use --force-reinstalls if you want to install anyway."] else unless dryRun $ warn verbosity @@ -535,11 +540,11 @@ checkPrintPlan verbosity comp installed installPlan sourcePkgDb dryRun = fromFlag (installDryRun installFlags) overrideReinstall = fromFlag (installOverrideReinstall installFlags) -linearizeInstallPlan :: Compiler - -> InstalledPackageIndex +--TODO: this type is too specific +linearizeInstallPlan :: InstalledPackageIndex -> InstallPlan -> [(ReadyPackage, PackageStatus)] -linearizeInstallPlan comp installedPkgIndex plan = +linearizeInstallPlan installedPkgIndex plan = unfoldr next plan where next plan' = case InstallPlan.ready plan' of @@ -547,12 +552,13 @@ linearizeInstallPlan comp installedPkgIndex plan = (pkg:_) -> Just ((pkg, status), plan'') where pkgid = installedPackageId pkg - status = packageStatus comp installedPkgIndex pkg - plan'' = InstallPlan.completed pkgid - (BuildOk DocsNotTried TestsNotTried - (Just $ Installed.emptyInstalledPackageInfo - { Installed.sourcePackageId = packageId pkg - , Installed.installedPackageId = pkgid })) + status = packageStatus installedPkgIndex pkg + ipkg = Installed.emptyInstalledPackageInfo { + Installed.sourcePackageId = packageId pkg, + Installed.installedPackageId = pkgid + } + plan'' = InstallPlan.completed pkgid (Just ipkg) + (BuildOk DocsNotTried TestsNotTried (Just ipkg)) (InstallPlan.processing [pkg] plan') --FIXME: This is a bit of a hack, -- pretending that each package is installed @@ -569,8 +575,10 @@ extractReinstalls :: PackageStatus -> [InstalledPackageId] extractReinstalls (Reinstall ipids _) = ipids extractReinstalls _ = [] -packageStatus :: Compiler -> InstalledPackageIndex -> ReadyPackage -> PackageStatus -packageStatus _comp installedPkgIndex cpkg = +packageStatus :: InstalledPackageIndex + -> ReadyPackage + -> PackageStatus +packageStatus installedPkgIndex cpkg = case PackageIndex.lookupPackageName installedPkgIndex (packageName cpkg) of [] -> NewPackage @@ -587,8 +595,10 @@ packageStatus _comp installedPkgIndex cpkg = -> [MergeResult PackageIdentifier PackageIdentifier] changes pkg pkg' = filter changed $ mergeBy (comparing packageName) - (resolveInstalledIds $ Installed.depends pkg) -- deps of installed pkg - (resolveInstalledIds $ CD.nonSetupDeps (depends pkg')) -- deps of configured pkg + -- deps of installed pkg + (resolveInstalledIds $ Installed.depends pkg) + -- deps of configured pkg + (resolveInstalledIds $ CD.nonSetupDeps (depends pkg')) -- convert to source pkg ids via index resolveInstalledIds :: [InstalledPackageId] -> [PackageIdentifier] @@ -624,7 +634,7 @@ printPlan dryRun verbosity plan sourcePkgDb = case plan of showPkg (pkg, _) = display (packageId pkg) ++ showLatest (pkg) - showPkgAndReason (pkg', pr) = display (packageId pkg') ++ + showPkgAndReason (ReadyPackage pkg' _, pr) = display (packageId pkg') ++ showLatest pkg' ++ showFlagAssignment (nonDefaultFlags pkg') ++ showStanzas (stanzas pkg') ++ " " ++ @@ -635,7 +645,7 @@ printPlan dryRun verbosity plan sourcePkgDb = case plan of [] -> "" diff -> " changes: " ++ intercalate ", " (map change diff) - showLatest :: ReadyPackage -> String + showLatest :: Package srcpkg => srcpkg -> String showLatest pkg = case mLatestVersion of Just latestVersion -> if packageVersion pkg < latestVersion @@ -653,15 +663,15 @@ printPlan dryRun verbosity plan sourcePkgDb = case plan of toFlagAssignment :: [Flag] -> FlagAssignment toFlagAssignment = map (\ f -> (flagName f, flagDefault f)) - nonDefaultFlags :: ReadyPackage -> FlagAssignment - nonDefaultFlags (ReadyPackage spkg fa _ _) = + nonDefaultFlags :: ConfiguredPackage -> FlagAssignment + nonDefaultFlags (ConfiguredPackage spkg fa _ _) = let defaultAssignment = toFlagAssignment (genPackageFlags (Source.packageDescription spkg)) in fa \\ defaultAssignment - stanzas :: ReadyPackage -> [OptionalStanza] - stanzas (ReadyPackage _ _ sts _) = sts + stanzas :: ConfiguredPackage -> [OptionalStanza] + stanzas (ConfiguredPackage _ _ sts _) = sts showStanzas :: [OptionalStanza] -> String showStanzas = concatMap ((' ' :) . showStanza) @@ -686,7 +696,8 @@ printPlan dryRun verbosity plan sourcePkgDb = case plan of -- | Report a solver failure. This works slightly differently to -- 'postInstallActions', as (by definition) we don't have an install plan. -reportPlanningFailure :: Verbosity -> InstallArgs -> InstallContext -> String -> IO () +reportPlanningFailure :: Verbosity -> InstallArgs -> InstallContext -> String + -> IO () reportPlanningFailure verbosity (_, _, comp, platform, _, _, _ ,_, configFlags, _, installFlags, _) @@ -696,12 +707,13 @@ reportPlanningFailure verbosity when reportFailure $ do -- Only create reports for explicitly named packages - let pkgids = - filter (SourcePackageIndex.elemByPackageId (packageIndex sourcePkgDb)) $ + let pkgids = filter + (SourcePackageIndex.elemByPackageId (packageIndex sourcePkgDb)) $ mapMaybe theSpecifiedPackage pkgSpecifiers - buildReports = BuildReports.fromPlanningFailure platform (compilerId comp) - pkgids (configConfigurationsFlags configFlags) + buildReports = BuildReports.fromPlanningFailure platform + (compilerId comp) pkgids + (configConfigurationsFlags configFlags) when (not (null buildReports)) $ info verbosity $ @@ -710,7 +722,8 @@ reportPlanningFailure verbosity -- Save reports BuildReports.storeLocal (compilerInfo comp) - (fromNubList $ installSummaryFile installFlags) buildReports platform + (fromNubList $ installSummaryFile installFlags) + buildReports platform -- Save solver log case logFile of @@ -730,7 +743,8 @@ reportPlanningFailure verbosity -- So we fail. dummyLibraryName = error "reportPlanningFailure: library name not available" --- | If a 'PackageSpecifier' refers to a single package, return Just that package. +-- | If a 'PackageSpecifier' refers to a single package, return Just that +-- package. theSpecifiedPackage :: Package pkg => PackageSpecifier pkg -> Maybe PackageId theSpecifiedPackage pkgSpec = case pkgSpec of @@ -774,9 +788,12 @@ postInstallActions verbosity [ World.WorldPkgInfo dep [] | UserTargetNamed dep <- targets ] - let buildReports = BuildReports.fromInstallPlan installPlan - BuildReports.storeLocal (compilerInfo comp) (fromNubList $ installSummaryFile installFlags) buildReports - (InstallPlan.planPlatform installPlan) + let buildReports = BuildReports.fromInstallPlan platform (compilerId comp) + installPlan + BuildReports.storeLocal (compilerInfo comp) + (fromNubList $ installSummaryFile installFlags) + buildReports + platform when (reportingLevel >= AnonymousReports) $ BuildReports.storeAnonymous buildReports when (reportingLevel == DetailedReports) $ @@ -785,7 +802,7 @@ postInstallActions verbosity regenerateHaddockIndex verbosity packageDBs comp platform conf useSandbox configFlags installFlags installPlan - symlinkBinaries verbosity comp configFlags installFlags installPlan + symlinkBinaries verbosity platform comp configFlags installFlags installPlan printBuildFailures installPlan @@ -876,8 +893,8 @@ regenerateHaddockIndex verbosity packageDBs comp platform conf useSandbox normalUserInstall = (UserPackageDB `elem` packageDBs) && all (not . isSpecificPackageDB) packageDBs - installedDocs (InstallPlan.Installed _ (BuildOk DocsOk _ _)) = True - installedDocs _ = False + installedDocs (InstallPlan.Installed _ _ (BuildOk DocsOk _ _)) = True + installedDocs _ = False isSpecificPackageDB (SpecificPackageDB _) = True isSpecificPackageDB _ = False @@ -895,12 +912,15 @@ regenerateHaddockIndex verbosity packageDBs comp platform conf useSandbox symlinkBinaries :: Verbosity - -> Compiler + -> Platform -> Compiler -> ConfigFlags -> InstallFlags - -> InstallPlan -> IO () -symlinkBinaries verbosity comp configFlags installFlags plan = do - failed <- InstallSymlink.symlinkBinaries comp configFlags installFlags plan + -> InstallPlan + -> IO () +symlinkBinaries verbosity platform comp configFlags installFlags plan = do + failed <- InstallSymlink.symlinkBinaries platform comp + configFlags installFlags + plan case failed of [] -> return () [(_, exe, path)] -> @@ -922,7 +942,8 @@ symlinkBinaries verbosity comp configFlags installFlags plan = do bindir = fromFlag (installSymlinkBinDir installFlags) -printBuildFailures :: InstallPlan -> IO () +printBuildFailures :: InstallPlan + -> IO () printBuildFailures plan = case [ (pkg, reason) | InstallPlan.Failed pkg reason <- InstallPlan.toList plan ] of @@ -966,15 +987,17 @@ printBuildFailures plan = -- | If we're working inside a sandbox and some add-source deps were installed, -- update the timestamps of those deps. updateSandboxTimestampsFile :: UseSandbox -> Maybe SandboxPackageInfo - -> Compiler -> Platform -> InstallPlan + -> Compiler -> Platform + -> InstallPlan -> IO () updateSandboxTimestampsFile (UseSandbox sandboxDir) (Just (SandboxPackageInfo _ _ _ allAddSourceDeps)) comp platform installPlan = withUpdateTimestamps sandboxDir (compilerId comp) platform $ \_ -> do - let allInstalled = [ pkg | InstallPlan.Installed pkg _ + let allInstalled = [ pkg | InstallPlan.Installed pkg _ _ <- InstallPlan.toList installPlan ] - allSrcPkgs = [ pkg | ReadyPackage pkg _ _ _ <- allInstalled ] + allSrcPkgs = [ pkg | ReadyPackage (ConfiguredPackage pkg _ _ _) _ + <- allInstalled ] allPaths = [ pth | LocalUnpackedPackage pth <- map packageSource allSrcPkgs] allPathsCanonical <- mapM tryCanonicalizePath allPaths @@ -1001,7 +1024,7 @@ performInstallations :: Verbosity -> InstallPlan -> IO InstallPlan performInstallations verbosity - (packageDBs, _, comp, _, conf, useSandbox, _, + (packageDBs, _, comp, platform, conf, useSandbox, _, globalFlags, configFlags, configExFlags, installFlags, haddockFlags) installedPkgIndex installPlan = do @@ -1018,7 +1041,8 @@ performInstallations verbosity installLock <- newLock -- serialise installation cacheLock <- newLock -- serialise access to setup exe cache - transport <- configureTransport verbosity (flagToMaybe (globalHttpTransport globalFlags)) + transport <- configureTransport verbosity + (flagToMaybe (globalHttpTransport globalFlags)) executeInstallPlan verbosity comp jobControl useLogFile installPlan $ \rpkg -> -- Calculate the package key (ToDo: Is this right for source install) @@ -1029,13 +1053,14 @@ performInstallations verbosity installLocalPackage verbosity buildLimit (packageId pkg) src' distPref $ \mpath -> installUnpackedPackage verbosity buildLimit installLock numJobs libname - (setupScriptOptions installedPkgIndex cacheLock rpkg) - miscOptions configFlags' installFlags haddockFlags + (setupScriptOptions installedPkgIndex + cacheLock rpkg) + miscOptions configFlags' + installFlags haddockFlags cinfo platform pkg pkgoverride mpath useLogFile where - platform = InstallPlan.planPlatform installPlan - cinfo = InstallPlan.planCompiler installPlan + cinfo = compilerInfo comp numJobs = determineNumJobs (installNumJobs installFlags) numFetchJobs = 2 @@ -1096,7 +1121,8 @@ performInstallations verbosity | parallelInstall = False | otherwise = False - substLogFileName :: PathTemplate -> PackageIdentifier -> LibraryName -> FilePath + substLogFileName :: PathTemplate -> PackageIdentifier -> LibraryName + -> FilePath substLogFileName template pkg libname = fromPathTemplate . substPathTemplate env $ template @@ -1149,12 +1175,15 @@ executeInstallPlan verbosity comp jobCtl useLogFile plan0 installPkg = plan' = updatePlan pkgid buildResult plan tryNewTasks taskCount' plan' - updatePlan :: PackageIdentifier -> BuildResult -> InstallPlan -> InstallPlan - updatePlan pkgid (Right buildSuccess) = - InstallPlan.completed (Source.fakeInstalledPackageId pkgid) buildSuccess + updatePlan :: PackageIdentifier -> BuildResult -> InstallPlan + -> InstallPlan + updatePlan pkgid (Right buildSuccess@(BuildOk _ _ mipkg)) = + InstallPlan.completed (Source.fakeInstalledPackageId pkgid) + mipkg buildSuccess updatePlan pkgid (Left buildFailure) = - InstallPlan.failed (Source.fakeInstalledPackageId pkgid) buildFailure depsFailure + InstallPlan.failed (Source.fakeInstalledPackageId pkgid) + buildFailure depsFailure where depsFailure = DependentFailed pkgid -- So this first pkgid failed for whatever reason (buildFailure). @@ -1189,16 +1218,20 @@ executeInstallPlan verbosity comp jobCtl useLogFile plan0 installPkg = -- NB: when updating this function, don't forget to also update -- 'configurePackage' in D.C.Configure. installReadyPackage :: Platform -> CompilerInfo - -> ConfigFlags - -> ReadyPackage - -> (ConfigFlags -> PackageLocation (Maybe FilePath) - -> PackageDescription - -> PackageDescriptionOverride -> a) - -> a + -> ConfigFlags + -> ReadyPackage + -> (ConfigFlags -> PackageLocation (Maybe FilePath) + -> PackageDescription + -> PackageDescriptionOverride + -> a) + -> a installReadyPackage platform cinfo configFlags - (ReadyPackage (SourcePackage _ gpkg source pkgoverride) - flags stanzas deps) - installPkg = installPkg configFlags { + (ReadyPackage (ConfiguredPackage + (SourcePackage _ gpkg source pkgoverride) + flags stanzas _) + deps) + installPkg = + installPkg configFlags { configConfigurationsFlags = flags, -- We generate the legacy constraints as well as the new style precise deps. -- In the end only one set gets passed to Setup.hs configure, depending on @@ -1397,7 +1430,8 @@ installUnpackedPackage verbosity buildLimit installLock numJobs libname maybePkgConf <- maybeGenPkgConf mLogPath -- Actual installation - withWin32SelfUpgrade verbosity libname configFlags cinfo platform pkg $ do + withWin32SelfUpgrade verbosity libname configFlags + cinfo platform pkg $ do case rootCmd miscOptions of (Just cmd) -> reexec cmd Nothing -> do @@ -1569,4 +1603,5 @@ withWin32SelfUpgrade verbosity libname configFlags cinfo platform pkg action = d platform templateDirs substTemplate = InstallDirs.fromPathTemplate . InstallDirs.substPathTemplate env - where env = InstallDirs.initialPathTemplateEnv pkgid libname cinfo platform + where env = InstallDirs.initialPathTemplateEnv pkgid libname + cinfo platform diff --git a/cabal-install/Distribution/Client/InstallPlan.hs b/cabal-install/Distribution/Client/InstallPlan.hs index 6e9afb8006079cfa858505384af054bb55047b35..671cfa903624fa985c0ea3683ca33850c9d1e0ee 100644 --- a/cabal-install/Distribution/Client/InstallPlan.hs +++ b/cabal-install/Distribution/Client/InstallPlan.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- -- | -- Module : Distribution.Client.InstallPlan @@ -13,8 +14,9 @@ ----------------------------------------------------------------------------- module Distribution.Client.InstallPlan ( InstallPlan, - ConfiguredPackage(..), - PlanPackage(..), + GenericInstallPlan, + PlanPackage, + GenericPlanPackage(..), -- * Operations on 'InstallPlan's new, @@ -27,55 +29,34 @@ module Distribution.Client.InstallPlan ( showPlanIndex, showInstallPlan, - -- ** Query functions - planPlatform, - planCompiler, - -- * Checking validity of plans valid, closed, consistent, acyclic, - configuredPackageValid, -- ** Details on invalid plans PlanProblem(..), showPlanProblem, - PackageProblem(..), - showPackageProblem, problems, - configuredPackageProblems, -- ** Querying the install plan dependencyClosure, ) where -import Distribution.Client.Types - ( SourcePackage(packageDescription), ConfiguredPackage(..) - , ReadyPackage(..), readyPackageToConfiguredPackage - , InstalledPackage, BuildFailure, BuildSuccess(..), enableStanzas - , InstalledPackage(..), fakeInstalledPackageId - , ConfiguredId(..) - ) +import Distribution.InstalledPackageInfo + ( InstalledPackageInfo ) import Distribution.Package - ( PackageIdentifier(..), PackageName(..), Package(..), packageName - , Dependency(..), PackageId, InstalledPackageId - , HasInstalledPackageId(..) ) + ( PackageIdentifier(..), PackageName(..), Package(..) + , InstalledPackageId, HasInstalledPackageId(..) ) +import Distribution.Client.Types + ( BuildSuccess, BuildFailure + , PackageFixedDeps(..), ConfiguredPackage + , GenericReadyPackage(..), fakeInstalledPackageId ) import Distribution.Version - ( Version, withinRange ) -import Distribution.PackageDescription - ( GenericPackageDescription(genPackageFlags) - , Flag(flagName), FlagName(..) - , SetupBuildInfo(..), setupBuildInfo - ) -import Distribution.Client.PackageUtils - ( externalBuildDepends ) -import Distribution.Client.PackageIndex - ( PackageFixedDeps(..) ) + ( Version ) import Distribution.Client.ComponentDeps (ComponentDeps) import qualified Distribution.Client.ComponentDeps as CD -import Distribution.PackageDescription.Configuration - ( finalizePackageDescription ) import Distribution.Simple.PackageIndex ( PackageIndex ) import qualified Distribution.Simple.PackageIndex as PackageIndex @@ -84,22 +65,12 @@ import Distribution.Client.PlanIndex import qualified Distribution.Client.PlanIndex as PlanIndex import Distribution.Text ( display ) -import Distribution.System - ( Platform ) -import Distribution.Compiler - ( CompilerInfo(..) ) -import Distribution.Client.Utils - ( duplicates, duplicatesBy, mergeBy, MergeResult(..) ) -import Distribution.Simple.Utils - ( comparing, intercalate ) -import qualified Distribution.InstalledPackageInfo as Installed import Data.List - ( sort, sortBy, nubBy ) + ( intercalate ) import Data.Maybe ( fromMaybe, maybeToList ) import qualified Data.Graph as Graph -import Data.Function (on) import Data.Graph (Graph) import Control.Exception ( assert ) @@ -107,7 +78,6 @@ import Data.Maybe (catMaybes) import qualified Data.Map as Map import qualified Data.Traversable as T -type PlanIndex = PackageIndex PlanPackage -- When cabal tries to install a number of packages, including all their -- dependencies it has a non-trivial problem to solve. @@ -153,70 +123,87 @@ type PlanIndex = PackageIndex PlanPackage -- | Packages in an install plan -- --- NOTE: 'ConfiguredPackage', 'ReadyPackage' and 'PlanPackage' intentionally --- have no 'PackageInstalled' instance. `This is important: PackageInstalled --- returns only library dependencies, but for package that aren't yet installed --- we know many more kinds of dependencies (setup dependencies, exe, test-suite, --- benchmark, ..). Any functions that operate on dependencies in cabal-install --- should consider what to do with these dependencies; if we give a --- 'PackageInstalled' instance it would be too easy to get this wrong (and, --- for instance, call graph traversal functions from Cabal rather than from --- cabal-install). Instead, see 'PackageFixedDeps'. -data PlanPackage = PreExisting InstalledPackage - | Configured ConfiguredPackage - | Processing ReadyPackage - | Installed ReadyPackage BuildSuccess - | Failed ConfiguredPackage BuildFailure - -- ^ NB: packages in the Failed state can be *either* Ready - -- or Configured. - -instance Package PlanPackage where - packageId (PreExisting pkg) = packageId pkg - packageId (Configured pkg) = packageId pkg - packageId (Processing pkg) = packageId pkg - packageId (Installed pkg _) = packageId pkg - packageId (Failed pkg _) = packageId pkg - -instance PackageFixedDeps PlanPackage where - depends (PreExisting pkg) = depends pkg - depends (Configured pkg) = depends pkg - depends (Processing pkg) = depends pkg - depends (Installed pkg _) = depends pkg - depends (Failed pkg _) = depends pkg - -instance HasInstalledPackageId PlanPackage where - installedPackageId (PreExisting pkg) = installedPackageId pkg - installedPackageId (Configured pkg) = installedPackageId pkg - installedPackageId (Processing pkg) = installedPackageId pkg +-- NOTE: 'ConfiguredPackage', 'GenericReadyPackage' and 'GenericPlanPackage' +-- intentionally have no 'PackageInstalled' instance. `This is important: +-- PackageInstalled returns only library dependencies, but for package that +-- aren't yet installed we know many more kinds of dependencies (setup +-- dependencies, exe, test-suite, benchmark, ..). Any functions that operate on +-- dependencies in cabal-install should consider what to do with these +-- dependencies; if we give a 'PackageInstalled' instance it would be too easy +-- to get this wrong (and, for instance, call graph traversal functions from +-- Cabal rather than from cabal-install). Instead, see 'PackageFixedDeps'. +data GenericPlanPackage ipkg srcpkg iresult ifailure + = PreExisting ipkg + | Configured srcpkg + | Processing (GenericReadyPackage srcpkg ipkg) + | Installed (GenericReadyPackage srcpkg ipkg) (Maybe ipkg) iresult + | Failed srcpkg ifailure + +type PlanPackage = GenericPlanPackage + InstalledPackageInfo ConfiguredPackage + BuildSuccess BuildFailure + +instance (Package ipkg, Package srcpkg) => + Package (GenericPlanPackage ipkg srcpkg iresult ifailure) where + packageId (PreExisting ipkg) = packageId ipkg + packageId (Configured spkg) = packageId spkg + packageId (Processing rpkg) = packageId rpkg + packageId (Installed rpkg _ _) = packageId rpkg + packageId (Failed spkg _) = packageId spkg + +instance (PackageFixedDeps srcpkg, + PackageFixedDeps ipkg, HasInstalledPackageId ipkg) => + PackageFixedDeps (GenericPlanPackage ipkg srcpkg iresult ifailure) where + depends (PreExisting pkg) = depends pkg + depends (Configured pkg) = depends pkg + depends (Processing pkg) = depends pkg + depends (Installed pkg _ _) = depends pkg + depends (Failed pkg _) = depends pkg + +instance (HasInstalledPackageId ipkg, HasInstalledPackageId srcpkg) => + HasInstalledPackageId + (GenericPlanPackage ipkg srcpkg iresult ifailure) where + installedPackageId (PreExisting ipkg ) = installedPackageId ipkg + installedPackageId (Configured spkg) = installedPackageId spkg + installedPackageId (Processing rpkg) = installedPackageId rpkg -- NB: defer to the actual installed package info in this case - installedPackageId (Installed _ (BuildOk _ _ (Just ipkg))) = installedPackageId ipkg - installedPackageId (Installed pkg _) = installedPackageId pkg - installedPackageId (Failed pkg _) = installedPackageId pkg + installedPackageId (Installed _ (Just ipkg) _) = installedPackageId ipkg + installedPackageId (Installed rpkg _ _) = installedPackageId rpkg + installedPackageId (Failed spkg _) = installedPackageId spkg + -data InstallPlan = InstallPlan { - planIndex :: PlanIndex, +data GenericInstallPlan ipkg srcpkg iresult ifailure = GenericInstallPlan { + planIndex :: (PlanIndex ipkg srcpkg iresult ifailure), planFakeMap :: FakeMap, planGraph :: Graph, planGraphRev :: Graph, - planPkgOf :: Graph.Vertex -> PlanPackage, + planPkgOf :: Graph.Vertex + -> GenericPlanPackage ipkg srcpkg iresult ifailure, planVertexOf :: InstalledPackageId -> Graph.Vertex, - planPlatform :: Platform, - planCompiler :: CompilerInfo, planIndepGoals :: Bool } -invariant :: InstallPlan -> Bool +-- | 'GenericInstallPlan' specialised to most commonly used types. +type InstallPlan = GenericInstallPlan + InstalledPackageInfo ConfiguredPackage + BuildSuccess BuildFailure + +type PlanIndex ipkg srcpkg iresult ifailure = + PackageIndex (GenericPlanPackage ipkg srcpkg iresult ifailure) + +invariant :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => GenericInstallPlan ipkg srcpkg iresult ifailure -> Bool invariant plan = - valid (planPlatform plan) - (planCompiler plan) - (planFakeMap plan) + valid (planFakeMap plan) (planIndepGoals plan) (planIndex plan) internalError :: String -> a internalError msg = error $ "InstallPlan: internal error: " ++ msg -showPlanIndex :: PlanIndex -> String +showPlanIndex :: (HasInstalledPackageId ipkg, HasInstalledPackageId srcpkg) + => PlanIndex ipkg srcpkg iresult ifailure -> String showPlanIndex index = intercalate "\n" (map showPlanPackage (PackageIndex.allPackages index)) where showPlanPackage p = @@ -224,42 +211,47 @@ showPlanIndex index = ++ display (packageId p) ++ " (" ++ display (installedPackageId p) ++ ")" -showInstallPlan :: InstallPlan -> String +showInstallPlan :: (HasInstalledPackageId ipkg, HasInstalledPackageId srcpkg) + => GenericInstallPlan ipkg srcpkg iresult ifailure -> String showInstallPlan plan = showPlanIndex (planIndex plan) ++ "\n" ++ - "fake map:\n " ++ intercalate "\n " (map showKV (Map.toList (planFakeMap plan))) + "fake map:\n " ++ + intercalate "\n " (map showKV (Map.toList (planFakeMap plan))) where showKV (k,v) = display k ++ " -> " ++ display v -showPlanPackageTag :: PlanPackage -> String -showPlanPackageTag (PreExisting _) = "PreExisting" -showPlanPackageTag (Configured _) = "Configured" -showPlanPackageTag (Processing _) = "Processing" -showPlanPackageTag (Installed _ _) = "Installed" -showPlanPackageTag (Failed _ _) = "Failed" +showPlanPackageTag :: GenericPlanPackage ipkg srcpkg iresult ifailure -> String +showPlanPackageTag (PreExisting _) = "PreExisting" +showPlanPackageTag (Configured _) = "Configured" +showPlanPackageTag (Processing _) = "Processing" +showPlanPackageTag (Installed _ _ _) = "Installed" +showPlanPackageTag (Failed _ _) = "Failed" -- | Build an installation plan from a valid set of resolved packages. -- -new :: Platform -> CompilerInfo -> Bool -> PlanIndex - -> Either [PlanProblem] InstallPlan -new platform cinfo indepGoals index = +new :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => Bool + -> PlanIndex ipkg srcpkg iresult ifailure + -> Either [PlanProblem ipkg srcpkg iresult ifailure] + (GenericInstallPlan ipkg srcpkg iresult ifailure) +new indepGoals index = -- NB: Need to pre-initialize the fake-map with pre-existing -- packages let isPreExisting (PreExisting _) = True isPreExisting _ = False fakeMap = Map.fromList - . map (\p -> (fakeInstalledPackageId (packageId p), installedPackageId p)) + . map (\p -> (fakeInstalledPackageId (packageId p) + ,installedPackageId p)) . filter isPreExisting $ PackageIndex.allPackages index in - case problems platform cinfo fakeMap indepGoals index of - [] -> Right InstallPlan { + case problems fakeMap indepGoals index of + [] -> Right GenericInstallPlan { planIndex = index, planFakeMap = fakeMap, planGraph = graph, planGraphRev = Graph.transposeG graph, planPkgOf = vertexToPkgId, planVertexOf = fromMaybe noSuchPkgId . pkgIdToVertex, - planPlatform = platform, - planCompiler = cinfo, planIndepGoals = indepGoals } where (graph, vertexToPkgId, pkgIdToVertex) = @@ -267,7 +259,8 @@ new platform cinfo indepGoals index = noSuchPkgId = internalError "package is not in the graph" probs -> Left probs -toList :: InstallPlan -> [PlanPackage] +toList :: GenericInstallPlan ipkg srcpkg iresult ifailure + -> [GenericPlanPackage ipkg srcpkg iresult ifailure] toList = PackageIndex.allPackages . planIndex -- | Remove packages from the install plan. This will result in an @@ -276,11 +269,14 @@ toList = PackageIndex.allPackages . planIndex -- the dependencies of a package or set of packages without actually -- installing the package itself, as when doing development. -- -remove :: (PlanPackage -> Bool) - -> InstallPlan - -> Either [PlanProblem] InstallPlan +remove :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => (GenericPlanPackage ipkg srcpkg iresult ifailure -> Bool) + -> GenericInstallPlan ipkg srcpkg iresult ifailure + -> Either [PlanProblem ipkg srcpkg iresult ifailure] + (GenericInstallPlan ipkg srcpkg iresult ifailure) remove shouldRemove plan = - new (planPlatform plan) (planCompiler plan) (planIndepGoals plan) newIndex + new (planIndepGoals plan) newIndex where newIndex = PackageIndex.fromList $ filter (not . shouldRemove) (toList plan) @@ -289,7 +285,9 @@ remove shouldRemove plan = -- configured state and have all their dependencies installed already. -- The plan is complete if the result is @[]@. -- -ready :: InstallPlan -> [ReadyPackage] +ready :: forall ipkg srcpkg iresult ifailure. PackageFixedDeps srcpkg + => GenericInstallPlan ipkg srcpkg iresult ifailure + -> [GenericReadyPackage srcpkg ipkg] ready plan = assert check readyPackages where check = if null readyPackages && null processingPackages @@ -298,29 +296,31 @@ ready plan = assert check readyPackages configuredPackages = [ pkg | Configured pkg <- toList plan ] processingPackages = [ pkg | Processing pkg <- toList plan] - readyPackages :: [ReadyPackage] + readyPackages :: [GenericReadyPackage srcpkg ipkg] readyPackages = - [ ReadyPackage srcPkg flags stanzas deps - | pkg@(ConfiguredPackage srcPkg flags stanzas _) <- configuredPackages + [ ReadyPackage srcpkg deps + | srcpkg <- configuredPackages -- select only the package that have all of their deps installed: - , deps <- maybeToList (hasAllInstalledDeps pkg) + , deps <- maybeToList (hasAllInstalledDeps srcpkg) ] - hasAllInstalledDeps :: ConfiguredPackage -> Maybe (ComponentDeps [Installed.InstalledPackageInfo]) + hasAllInstalledDeps :: srcpkg -> Maybe (ComponentDeps [ipkg]) hasAllInstalledDeps = T.mapM (mapM isInstalledDep) . depends - isInstalledDep :: InstalledPackageId -> Maybe Installed.InstalledPackageInfo + isInstalledDep :: InstalledPackageId -> Maybe ipkg isInstalledDep pkgid = - -- NB: Need to check if the ID has been updated in planFakeMap, in which case we - -- might be dealing with an old pointer - case PlanIndex.fakeLookupInstalledPackageId (planFakeMap plan) (planIndex plan) pkgid of - Just (Configured _) -> Nothing - Just (Processing _) -> Nothing - Just (Failed _ _) -> internalError depOnFailed - Just (PreExisting (InstalledPackage instPkg _)) -> Just instPkg - Just (Installed _ (BuildOk _ _ (Just instPkg))) -> Just instPkg - Just (Installed _ (BuildOk _ _ Nothing)) -> internalError depOnNonLib - Nothing -> internalError incomplete + -- NB: Need to check if the ID has been updated in planFakeMap, in which + -- case we might be dealing with an old pointer + case PlanIndex.fakeLookupInstalledPackageId + (planFakeMap plan) (planIndex plan) pkgid + of + Just (PreExisting ipkg) -> Just ipkg + Just (Configured _) -> Nothing + Just (Processing _) -> Nothing + Just (Installed _ (Just ipkg) _) -> Just ipkg + Just (Installed _ Nothing _) -> internalError depOnNonLib + Just (Failed _ _) -> internalError depOnFailed + Nothing -> internalError incomplete incomplete = "install plan is not closed" depOnFailed = "configured package depends on failed package" depOnNonLib = "configured package depends on a non-library package" @@ -329,7 +329,11 @@ ready plan = assert check readyPackages -- -- * The package must exist in the graph and be in the configured state. -- -processing :: [ReadyPackage] -> InstallPlan -> InstallPlan +processing :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => [GenericReadyPackage srcpkg ipkg] + -> GenericInstallPlan ipkg srcpkg iresult ifailure + -> GenericInstallPlan ipkg srcpkg iresult ifailure processing pkgs plan = assert (invariant plan') plan' where plan' = plan { @@ -343,15 +347,18 @@ processing pkgs plan = assert (invariant plan') plan' -- * The package must exist in the graph and be in the processing state. -- * The package must have had no uninstalled dependent packages. -- -completed :: InstalledPackageId - -> BuildSuccess - -> InstallPlan -> InstallPlan -completed pkgid buildResult plan = assert (invariant plan') plan' +completed :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => InstalledPackageId + -> Maybe ipkg -> iresult + -> GenericInstallPlan ipkg srcpkg iresult ifailure + -> GenericInstallPlan ipkg srcpkg iresult ifailure +completed pkgid mipkg buildResult plan = assert (invariant plan') plan' where plan' = plan { -- NB: installation can change the IPID, so better -- record it in the fake mapping... - planFakeMap = insert_fake_mapping buildResult + planFakeMap = insert_fake_mapping mipkg $ planFakeMap plan, planIndex = PackageIndex.insert installed . PackageIndex.deleteInstalledPackageId pkgid @@ -359,9 +366,9 @@ completed pkgid buildResult plan = assert (invariant plan') plan' } -- ...but be sure to use the *old* IPID for the lookup for the -- preexisting record - installed = Installed (lookupProcessingPackage plan pkgid) buildResult - insert_fake_mapping (BuildOk _ _ (Just ipi)) = Map.insert pkgid (installedPackageId ipi) - insert_fake_mapping _ = id + installed = Installed (lookupProcessingPackage plan pkgid) mipkg buildResult + insert_fake_mapping (Just ipkg) = Map.insert pkgid (installedPackageId ipkg) + insert_fake_mapping _ = id -- | Marks a package in the graph as having failed. It also marks all the -- packages that depended on it as having failed. @@ -369,28 +376,31 @@ completed pkgid buildResult plan = assert (invariant plan') plan' -- * The package must exist in the graph and be in the processing -- state. -- -failed :: InstalledPackageId -- ^ The id of the package that failed to install - -> BuildFailure -- ^ The build result to use for the failed package - -> BuildFailure -- ^ The build result to use for its dependencies - -> InstallPlan - -> InstallPlan +failed :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => InstalledPackageId -- ^ The id of the package that failed to install + -> ifailure -- ^ The build result to use for the failed package + -> ifailure -- ^ The build result to use for its dependencies + -> GenericInstallPlan ipkg srcpkg iresult ifailure + -> GenericInstallPlan ipkg srcpkg iresult ifailure failed pkgid buildResult buildResult' plan = assert (invariant plan') plan' where -- NB: failures don't update IPIDs plan' = plan { planIndex = PackageIndex.merge (planIndex plan) failures } - pkg = lookupProcessingPackage plan pkgid + ReadyPackage srcpkg _deps = lookupProcessingPackage plan pkgid failures = PackageIndex.fromList - $ Failed (readyPackageToConfiguredPackage pkg) buildResult + $ Failed srcpkg buildResult : [ Failed pkg' buildResult' | Just pkg' <- map checkConfiguredPackage $ packagesThatDependOn plan pkgid ] -- | Lookup the reachable packages in the reverse dependency graph. -- -packagesThatDependOn :: InstallPlan - -> InstalledPackageId -> [PlanPackage] +packagesThatDependOn :: GenericInstallPlan ipkg srcpkg iresult ifailure + -> InstalledPackageId + -> [GenericPlanPackage ipkg srcpkg iresult ifailure] packagesThatDependOn plan pkgid = map (planPkgOf plan) . tail . Graph.reachable (planGraphRev plan) @@ -399,18 +409,22 @@ packagesThatDependOn plan pkgid = map (planPkgOf plan) -- | Lookup a package that we expect to be in the processing state. -- -lookupProcessingPackage :: InstallPlan - -> InstalledPackageId -> ReadyPackage +lookupProcessingPackage :: GenericInstallPlan ipkg srcpkg iresult ifailure + -> InstalledPackageId + -> GenericReadyPackage srcpkg ipkg lookupProcessingPackage plan pkgid = -- NB: processing packages are guaranteed to not indirect through -- planFakeMap case PackageIndex.lookupInstalledPackageId (planIndex plan) pkgid of Just (Processing pkg) -> pkg - _ -> internalError $ "not in processing state or no such pkg " ++ display pkgid + _ -> internalError $ "not in processing state or no such pkg " ++ + display pkgid -- | Check a package that we expect to be in the configured or failed state. -- -checkConfiguredPackage :: PlanPackage -> Maybe ConfiguredPackage +checkConfiguredPackage :: (Package srcpkg, Package ipkg) + => GenericPlanPackage ipkg srcpkg iresult ifailure + -> Maybe srcpkg checkConfiguredPackage (Configured pkg) = Just pkg checkConfiguredPackage (Failed _ _) = Nothing checkConfiguredPackage pkg = @@ -426,24 +440,24 @@ checkConfiguredPackage pkg = -- -- * if the result is @False@ use 'problems' to get a detailed list. -- -valid :: Platform -> CompilerInfo -> FakeMap -> Bool -> PlanIndex -> Bool -valid platform cinfo fakeMap indepGoals index = - null $ problems platform cinfo fakeMap indepGoals index - -data PlanProblem = - PackageInvalid ConfiguredPackage [PackageProblem] - | PackageMissingDeps PlanPackage [PackageIdentifier] - | PackageCycle [PlanPackage] +valid :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => FakeMap -> Bool + -> PlanIndex ipkg srcpkg iresult ifailure + -> Bool +valid fakeMap indepGoals index = + null $ problems fakeMap indepGoals index + +data PlanProblem ipkg srcpkg iresult ifailure = + PackageMissingDeps (GenericPlanPackage ipkg srcpkg iresult ifailure) + [PackageIdentifier] + | PackageCycle [GenericPlanPackage ipkg srcpkg iresult ifailure] | PackageInconsistency PackageName [(PackageIdentifier, Version)] - | PackageStateInvalid PlanPackage PlanPackage - -showPlanProblem :: PlanProblem -> String -showPlanProblem (PackageInvalid pkg packageProblems) = - "Package " ++ display (packageId pkg) - ++ " has an invalid configuration, in particular:\n" - ++ unlines [ " " ++ showPackageProblem problem - | problem <- packageProblems ] + | PackageStateInvalid (GenericPlanPackage ipkg srcpkg iresult ifailure) + (GenericPlanPackage ipkg srcpkg iresult ifailure) +showPlanProblem :: (Package ipkg, Package srcpkg) + => PlanProblem ipkg srcpkg iresult ifailure -> String showPlanProblem (PackageMissingDeps pkg missingDeps) = "Package " ++ display (packageId pkg) ++ " depends on the following packages which are missing from the plan: " @@ -468,36 +482,41 @@ showPlanProblem (PackageStateInvalid pkg pkg') = ++ " which is in the " ++ showPlanState pkg' ++ " state" where - showPlanState (PreExisting _) = "pre-existing" - showPlanState (Configured _) = "configured" - showPlanState (Processing _) = "processing" - showPlanState (Installed _ _) = "installed" - showPlanState (Failed _ _) = "failed" + showPlanState (PreExisting _) = "pre-existing" + showPlanState (Configured _) = "configured" + showPlanState (Processing _) = "processing" + showPlanState (Installed _ _ _) = "installed" + showPlanState (Failed _ _) = "failed" -- | For an invalid plan, produce a detailed list of problems as human readable -- error messages. This is mainly intended for debugging purposes. -- Use 'showPlanProblem' for a human readable explanation. -- -problems :: Platform -> CompilerInfo -> FakeMap -> Bool - -> PlanIndex -> [PlanProblem] -problems platform cinfo fakeMap indepGoals index = - [ PackageInvalid pkg packageProblems - | Configured pkg <- PackageIndex.allPackages index - , let packageProblems = configuredPackageProblems platform cinfo pkg - , not (null packageProblems) ] - - ++ [ PackageMissingDeps pkg (catMaybes (map (fmap packageId . PlanIndex.fakeLookupInstalledPackageId fakeMap index) missingDeps)) +problems :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => FakeMap -> Bool + -> PlanIndex ipkg srcpkg iresult ifailure + -> [PlanProblem ipkg srcpkg iresult ifailure] +problems fakeMap indepGoals index = + + [ PackageMissingDeps pkg + (catMaybes + (map + (fmap packageId . PlanIndex.fakeLookupInstalledPackageId fakeMap index) + missingDeps)) | (pkg, missingDeps) <- PlanIndex.brokenPackages fakeMap index ] ++ [ PackageCycle cycleGroup | cycleGroup <- PlanIndex.dependencyCycles fakeMap index ] ++ [ PackageInconsistency name inconsistencies - | (name, inconsistencies) <- PlanIndex.dependencyInconsistencies fakeMap indepGoals index ] + | (name, inconsistencies) <- + PlanIndex.dependencyInconsistencies fakeMap indepGoals index ] ++ [ PackageStateInvalid pkg pkg' | pkg <- PackageIndex.allPackages index - , Just pkg' <- map (PlanIndex.fakeLookupInstalledPackageId fakeMap index) (CD.nonSetupDeps (depends pkg)) + , Just pkg' <- map (PlanIndex.fakeLookupInstalledPackageId fakeMap index) + (CD.nonSetupDeps (depends pkg)) , not (stateDependencyRelation pkg pkg') ] -- | The graph of packages (nodes) and dependencies (edges) must be acyclic. @@ -505,7 +524,9 @@ problems platform cinfo fakeMap indepGoals index = -- * if the result is @False@ use 'PackageIndex.dependencyCycles' to find out -- which packages are involved in dependency cycles. -- -acyclic :: FakeMap -> PlanIndex -> Bool +acyclic :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => FakeMap -> PlanIndex ipkg srcpkg iresult ifailure -> Bool acyclic fakeMap = null . PlanIndex.dependencyCycles fakeMap -- | An installation plan is closed if for every package in the set, all of @@ -515,7 +536,9 @@ acyclic fakeMap = null . PlanIndex.dependencyCycles fakeMap -- * if the result is @False@ use 'PackageIndex.brokenPackages' to find out -- which packages depend on packages not in the index. -- -closed :: FakeMap -> PlanIndex -> Bool +closed :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + PackageFixedDeps srcpkg) + => FakeMap -> PlanIndex ipkg srcpkg iresult ifailure -> Bool closed fakeMap = null . PlanIndex.brokenPackages fakeMap -- | An installation plan is consistent if all dependencies that target a @@ -534,148 +557,54 @@ closed fakeMap = null . PlanIndex.brokenPackages fakeMap -- * if the result is @False@ use 'PackageIndex.dependencyInconsistencies' to -- find out which packages are. -- -consistent :: FakeMap -> PlanIndex -> Bool +consistent :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => FakeMap -> PlanIndex ipkg srcpkg iresult ifailure -> Bool consistent fakeMap = null . PlanIndex.dependencyInconsistencies fakeMap False -- | The states of packages have that depend on each other must respect -- this relation. That is for very case where package @a@ depends on -- package @b@ we require that @dependencyStatesOk a b = True@. -- -stateDependencyRelation :: PlanPackage -> PlanPackage -> Bool -stateDependencyRelation (PreExisting _) (PreExisting _) = True +stateDependencyRelation :: GenericPlanPackage ipkg srcpkg iresult ifailure + -> GenericPlanPackage ipkg srcpkg iresult ifailure + -> Bool +stateDependencyRelation (PreExisting _) (PreExisting _) = True -stateDependencyRelation (Configured _) (PreExisting _) = True -stateDependencyRelation (Configured _) (Configured _) = True -stateDependencyRelation (Configured _) (Processing _) = True -stateDependencyRelation (Configured _) (Installed _ _) = True +stateDependencyRelation (Configured _) (PreExisting _) = True +stateDependencyRelation (Configured _) (Configured _) = True +stateDependencyRelation (Configured _) (Processing _) = True +stateDependencyRelation (Configured _) (Installed _ _ _) = True -stateDependencyRelation (Processing _) (PreExisting _) = True -stateDependencyRelation (Processing _) (Installed _ _) = True +stateDependencyRelation (Processing _) (PreExisting _) = True +stateDependencyRelation (Processing _) (Installed _ _ _) = True -stateDependencyRelation (Installed _ _) (PreExisting _) = True -stateDependencyRelation (Installed _ _) (Installed _ _) = True +stateDependencyRelation (Installed _ _ _) (PreExisting _) = True +stateDependencyRelation (Installed _ _ _) (Installed _ _ _) = True -stateDependencyRelation (Failed _ _) (PreExisting _) = True +stateDependencyRelation (Failed _ _) (PreExisting _) = True -- failed can depends on configured because a package can depend on -- several other packages and if one of the deps fail then we fail -- but we still depend on the other ones that did not fail: -stateDependencyRelation (Failed _ _) (Configured _) = True -stateDependencyRelation (Failed _ _) (Processing _) = True -stateDependencyRelation (Failed _ _) (Installed _ _) = True -stateDependencyRelation (Failed _ _) (Failed _ _) = True - -stateDependencyRelation _ _ = False - --- | A 'ConfiguredPackage' is valid if the flag assignment is total and if --- in the configuration given by the flag assignment, all the package --- dependencies are satisfied by the specified packages. --- -configuredPackageValid :: Platform -> CompilerInfo -> ConfiguredPackage -> Bool -configuredPackageValid platform cinfo pkg = - null (configuredPackageProblems platform cinfo pkg) - -data PackageProblem = DuplicateFlag FlagName - | MissingFlag FlagName - | ExtraFlag FlagName - | DuplicateDeps [PackageIdentifier] - | MissingDep Dependency - | ExtraDep PackageIdentifier - | InvalidDep Dependency PackageIdentifier - -showPackageProblem :: PackageProblem -> String -showPackageProblem (DuplicateFlag (FlagName flag)) = - "duplicate flag in the flag assignment: " ++ flag - -showPackageProblem (MissingFlag (FlagName flag)) = - "missing an assignment for the flag: " ++ flag - -showPackageProblem (ExtraFlag (FlagName flag)) = - "extra flag given that is not used by the package: " ++ flag - -showPackageProblem (DuplicateDeps pkgids) = - "duplicate packages specified as selected dependencies: " - ++ intercalate ", " (map display pkgids) - -showPackageProblem (MissingDep dep) = - "the package has a dependency " ++ display dep - ++ " but no package has been selected to satisfy it." - -showPackageProblem (ExtraDep pkgid) = - "the package configuration specifies " ++ display pkgid - ++ " but (with the given flag assignment) the package does not actually" - ++ " depend on any version of that package." - -showPackageProblem (InvalidDep dep pkgid) = - "the package depends on " ++ display dep - ++ " but the configuration specifies " ++ display pkgid - ++ " which does not satisfy the dependency." - -configuredPackageProblems :: Platform -> CompilerInfo - -> ConfiguredPackage -> [PackageProblem] -configuredPackageProblems platform cinfo - (ConfiguredPackage pkg specifiedFlags stanzas specifiedDeps') = - [ DuplicateFlag flag | ((flag,_):_) <- duplicates specifiedFlags ] - ++ [ MissingFlag flag | OnlyInLeft flag <- mergedFlags ] - ++ [ ExtraFlag flag | OnlyInRight flag <- mergedFlags ] - ++ [ DuplicateDeps pkgs - | pkgs <- CD.nonSetupDeps (fmap (duplicatesBy (comparing packageName)) specifiedDeps) ] - ++ [ MissingDep dep | OnlyInLeft dep <- mergedDeps ] - ++ [ ExtraDep pkgid | OnlyInRight pkgid <- mergedDeps ] - ++ [ InvalidDep dep pkgid | InBoth dep pkgid <- mergedDeps - , not (packageSatisfiesDependency pkgid dep) ] - where - specifiedDeps :: ComponentDeps [PackageId] - specifiedDeps = fmap (map confSrcId) specifiedDeps' - - mergedFlags = mergeBy compare - (sort $ map flagName (genPackageFlags (packageDescription pkg))) - (sort $ map fst specifiedFlags) - - packageSatisfiesDependency - (PackageIdentifier name version) - (Dependency name' versionRange) = assert (name == name') $ - version `withinRange` versionRange - - dependencyName (Dependency name _) = name - - mergedDeps :: [MergeResult Dependency PackageId] - mergedDeps = mergeDeps requiredDeps (CD.flatDeps specifiedDeps) - - mergeDeps :: [Dependency] -> [PackageId] -> [MergeResult Dependency PackageId] - mergeDeps required specified = - let sortNubOn f = nubBy ((==) `on` f) . sortBy (compare `on` f) in - mergeBy - (\dep pkgid -> dependencyName dep `compare` packageName pkgid) - (sortNubOn dependencyName required) - (sortNubOn packageName specified) - - -- TODO: It would be nicer to use ComponentDeps here so we can be more precise - -- in our checks. That's a bit tricky though, as this currently relies on - -- the 'buildDepends' field of 'PackageDescription'. (OTOH, that field is - -- deprecated and should be removed anyway.) - -- As long as we _do_ use a flat list here, we have to allow for duplicates - -- when we fold specifiedDeps; once we have proper ComponentDeps here we - -- should get rid of the `nubOn` in `mergeDeps`. - requiredDeps :: [Dependency] - requiredDeps = - --TODO: use something lower level than finalizePackageDescription - case finalizePackageDescription specifiedFlags - (const True) - platform cinfo - [] - (enableStanzas stanzas $ packageDescription pkg) of - Right (resolvedPkg, _) -> - externalBuildDepends resolvedPkg - ++ maybe [] setupDepends (setupBuildInfo resolvedPkg) - Left _ -> - error "configuredPackageInvalidDeps internal error" +stateDependencyRelation (Failed _ _) (Configured _) = True +stateDependencyRelation (Failed _ _) (Processing _) = True +stateDependencyRelation (Failed _ _) (Installed _ _ _) = True +stateDependencyRelation (Failed _ _) (Failed _ _) = True + +stateDependencyRelation _ _ = False + -- | Compute the dependency closure of a _source_ package in a install plan -- --- See `Distribution.Simple.dependencyClosure` -dependencyClosure :: InstallPlan +-- See `Distribution.Client.PlanIndex.dependencyClosure` +dependencyClosure :: (HasInstalledPackageId ipkg, PackageFixedDeps ipkg, + HasInstalledPackageId srcpkg, PackageFixedDeps srcpkg) + => GenericInstallPlan ipkg srcpkg iresult ifailure -> [PackageIdentifier] - -> Either (PackageIndex PlanPackage) [(PlanPackage, [InstalledPackageId])] + -> Either [(GenericPlanPackage ipkg srcpkg iresult ifailure, + [InstalledPackageId])] + (PackageIndex + (GenericPlanPackage ipkg srcpkg iresult ifailure)) dependencyClosure installPlan pids = PlanIndex.dependencyClosure (planFakeMap installPlan) diff --git a/cabal-install/Distribution/Client/InstallSymlink.hs b/cabal-install/Distribution/Client/InstallSymlink.hs index 6a171986ea786bd3cc8a90eb9fca44cd5b4433c4..4b9de74b5dfd6f39b98ebac5c33f9e22f6e68904 100644 --- a/cabal-install/Distribution/Client/InstallSymlink.hs +++ b/cabal-install/Distribution/Client/InstallSymlink.hs @@ -23,13 +23,16 @@ import Distribution.Client.InstallPlan (InstallPlan) import Distribution.Client.Setup (InstallFlags) import Distribution.Simple.Setup (ConfigFlags) import Distribution.Simple.Compiler +import Distribution.System -symlinkBinaries :: Compiler +symlinkBinaries :: Platform -> Compiler -> ConfigFlags -> InstallFlags - -> InstallPlan + -> InstallPlan InstalledPackageInfo + ConfiguredPackage + iresult ifailure -> IO [(PackageIdentifier, String, FilePath)] -symlinkBinaries _ _ _ _ = return [] +symlinkBinaries _ _ _ _ _ = return [] symlinkBinary :: FilePath -> FilePath -> String -> String -> IO Bool symlinkBinary _ _ _ _ = fail "Symlinking feature not available on Windows" @@ -37,7 +40,9 @@ symlinkBinary _ _ _ _ = fail "Symlinking feature not available on Windows" #else import Distribution.Client.Types - ( SourcePackage(..), ReadyPackage(..), enableStanzas ) + ( SourcePackage(..) + , GenericReadyPackage(..), ReadyPackage, enableStanzas + , ConfiguredPackage(..) ) import Distribution.Client.Setup ( InstallFlags(installSymlinkBinDir) ) import qualified Distribution.Client.InstallPlan as InstallPlan @@ -59,7 +64,9 @@ import Distribution.Simple.Setup import qualified Distribution.Simple.InstallDirs as InstallDirs import qualified Distribution.InstalledPackageInfo as Installed import Distribution.Simple.Compiler - ( Compiler, CompilerInfo(..), packageKeySupported ) + ( Compiler, compilerInfo, CompilerInfo(..), packageKeySupported ) +import Distribution.System + ( Platform ) import System.Posix.Files ( getSymbolicLinkStatus, isSymbolicLink, createSymbolicLink @@ -98,12 +105,12 @@ import Data.Maybe -- controlled from the config file. Of course it only works on POSIX systems -- with symlinks so is not available to Windows users. -- -symlinkBinaries :: Compiler +symlinkBinaries :: Platform -> Compiler -> ConfigFlags -> InstallFlags -> InstallPlan -> IO [(PackageIdentifier, String, FilePath)] -symlinkBinaries comp configFlags installFlags plan = +symlinkBinaries platform comp configFlags installFlags plan = case flagToMaybe (installSymlinkBinDir installFlags) of Nothing -> return [] Just symlinkBinDir @@ -121,10 +128,11 @@ symlinkBinaries comp configFlags installFlags plan = then return Nothing else return (Just (pkgid, publicExeName, privateBinDir </> privateExeName)) - | (ReadyPackage _ _flags _ deps, pkg, exe) <- exes + | (ReadyPackage (ConfiguredPackage _ _flags _ _) deps, pkg, exe) <- exes , let pkgid = packageId pkg pkg_key = mkPackageKey (packageKeySupported comp) pkgid - (map Installed.libraryName (CD.nonSetupDeps deps)) + (map Installed.libraryName + (CD.nonSetupDeps deps)) libname = packageKeyLibraryName pkgid pkg_key publicExeName = PackageDescription.exeName exe privateExeName = prefix ++ publicExeName ++ suffix @@ -133,13 +141,16 @@ symlinkBinaries comp configFlags installFlags plan = where exes = [ (cpkg, pkg, exe) - | InstallPlan.Installed cpkg _ <- InstallPlan.toList plan + | InstallPlan.Installed cpkg _ _ <- InstallPlan.toList plan , let pkg = pkgDescription cpkg , exe <- PackageDescription.executables pkg , PackageDescription.buildable (PackageDescription.buildInfo exe) ] pkgDescription :: ReadyPackage -> PackageDescription - pkgDescription (ReadyPackage (SourcePackage _ pkg _ _) flags stanzas _) = + pkgDescription (ReadyPackage (ConfiguredPackage + (SourcePackage _ pkg _ _) + flags stanzas _) + _) = case finalizePackageDescription flags (const True) platform cinfo [] (enableStanzas stanzas pkg) of @@ -170,8 +181,7 @@ symlinkBinaries comp configFlags installFlags plan = fromFlagTemplate = fromFlagOrDefault (InstallDirs.toPathTemplate "") prefixTemplate = fromFlagTemplate (configProgPrefix configFlags) suffixTemplate = fromFlagTemplate (configProgSuffix configFlags) - platform = InstallPlan.planPlatform plan - cinfo = InstallPlan.planCompiler plan + cinfo = compilerInfo comp (CompilerId compilerFlavor _) = compilerInfoId cinfo symlinkBinary :: FilePath -- ^ The canonical path of the public bin dir diff --git a/cabal-install/Distribution/Client/List.hs b/cabal-install/Distribution/Client/List.hs index 3ce3dd253335c1d7fd89bce666fb68b60a0eba6b..e94e59cceff94da532bed4194020adedd4aff19d 100644 --- a/cabal-install/Distribution/Client/List.hs +++ b/cabal-install/Distribution/Client/List.hs @@ -15,7 +15,8 @@ module Distribution.Client.List ( import Distribution.Package ( PackageName(..), Package(..), packageName, packageVersion - , Dependency(..), simplifyDependency ) + , Dependency(..), simplifyDependency + , InstalledPackageId ) import Distribution.ModuleName (ModuleName) import Distribution.License (License) import qualified Distribution.InstalledPackageInfo as Installed @@ -44,7 +45,7 @@ import Distribution.Text import Distribution.Client.Types ( SourcePackage(..), Repo, SourcePackageDb(..) ) import Distribution.Client.Dependency.Types - ( PackageConstraint(..), ExtDependency(..) ) + ( PackageConstraint(..) ) import Distribution.Client.Targets ( UserTarget, resolveUserTargets, PackageSpecifier(..) ) import Distribution.Client.Setup @@ -190,7 +191,7 @@ info verbosity packageDBs repos comp conf ++ map packageId (PackageIndex.allPackages sourcePkgIndex) transport <- configureTransport verbosity (flagToMaybe (globalHttpTransport globalFlags)) - pkgSpecifiers <- resolveUserTargets transport verbosity + pkgSpecifiers <- resolveUserTargets verbosity transport (fromFlag $ globalWorldFile globalFlags) sourcePkgs' userTargets @@ -292,6 +293,11 @@ data PackageDisplayInfo = PackageDisplayInfo { haveTarball :: Bool } +-- | Covers source dependencies and installed dependencies in +-- one type. +data ExtDependency = SourceDependency Dependency + | InstalledDependency InstalledPackageId + showPackageSummaryInfo :: PackageDisplayInfo -> String showPackageSummaryInfo pkginfo = renderStyle (style {lineLength = 80, ribbonsPerLine = 1}) $ @@ -344,7 +350,7 @@ showPackageDetailedInfo pkginfo = , entry "Source repo" sourceRepo orNotSpecified text , entry "Executables" executables hideIfNull (commaSep text) , entry "Flags" flags hideIfNull (commaSep dispFlag) - , entry "Dependencies" dependencies hideIfNull (commaSep disp) + , entry "Dependencies" dependencies hideIfNull (commaSep dispExtDep) , entry "Documentation" haddockHtml showIfInstalled text , entry "Cached" haveTarball alwaysShow dispYesNo , if not (hasLib pkginfo) then empty else @@ -378,6 +384,9 @@ showPackageDetailedInfo pkginfo = dispYesNo True = text "Yes" dispYesNo False = text "No" + dispExtDep (SourceDependency dep) = disp dep + dispExtDep (InstalledDependency dep) = disp dep + isInstalled = not (null (installedVersions pkginfo)) hasExes = length (executables pkginfo) >= 2 --TODO: exclude non-buildable exes diff --git a/cabal-install/Distribution/Client/PackageIndex.hs b/cabal-install/Distribution/Client/PackageIndex.hs index c975177d613aa1e3ebe206dc0376092e78102628..0efb485c7856025fa78833aba5a9d575633a165b 100644 --- a/cabal-install/Distribution/Client/PackageIndex.hs +++ b/cabal-install/Distribution/Client/PackageIndex.hs @@ -16,9 +16,6 @@ module Distribution.Client.PackageIndex ( -- * Package index data type PackageIndex, - -- * Fine-grained package dependencies - PackageFixedDeps(..), - -- * Creating an index fromList, @@ -61,30 +58,12 @@ import Data.Maybe (isJust, fromMaybe) import Distribution.Package ( PackageName(..), PackageIdentifier(..) , Package(..), packageName, packageVersion - , Dependency(Dependency) - , InstalledPackageId, installedDepends ) + , Dependency(Dependency) ) import Distribution.Version ( withinRange ) -import Distribution.InstalledPackageInfo - ( InstalledPackageInfo_ ) import Distribution.Simple.Utils ( lowercase, comparing ) -import Distribution.Client.ComponentDeps (ComponentDeps) -import qualified Distribution.Client.ComponentDeps as CD - --- | Subclass of packages that have specific versioned dependencies. --- --- So for example a not-yet-configured package has dependencies on version --- ranges, not specific versions. A configured or an already installed package --- depends on exact versions. Some operations or data structures (like --- dependency graphs) only make sense on this subclass of package types. --- -class Package pkg => PackageFixedDeps pkg where - depends :: pkg -> ComponentDeps [InstalledPackageId] - -instance PackageFixedDeps (InstalledPackageInfo_ str) where - depends = CD.fromInstalled . installedDepends -- | The collection of information about packages from one or more 'PackageDB's. -- diff --git a/cabal-install/Distribution/Client/PlanIndex.hs b/cabal-install/Distribution/Client/PlanIndex.hs index 67c2801c0dcc88f783049ba6c7216c1165a71f58..5e837c6eedb3b9b659b2a7831a29a2789eabc988 100644 --- a/cabal-install/Distribution/Client/PlanIndex.hs +++ b/cabal-install/Distribution/Client/PlanIndex.hs @@ -27,7 +27,7 @@ import qualified Data.Graph as Graph import Data.Array ((!)) import Data.Map (Map) import Data.Maybe (isNothing, fromMaybe, fromJust) -import Data.Either (lefts) +import Data.Either (rights) #if !MIN_VERSION_base(4,8,0) import Data.Monoid (Monoid(..)) @@ -42,7 +42,7 @@ import Distribution.Version import Distribution.Client.ComponentDeps (ComponentDeps) import qualified Distribution.Client.ComponentDeps as CD -import Distribution.Client.PackageIndex +import Distribution.Client.Types ( PackageFixedDeps(..) ) import Distribution.Simple.PackageIndex ( PackageIndex, allPackages, insert, lookupInstalledPackageId ) @@ -131,7 +131,7 @@ dependencyInconsistencies fakeMap indepGoals index = concatMap (dependencyInconsistencies' fakeMap) subplans where subplans :: [PackageIndex pkg] - subplans = lefts $ + subplans = rights $ map (dependencyClosure fakeMap index) (rootSets fakeMap indepGoals index) @@ -253,11 +253,11 @@ dependencyClosure :: (PackageFixedDeps pkg, HasInstalledPackageId pkg) => FakeMap -> PackageIndex pkg -> [InstalledPackageId] - -> Either (PackageIndex pkg) - [(pkg, [InstalledPackageId])] + -> Either [(pkg, [InstalledPackageId])] + (PackageIndex pkg) dependencyClosure fakeMap index pkgids0 = case closure mempty [] pkgids0 of - (completed, []) -> Left completed - (completed, _) -> Right (brokenPackages fakeMap completed) + (completed, []) -> Right completed + (completed, _) -> Left (brokenPackages fakeMap completed) where closure completed failed [] = (completed, failed) closure completed failed (pkgid:pkgids) = diff --git a/cabal-install/Distribution/Client/Targets.hs b/cabal-install/Distribution/Client/Targets.hs index dbc5731198d98bb3bd454915ea603a56fdb437e7..25b3ec2f61d42fcb41645194b2745fcfbb727679 100644 --- a/cabal-install/Distribution/Client/Targets.hs +++ b/cabal-install/Distribution/Client/Targets.hs @@ -351,13 +351,13 @@ reportUserTargetProblems problems = do -- or they can be named packages (with or without version info). -- resolveUserTargets :: Package pkg - => HttpTransport - -> Verbosity + => Verbosity + -> HttpTransport -> FilePath -> PackageIndex pkg -> [UserTarget] -> IO [PackageSpecifier SourcePackage] -resolveUserTargets transport verbosity worldFile available userTargets = do +resolveUserTargets verbosity transport worldFile available userTargets = do -- given the user targets, get a list of fully or partially resolved -- package references diff --git a/cabal-install/Distribution/Client/Types.hs b/cabal-install/Distribution/Client/Types.hs index 129f65b489d7cca51249169b2e075f93ada8b122..a5991d88e23115a015cea41847b650208f988dc0 100644 --- a/cabal-install/Distribution/Client/Types.hs +++ b/cabal-install/Distribution/Client/Types.hs @@ -20,14 +20,14 @@ import Distribution.Package , HasInstalledPackageId(..), PackageInstalled(..) , LibraryName, packageKeyLibraryName ) import Distribution.InstalledPackageInfo - ( InstalledPackageInfo ) + ( InstalledPackageInfo, InstalledPackageInfo_ ) import Distribution.PackageDescription ( Benchmark(..), GenericPackageDescription(..), FlagAssignment , TestSuite(..) ) import Distribution.PackageDescription.Configuration ( mapTreeData ) import Distribution.Client.PackageIndex - ( PackageIndex, PackageFixedDeps(..) ) + ( PackageIndex ) import Distribution.Client.ComponentDeps ( ComponentDeps ) import qualified Distribution.Client.ComponentDeps as CD @@ -58,20 +58,18 @@ data SourcePackageDb = SourcePackageDb { -- * Various kinds of information about packages -- ------------------------------------------------------------ --- | InstalledPackage caches its dependencies as source package IDs. --- This is for the benefit of the top-down solver only. -data InstalledPackage = InstalledPackage - InstalledPackageInfo - [PackageId] +-- | Subclass of packages that have specific versioned dependencies. +-- +-- So for example a not-yet-configured package has dependencies on version +-- ranges, not specific versions. A configured or an already installed package +-- depends on exact versions. Some operations or data structures (like +-- dependency graphs) only make sense on this subclass of package types. +-- +class Package pkg => PackageFixedDeps pkg where + depends :: pkg -> ComponentDeps [InstalledPackageId] -instance Package InstalledPackage where - packageId (InstalledPackage pkg _) = packageId pkg -instance PackageFixedDeps InstalledPackage where - depends (InstalledPackage pkg _) = depends pkg -instance HasInstalledPackageId InstalledPackage where - installedPackageId (InstalledPackage pkg _) = installedPackageId pkg -instance PackageInstalled InstalledPackage where - installedDepends (InstalledPackage pkg _) = installedDepends pkg +instance PackageFixedDeps (InstalledPackageInfo_ str) where + depends = CD.fromInstalled . installedDepends -- | In order to reuse the implementation of PackageIndex which relies on @@ -133,51 +131,40 @@ instance HasInstalledPackageId ConfiguredPackage where -- | Like 'ConfiguredPackage', but with all dependencies guaranteed to be -- installed already, hence itself ready to be installed. -data ReadyPackage = ReadyPackage - SourcePackage -- see 'ConfiguredPackage'. - FlagAssignment -- - [OptionalStanza] -- - (ComponentDeps [InstalledPackageInfo]) -- Installed dependencies. - deriving Show +data GenericReadyPackage srcpkg ipkg + = ReadyPackage + srcpkg -- see 'ConfiguredPackage'. + (ComponentDeps [ipkg]) -- Installed dependencies. + deriving (Eq, Show) -instance Package ReadyPackage where - packageId (ReadyPackage pkg _ _ _) = packageId pkg +type ReadyPackage = GenericReadyPackage ConfiguredPackage InstalledPackageInfo -instance PackageFixedDeps ReadyPackage where - depends (ReadyPackage _ _ _ deps) = fmap (map installedPackageId) deps +instance Package srcpkg => Package (GenericReadyPackage srcpkg ipkg) where + packageId (ReadyPackage srcpkg _deps) = packageId srcpkg -instance HasInstalledPackageId ReadyPackage where - installedPackageId = fakeInstalledPackageId . packageId +instance (Package srcpkg, HasInstalledPackageId ipkg) => + PackageFixedDeps (GenericReadyPackage srcpkg ipkg) where + depends (ReadyPackage _ deps) = fmap (map installedPackageId) deps + +instance HasInstalledPackageId srcpkg => + HasInstalledPackageId (GenericReadyPackage srcpkg ipkg) where + installedPackageId (ReadyPackage pkg _) = installedPackageId pkg -- | Extracts a package key from ReadyPackage, a common operation needed -- to calculate build paths. readyPackageKey :: Compiler -> ReadyPackage -> PackageKey -readyPackageKey comp (ReadyPackage pkg _ _ deps) = +readyPackageKey comp (ReadyPackage pkg deps) = mkPackageKey (packageKeySupported comp) (packageId pkg) (map Info.libraryName (CD.nonSetupDeps deps)) -- | Extracts a library name from ReadyPackage, a common operation needed -- to calculate build paths. readyLibraryName :: Compiler -> ReadyPackage -> LibraryName -readyLibraryName comp ready@(ReadyPackage pkg _ _ _) = +readyLibraryName comp ready@(ReadyPackage pkg _) = packageKeyLibraryName (packageId pkg) (readyPackageKey comp ready) --- | Sometimes we need to convert a 'ReadyPackage' back to a --- 'ConfiguredPackage'. For example, a failed 'PlanPackage' can be *either* --- Ready or Configured. -readyPackageToConfiguredPackage :: ReadyPackage -> ConfiguredPackage -readyPackageToConfiguredPackage (ReadyPackage srcpkg flags stanzas deps) = - ConfiguredPackage srcpkg flags stanzas (fmap (map aux) deps) - where - aux :: InstalledPackageInfo -> ConfiguredId - aux info = ConfiguredId { - confSrcId = Info.sourcePackageId info - , confInstId = installedPackageId info - } - - -- | A package description along with the location of the package sources. -- data SourcePackage = SourcePackage { diff --git a/cabal-install/Distribution/Client/Utils/LabeledGraph.hs b/cabal-install/Distribution/Client/Utils/LabeledGraph.hs index 567f15609acc445dd9edb59760b90647df979e31..1c42a1d51b7db3c8ce40f1ee341201264c839893 100644 --- a/cabal-install/Distribution/Client/Utils/LabeledGraph.hs +++ b/cabal-install/Distribution/Client/Utils/LabeledGraph.hs @@ -51,8 +51,10 @@ graphFromEdges edges0 = sorted_edges = sortBy lt edges0 edges1 = zipWith (,) [0..] sorted_edges - graph = array bounds0 [(v, (mapMaybe mk_edge ks)) | (v, (_, _, ks)) <- edges1] - key_map = array bounds0 [(v, k ) | (v, (_, k, _ )) <- edges1] + graph = array bounds0 [(v, (mapMaybe mk_edge ks)) + | (v, (_, _, ks)) <- edges1] + key_map = array bounds0 [(v, k ) + | (v, (_, k, _ )) <- edges1] vertex_map = array bounds0 edges1 (_,k1,_) `lt` (_,k2,_) = k1 `compare` k2 diff --git a/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs b/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs index e5e3dd6e5825adc7446b29e6c88fed5413933cc7..b5e67bb28a5f10a1564e3e0a846860bbafe98fb4 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs @@ -20,7 +20,8 @@ import qualified Data.Map as Map -- Cabal import qualified Distribution.Compiler as C import qualified Distribution.InstalledPackageInfo as C -import qualified Distribution.Package as C hiding (HasInstalledPackageId(..)) +import qualified Distribution.Package as C + hiding (HasInstalledPackageId(..)) import qualified Distribution.PackageDescription as C import qualified Distribution.Simple.PackageIndex as C.PackageIndex import qualified Distribution.System as C @@ -102,8 +103,10 @@ data ExampleAvailable = ExAv { , exAvDeps :: ComponentDeps [ExampleDependency] } -exAv :: ExamplePkgName -> ExamplePkgVersion -> [ExampleDependency] -> ExampleAvailable -exAv n v ds = ExAv { exAvName = n, exAvVersion = v, exAvDeps = CD.fromLibraryDeps ds } +exAv :: ExamplePkgName -> ExamplePkgVersion -> [ExampleDependency] + -> ExampleAvailable +exAv n v ds = ExAv { exAvName = n, exAvVersion = v + , exAvDeps = CD.fromLibraryDeps ds } withSetupDeps :: ExampleAvailable -> [ExampleDependency] -> ExampleAvailable withSetupDeps ex setupDeps = ex { @@ -117,7 +120,8 @@ data ExampleInstalled = ExInst { , exInstBuildAgainst :: [ExampleInstalled] } -exInst :: ExamplePkgName -> ExamplePkgVersion -> ExamplePkgHash -> [ExampleInstalled] -> ExampleInstalled +exInst :: ExamplePkgName -> ExamplePkgVersion -> ExamplePkgHash + -> [ExampleInstalled] -> ExampleInstalled exInst = ExInst type ExampleDb = [Either ExampleInstalled ExampleAvailable] @@ -146,10 +150,12 @@ exAvSrcPkg ex = C.setupDepends = mkSetupDeps (CD.setupDeps (exAvDeps ex)) } } - , C.genPackageFlags = concatMap extractFlags (CD.libraryDeps (exAvDeps ex)) + , C.genPackageFlags = concatMap extractFlags + (CD.libraryDeps (exAvDeps ex)) , C.condLibrary = Just $ mkCondTree libraryDeps , C.condExecutables = [] - , C.condTestSuites = map (\(t, deps) -> (t, mkCondTree deps)) testSuites + , C.condTestSuites = map (\(t, deps) -> (t, mkCondTree deps)) + testSuites , C.condBenchmarks = [] } } @@ -159,10 +165,12 @@ exAvSrcPkg ex = , [(ExampleTestName, [ExampleDependency])] ) splitTopLevel [] = ([], []) - splitTopLevel (ExTest t a:deps) = let (other, testSuites) = splitTopLevel deps - in (other, (t, a):testSuites) - splitTopLevel (dep:deps) = let (other, testSuites) = splitTopLevel deps - in (dep:other, testSuites) + splitTopLevel (ExTest t a:deps) = + let (other, testSuites) = splitTopLevel deps + in (other, (t, a):testSuites) + splitTopLevel (dep:deps) = + let (other, testSuites) = splitTopLevel deps + in (dep:other, testSuites) extractFlags :: ExampleDependency -> [C.Flag] extractFlags (ExAny _) = [] @@ -193,7 +201,8 @@ exAvSrcPkg ex = mkFlagged :: Monoid a => (ExampleFlagName, [ExampleDependency], [ExampleDependency]) - -> (C.Condition C.ConfVar, DependencyTree a, Maybe (DependencyTree a)) + -> (C.Condition C.ConfVar + , DependencyTree a, Maybe (DependencyTree a)) mkFlagged (f, a, b) = ( C.Var (C.Flag (C.FlagName f)) , mkCondTree a , Just (mkCondTree b) @@ -274,7 +283,8 @@ exResolve db targets indepGoals = runProgress $ packageIndex = exAvIdx avai , packagePreferences = Map.empty } - enableTests = map (\p -> PackageConstraintStanzas (C.PackageName p) [TestStanzas]) + enableTests = map (\p -> PackageConstraintStanzas + (C.PackageName p) [TestStanzas]) (exDbPkgs db) targets' = map (\p -> NamedPackage (C.PackageName p) []) targets params = addConstraints enableTests @@ -292,7 +302,8 @@ extractInstallPlan = catMaybes . map confPkg . CI.InstallPlan.toList srcPkg :: ConfiguredPackage -> (String, Int) srcPkg (ConfiguredPackage pkg _flags _stanzas _deps) = - let C.PackageIdentifier (C.PackageName p) (Version (n:_) _) = packageInfoId pkg + let C.PackageIdentifier (C.PackageName p) (Version (n:_) _) = + packageInfoId pkg in (p, n) {-------------------------------------------------------------------------------