packages feed

module-management-0.20.2: testdata/debian/Debian/Repo/State/AptImage.hs

{-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, TemplateHaskell #-}
{-# OPTIONS -Wall -fno-warn-orphans #-}
module Debian.Repo.State.AptImage
    ( withAptImage
    , aptSourcePackages
    , aptBinaryPackages
    , prepareSource
    ) where

import Control.Applicative ((<$>))
import Control.Exception (SomeException)
import Control.Monad.Catch (MonadCatch, catch)
import Control.Monad.State (StateT)
import Control.Monad.Trans (MonadIO(..), MonadTrans(lift))
import Data.List (sort)
import Data.Maybe (listToMaybe)
import Debian.Changes (ChangeLogEntry(logVersion))
import Debian.Pretty (ppDisplay)
import Debian.Relation (SrcPkgName(unSrcPkgName))
import Debian.Release (ReleaseName)
import Debian.Repo.AptImage (aptDir, aptGetSource, aptGetUpdate)
import Debian.Repo.EnvPath (EnvRoot(rootPath))
import Debian.Repo.Internal.Apt (AptImage(aptImageArch, aptImageRoot, aptImageSources,
                                          aptBinaryPackageCache, aptSourcePackageCache),
                                 cacheRootDir, createAptImage, MonadApt(..), modifyApt)
import Debian.Repo.Internal.Repos (AptKey, evalMonadApt, getAptKey, MonadRepos(..), putAptImage)
import Debian.Repo.PackageID (PackageID(packageName), PackageID(packageVersion))
import Debian.Repo.PackageIndex (BinaryPackage, SourcePackage(sourcePackageID))
import Debian.Repo.Prelude (symbol)
import Debian.Repo.Prelude.Verbosity (qPutStr, qPutStrLn)
import Debian.Repo.Slice (NamedSliceList(sliceList, sliceListName), SliceList, SourcesChangedAction)
import Debian.Repo.SourceTree (DebianBuildTree(debTree'), DebianSourceTree(tree'), HasChangeLog(entry), findDebianBuildTrees, SourceTree(dir'))
import Debian.Repo.State.PackageIndex (binaryPackagesFromSources, sourcePackagesFromSources)
import Debian.Repo.State.Slice (updateCacheSources)
import Debian.Repo.Top (MonadTop)
import Debian.Version (DebianVersion)
import System.Directory (createDirectoryIfMissing)
import System.Unix.Directory (removeRecursiveSafely)

instance MonadApt m => MonadApt (StateT EnvRoot m) where
    getApt = lift getApt
    putApt = lift . putApt

instance MonadRepos m => MonadRepos (StateT AptImage m) where
    getRepos = lift getRepos
    putRepos = lift . putRepos

withAptImage :: (MonadRepos m, MonadTop m) => SourcesChangedAction -> NamedSliceList -> StateT AptImage m a -> m a
withAptImage sourcesChangedAction sources action = prepareAptImage sourcesChangedAction sources >>= evalMonadApt action

-- |Create a skeletal enviroment sufficient to run apt-get.
prepareAptImage :: forall m. (MonadTop m, MonadRepos m) =>
                 SourcesChangedAction	-- What to do if environment already exists and sources.list is different
              -> NamedSliceList		-- The sources.list
              -> m AptKey		-- The resulting environment
prepareAptImage sourcesChangedAction sources = do
  mkey <- getAptKey =<< cacheRootDir (sliceListName sources)
  maybe (prepareAptImage' sourcesChangedAction sources) return mkey

prepareAptImage' :: forall m. (MonadCatch m, MonadTop m, MonadRepos m) => SourcesChangedAction -> NamedSliceList -> m AptKey
prepareAptImage' sourcesChangedAction sources = do
  mkey <- getAptKey =<< cacheRootDir (sliceListName sources)
  maybe (prepareAptImage'' `catch` handle) return mkey
    where
      handle :: SomeException -> m AptKey
      handle e = do
        qPutStrLn ("Exception preparing " ++ (ppDisplay . sliceListName $ sources) ++ ": " ++ show e)
        removeAptImage
        prepareAptImage''
      prepareAptImage'' = do
        qPutStrLn ($(symbol 'prepareAptImage) ++ ": " ++ (ppDisplay . sliceListName $ sources))
        key <- putAptImage =<< createAptImage sources
        evalMonadApt (updateCacheSources sourcesChangedAction sources >> aptGetUpdate) key
        return key
      removeAptImage = cacheRootDir (sliceListName sources) >>= liftIO . removeRecursiveSafely . rootPath

-- |Run apt-get update and then retrieve all the packages referenced
-- by the sources.list.  The source packages are sorted so that
-- packages with the same name are together with the newest first.
-- updateAptEnv :: (MonadRepos m, MonadApt m) => m ()
-- updateAptEnv = aptGetUpdate

aptSourcePackages :: (MonadRepos m, MonadApt m) => m [SourcePackage]
aptSourcePackages = do
  mpkgs <- aptSourcePackageCache <$> getApt
  maybe aptSourcePackages' return mpkgs
    where
      aptSourcePackages' = do
        root <- aptImageRoot <$> getApt
        arch <- aptImageArch <$> getApt
        sources <- aptImageSources <$> getApt
        -- qPutStrLn ($(symbol 'aptSourcePackages) ++ " " ++ show (pretty (sliceListName sources)))
        pkgs <- sourcePackagesFromSources root arch (sliceList sources)
        modifyApt (\ s -> s {aptSourcePackageCache = Just pkgs})
        return pkgs

aptBinaryPackages :: (MonadRepos m, MonadApt m) => m [BinaryPackage]
aptBinaryPackages = do
  mpkgs <- aptBinaryPackageCache <$> getApt
  maybe aptBinaryPackages' return mpkgs
    where
      aptBinaryPackages' = do
        root <- aptImageRoot <$> getApt
        arch <- aptImageArch <$> getApt
        sources <- aptImageSources <$> getApt
        -- qPutStrLn ($(symbol 'aptBinaryPackages) ++ " " ++ show (pretty (sliceListName sources)))
        pkgs <- binaryPackagesFromSources root arch (sliceList sources)
        modifyApt (\ s -> s {aptBinaryPackageCache = Just pkgs})
        return pkgs
{-
    do qPutStrLn "AptImage.getBinaryPackages"
       root <- getL aptImageRoot <$> getApt
       arch <- getL aptImageArch <$> getApt
       sources <- (sliceList . getL aptImageSources) <$> getApt
       binaryPackagesFromSources root arch sources
-}

data UpdateError
    = Changed ReleaseName FilePath SliceList SliceList
    | Missing ReleaseName FilePath
    | Flushed

instance Show UpdateError where
    show (Changed r p l1 l2) = unwords ["Changed", show r, show p, ppDisplay l1, ppDisplay l2]
    show (Missing r p) = unwords ["Missing", show r, show p]
    show Flushed = "Flushed"

-- |Retrieve a source package via apt-get.
prepareSource :: (MonadRepos m, MonadApt m, MonadTop m, MonadIO m) =>
                 SrcPkgName			-- The name of the package
              -> Maybe DebianVersion		-- The desired version, if Nothing get newest
              -> m DebianBuildTree		-- The resulting source tree
prepareSource package version =
    do root <- (rootPath . aptImageRoot) <$> getApt
       dir <- aptDir package
       liftIO $ createDirectoryIfMissing True dir
       ready <- liftIO $ findDebianBuildTrees dir
       version' <- latestVersion package version
       case (version', ready) of
         (Nothing, _) ->
             fail $ "No available versions of " ++ unSrcPkgName package ++ " in " ++ root
         (Just requested, [tree])
             | requested == (logVersion . entry $ tree) ->
                 return tree
         (Just requested, []) ->
             do aptGetSource dir [(package, Just requested)]
                trees <- liftIO $ findDebianBuildTrees dir
                case trees of
                  [tree] -> return tree
                  _ -> fail $ "apt-get source failed in " ++ dir ++ " (1): trees=" ++ show (map (dir' . tree' . debTree') trees)
         (Just requested, _) ->
             do -- One or more incorrect versions are available, remove them
                liftIO $ removeRecursiveSafely dir
                qPutStr $ "Retrieving APT source for " ++ unSrcPkgName package
                aptGetSource dir [(package, Just requested)]
                trees <- liftIO $ findDebianBuildTrees dir
                case trees of
                  [tree] -> return tree
                  _ -> fail $ "apt-get source failed (2): trees=" ++ show (map (dir' . tree' . debTree') trees)

-- | Find the most recent version of a source package.
latestVersion :: (MonadRepos m, MonadApt m) => SrcPkgName -> Maybe DebianVersion -> m (Maybe DebianVersion)
latestVersion package exact = do
  pkgs <- aptSourcePackages
  let versions = map (packageVersion . sourcePackageID) $ filter ((== package) . packageName . sourcePackageID) $ pkgs
      newest = listToMaybe $ reverse $ sort $ versions
  return $ maybe newest Just exact