CurryInfo: cpm-3.3.0 / CPM.PackageCopy

classes:

              
documentation:
--------------------------------------------------------------------------------
--- This module contains operations that operate on a package copy.
--------------------------------------------------------------------------------
name:
CPM.PackageCopy
operations:
acquireAndInstallPackageWithDependencies installLocalDependencies linkToLocalCache resolveAndCopyDependencies resolveAndCopyDependenciesForPackage resolveDependencies resolveDependenciesForPackageCopy upgradeAllPackages upgradeSinglePackage
sourcecode:
module CPM.PackageCopy
  ( resolveDependenciesForPackageCopy
  , resolveAndCopyDependencies, resolveAndCopyDependenciesForPackage
  , resolveDependencies
  , upgradeAllPackages
  , upgradeSinglePackage
  , linkToLocalCache
  , acquireAndInstallPackageWithDependencies
  , installLocalDependencies
  ) where

import System.Directory ( doesDirectoryExist )
import Data.List        ( intercalate )
import Data.Maybe       ( mapMaybe )

import CPM.Config       ( Config, compilerBaseVersion )
import CPM.Repository   ( Repository, allPackages )
import CPM.Repository.Select
import qualified CPM.LookupSet as LS
import CPM.ErrorLogger
import qualified CPM.PackageCache.Global as GC
import qualified CPM.PackageCache.Runtime as RuntimeCache
import qualified CPM.PackageCache.Local as LocalCache
import CPM.Package
import CPM.Resolution

--- Resolves dependencies for a package copy.
resolveDependenciesForPackageCopy :: Config -> Package -> Repository
                                  -> GC.GlobalCache -> String
                                  -> ErrorLogger ResolutionResult
resolveDependenciesForPackageCopy cfg pkg repo gc dir =
  lookupSetForPackageCopy cfg pkg repo gc dir >>= \lookupSet ->
  resolveDependenciesFromLookupSet cfg (setBaseDependency cfg pkg) lookupSet

--- Calculates the lookup set needed for dependency resolution on a package
--- copy.
lookupSetForPackageCopy :: Config -> Package -> Repository -> GC.GlobalCache
                        -> String -> ErrorLogger LS.LookupSet
lookupSetForPackageCopy cfg _ repo gc dir = do
  localPkgs <- LocalCache.allPackages dir
  diffInLC <- liftIOEL $ mapM filterGCLinked localPkgs
  let lsLC = addPackagesWOBase cfg lsGC localPkgs LS.FromLocalCache
  mapM logSymlinkedPackage (mapMaybe id diffInLC)
  return lsLC
 where
  allRepoPackages = allPackages repo
  logSymlinkedPackage p = logDebug $ "Using symlinked version of '" ++
                                      packageId p ++ "' from local cache."
  lsRepo = addPackagesWOBase cfg LS.emptySet allRepoPackages LS.FromRepository
  -- Find all packages that are in the global cache, but not in the repo
  newInGC = filter (\p -> not $ any (packageIdEq p) allRepoPackages)
                   (GC.allPackages gc)
  lsGC = addPackagesWOBase cfg lsRepo newInGC LS.FromGlobalCache
  filterGCLinked p = do
    points <- LocalCache.doesLinkPointToGlobalCache cfg dir (packageId p)
    return $ if points
      then Nothing
      else Just p

--- Resolves dependencies for a package.
resolveDependenciesForPackage :: Config -> Package -> Repository
                              -> GC.GlobalCache
                              -> ErrorLogger ResolutionResult
resolveDependenciesForPackage cfg pkg repo gc =
  resolveDependenciesFromLookupSet cfg (setBaseDependency cfg pkg) lookupSet
 where
  lsRepo = addPackagesWOBase cfg LS.emptySet (allPackages repo)
                             LS.FromRepository
  -- Find all packages that are in the global cache, but not in the repo
  newInGC = filter inGCButNotInRepo $ GC.allPackages gc
  inGCButNotInRepo p = not $ any (packageIdEq p) (allPackages repo)
  lookupSet = addPackagesWOBase cfg lsRepo newInGC LS.FromGlobalCache

--- Acquires a package and its dependencies and installs them to the global
--- package cache.
acquireAndInstallPackageWithDependencies :: Config -> Repository -> Package
                                         -> ErrorLogger ()
acquireAndInstallPackageWithDependencies cfg repo pkg = do
  gc     <- GC.readGlobalCache cfg repo
  result <- resolveDependenciesForPackage cfg pkg repo gc
  GC.installMissingDependencies cfg gc (resolvedPackages result)
  GC.acquireAndInstallPackage cfg pkg

--- Links the dependencies of a package to its local cache and copies them to
--- its runtime cache. Returns the package specifications of the dependencies.
copyDependencies :: Config -> Package -> [Package] -> String 
                 -> ErrorLogger [Package]
copyDependencies cfg pkg pkgs dir = do
  LocalCache.linkPackages cfg dir pkgs
  pkgspecs <- RuntimeCache.copyPackages cfg pkgs' dir
  return $ if pkg `elem` pkgs then pkg : pkgspecs else pkgspecs
 where 
  pkgs' = filter (/= pkg) pkgs

--- Upgrades all dependencies of a package copy.
upgradeAllPackages :: Config -> String -> ErrorLogger ()
upgradeAllPackages cfg dir = do
  pkgspec <- loadPackageSpec dir
  LocalCache.clearCache dir
  (_,deps) <- installLocalDependencies cfg dir
  copyDependencies cfg pkgspec deps dir
  return ()

