packages feed

cabal-install-0.6.2: Distribution/Client/Dependency/Bogus.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Client.Dependency.Bogus
-- Copyright   :  (c) David Himmelstrup 2005, Bjorn Bringert 2007
--                    Duncan Coutts 2008
-- License     :  BSD-like
--
-- Maintainer  :  cabal-devel@haskell.org
-- Stability   :  provisional
-- Portability :  portable
--
-- A dependency resolver for when we do not know what packages are installed.
-----------------------------------------------------------------------------
module Distribution.Client.Dependency.Bogus (
    bogusResolver
  ) where

import Distribution.Client.Types
         ( AvailablePackage(..), ConfiguredPackage(..) )
import Distribution.Client.Dependency.Types
         ( DependencyResolver, Progress(..)
         , PackageConstraint(..), PackagePreferences(..) )
import qualified Distribution.Client.InstallPlan as InstallPlan

import Distribution.Package
         ( PackageName, PackageIdentifier(..), Dependency(..)
         , Package(..), packageVersion )
import Distribution.PackageDescription
         ( GenericPackageDescription(..), CondTree(..), FlagAssignment )
import Distribution.PackageDescription.Configuration
         ( finalizePackageDescription )
import qualified Distribution.Simple.PackageIndex as PackageIndex
import Distribution.Simple.PackageIndex (PackageIndex)
import Distribution.Version
         ( VersionRange(AnyVersion, IntersectVersionRanges), withinRange )
import Distribution.Simple.Utils
         ( comparing )
import Distribution.Text
         ( display )
import Distribution.System
         ( Platform(Platform) )

import Data.List
         ( maximumBy )
import Data.Maybe
         ( fromMaybe )
import qualified Data.Map as Map

-- | This resolver thinks that every package is already installed.
--
-- We need this for hugs and nhc98 which do not track installed packages.
-- We just pretend that everything is installed and hope for the best.
--
bogusResolver :: DependencyResolver
bogusResolver (Platform arch os) comp _ available
              preferences constraints targets =
    resolveFromAvailable []
      (combineConstraints preferences constraints targets)
  where
    resolveFromAvailable chosen [] = Done chosen
    resolveFromAvailable chosen ((name, verConstraint, flags, verPref): deps) =
      case latestAvailableSatisfying available name verConstraint verPref of
        Nothing  -> Fail ("Unresolved dependency: " ++ display dep)
        Just apkg@(AvailablePackage _ pkg _) ->
          case finalizePackageDescription flags none os arch comp [] pkg of
            Right (_, flags') -> Step msg (resolveFromAvailable chosen' deps)
              where
                msg     = "selecting " ++ display (packageId pkg)
                cpkg    = fudgeChosenPackage apkg flags'
                chosen' = InstallPlan.Configured cpkg : chosen
            _ -> error "bogusResolver: impossible happened"
          where
            none :: Maybe (PackageIndex PackageIdentifier)
            none = Nothing
      where
        dep = Dependency name verConstraint

fudgeChosenPackage :: AvailablePackage -> FlagAssignment -> ConfiguredPackage
fudgeChosenPackage (AvailablePackage pkgid pkg source) flags =
  ConfiguredPackage (AvailablePackage pkgid (stripDependencies pkg) source)
                    flags ([] :: [PackageIdentifier]) -- empty list of deps
  where
    -- | Pretend that a package has no dependencies. Go through the
    -- 'GenericPackageDescription' and strip them all out.
    --
    stripDependencies :: GenericPackageDescription -> GenericPackageDescription
    stripDependencies gpkg = gpkg {
        condLibrary     = fmap stripDeps (condLibrary gpkg),
        condExecutables = [ (name, stripDeps tree)
                          | (name, tree) <- condExecutables gpkg ]
      }
    stripDeps :: CondTree v [Dependency] a -> CondTree v [Dependency] a
    stripDeps = mapTreeConstrs (const [])

    mapTreeConstrs :: (c -> c) -> CondTree v c a -> CondTree v c a
    mapTreeConstrs f (CondNode a c ifs) = CondNode a (f c) (map g ifs)
      where
        g (cnd, t, me) = (cnd, mapTreeConstrs f t, fmap (mapTreeConstrs f) me)

combineConstraints :: (PackageName -> PackagePreferences)
                   -> [PackageConstraint]
                   -> [PackageName]
                   -> [(PackageName, VersionRange, FlagAssignment, VersionRange)]
combineConstraints preferences constraints targets =
  [ (name, ver, flags, pref)
  | name <- targets
  , let ver   = fromMaybe AnyVersion (Map.lookup name versionConstraints)
        flags = fromMaybe []         (Map.lookup name flagsConstraints)
        PackagePreferences pref _ = preferences name ]
  where
    versionConstraints = Map.fromListWith IntersectVersionRanges
      [ (name, versionRange)
      | PackageVersionConstraint name versionRange <- constraints ]

    flagsConstraints =  Map.fromListWith (++)
      [ (name, flags)
      | PackageFlagsConstraint name flags <- constraints ]

-- | Gets the best available package satisfying a dependency.
--
latestAvailableSatisfying :: PackageIndex AvailablePackage
                          -> PackageName -> VersionRange -> VersionRange
                          -> Maybe AvailablePackage
latestAvailableSatisfying index name versionConstraint versionPreference =
  case PackageIndex.lookupDependency index dep of
    []   -> Nothing
    pkgs -> Just (maximumBy best pkgs)
  where
    dep  = Dependency name versionConstraint
    best = comparing (\p -> (isPreferred p, packageVersion p))
    isPreferred p = packageVersion p `withinRange` versionPreference