packages feed

cblrepo-0.4.0: src/Add.hs

{-
 - Copyright 2011 Per Magnus Therning
 -
 - Licensed under the Apache License, Version 2.0 (the "License");
 - you may not use this file except in compliance with the License.
 - You may obtain a copy of the License at
 -
 -     http://www.apache.org/licenses/LICENSE-2.0
 -
 - Unless required by applicable law or agreed to in writing, software
 - distributed under the License is distributed on an "AS IS" BASIS,
 - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 - See the License for the specific language governing permissions and
 - limitations under the License.
 -}

module Add where

import Debug.Trace

-- {{{1 imports
-- {{{2 local
import PkgDB
import Util.Misc

-- {{{2 system
import Codec.Archive.Tar as Tar
import Codec.Compression.GZip as GZip
import Control.Monad
import Control.Monad.Error
import Control.Monad.Reader
import Data.Either
import Data.List
import Data.Maybe
import Data.Version
import Distribution.Compiler
import Distribution.PackageDescription
import Distribution.PackageDescription.Configuration
import Distribution.PackageDescription.Parse
import Distribution.System
import Distribution.Text
import Distribution.Verbosity
import Distribution.Version
import System.Directory
import System.FilePath
import qualified Data.ByteString.Lazy.Char8 as BS
import qualified Distribution.Package as P
import System.Posix.Files
import System.Unix.Directory
import System.Process
import System.Exit
import System.IO

-- {{{1 add
add :: ReaderT Cmds IO ()
add = do
    t <- cfgGet pkgType
    case t of
        GhcPkgT -> addGhc
        DistroPkgT -> addDistro
        RepoPkgT -> addRepo

-- {{{2 Add ghc package
addGhc :: ReaderT Cmds IO ()
addGhc = let
        unpackPkgVer s = (p, v)
            where
                (p, _:v) = span (/= ',') s
    in do
        pkgs <- liftM (map unpackPkgVer) (cfgGet cbls)
        dR <- cfgGet dryRun
        guard $ isJust $ (sequence $ map (simpleParse . snd) pkgs :: Maybe [Version])
        let ps = map (\ (n, v) -> (n, fromJust $ (simpleParse v :: Maybe Version))) pkgs
        dbFn <- cfgGet dbFile
        db <- liftIO $ readDb dbFn
        case doAddGhc db ps of
            Left brkOthrs -> liftIO $ mapM_ printBrksOth brkOthrs
            Right newDb -> liftIO $ unless dR $ saveDb newDb dbFn

doAddGhc db pkgs = let
        canBeAdded db n v = null $ checkDependants db n v
        (_, fails) = partition (\ (n, v) -> canBeAdded db n v) pkgs
        newDb = foldl (\ d (n, v) -> addGhcPkg d n v) db pkgs
        brkOthrs = map (\ (n, v) -> ((n, v), checkDependants db n v)) fails
    in if null fails
        then Right newDb
        else Left brkOthrs

-- {{{2 Add distro package
addDistro :: ReaderT Cmds IO ()
addDistro = let
        unpackPkgVer s = (p, v, r)
            where
                (p, _:s2) = span (/= ',') s
                (v, _:r) = span (/= ',') s2
        getVersion (_, v, _) = simpleParse v :: Maybe Version
    in do
        pkgs <- liftM (map unpackPkgVer) (cfgGet cbls)
        dR <- cfgGet dryRun
        guard $ isJust $ sequence $ map getVersion pkgs
        let ps = map (\ p@(n, v, r) -> (n, fromJust $ getVersion p, r)) pkgs
        dbFn <- cfgGet dbFile
        db <- liftIO $ readDb dbFn
        case doAddDistro db ps of
            Left brkOthrs -> liftIO $ mapM_ printBrksOth brkOthrs
            Right newDb -> liftIO $ unless dR $ saveDb newDb dbFn

doAddDistro db pkgs = let
        canBeAdded db n v = null $ checkDependants db n v
        (_, fails) = partition (\ (n, v, _) -> canBeAdded db n v) pkgs
        newDb = foldl (\ d (n, v, r) -> addDistroPkg d n v r) db pkgs
        brkOthrs = map (\ (n, v, _) -> ((n, v), checkDependants db n v)) fails
    in if null fails
        then Right newDb
        else Left brkOthrs

-- {{{2 Add repo package
addRepo :: ReaderT Cmds IO ()
addRepo = do
    dbFn <- cfgGet dbFile
    db <- liftIO $ readDb dbFn
    pD <- cfgGet patchDir
    cbls <- cfgGet cbls
    dR <- cfgGet dryRun
    genPkgs <- mapM (\ c -> runErrorT $ withTempDirErrT "/tmp/cblrepo." (\ d -> readCabal pD c d)) cbls >>= exitOnErrors
    let pkgNames = map ((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription) genPkgs
    exitOnErrors $ map (Left . (++) "Trying to add base package: ") (filter (maybe False isBasePkg . lookupPkg db) pkgNames)
    let tmpDb = filter (\ p -> not $ pkgName p `elem` pkgNames) db
    case doAddRepo tmpDb genPkgs of
        Left (unSats, brksOthrs) -> liftIO (mapM_ printUnSat unSats >> mapM_ printBrksOth brksOthrs)
        Right newDb -> liftIO $ unless dR $ saveDb newDb dbFn

doAddRepo db pkgs = let
        (succs, fails) = partition (canBeAdded db) pkgs
        newDb = foldl addPkg2 db (map (fromJust . finalizeToCblPkg db) succs)
        unSats = catMaybes $ map (finalizeToDeps db) fails
        genPkgName = ((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription)
        genPkgVer = P.pkgVersion . package . packageDescription
        brksOthrs = filter (not . null . snd) $ map (\ p -> ((genPkgName p, genPkgVer p), checkDependants db (genPkgName p) (genPkgVer p))) fails
    in case (succs, fails) of
        (_, []) -> Right newDb
        ([], _) -> Left (unSats, brksOthrs)
        (_, _) -> doAddRepo newDb fails

canBeAdded db p = let
        finable = either (const False) (const True) (finalizePkg db p)
        n = ((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription) p
        v = P.pkgVersion $ package $ packageDescription p
        depsOK = null $ checkDependants db n v
    in finable && depsOK

finalizeToCblPkg db p = case finalizePkg db p of
    Right (pd, _) -> Just $ createCblPkg pd
    _ -> Nothing

finalizeToDeps db p = case finalizePkg db p of
    Left ds -> Just $ (((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription) p, ds)
    _ -> Nothing