--- Upgrades a single package and its transitive dependencies.
upgradeSinglePackage :: Config -> String -> String -> ErrorLogger ()
upgradeSinglePackage cfg dir pkgName = do
  pkgspec <- loadPackageSpec dir
  repo    <- getRepoForPackageSpec cfg pkgspec
  gc      <- GC.readGlobalCache cfg repo
  originalLS <- lookupSetForPackageCopy cfg pkgspec repo gc dir
  let transitiveDeps = pkgName : allTransitiveDependencies originalLS pkgName
  result <- resolveDependenciesFromLookupSet cfg (setBaseDependency cfg pkgspec)
                        (LS.setLocallyIgnored originalLS transitiveDeps)
  GC.installMissingDependencies cfg gc (resolvedPackages result)
  logInfo (showShortDependencies result)
  copyDependencies cfg pkgspec (resolvedPackages result) dir
  return ()

--- Installs the dependencies of a package.
installLocalDependencies :: Config -> String
                         -> ErrorLogger (Package,[Package])
installLocalDependencies cfg dir = do
  pkgSpec <- loadPackageSpec dir
  repo    <- getRepoForPackageSpec cfg pkgSpec
  installLocalDependenciesWithRepo cfg repo dir pkgSpec

--- Installs the dependencies of a package.
installLocalDependenciesWithRepo :: Config -> Repository -> String -> Package
                                 -> ErrorLogger (Package,[Package])
installLocalDependenciesWithRepo cfg repo dir pkgSpec = do
  gc <- GC.readGlobalCache cfg repo
  result <- resolveDependenciesForPackageCopy cfg pkgSpec repo gc dir
  GC.installMissingDependencies cfg gc (resolvedPackages result)
  logInfo (showShortDependencies result)
  cpkgs <- copyDependencies cfg pkgSpec (resolvedPackages result) dir
  return (pkgSpec, cpkgs)

--- Links a directory into the local package cache. Used for `cypm link`.
linkToLocalCache :: Config -> String -> String -> ErrorLogger ()
linkToLocalCache cfg src pkgDir = do
  dirExists <- liftIOEL $ doesDirectoryExist src
  if dirExists
    then do
      pkgSpec <- loadPackageSpec src
      mbp <- getPackageVersion cfg (name pkgSpec) (version pkgSpec)
      maybe (logCritical $
               "Package '" ++ packageId pkgSpec ++ "' not in repository!\n" ++
               "Note: you can only link copies of existing packages.")
            (\_ -> do LocalCache.createLink pkgDir src
                                (packageId pkgSpec) True
                      return ())
            mbp
    else logCritical $ "Directory '" ++ src ++ "' does not exist."

--- Resolves the dependencies for a package copy and fills the package caches.
resolveAndCopyDependencies :: Config -> Repository -> GC.GlobalCache -> String 
                           -> ErrorLogger [Package]
resolveAndCopyDependencies cfg repo gc dir = do
  pkgspec <- loadPackageSpec dir
  resolveAndCopyDependenciesForPackage' cfg repo gc dir pkgspec

--- Resolves the dependencies for a package copy and fills the package caches.
resolveAndCopyDependenciesForPackage ::
     Config -> String -> Package -> ErrorLogger [Package]
resolveAndCopyDependenciesForPackage cfg dir pkgSpec = do
  repo <- getRepoForPackageSpec cfg pkgSpec
  gc   <- GC.readGlobalCache cfg repo
  resolveAndCopyDependenciesForPackage' cfg repo gc dir pkgSpec

resolveAndCopyDependenciesForPackage' ::
     Config -> Repository -> GC.GlobalCache -> String -> Package
  -> ErrorLogger [Package]
resolveAndCopyDependenciesForPackage' cfg repo gc dir pkgSpec = do
  result <- resolveDependenciesForPackageCopy cfg pkgSpec repo gc dir
  let deps = resolvedPackages result
      missingDeps = GC.missingPackages gc deps 
      failMsg = "Missing dependencies " 
                ++ (intercalate "," $ map packageId missingDeps) 
                ++ "\nUse `cypm install` to install missing dependencies."
  if null missingDeps
    then copyDependencies cfg pkgSpec deps dir
    else fail failMsg

--- Resolves the dependencies for a package copy.
resolveDependencies :: Config -> String -> ErrorLogger ResolutionResult
resolveDependencies cfg dir = do
  pkgSpec <- loadPackageSpec dir
  logInfo $ "Read package spec from " ++ dir
  repo <- getRepoForPackageSpec cfg pkgSpec
  gc <- GC.readGlobalCache cfg repo
  resolveDependenciesForPackageCopy cfg pkgSpec repo gc dir

------------------------------------------------------------------------------
--- Sets `base` package dependency in a package to requiring equality
--- with the current `compilerBaseVersion`.
--- Hence, a conflict occurs if some package requires a different version
--- of the `base` package.
setBaseDependency :: Config -> Package -> Package
setBaseDependency cfg pkg =
  pkg { dependencies = map setBase (dependencies pkg) }
 where
  bv = maybe (0,0,0,Nothing) id (readVersion (compilerBaseVersion cfg))

  setBase (Dependency n disj) =
    Dependency n $ if n == "base"
                     then map (\conj -> VExact bv : conj) disj
                     else disj

--- Same as `LS.addPackages` but set the `base` package dependency.
addPackagesWOBase :: Config -> LS.LookupSet -> [Package] -> LS.LookupSource
                  -> LS.LookupSet
addPackagesWOBase cfg ls pkgs src =
  LS.addPackages ls (map (setBaseDependency cfg) pkgs) src

------------------------------------------------------------------------------
types:

              
unsafe:
unsafe due to modules Data.Global System.IO.Unsafe